Author: Charles Zablit Date: 2026-02-16T15:49:34+01:00 New Revision: 5d5301dc0cc4a84fb91f103e4afec62de4622e33
URL: https://github.com/llvm/llvm-project/commit/5d5301dc0cc4a84fb91f103e4afec62de4622e33 DIFF: https://github.com/llvm/llvm-project/commit/5d5301dc0cc4a84fb91f103e4afec62de4622e33.diff LOG: [lldb] add a marker around hidden frames (#181143) This is a reland of https://github.com/llvm/llvm-project/pull/167550. Instead of relying on libcpp for testing, we emulate our own hidden frames. This was originally causing tests failures on Windows. Added: lldb/test/API/terminal/hidden_frame_markers/Makefile lldb/test/API/terminal/hidden_frame_markers/TestHiddenFrameMarkers.py lldb/test/API/terminal/hidden_frame_markers/main.cpp Modified: lldb/include/lldb/Core/Debugger.h lldb/include/lldb/Target/StackFrame.h lldb/include/lldb/Target/StackFrameList.h lldb/packages/Python/lldbsuite/test/decorators.py lldb/source/Core/CoreProperties.td lldb/source/Core/Debugger.cpp lldb/source/Target/StackFrame.cpp lldb/source/Target/StackFrameList.cpp lldb/source/Target/Thread.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 87a57f7f1a538..a38caa7ac594e 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -347,6 +347,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>, bool SetUseSourceCache(bool use_source_cache); + bool GetMarkHiddenFrames() const; + bool GetHighlightSource() const; lldb::StopShowColumn GetStopShowColumn() const; diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h index c114cd2a13fdf..fe42be3587433 100644 --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -368,7 +368,7 @@ class StackFrame : public ExecutionContextScope, /// \param [in] frame_marker /// Optional string that will be prepended to the frame output description. virtual void DumpUsingSettingsFormat(Stream *strm, bool show_unique = false, - const char *frame_marker = nullptr); + const llvm::StringRef frame_marker = ""); /// Print a description for this frame using a default format. /// @@ -405,7 +405,7 @@ class StackFrame : public ExecutionContextScope, /// Returns true if successful. virtual bool GetStatus(Stream &strm, bool show_frame_info, bool show_source, bool show_unique = false, - const char *frame_marker = nullptr); + const llvm::StringRef frame_marker = ""); /// Query whether this frame is a concrete frame on the call stack, or if it /// is an inlined frame derived from the debug information and presented by diff --git a/lldb/include/lldb/Target/StackFrameList.h b/lldb/include/lldb/Target/StackFrameList.h index 715781abb83a3..f8822a8dadc9b 100644 --- a/lldb/include/lldb/Target/StackFrameList.h +++ b/lldb/include/lldb/Target/StackFrameList.h @@ -50,6 +50,23 @@ class StackFrameList : public std::enable_shared_from_this<StackFrameList> { /// Resets the selected frame index of this object. void ClearSelectedFrameIndex(); + /// Returns \p true if the next frame is hidden. + bool IsNextFrameHidden(lldb_private::StackFrame &frame); + + /// Returns \p true if the previous frame is hidden. + bool IsPreviousFrameHidden(lldb_private::StackFrame &frame); + + /// Returns the stack frame marker depending on if \p frame_sp: + /// @li is selected: * + /// @li is the first non hidden frame: ﹍ + /// @li is the last non hidden frame: ﹉ + /// + /// If the terminal does not support Unicode rendering, the hidden frame + /// markers are replaced with whitespaces. + std::string GetFrameMarker(lldb::StackFrameSP frame_sp, + lldb::StackFrameSP selected_frame_sp, + bool show_hidden_marker); + /// Get the currently selected frame index. /// We should only call SelectMostRelevantFrame if (a) the user hasn't already /// selected a frame, and (b) if this really is a user facing @@ -97,7 +114,8 @@ class StackFrameList : public std::enable_shared_from_this<StackFrameList> { size_t GetStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames, bool show_frame_info, uint32_t num_frames_with_source, bool show_unique = false, bool show_hidden = false, - const char *frame_marker = nullptr); + bool show_hidden_marker = true, + bool show_selected_frame = false); /// Returns whether we have currently fetched all the frames of a stack. bool WereAllFramesFetched() const; diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py index df0b2cba4c573..b4658b149af90 100644 --- a/lldb/packages/Python/lldbsuite/test/decorators.py +++ b/lldb/packages/Python/lldbsuite/test/decorators.py @@ -442,6 +442,35 @@ def impl(func): return impl +def unicode_test(func): + """Decorate the item as a test which requires Unicode to be enabled. + + lldb checks the value of the `LANG` environment variable for the substring "utf-8" + to determine if the terminal supports Unicode (except on Windows, were we assume + it's always supported). + This decorator sets LANG to `utf-8` before running the test and resets it to its + previous value afterwards. + """ + + def unicode_wrapped(*args, **kwargs): + import os + + previous_lang = os.environ.get("LANG", None) + os.environ["LANG"] = "en_US.UTF-8" + try: + func(*args, **kwargs) + except Exception as err: + raise err + finally: + # Reset the value, whether the test failed or not. + if previous_lang is not None: + os.environ["LANG"] = previous_lang + else: + del os.environ["LANG"] + + return unicode_wrapped + + def no_debug_info_test(func): """Decorate the item as a test what don't use any debug info. If this annotation is specified then the test runner won't generate a separate test for each debug info format.""" diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td index 698f282488f72..c9141959a50f2 100644 --- a/lldb/source/Core/CoreProperties.td +++ b/lldb/source/Core/CoreProperties.td @@ -129,6 +129,10 @@ let Definition = "debugger", Path = "" in { Global, DefaultTrue, Desc<"If true, LLDB will highlight the displayed source code.">; + def MarkHiddenFrames: Property<"mark-hidden-frames", "Boolean">, + Global, + DefaultTrue, + Desc<"If true, LLDB will add a marker to delimit hidden frames in backtraces.">; def StopShowColumn: Property<"stop-show-column", "Enum">, DefaultEnumValue<"eStopShowColumnAnsiOrCaret">, EnumValues<"OptionEnumValues(s_stop_show_column_values)">, diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 00dea7da3497e..12f8039da947e 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -612,6 +612,13 @@ bool Debugger::SetUseSourceCache(bool b) { } return ret; } + +bool Debugger::GetMarkHiddenFrames() const { + const uint32_t idx = ePropertyMarkHiddenFrames; + return GetPropertyAtIndexAs<bool>( + idx, g_debugger_properties[idx].default_uint_value != 0); +} + bool Debugger::GetHighlightSource() const { const uint32_t idx = ePropertyHighlightSource; return GetPropertyAtIndexAs<bool>( diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index a4bdc20410007..ec3c3a9b32010 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -1946,7 +1946,7 @@ bool StackFrame::DumpUsingFormat(Stream &strm, } void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique, - const char *frame_marker) { + const llvm::StringRef frame_marker) { if (strm == nullptr) return; @@ -2045,7 +2045,8 @@ bool StackFrame::HasCachedData() const { } bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source, - bool show_unique, const char *frame_marker) { + bool show_unique, + const llvm::StringRef frame_marker) { if (show_frame_info) { strm.Indent(); DumpUsingSettingsFormat(&strm, show_unique, frame_marker); diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp index b46916a9af35e..5182eb6151b23 100644 --- a/lldb/source/Target/StackFrameList.cpp +++ b/lldb/source/Target/StackFrameList.cpp @@ -27,6 +27,7 @@ #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/ConvertUTF.h" #include <memory> @@ -948,11 +949,45 @@ StackFrameList::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) { return ret_sp; } +bool StackFrameList::IsNextFrameHidden(lldb_private::StackFrame &frame) { + uint32_t frame_idx = frame.GetFrameIndex(); + StackFrameSP frame_sp = GetFrameAtIndex(frame_idx + 1); + if (!frame_sp) + return false; + return frame_sp->IsHidden(); +} + +bool StackFrameList::IsPreviousFrameHidden(lldb_private::StackFrame &frame) { + uint32_t frame_idx = frame.GetFrameIndex(); + if (frame_idx == 0) + return false; + StackFrameSP frame_sp = GetFrameAtIndex(frame_idx - 1); + if (!frame_sp) + return false; + return frame_sp->IsHidden(); +} + +std::string StackFrameList::GetFrameMarker(lldb::StackFrameSP frame_sp, + lldb::StackFrameSP selected_frame_sp, + bool show_hidden_marker) { + bool show_unicode_marker = Terminal::SupportsUnicode() && show_hidden_marker; + if (frame_sp == selected_frame_sp) + return show_unicode_marker ? u8" * " : u8"* "; + if (!show_unicode_marker) + return u8" "; + if (IsPreviousFrameHidden(*frame_sp)) + return u8"﹉ "; + if (IsNextFrameHidden(*frame_sp)) + return u8"﹍ "; + return u8" "; +} + size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames, bool show_frame_info, uint32_t num_frames_with_source, bool show_unique, bool show_hidden, - const char *selected_frame_marker) { + bool show_hidden_marker, + bool show_selected_frame) { size_t num_frames_displayed = 0; if (num_frames == 0) @@ -970,25 +1005,18 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame, StackFrameSP selected_frame_sp = m_thread.GetSelectedFrame(DoNoSelectMostRelevantFrame); - const char *unselected_marker = nullptr; std::string buffer; - if (selected_frame_marker) { - size_t len = strlen(selected_frame_marker); - buffer.insert(buffer.begin(), len, ' '); - unselected_marker = buffer.c_str(); - } - const char *marker = nullptr; + std::string marker; for (frame_idx = first_frame; frame_idx < last_frame; ++frame_idx) { frame_sp = GetFrameAtIndex(frame_idx); if (!frame_sp) break; - if (selected_frame_marker != nullptr) { - if (frame_sp == selected_frame_sp) - marker = selected_frame_marker; - else - marker = unselected_marker; - } + if (show_selected_frame) + marker = GetFrameMarker(frame_sp, selected_frame_sp, show_hidden_marker); + else + marker = GetFrameMarker(frame_sp, /*selected_frame_sp=*/nullptr, + show_hidden_marker); // Hide uninteresting frames unless it's the selected frame. if (!show_hidden && frame_sp != selected_frame_sp && frame_sp->IsHidden()) diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index 44b664ac70d29..aa005a2b9b3db 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -1961,9 +1961,9 @@ size_t Thread::GetStatus(Stream &strm, uint32_t start_frame, uint32_t num_frames, uint32_t num_frames_with_source, bool stop_format, bool show_hidden, bool only_stacks) { + ExecutionContext exe_ctx(shared_from_this()); + Target *target = exe_ctx.GetTargetPtr(); if (!only_stacks) { - ExecutionContext exe_ctx(shared_from_this()); - Target *target = exe_ctx.GetTargetPtr(); Process *process = exe_ctx.GetProcessPtr(); strm.Indent(); bool is_selected = false; @@ -1997,16 +1997,19 @@ size_t Thread::GetStatus(Stream &strm, uint32_t start_frame, const bool show_frame_info = true; const bool show_frame_unique = only_stacks; - const char *selected_frame_marker = nullptr; + bool show_selected_frame = false; if (num_frames == 1 || only_stacks || (GetID() != GetProcess()->GetThreadList().GetSelectedThread()->GetID())) strm.IndentMore(); else - selected_frame_marker = "* "; + show_selected_frame = true; + bool show_hidden_marker = + target && target->GetDebugger().GetMarkHiddenFrames(); num_frames_shown = GetStackFrameList()->GetStatus( strm, start_frame, num_frames, show_frame_info, num_frames_with_source, - show_frame_unique, show_hidden, selected_frame_marker); + show_frame_unique, show_hidden, show_hidden_marker, + show_selected_frame); if (num_frames == 1) strm.IndentLess(); strm.IndentLess(); @@ -2106,9 +2109,13 @@ size_t Thread::GetStackFrameStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames, bool show_frame_info, uint32_t num_frames_with_source, bool show_hidden) { - return GetStackFrameList()->GetStatus(strm, first_frame, num_frames, - show_frame_info, num_frames_with_source, - /*show_unique*/ false, show_hidden); + ExecutionContext exe_ctx(shared_from_this()); + Target *target = exe_ctx.GetTargetPtr(); + bool show_hidden_marker = + target && target->GetDebugger().GetMarkHiddenFrames(); + return GetStackFrameList()->GetStatus( + strm, first_frame, num_frames, show_frame_info, num_frames_with_source, + /*show_unique*/ false, show_hidden, show_hidden_marker); } Unwind &Thread::GetUnwinder() { diff --git a/lldb/test/API/terminal/hidden_frame_markers/Makefile b/lldb/test/API/terminal/hidden_frame_markers/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/terminal/hidden_frame_markers/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/terminal/hidden_frame_markers/TestHiddenFrameMarkers.py b/lldb/test/API/terminal/hidden_frame_markers/TestHiddenFrameMarkers.py new file mode 100644 index 0000000000000..50e648befa65b --- /dev/null +++ b/lldb/test/API/terminal/hidden_frame_markers/TestHiddenFrameMarkers.py @@ -0,0 +1,157 @@ +""" +Test that hidden frames are delimited with markers. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class HiddenFrameMarkerTest(TestBase): + @unicode_test + def test_hidden_frame_markers(self): + """Test that hidden frame markers are rendered in backtraces""" + self.build() + lldbutil.run_to_source_breakpoint( + self, "// break here first", lldb.SBFileSpec("main.cpp") + ) + self.expect( + "bt", + substrs=[ + " * frame #0:", + " ﹉ frame #2:", + " frame #3:", + " frame #4:", + ], + ) + + self.runCmd("f 2") + self.expect( + "bt", + substrs=[ + " ﹍ frame #0:", + " * frame #2:", + " frame #3:", + " frame #4:", + ], + ) + + self.runCmd("f 3") + self.expect( + "bt", + substrs=[ + " ﹍ frame #0:", + " ﹉ frame #2:", + " * frame #3:", + " frame #4:", + ], + ) + + @unicode_test + def test_nested_hidden_frame_markers(self): + """Test that nested hidden frame markers are rendered in backtraces""" + self.build() + lldbutil.run_to_source_breakpoint( + self, "// break here after", lldb.SBFileSpec("main.cpp") + ) + self.expect( + "bt", + substrs=[ + " * frame #0:", + " ﹉ frame #2:", + " ﹍ frame #3:", + " ﹉ frame #5:", + " frame #6:", + ], + ) + + self.runCmd("f 2") + self.expect( + "bt", + substrs=[ + " ﹍ frame #0:", + " * frame #2:", + " ﹍ frame #3:", + " ﹉ frame #5:", + " frame #6:", + ], + ) + + self.runCmd("f 3") + self.expect( + "bt", + substrs=[ + " ﹍ frame #0:", + " ﹉ frame #2:", + " * frame #3:", + " ﹉ frame #5:", + " frame #6:", + ], + ) + + self.runCmd("f 5") + self.expect( + "bt", + substrs=[ + " ﹍ frame #0:", + " ﹉ frame #2:", + " ﹍ frame #3:", + " * frame #5:", + " frame #6:", + ], + ) + + self.runCmd("f 6") + self.expect( + "bt", + substrs=[ + " ﹍ frame #0:", + " ﹉ frame #2:", + " ﹍ frame #3:", + " ﹉ frame #5:", + " * frame #6:", + ], + ) + + def test_deactivated_hidden_frame_markers(self): + """ + Test that hidden frame markers are not rendered in backtraces when + mark-hidden-frames is set to false + """ + self.build() + self.runCmd("settings set mark-hidden-frames 0") + lldbutil.run_to_source_breakpoint( + self, "// break here first", lldb.SBFileSpec("main.cpp") + ) + self.expect( + "bt", + substrs=[ + " * frame #0:", + " frame #2:", + " frame #3:", + " frame #4:", + ], + ) + + self.runCmd("f 2") + self.expect( + "bt", + substrs=[ + " frame #0:", + " * frame #2:", + " frame #3:", + " frame #4:", + ], + ) + + self.runCmd("f 3") + self.expect( + "bt", + substrs=[ + " frame #0:", + " frame #2:", + " * frame #3:", + " frame #4:", + ], + ) diff --git a/lldb/test/API/terminal/hidden_frame_markers/main.cpp b/lldb/test/API/terminal/hidden_frame_markers/main.cpp new file mode 100644 index 0000000000000..0523d3cacf581 --- /dev/null +++ b/lldb/test/API/terminal/hidden_frame_markers/main.cpp @@ -0,0 +1,25 @@ +void foo(); +void bar(); + +namespace std { +namespace __1 { +void __test_hidden_frame() { foo(); } +void __test_nested_hidden_frame() { bar(); } + +void outer_function() { __test_hidden_frame(); } +void other_outer_function() { __test_nested_hidden_frame(); } +} // namespace __1 +} // namespace std + +void foo() { + std::__1::other_outer_function(); // break here first +} + +void bar() { + int a = 0; // break here after +} + +int main() { + std::__1::outer_function(); + return 0; +} _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
