llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Walter Erquinigo (walter-erquinigo) <details> <summary>Changes</summary> 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. --- Full diff: https://github.com/llvm/llvm-project/pull/71843.diff 11 Files Affected: - (modified) lldb/include/lldb/API/SBFrame.h (+8-1) - (modified) lldb/include/lldb/Target/StackFrame.h (+17-1) - (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py (+2) - (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py (+4) - (modified) lldb/source/API/SBFrame.cpp (+33-5) - (modified) lldb/source/Target/StackFrame.cpp (+19-11) - (modified) lldb/test/API/tools/lldb-dap/stackTrace/TestDAP_stackTrace.py (+20-3) - (modified) lldb/tools/lldb-dap/DAP.h (+1) - (modified) lldb/tools/lldb-dap/JSONUtils.cpp (+12-5) - (modified) lldb/tools/lldb-dap/lldb-dap.cpp (+4) - (modified) lldb/tools/lldb-dap/package.json (+10) ``````````diff 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" } } } `````````` </details> https://github.com/llvm/llvm-project/pull/71843 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits