Skip to content

[lldb-dap] Add 'source' references to stack frames without source files. #128268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 24, 2025

Conversation

ashgti
Copy link
Contributor

@ashgti ashgti commented Feb 22, 2025

This adds 'source' references to all stack frames. When opening a stack frame users will see the disassembly of the frame if the source is not available.

This works around the odd behavior of navigating frames without the VSCode disassembly view open, which causes 'step' to step in the first frame with a source instead of the active frame.

This fixes #128260

Old behavior:
https://github.com/user-attachments/assets/3f40582d-ac96-451a-a5ae-498a323bf30e

New behavior:
https://github.com/user-attachments/assets/3a3f9ac6-3e6c-4795-9bb2-1132b3916b6f

@llvmbot
Copy link
Member

llvmbot commented Feb 22, 2025

@llvm/pr-subscribers-lldb

Author: John Harrison (ashgti)

Changes

This adds 'source' references to all stack frames. When opening a stack frame users will see the disassembly of the frame if the source is not available.

This works around the odd behavior of navigating frames without the VSCode disassembly view open, which causes 'step' to step in the first frame with a source instead of the active frame.

This fixes #128260

Old behavior:
https://github.com/user-attachments/assets/3f40582d-ac96-451a-a5ae-498a323bf30e

New behavior:
https://github.com/user-attachments/assets/3a3f9ac6-3e6c-4795-9bb2-1132b3916b6f


Full diff: https://github.com/llvm/llvm-project/pull/128268.diff

2 Files Affected:

  • (modified) lldb/tools/lldb-dap/JSONUtils.cpp (+23-15)
  • (modified) lldb/tools/lldb-dap/lldb-dap.cpp (+25-3)
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 6ca4dfb4711a1..ee8fcef6f2503 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -45,7 +45,6 @@
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/raw_ostream.h"
 #include <chrono>
-#include <climits>
 #include <cstddef>
 #include <iomanip>
 #include <optional>
@@ -698,14 +697,22 @@ llvm::json::Value CreateSource(llvm::StringRef source_path) {
   return llvm::json::Value(std::move(source));
 }
 
-static std::optional<llvm::json::Value> CreateSource(lldb::SBFrame &frame) {
+static llvm::json::Value CreateSource(lldb::SBFrame &frame,
+                                      llvm::StringRef frame_name) {
   auto line_entry = frame.GetLineEntry();
   // A line entry of 0 indicates the line is compiler generated i.e. no source
   // file is associated with the frame.
   if (line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0)
     return CreateSource(line_entry);
 
-  return {};
+  llvm::json::Object source;
+  EmplaceSafeString(source, "name", frame_name);
+  source.try_emplace("sourceReference", MakeDAPFrameID(frame));
+  // If we don't have a filespec then we don't have the original source. Mark
+  // the source as deemphasized since users will only be able to view assembly
+  // for these frames.
+  EmplaceSafeString(source, "presentationHint", "deemphasize");
+  return std::move(source);
 }
 
 // "StackFrame": {
@@ -799,21 +806,22 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
 
   EmplaceSafeString(object, "name", frame_name);
 
-  auto source = CreateSource(frame);
-
-  if (source) {
-    object.try_emplace("source", *source);
-    auto line_entry = frame.GetLineEntry();
-    auto line = line_entry.GetLine();
-    if (line && line != LLDB_INVALID_LINE_NUMBER)
-      object.try_emplace("line", line);
-    else
-      object.try_emplace("line", 0);
+  object.try_emplace("source", CreateSource(frame, frame_name));
+  auto line_entry = frame.GetLineEntry();
+  if (line_entry.IsValid() &&
+      (line_entry.GetLine() != 0 ||
+       line_entry.GetLine() != LLDB_INVALID_LINE_NUMBER)) {
+    object.try_emplace("line", line_entry.GetLine());
     auto column = line_entry.GetColumn();
     object.try_emplace("column", column);
   } else {
-    object.try_emplace("line", 0);
-    object.try_emplace("column", 0);
+    lldb::addr_t inst_offset = frame.GetPCAddress().GetOffset() -
+                               frame.GetSymbol().GetStartAddress().GetOffset();
+    lldb::addr_t inst_line =
+        inst_offset / (frame.GetThread().GetProcess().GetAddressByteSize() / 2);
+    // lines are base-1 indexed
+    object.try_emplace("line", inst_line + 1);
+    object.try_emplace("column", 1);
   }
 
   const auto pc = frame.GetPC();
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index e323990d8b6ed..1da8358eb5224 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -13,7 +13,9 @@
 #include "RunInTerminal.h"
 #include "Watchpoint.h"
 #include "lldb/API/SBDeclaration.h"
+#include "lldb/API/SBDefines.h"
 #include "lldb/API/SBEvent.h"
+#include "lldb/API/SBFile.h"
 #include "lldb/API/SBInstruction.h"
 #include "lldb/API/SBListener.h"
 #include "lldb/API/SBMemoryRegionInfo.h"
@@ -38,9 +40,6 @@
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <array>
-#include <cassert>
-#include <climits>
-#include <cstdarg>
 #include <cstdint>
 #include <cstdio>
 #include <cstdlib>
@@ -3488,6 +3487,29 @@ void request_source(DAP &dap, const llvm::json::Object &request) {
   llvm::json::Object response;
   FillResponse(request, response);
   llvm::json::Object body{{"content", ""}};
+
+  const auto *arguments = request.getObject("arguments");
+  const auto *source = arguments->getObject("source");
+  int64_t source_ref = GetUnsigned(
+      source, "sourceReference", GetUnsigned(arguments, "sourceReference", 0));
+
+  lldb::SBProcess process = dap.target.GetProcess();
+  // Upper 32 bits is the thread index ID
+  lldb::SBThread thread =
+      process.GetThreadByIndexID(GetLLDBThreadIndexID(source_ref));
+  // Lower 32 bits is the frame index
+  lldb::SBFrame frame = thread.GetFrameAtIndex(GetLLDBFrameID(source_ref));
+  if (!frame.IsValid()) {
+    response["success"] = false;
+    response["message"] = "source not found";
+  } else {
+    lldb::SBInstructionList insts = frame.GetSymbol().GetInstructions(dap.target);
+    lldb::SBStream stream;
+    insts.GetDescription(stream);
+    body["content"] = stream.GetData();
+    body["mimeType"] = "text/x-lldb.disassembly";
+  }
+
   response.try_emplace("body", std::move(body));
   dap.SendJSON(llvm::json::Value(std::move(response)));
 }

Copy link

github-actions bot commented Feb 22, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need a test.

@@ -698,14 +697,22 @@ llvm::json::Value CreateSource(llvm::StringRef source_path) {
return llvm::json::Value(std::move(source));
}

static std::optional<llvm::json::Value> CreateSource(lldb::SBFrame &frame) {
static llvm::json::Value CreateSource(lldb::SBFrame &frame,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this function is used elsewhere? The line number and column have a different meaning based on whether we have source info and having the logic spread across this function and CreateStackFrame seems like a pretty big foot gun. Any chance we can localize this logic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merged this into the only place that called the helper (CreateStackFrame) to make sure its more clear.

Copy link

github-actions bot commented Feb 23, 2025

✅ With the latest revision this PR passed the Python code formatter.

@labath
Copy link
Collaborator

labath commented Feb 24, 2025

Could we make the test self contained (i.e. without the qsort reference)? If all you need is a function without debug info, then __attribute__((nodebug)) should suffice.

@ashgti
Copy link
Contributor Author

ashgti commented Feb 24, 2025

Could we make the test self contained (i.e. without the qsort reference)? If all you need is a function without debug info, then __attribute__((nodebug)) should suffice.

Thanks for the suggestion! That works a lot more consistently than depending on the implementation details of libc's qsort.

@ashgti ashgti requested a review from labath February 24, 2025 17:35
Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

ashgti and others added 8 commits February 24, 2025 14:06
This adds 'source' references to all stack frames. When opening a stack frame users will see the disassembly of the frame if the source is not available.

This works around the odd behavior of navigating frames without the VSCode disassembly view open, which causes 'step' to step in the first frame with a source instead of the active frame.
Co-authored-by: Jonas Devlieghere <[email protected]>
…bly formatted like '<module>`<symbol>' for example '/usr/lib/system/libsystem_c.dylib`_qsort'.
…y stack frames without sources return disassembly.
@ashgti
Copy link
Contributor Author

ashgti commented Feb 24, 2025

Pulled and rebased my changes on the request handler refactor.

@ashgti ashgti merged commit 162eb32 into llvm:main Feb 24, 2025
10 checks passed
@ashgti ashgti deleted the lldb-dap-source branch February 24, 2025 22:56
@JDevlieghere
Copy link
Member

Totally optional & I didn't think about it earlier, but this seems like something you might want to advertise through a release note.

SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Mar 8, 2025
…es. (llvm#128268)

This adds 'source' references to all stack frames. When opening a stack
frame users will see the disassembly of the frame if the source is not
available.

This works around the odd behavior of navigating frames without the
VSCode disassembly view open, which causes 'step' to step in the first
frame with a source instead of the active frame.

This fixes llvm#128260

Old behavior:

https://github.com/user-attachments/assets/3f40582d-ac96-451a-a5ae-498a323bf30e

New behavior:

https://github.com/user-attachments/assets/3a3f9ac6-3e6c-4795-9bb2-1132b3916b6f

---------

Co-authored-by: Jonas Devlieghere <[email protected]>
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Mar 20, 2025
…es. (llvm#128268)

This adds 'source' references to all stack frames. When opening a stack
frame users will see the disassembly of the frame if the source is not
available.

This works around the odd behavior of navigating frames without the
VSCode disassembly view open, which causes 'step' to step in the first
frame with a source instead of the active frame.

This fixes llvm#128260

Old behavior:

https://github.com/user-attachments/assets/3f40582d-ac96-451a-a5ae-498a323bf30e

New behavior:

https://github.com/user-attachments/assets/3a3f9ac6-3e6c-4795-9bb2-1132b3916b6f

---------

Co-authored-by: Jonas Devlieghere <[email protected]>
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Apr 2, 2025
…es. (llvm#128268)

This adds 'source' references to all stack frames. When opening a stack
frame users will see the disassembly of the frame if the source is not
available.

This works around the odd behavior of navigating frames without the
VSCode disassembly view open, which causes 'step' to step in the first
frame with a source instead of the active frame.

This fixes llvm#128260

Old behavior:

https://github.com/user-attachments/assets/3f40582d-ac96-451a-a5ae-498a323bf30e

New behavior:

https://github.com/user-attachments/assets/3a3f9ac6-3e6c-4795-9bb2-1132b3916b6f

---------

Co-authored-by: Jonas Devlieghere <[email protected]>
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Apr 17, 2025
…es. (llvm#128268)

This adds 'source' references to all stack frames. When opening a stack
frame users will see the disassembly of the frame if the source is not
available.

This works around the odd behavior of navigating frames without the
VSCode disassembly view open, which causes 'step' to step in the first
frame with a source instead of the active frame.

This fixes llvm#128260

Old behavior:

https://github.com/user-attachments/assets/3f40582d-ac96-451a-a5ae-498a323bf30e

New behavior:

https://github.com/user-attachments/assets/3a3f9ac6-3e6c-4795-9bb2-1132b3916b6f

---------

Co-authored-by: Jonas Devlieghere <[email protected]>
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Apr 30, 2025
…es. (llvm#128268)

This adds 'source' references to all stack frames. When opening a stack
frame users will see the disassembly of the frame if the source is not
available.

This works around the odd behavior of navigating frames without the
VSCode disassembly view open, which causes 'step' to step in the first
frame with a source instead of the active frame.

This fixes llvm#128260

Old behavior:

https://github.com/user-attachments/assets/3f40582d-ac96-451a-a5ae-498a323bf30e

New behavior:

https://github.com/user-attachments/assets/3a3f9ac6-3e6c-4795-9bb2-1132b3916b6f

---------

Co-authored-by: Jonas Devlieghere <[email protected]>
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request May 15, 2025
…es. (llvm#128268)

This adds 'source' references to all stack frames. When opening a stack
frame users will see the disassembly of the frame if the source is not
available.

This works around the odd behavior of navigating frames without the
VSCode disassembly view open, which causes 'step' to step in the first
frame with a source instead of the active frame.

This fixes llvm#128260

Old behavior:

https://github.com/user-attachments/assets/3f40582d-ac96-451a-a5ae-498a323bf30e

New behavior:

https://github.com/user-attachments/assets/3a3f9ac6-3e6c-4795-9bb2-1132b3916b6f

---------

Co-authored-by: Jonas Devlieghere <[email protected]>
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request May 29, 2025
…es. (llvm#128268)

This adds 'source' references to all stack frames. When opening a stack
frame users will see the disassembly of the frame if the source is not
available.

This works around the odd behavior of navigating frames without the
VSCode disassembly view open, which causes 'step' to step in the first
frame with a source instead of the active frame.

This fixes llvm#128260

Old behavior:

https://github.com/user-attachments/assets/3f40582d-ac96-451a-a5ae-498a323bf30e

New behavior:

https://github.com/user-attachments/assets/3a3f9ac6-3e6c-4795-9bb2-1132b3916b6f

---------

Co-authored-by: Jonas Devlieghere <[email protected]>
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Jun 13, 2025
…es. (llvm#128268)

This adds 'source' references to all stack frames. When opening a stack
frame users will see the disassembly of the frame if the source is not
available.

This works around the odd behavior of navigating frames without the
VSCode disassembly view open, which causes 'step' to step in the first
frame with a source instead of the active frame.

This fixes llvm#128260

Old behavior:

https://github.com/user-attachments/assets/3f40582d-ac96-451a-a5ae-498a323bf30e

New behavior:

https://github.com/user-attachments/assets/3a3f9ac6-3e6c-4795-9bb2-1132b3916b6f

---------

Co-authored-by: Jonas Devlieghere <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[lldb-dap] Stepping into a frame without sources can be frustrating
4 participants