[Lldb-commits] [lldb] [lldb-dap] Add an option to show function args in stack frames (PR #71843)
Walter Erquinigo via lldb-commits
lldb-commits at lists.llvm.org
Thu Nov 9 10:57:13 PST 2023
https://github.com/walter-erquinigo created https://github.com/llvm/llvm-project/pull/71843
When this option is enabled, display names of stack frames are generated using the `${function.name-with-args}` formatter instead of simply calling `SBFrame::GetDisplayFunctionName`. This makes lldb-dap show an output similar to the one in the CLI.
This option is disabled by default because of its performance cost. It's a good option for non-gigantic programs.
>From 233aade4eb058ad2eba04334b0bbd96f5307ac48 Mon Sep 17 00:00:00 2001
From: walter erquinigo <walter at modular.com>
Date: Thu, 9 Nov 2023 13:15:55 -0500
Subject: [PATCH] [lldb-dap] Add an option to show function args in stack
frames
When this option is enabled, display names of stack frames are generated using the `${function.name-with-args}` formatter instead of simply calling `SBFrame::GetDisplayFunctionName`. This makes lldb-dap show an output similar to the one in the CLI.
This option is disabled by default because of its performance cost. It's a good option for non-gigantic programs.
---
lldb/include/lldb/API/SBFrame.h | 9 ++++-
lldb/include/lldb/Target/StackFrame.h | 18 ++++++++-
.../test/tools/lldb-dap/dap_server.py | 2 +
.../test/tools/lldb-dap/lldbdap_testcase.py | 4 ++
lldb/source/API/SBFrame.cpp | 38 ++++++++++++++++---
lldb/source/Target/StackFrame.cpp | 30 +++++++++------
.../lldb-dap/stackTrace/TestDAP_stackTrace.py | 23 +++++++++--
lldb/tools/lldb-dap/DAP.h | 1 +
lldb/tools/lldb-dap/JSONUtils.cpp | 17 ++++++---
lldb/tools/lldb-dap/lldb-dap.cpp | 4 ++
lldb/tools/lldb-dap/package.json | 10 +++++
11 files changed, 130 insertions(+), 26 deletions(-)
diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h
index 7c4477f9125d1cd..75e04d794baf848 100644
--- a/lldb/include/lldb/API/SBFrame.h
+++ b/lldb/include/lldb/API/SBFrame.h
@@ -87,8 +87,15 @@ class LLDB_API SBFrame {
// display to a user
const char *GetDisplayFunctionName();
+ /// Similar to \a GetDisplayFunctionName() but with function arguments and
+ /// their values inserted into the function display name whenever possible.
+ ///
+ /// \param[in] output
+ /// The stream where the display name is written to.
+ void GetDisplayFunctionNameWithArgs(SBStream &output);
+
const char *GetFunctionName() const;
-
+
// Return the frame function's language. If there isn't a function, then
// guess the language type from the mangled name.
lldb::LanguageType GuessLanguage() const;
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 6824d916030a024..7b8510dfb4b40c6 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -14,6 +14,7 @@
#include "lldb/Utility/Flags.h"
+#include "lldb/Core/FormatEntity.h"
#include "lldb/Core/ValueObjectList.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/ExecutionContextScope.h"
@@ -324,8 +325,23 @@ class StackFrame : public ExecutionContextScope,
/// C string with the assembly instructions for this function.
const char *Disassemble();
+ /// Print a description of this frame using the provided frame format.
+ /// If the format is invalid, then the default formatter will be used (see \a
+ /// StackFrame::Dump()), in which case \b false is returned. Otherwise, \b
+ /// true is returned.
+ ///
+ /// \param[in] strm
+ /// The Stream to print the description to.
+ ///
+ /// \param[in] frame_marker
+ /// Optional string that will be prepended to the frame output description.
+ bool DumpUsingFormat(Stream &strm,
+ const lldb_private::FormatEntity::Entry *format,
+ llvm::StringRef frame_marker = {});
+
/// Print a description for this frame using the frame-format formatter
- /// settings.
+ /// settings. If the current frame-format settings are invalid, then the
+ /// default formatter will be used (see \a StackFrame::Dump()).
///
/// \param [in] strm
/// The Stream to print the description to.
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index d1fb478bc8bb9ee..0c305fdd1ad9bbe 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -732,6 +732,7 @@ def request_launch(
enableAutoVariableSummaries=False,
enableSyntheticChildDebugging=False,
commandEscapePrefix="`",
+ showFramesWithFunctionArgs=False,
):
args_dict = {"program": program}
if args:
@@ -773,6 +774,7 @@ def request_launch(
args_dict["runInTerminal"] = runInTerminal
if postRunCommands:
args_dict["postRunCommands"] = postRunCommands
+ args_dict["showFramesWithFunctionArgs"] = showFramesWithFunctionArgs
args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging
args_dict["commandEscapePrefix"] = commandEscapePrefix
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index aa89ffe24c3e026..0775922500ea4a8 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -352,6 +352,7 @@ def launch(
enableAutoVariableSummaries=False,
enableSyntheticChildDebugging=False,
commandEscapePrefix="`",
+ showFramesWithFunctionArgs=False,
):
"""Sending launch request to dap"""
@@ -391,6 +392,7 @@ def cleanup():
enableAutoVariableSummaries=enableAutoVariableSummaries,
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
commandEscapePrefix=commandEscapePrefix,
+ showFramesWithFunctionArgs=showFramesWithFunctionArgs,
)
if expectFailure:
@@ -428,6 +430,7 @@ def build_and_launch(
enableAutoVariableSummaries=False,
enableSyntheticChildDebugging=False,
commandEscapePrefix="`",
+ showFramesWithFunctionArgs=False,
):
"""Build the default Makefile target, create the DAP debug adaptor,
and launch the process.
@@ -459,4 +462,5 @@ def build_and_launch(
enableAutoVariableSummaries=enableAutoVariableSummaries,
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
commandEscapePrefix=commandEscapePrefix,
+ showFramesWithFunctionArgs=showFramesWithFunctionArgs,
)
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp
index da5c6075e8f7b4b..814966f966ef7f2 100644
--- a/lldb/source/API/SBFrame.cpp
+++ b/lldb/source/API/SBFrame.cpp
@@ -601,8 +601,8 @@ SBValue SBFrame::FindValue(const char *name, ValueType value_type,
stop_if_block_is_inlined_function,
[frame](Variable *v) { return v->IsInScope(frame); },
&variable_list);
- if (value_type == eValueTypeVariableGlobal
- || value_type == eValueTypeVariableStatic) {
+ if (value_type == eValueTypeVariableGlobal ||
+ value_type == eValueTypeVariableStatic) {
const bool get_file_globals = true;
VariableList *frame_vars = frame->GetVariableList(get_file_globals,
nullptr);
@@ -814,9 +814,11 @@ SBValueList SBFrame::GetVariables(const lldb::SBVariablesOptions &options) {
if (num_variables) {
size_t num_produced = 0;
for (const VariableSP &variable_sp : *variable_list) {
- if (INTERRUPT_REQUESTED(dbg,
- "Interrupted getting frame variables with {0} of {1} "
- "produced.", num_produced, num_variables))
+ if (INTERRUPT_REQUESTED(
+ dbg,
+ "Interrupted getting frame variables with {0} of {1} "
+ "produced.",
+ num_produced, num_variables))
return {};
if (variable_sp) {
@@ -1232,6 +1234,32 @@ const char *SBFrame::GetFunctionName() const {
return name;
}
+void SBFrame::GetDisplayFunctionNameWithArgs(SBStream &output) {
+ Stream &strm = output.ref();
+
+ std::unique_lock<std::recursive_mutex> lock;
+ ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+ StackFrame *frame = nullptr;
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+
+ if (target && process) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process->GetRunLock())) {
+ frame = exe_ctx.GetFramePtr();
+ if (frame) {
+ FormatEntity::Entry format;
+ Status s = FormatEntity::Parse("${function.name-with-args}", format);
+ assert(
+ s.Success() &&
+ "The ${function.name-with-args} format must be parsed correctly");
+ frame->DumpUsingFormat(strm, &format);
+ }
+ }
+ }
+}
+
const char *SBFrame::GetDisplayFunctionName() {
LLDB_INSTRUMENT_VA(this);
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 11ada92348ecee2..71e7f356245f1c3 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -1779,18 +1779,32 @@ void StackFrame::CalculateExecutionContext(ExecutionContext &exe_ctx) {
exe_ctx.SetContext(shared_from_this());
}
+bool StackFrame::DumpUsingFormat(Stream &strm,
+ const FormatEntity::Entry *format,
+ llvm::StringRef frame_marker) {
+ GetSymbolContext(eSymbolContextEverything);
+ ExecutionContext exe_ctx(shared_from_this());
+ StreamString s;
+ s.PutCString(frame_marker);
+
+ if (format && FormatEntity::Format(*format, s, &m_sc, &exe_ctx, nullptr,
+ nullptr, false, false)) {
+ strm.PutCString(s.GetString());
+ return true;
+ }
+ Dump(&strm, true, false);
+ strm.EOL();
+ return false;
+}
+
void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
const char *frame_marker) {
if (strm == nullptr)
return;
- GetSymbolContext(eSymbolContextEverything);
ExecutionContext exe_ctx(shared_from_this());
StreamString s;
- if (frame_marker)
- s.PutCString(frame_marker);
-
const FormatEntity::Entry *frame_format = nullptr;
Target *target = exe_ctx.GetTargetPtr();
if (target) {
@@ -1800,13 +1814,7 @@ void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
frame_format = target->GetDebugger().GetFrameFormat();
}
}
- if (frame_format && FormatEntity::Format(*frame_format, s, &m_sc, &exe_ctx,
- nullptr, nullptr, false, false)) {
- strm->PutCString(s.GetString());
- } else {
- Dump(strm, true, false);
- strm->EOL();
- }
+ DumpUsingFormat(*strm, frame_format, frame_marker);
}
void StackFrame::Dump(Stream *strm, bool show_frame_index,
diff --git a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py
index 245b3f34b70c868..153dd77169b3a8d 100644
--- a/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py
+++ b/lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py
@@ -3,12 +3,13 @@
"""
+import os
+
import dap_server
+import lldbdap_testcase
+from lldbsuite.test import lldbutil
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-import lldbdap_testcase
-import os
class TestDAP_stackTrace(lldbdap_testcase.DAPTestCaseBase):
@@ -187,3 +188,19 @@ def test_stackTrace(self):
self.assertEquals(
0, len(stackFrames), "verify zero frames with startFrame out of bounds"
)
+
+ @skipIfWindows
+ @skipIfRemote
+ def test_functionNameWithArgs(self):
+ """
+ Test that the stack frame without a function name is given its pc in the response.
+ """
+ program = self.getBuildArtifact("a.out")
+ self.build_and_launch(program, showFramesWithFunctionArgs=True)
+ source = "main.c"
+
+ self.set_source_breakpoints(source, [line_number(source, "recurse end")])
+
+ self.continue_to_next_stop()
+ frame = self.get_stackFrames()[0]
+ self.assertEquals(frame["name"], "recurse(x=1)")
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index b00c103c33b7a92..765e64a321f1530 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -189,6 +189,7 @@ struct DAP {
ReplMode repl_mode;
bool auto_repl_mode_collision_warning;
std::string command_escape_prefix = "`";
+ bool show_frames_with_function_args = false;
DAP();
~DAP();
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 2ff17616c2e9986..4dcd792b3ec4213 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -785,11 +785,18 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
int64_t frame_id = MakeDAPFrameID(frame);
object.try_emplace("id", frame_id);
- // `function_name` can be a nullptr, which throws an error when assigned to an
- // `std::string`.
- const char *function_name = frame.GetDisplayFunctionName();
- std::string frame_name =
- function_name == nullptr ? std::string() : function_name;
+ std::string frame_name;
+ if (g_dap.show_frames_with_function_args) {
+ lldb::SBStream stream;
+ frame.GetDisplayFunctionNameWithArgs(stream);
+ frame_name = stream.GetData();
+ } else {
+ // `function_name` can be a nullptr, which throws an error when assigned to
+ // an `std::string`.
+ if (const char *name = frame.GetDisplayFunctionName())
+ frame_name = name;
+ }
+
if (frame_name.empty()) {
// If the function name is unavailable, display the pc address as a 16-digit
// hex string, e.g. "0x0000000000012345"
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index e103aabb870207f..3b8aa7851274e1d 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -653,6 +653,8 @@ void request_attach(const llvm::json::Object &request) {
GetBoolean(arguments, "enableSyntheticChildDebugging", false);
g_dap.command_escape_prefix =
GetString(arguments, "commandEscapePrefix", "`");
+ g_dap.show_frames_with_function_args =
+ GetBoolean(arguments, "showFramesWithFunctionArgs", false);
// This is a hack for loading DWARF in .o files on Mac where the .o files
// in the debug map of the main executable have relative paths which require
@@ -1805,6 +1807,8 @@ void request_launch(const llvm::json::Object &request) {
GetBoolean(arguments, "enableSyntheticChildDebugging", false);
g_dap.command_escape_prefix =
GetString(arguments, "commandEscapePrefix", "`");
+ g_dap.show_frames_with_function_args =
+ GetBoolean(arguments, "showFramesWithFunctionArgs", false);
// This is a hack for loading DWARF in .o files on Mac where the .o files
// in the debug map of the main executable have relative paths which require
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index a0ae7ac834939d5..753887a8ef0161c 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -255,6 +255,11 @@
"type": "string",
"description": "The escape prefix to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If it's an empty string, then all expression in the Debug Console are treated as regular LLDB commands.",
"default": "`"
+ },
+ "showFramesWithFunctionArgs": {
+ "type": "boolean",
+ "description": "When enabled, show function arguments along with their corresponding values in the stack frames. This comes with a performance cost because debug information needs to be processed to generate such values.",
+ "default": "false"
}
}
},
@@ -349,6 +354,11 @@
"type": "string",
"description": "The escape prefix character to use for executing regular LLDB commands in the Debug Console, instead of printing variables. Defaults to a back-tick (`). If empty, then all expression in the Debug Console are treated as regular LLDB commands.",
"default": "`"
+ },
+ "showFramesWithFunctionArgs": {
+ "type": "boolean",
+ "description": "When enabled, show function arguments along with their corresponding values in the stack frames. This comes with a performance cost because debug information needs to be processed to generate such values.",
+ "default": "false"
}
}
}
More information about the lldb-commits
mailing list