Skip to content

Commit e43331f

Browse files
[lldb] add a marker before hidden frames (llvm#167550)
**This patch adds a marker to make hidden frames more explicit.** --- Hidden frames can be confusing for some users, who see that the indexes of the frames in a backtrace are not contiguous. This patch aims to lessen the confusion by adding a delimiter for the first and last non hidden frame, i.e the boundaries. IDE's like Xcode and VSCode represent those in the UI by having the hidden frames either greyed out or collapsed. It's not possible to do this in the CLI, therefore, this patch makes use of 2 unicode characters to mark the beginning and end of the hidden frames range. This patch depends on: - llvm#168603 # Examples In the example below, frame `#2` to `#7` are is hidden, and therefore, frame `#1` is the first non hidden frame of the range while frame `#8` is the last non hidden frame: <img width="488" height="112" alt="Screenshot 2025-11-18 at 18 41 11" src="https://github.com/user-attachments/assets/a21431da-9729-4cf0-a6bc-024aa306fc45" /> If the selected frame is one of the 2 boundary frames, we replace the delimiter character with the select character (`*`). <img width="487" height="111" alt="Screenshot 2025-11-18 at 18 41 03" src="https://github.com/user-attachments/assets/5616fa81-6db6-457d-9d1e-bbe46e710c26" /> <img width="488" height="111" alt="Screenshot 2025-11-18 at 18 40 55" src="https://github.com/user-attachments/assets/93dfa6cf-0956-4718-b31c-f965ec72b56d" />
1 parent d2c5892 commit e43331f

12 files changed

Lines changed: 231 additions & 28 deletions

File tree

lldb/include/lldb/Core/Debugger.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
340340

341341
bool SetUseSourceCache(bool use_source_cache);
342342

343+
bool GetMarkHiddenFrames() const;
344+
343345
bool GetHighlightSource() const;
344346

345347
lldb::StopShowColumn GetStopShowColumn() const;

lldb/include/lldb/Target/StackFrame.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ class StackFrame : public ExecutionContextScope,
363363
/// \param [in] frame_marker
364364
/// Optional string that will be prepended to the frame output description.
365365
virtual void DumpUsingSettingsFormat(Stream *strm, bool show_unique = false,
366-
const char *frame_marker = nullptr);
366+
const llvm::StringRef frame_marker = "");
367367

368368
/// Print a description for this frame using a default format.
369369
///
@@ -400,7 +400,7 @@ class StackFrame : public ExecutionContextScope,
400400
/// Returns true if successful.
401401
virtual bool GetStatus(Stream &strm, bool show_frame_info, bool show_source,
402402
bool show_unique = false,
403-
const char *frame_marker = nullptr);
403+
const llvm::StringRef frame_marker = "");
404404

405405
/// Query whether this frame is a concrete frame on the call stack, or if it
406406
/// is an inlined frame derived from the debug information and presented by

lldb/include/lldb/Target/StackFrameList.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,22 @@ class StackFrameList : public std::enable_shared_from_this<StackFrameList> {
4949
/// Resets the selected frame index of this object.
5050
void ClearSelectedFrameIndex();
5151

52+
/// Returns \p true if the next frame is hidden.
53+
bool IsNextFrameHidden(lldb_private::StackFrame &frame);
54+
55+
/// Returns \p true if the previous frame is hidden.
56+
bool IsPreviousFrameHidden(lldb_private::StackFrame &frame);
57+
58+
/// Returns the stack frame marker depending on if \p frame_sp:
59+
/// @li is selected: *
60+
/// @li is the first non hidden frame: ﹍
61+
/// @li is the last non hidden frame: ﹉
62+
///
63+
/// If the terminal does not support Unicode rendering, the hidden frame
64+
/// markers are replaced with whitespaces.
65+
std::string FrameMarker(lldb::StackFrameSP frame_sp,
66+
lldb::StackFrameSP selected_frame_sp);
67+
5268
/// Get the currently selected frame index.
5369
/// We should only call SelectMostRelevantFrame if (a) the user hasn't already
5470
/// selected a frame, and (b) if this really is a user facing
@@ -96,7 +112,8 @@ class StackFrameList : public std::enable_shared_from_this<StackFrameList> {
96112
size_t GetStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames,
97113
bool show_frame_info, uint32_t num_frames_with_source,
98114
bool show_unique = false, bool show_hidden = false,
99-
const char *frame_marker = nullptr);
115+
bool show_hidden_marker = true,
116+
bool show_selected_frame = false);
100117

101118
/// Returns whether we have currently fetched all the frames of a stack.
102119
bool WereAllFramesFetched() const;

lldb/packages/Python/lldbsuite/test/decorators.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,35 @@ def impl(func):
439439
return impl
440440

441441

442+
def unicode_test(func):
443+
"""Decorate the item as a test which requires Unicode to be enabled.
444+
445+
lldb checks the value of the `LANG` environment variable for the substring "utf-8"
446+
to determine if the terminal supports Unicode (except on Windows, were we assume
447+
it's always supported).
448+
This decorator sets LANG to `utf-8` before running the test and resets it to its
449+
previous value afterwards.
450+
"""
451+
452+
def unicode_wrapped(*args, **kwargs):
453+
import os
454+
455+
previous_lang = os.environ.get("LANG", None)
456+
os.environ["LANG"] = "en_US.UTF-8"
457+
try:
458+
func(*args, **kwargs)
459+
except Exception as err:
460+
raise err
461+
finally:
462+
# Reset the value, whether the test failed or not.
463+
if previous_lang is not None:
464+
os.environ["LANG"] = previous_lang
465+
else:
466+
del os.environ["LANG"]
467+
468+
return unicode_wrapped
469+
470+
442471
def no_debug_info_test(func):
443472
"""Decorate the item as a test what don't use any debug info. If this annotation is specified
444473
then the test runner won't generate a separate test for each debug info format."""

lldb/source/Core/CoreProperties.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ let Definition = "debugger" in {
114114
Global,
115115
DefaultTrue,
116116
Desc<"If true, LLDB will highlight the displayed source code.">;
117+
def MarkHiddenFrames: Property<"mark-hidden-frames", "Boolean">,
118+
Global,
119+
DefaultTrue,
120+
Desc<"If true, LLDB will add a marker to delimit hidden frames in backtraces.">;
117121
def StopShowColumn: Property<"stop-show-column", "Enum">,
118122
DefaultEnumValue<"eStopShowColumnAnsiOrCaret">,
119123
EnumValues<"OptionEnumValues(s_stop_show_column_values)">,

lldb/source/Core/Debugger.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,13 @@ bool Debugger::SetUseSourceCache(bool b) {
584584
}
585585
return ret;
586586
}
587+
588+
bool Debugger::GetMarkHiddenFrames() const {
589+
const uint32_t idx = ePropertyMarkHiddenFrames;
590+
return GetPropertyAtIndexAs<bool>(
591+
idx, g_debugger_properties[idx].default_uint_value != 0);
592+
}
593+
587594
bool Debugger::GetHighlightSource() const {
588595
const uint32_t idx = ePropertyHighlightSource;
589596
return GetPropertyAtIndexAs<bool>(

lldb/source/Target/StackFrame.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,7 +1945,7 @@ bool StackFrame::DumpUsingFormat(Stream &strm,
19451945
}
19461946

19471947
void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
1948-
const char *frame_marker) {
1948+
const llvm::StringRef frame_marker) {
19491949
if (strm == nullptr)
19501950
return;
19511951

@@ -2044,7 +2044,8 @@ bool StackFrame::HasCachedData() const {
20442044
}
20452045

20462046
bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
2047-
bool show_unique, const char *frame_marker) {
2047+
bool show_unique,
2048+
const llvm::StringRef frame_marker) {
20482049
if (show_frame_info) {
20492050
strm.Indent();
20502051
DumpUsingSettingsFormat(&strm, show_unique, frame_marker);

lldb/source/Target/StackFrameList.cpp

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "lldb/Utility/LLDBLog.h"
2828
#include "lldb/Utility/Log.h"
2929
#include "llvm/ADT/SmallPtrSet.h"
30+
#include "llvm/Support/ConvertUTF.h"
3031

3132
#include <memory>
3233

@@ -929,11 +930,43 @@ StackFrameList::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) {
929930
return ret_sp;
930931
}
931932

933+
bool StackFrameList::IsNextFrameHidden(lldb_private::StackFrame &frame) {
934+
uint32_t frame_idx = frame.GetFrameIndex();
935+
StackFrameSP frame_sp = GetFrameAtIndex(frame_idx + 1);
936+
if (!frame_sp)
937+
return false;
938+
return frame_sp->IsHidden();
939+
}
940+
941+
bool StackFrameList::IsPreviousFrameHidden(lldb_private::StackFrame &frame) {
942+
uint32_t frame_idx = frame.GetFrameIndex();
943+
if (frame_idx == 0)
944+
return false;
945+
StackFrameSP frame_sp = GetFrameAtIndex(frame_idx - 1);
946+
if (!frame_sp)
947+
return false;
948+
return frame_sp->IsHidden();
949+
}
950+
951+
std::string StackFrameList::FrameMarker(lldb::StackFrameSP frame_sp,
952+
lldb::StackFrameSP selected_frame_sp) {
953+
if (frame_sp == selected_frame_sp)
954+
return Terminal::SupportsUnicode() ? u8" * " : u8"* ";
955+
else if (!Terminal::SupportsUnicode())
956+
return u8" ";
957+
else if (IsPreviousFrameHidden(*frame_sp))
958+
return u8"";
959+
else if (IsNextFrameHidden(*frame_sp))
960+
return u8"";
961+
return u8" ";
962+
}
963+
932964
size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
933965
uint32_t num_frames, bool show_frame_info,
934966
uint32_t num_frames_with_source,
935967
bool show_unique, bool show_hidden,
936-
const char *selected_frame_marker) {
968+
bool show_hidden_marker,
969+
bool show_selected_frame) {
937970
size_t num_frames_displayed = 0;
938971

939972
if (num_frames == 0)
@@ -951,25 +984,17 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
951984

952985
StackFrameSP selected_frame_sp =
953986
m_thread.GetSelectedFrame(DoNoSelectMostRelevantFrame);
954-
const char *unselected_marker = nullptr;
955987
std::string buffer;
956-
if (selected_frame_marker) {
957-
size_t len = strlen(selected_frame_marker);
958-
buffer.insert(buffer.begin(), len, ' ');
959-
unselected_marker = buffer.c_str();
960-
}
961-
const char *marker = nullptr;
988+
std::string marker;
962989
for (frame_idx = first_frame; frame_idx < last_frame; ++frame_idx) {
963990
frame_sp = GetFrameAtIndex(frame_idx);
964991
if (!frame_sp)
965992
break;
966993

967-
if (selected_frame_marker != nullptr) {
968-
if (frame_sp == selected_frame_sp)
969-
marker = selected_frame_marker;
970-
else
971-
marker = unselected_marker;
972-
}
994+
if (show_selected_frame)
995+
marker = FrameMarker(frame_sp, selected_frame_sp);
996+
else
997+
marker = FrameMarker(frame_sp, nullptr);
973998

974999
// Hide uninteresting frames unless it's the selected frame.
9751000
if (!show_hidden && frame_sp != selected_frame_sp && frame_sp->IsHidden())
@@ -983,7 +1008,6 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
9831008
m_thread.GetID(), num_frames_displayed))
9841009
break;
9851010

986-
9871011
if (!frame_sp->GetStatus(strm, show_frame_info,
9881012
num_frames_with_source > (first_frame - frame_idx),
9891013
show_unique, marker))

lldb/source/Target/Thread.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,9 +1888,9 @@ size_t Thread::GetStatus(Stream &strm, uint32_t start_frame,
18881888
uint32_t num_frames, uint32_t num_frames_with_source,
18891889
bool stop_format, bool show_hidden, bool only_stacks) {
18901890

1891+
ExecutionContext exe_ctx(shared_from_this());
1892+
Target *target = exe_ctx.GetTargetPtr();
18911893
if (!only_stacks) {
1892-
ExecutionContext exe_ctx(shared_from_this());
1893-
Target *target = exe_ctx.GetTargetPtr();
18941894
Process *process = exe_ctx.GetProcessPtr();
18951895
strm.Indent();
18961896
bool is_selected = false;
@@ -1924,16 +1924,19 @@ size_t Thread::GetStatus(Stream &strm, uint32_t start_frame,
19241924

19251925
const bool show_frame_info = true;
19261926
const bool show_frame_unique = only_stacks;
1927-
const char *selected_frame_marker = nullptr;
1927+
bool show_selected_frame = false;
19281928
if (num_frames == 1 || only_stacks ||
19291929
(GetID() != GetProcess()->GetThreadList().GetSelectedThread()->GetID()))
19301930
strm.IndentMore();
19311931
else
1932-
selected_frame_marker = "* ";
1932+
show_selected_frame = true;
19331933

1934+
bool show_hidden_marker =
1935+
target && target->GetDebugger().GetMarkHiddenFrames();
19341936
num_frames_shown = GetStackFrameList()->GetStatus(
19351937
strm, start_frame, num_frames, show_frame_info, num_frames_with_source,
1936-
show_frame_unique, show_hidden, selected_frame_marker);
1938+
show_frame_unique, show_hidden, show_hidden_marker,
1939+
show_selected_frame);
19371940
if (num_frames == 1)
19381941
strm.IndentLess();
19391942
strm.IndentLess();
@@ -2033,9 +2036,13 @@ size_t Thread::GetStackFrameStatus(Stream &strm, uint32_t first_frame,
20332036
uint32_t num_frames, bool show_frame_info,
20342037
uint32_t num_frames_with_source,
20352038
bool show_hidden) {
2036-
return GetStackFrameList()->GetStatus(strm, first_frame, num_frames,
2037-
show_frame_info, num_frames_with_source,
2038-
/*show_unique*/ false, show_hidden);
2039+
ExecutionContext exe_ctx(shared_from_this());
2040+
Target *target = exe_ctx.GetTargetPtr();
2041+
bool show_hidden_marker =
2042+
target && target->GetDebugger().GetMarkHiddenFrames();
2043+
return GetStackFrameList()->GetStatus(
2044+
strm, first_frame, num_frames, show_frame_info, num_frames_with_source,
2045+
/*show_unique*/ false, show_hidden, show_hidden_marker);
20392046
}
20402047

20412048
Unwind &Thread::GetUnwinder() {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CXX_SOURCES := main.cpp
2+
3+
include Makefile.rules

0 commit comments

Comments
 (0)