[Lldb-commits] [lldb] [lldb] add a marker before skipped frames (PR #167550)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Wed Nov 26 07:34:08 PST 2025
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/167550
>From 8615f3d2a10a106f45815f28340ae3e8770f6186 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 11 Nov 2025 18:09:05 +0100
Subject: [PATCH 1/6] [lldb] add a marker before skipped frames
---
lldb/include/lldb/Target/StackFrame.h | 21 +++++++++++++++---
lldb/include/lldb/Target/StackFrameList.h | 8 +++++++
lldb/source/Target/StackFrame.cpp | 22 +++++++++++++------
lldb/source/Target/StackFrameList.cpp | 26 +++++++++++++++++++++--
4 files changed, 66 insertions(+), 11 deletions(-)
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index cdbe8ae3c6779..c3e3c945e114c 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -335,11 +335,16 @@ class StackFrame : public ExecutionContextScope,
/// \param[in] frame_marker
/// Optional string that will be prepended to the frame output description.
///
+ /// \param[in] skipped_frame_marker
+ /// Optional string that will be prepended to the first or last non skipped
+ /// frame output description.
+ ///
/// \return
/// \b true if and only if dumping with the given \p format worked.
bool DumpUsingFormat(Stream &strm,
const lldb_private::FormatEntity::Entry *format,
- llvm::StringRef frame_marker = {});
+ llvm::StringRef frame_marker = {},
+ llvm::StringRef skipped_frame_marker = {});
/// Print a description for this frame using the frame-format formatter
/// settings. If the current frame-format settings are invalid, then the
@@ -353,8 +358,13 @@ class StackFrame : public ExecutionContextScope,
///
/// \param [in] frame_marker
/// Optional string that will be prepended to the frame output description.
+ ///
+ /// \param[in] skipped_frame_marker
+ /// Optional string that will be prepended to the first or last non skipped
+ /// frame output description.
void DumpUsingSettingsFormat(Stream *strm, bool show_unique = false,
- const char *frame_marker = nullptr);
+ const char *frame_marker = nullptr,
+ const std::wstring skipped_frame_marker = L"");
/// Print a description for this frame using a default format.
///
@@ -387,10 +397,15 @@ class StackFrame : public ExecutionContextScope,
/// \param[in] frame_marker
/// Passed to DumpUsingSettingsFormat() for the frame info printing.
///
+ ///
+ /// \param[in] skipped_frame_marker
+ /// Optional string that will be prepended to the first or last non skipped
+ /// frame output description.
/// \return
/// Returns true if successful.
bool GetStatus(Stream &strm, bool show_frame_info, bool show_source,
- bool show_unique = false, const char *frame_marker = nullptr);
+ bool show_unique = false, const char *frame_marker = nullptr,
+ const std::wstring skipped_frame_marker = L"");
/// 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 5b0df0ddb3e29..8d6b8fc284336 100644
--- a/lldb/include/lldb/Target/StackFrameList.h
+++ b/lldb/include/lldb/Target/StackFrameList.h
@@ -49,6 +49,14 @@ class StackFrameList {
/// Resets the selected frame index of this object.
void ClearSelectedFrameIndex();
+ /// Return \code true if the next frame is hidden. False otherwise or if it's
+ /// the last frame.
+ bool IsNextFrameHidden(lldb_private::StackFrame &frame);
+
+ /// Return \code true if the previous frame is hidden. False otherwise or if
+ /// it's the first frame.
+ bool IsPreviousFrameHidden(lldb_private::StackFrame &frame);
+
/// 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
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 78f67d21d6600..362980f17d689 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -37,6 +37,7 @@
#include "lldb/ValueObject/ValueObjectConstResult.h"
#include "lldb/ValueObject/ValueObjectMemory.h"
#include "lldb/ValueObject/ValueObjectVariable.h"
+#include "llvm/Support/ConvertUTF.h"
#include "lldb/lldb-enumerations.h"
@@ -1920,11 +1921,13 @@ void StackFrame::CalculateExecutionContext(ExecutionContext &exe_ctx) {
bool StackFrame::DumpUsingFormat(Stream &strm,
const FormatEntity::Entry *format,
- llvm::StringRef frame_marker) {
+ llvm::StringRef frame_marker,
+ llvm::StringRef skipped_frame_marker) {
GetSymbolContext(eSymbolContextEverything);
ExecutionContext exe_ctx(shared_from_this());
StreamString s;
s.PutCString(frame_marker);
+ s.PutCString(skipped_frame_marker);
if (format && FormatEntity::Format(*format, s, &m_sc, &exe_ctx, nullptr,
nullptr, false, false)) {
@@ -1934,8 +1937,9 @@ bool StackFrame::DumpUsingFormat(Stream &strm,
return false;
}
-void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
- const char *frame_marker) {
+void StackFrame::DumpUsingSettingsFormat(
+ Stream *strm, bool show_unique, const char *frame_marker,
+ const std::wstring skipped_frame_marker) {
if (strm == nullptr)
return;
@@ -1953,7 +1957,11 @@ void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
frame_format = &format_entry;
}
}
- if (!DumpUsingFormat(*strm, frame_format, frame_marker)) {
+
+ std::string skipped_frame_delimiter_utf8;
+ llvm::convertWideToUTF8(skipped_frame_marker, skipped_frame_delimiter_utf8);
+ if (!DumpUsingFormat(*strm, frame_format, frame_marker,
+ skipped_frame_delimiter_utf8)) {
Dump(strm, true, false);
strm->EOL();
}
@@ -2034,10 +2042,12 @@ 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 char *frame_marker,
+ const std::wstring skipped_frame_marker) {
if (show_frame_info) {
strm.Indent();
- DumpUsingSettingsFormat(&strm, show_unique, frame_marker);
+ DumpUsingSettingsFormat(&strm, show_unique, frame_marker,
+ skipped_frame_marker);
}
if (show_source) {
diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp
index ccf874fc03ebd..80f77e2bcee16 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -879,6 +879,24 @@ 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();
+}
+
size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
uint32_t num_frames, bool show_frame_info,
uint32_t num_frames_with_source,
@@ -920,6 +938,11 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
else
marker = unselected_marker;
}
+ std::wstring skipped_frame_marker = L" ";
+ if (IsPreviousFrameHidden(*frame_sp))
+ skipped_frame_marker = L"﹈";
+ else if (IsNextFrameHidden(*frame_sp))
+ skipped_frame_marker = L"﹇";
// Hide uninteresting frames unless it's the selected frame.
if (!show_hidden && frame_sp != selected_frame_sp && frame_sp->IsHidden())
@@ -933,10 +956,9 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
m_thread.GetID(), num_frames_displayed))
break;
-
if (!frame_sp->GetStatus(strm, show_frame_info,
num_frames_with_source > (first_frame - frame_idx),
- show_unique, marker))
+ show_unique, marker, skipped_frame_marker))
break;
++num_frames_displayed;
}
>From 0247666cc8a7b9bf4f32788c7f162c9ccf8e6438 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 18 Nov 2025 19:35:05 +0100
Subject: [PATCH 2/6] align all markers
---
.../lldb/Host/common/DiagnosticsRendering.h | 11 +++++
lldb/include/lldb/Target/StackFrame.h | 8 ++--
lldb/include/lldb/Target/StackFrameList.h | 5 ++-
.../Host/common/DiagnosticsRendering.cpp | 17 +++++++-
lldb/source/Target/StackFrame.cpp | 22 ++++------
lldb/source/Target/StackFrameList.cpp | 42 ++++++++++---------
lldb/source/Target/Thread.cpp | 6 +--
7 files changed, 67 insertions(+), 44 deletions(-)
diff --git a/lldb/include/lldb/Host/common/DiagnosticsRendering.h b/lldb/include/lldb/Host/common/DiagnosticsRendering.h
index dd33d671c24a5..3dfa9b0880f70 100644
--- a/lldb/include/lldb/Host/common/DiagnosticsRendering.h
+++ b/lldb/include/lldb/Host/common/DiagnosticsRendering.h
@@ -64,6 +64,17 @@ void RenderDiagnosticDetails(Stream &stream,
bool show_inline,
llvm::ArrayRef<DiagnosticDetail> details);
+/// Returns whether or not the current terminal supports Unicode rendering.
+///
+/// The value is cached after the first computation.
+///
+/// On POSIX systems, we check if the LANG environment variable contains the
+/// substring "UTF-8";
+///
+/// On Windows, we check that we are running from the Windows Terminal
+/// application.
+bool TerminalSupportsUnicode();
+
class DiagnosticError
: public llvm::ErrorInfo<DiagnosticError, CloneableECError> {
public:
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index c3e3c945e114c..7cae4d6e69972 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -343,8 +343,7 @@ class StackFrame : public ExecutionContextScope,
/// \b true if and only if dumping with the given \p format worked.
bool DumpUsingFormat(Stream &strm,
const lldb_private::FormatEntity::Entry *format,
- llvm::StringRef frame_marker = {},
- llvm::StringRef skipped_frame_marker = {});
+ llvm::StringRef frame_marker = {});
/// Print a description for this frame using the frame-format formatter
/// settings. If the current frame-format settings are invalid, then the
@@ -363,8 +362,7 @@ class StackFrame : public ExecutionContextScope,
/// Optional string that will be prepended to the first or last non skipped
/// frame output description.
void DumpUsingSettingsFormat(Stream *strm, bool show_unique = false,
- const char *frame_marker = nullptr,
- const std::wstring skipped_frame_marker = L"");
+ const std::wstring frame_marker = L"");
/// Print a description for this frame using a default format.
///
@@ -404,7 +402,7 @@ class StackFrame : public ExecutionContextScope,
/// \return
/// Returns true if successful.
bool GetStatus(Stream &strm, bool show_frame_info, bool show_source,
- bool show_unique = false, const char *frame_marker = nullptr,
+ bool show_unique = false,
const std::wstring skipped_frame_marker = L"");
/// Query whether this frame is a concrete frame on the call stack, or if it
diff --git a/lldb/include/lldb/Target/StackFrameList.h b/lldb/include/lldb/Target/StackFrameList.h
index 8d6b8fc284336..953b3c323f609 100644
--- a/lldb/include/lldb/Target/StackFrameList.h
+++ b/lldb/include/lldb/Target/StackFrameList.h
@@ -57,6 +57,9 @@ class StackFrameList {
/// it's the first frame.
bool IsPreviousFrameHidden(lldb_private::StackFrame &frame);
+ std::wstring FrameMarker(lldb::StackFrameSP frame_sp,
+ lldb::StackFrameSP selected_frame_sp);
+
/// 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
@@ -104,7 +107,7 @@ class 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_selected_frame = false);
/// Returns whether we have currently fetched all the frames of a stack.
bool WereAllFramesFetched() const;
diff --git a/lldb/source/Host/common/DiagnosticsRendering.cpp b/lldb/source/Host/common/DiagnosticsRendering.cpp
index f2cd3968967fb..b37f93aefa51d 100644
--- a/lldb/source/Host/common/DiagnosticsRendering.cpp
+++ b/lldb/source/Host/common/DiagnosticsRendering.cpp
@@ -102,7 +102,7 @@ void RenderDiagnosticDetails(Stream &stream,
// characters. In the future it might make sense to move this into
// Host so it can be customized for a specific platform.
llvm::StringRef cursor, underline, vbar, joint, hbar, spacer;
- if (stream.AsRawOstream().colors_enabled()) {
+ if (TerminalSupportsUnicode()) {
cursor = "˄";
underline = "˜";
vbar = "│";
@@ -232,4 +232,19 @@ void RenderDiagnosticDetails(Stream &stream,
}
}
+bool TerminalSupportsUnicode() {
+ static std::optional<bool> result;
+ if (result)
+ return result.value();
+#ifndef _WIN32
+ if (const char *lang_var = std::getenv("LANG"))
+ result = std::string(lang_var).find("UTF-8");
+ else
+ result = false;
+#else
+ result = std::getenv("WT_SESSION") != nullptr;
+#endif
+ return result.value();
+}
+
} // namespace lldb_private
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 362980f17d689..303325927aff1 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -1921,13 +1921,11 @@ void StackFrame::CalculateExecutionContext(ExecutionContext &exe_ctx) {
bool StackFrame::DumpUsingFormat(Stream &strm,
const FormatEntity::Entry *format,
- llvm::StringRef frame_marker,
- llvm::StringRef skipped_frame_marker) {
+ llvm::StringRef frame_marker) {
GetSymbolContext(eSymbolContextEverything);
ExecutionContext exe_ctx(shared_from_this());
StreamString s;
s.PutCString(frame_marker);
- s.PutCString(skipped_frame_marker);
if (format && FormatEntity::Format(*format, s, &m_sc, &exe_ctx, nullptr,
nullptr, false, false)) {
@@ -1937,9 +1935,8 @@ bool StackFrame::DumpUsingFormat(Stream &strm,
return false;
}
-void StackFrame::DumpUsingSettingsFormat(
- Stream *strm, bool show_unique, const char *frame_marker,
- const std::wstring skipped_frame_marker) {
+void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
+ const std::wstring frame_marker) {
if (strm == nullptr)
return;
@@ -1958,10 +1955,9 @@ void StackFrame::DumpUsingSettingsFormat(
}
}
- std::string skipped_frame_delimiter_utf8;
- llvm::convertWideToUTF8(skipped_frame_marker, skipped_frame_delimiter_utf8);
- if (!DumpUsingFormat(*strm, frame_format, frame_marker,
- skipped_frame_delimiter_utf8)) {
+ std::string frame_marker_utf8;
+ llvm::convertWideToUTF8(frame_marker, frame_marker_utf8);
+ if (!DumpUsingFormat(*strm, frame_format, frame_marker_utf8)) {
Dump(strm, true, false);
strm->EOL();
}
@@ -2042,12 +2038,10 @@ bool StackFrame::HasCachedData() const {
}
bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
- bool show_unique, const char *frame_marker,
- const std::wstring skipped_frame_marker) {
+ bool show_unique, const std::wstring frame_marker) {
if (show_frame_info) {
strm.Indent();
- DumpUsingSettingsFormat(&strm, show_unique, frame_marker,
- skipped_frame_marker);
+ DumpUsingSettingsFormat(&strm, show_unique, frame_marker);
}
if (show_source) {
diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp
index 80f77e2bcee16..97374ee7123f0 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -23,6 +23,7 @@
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/Unwind.h"
+#include "lldb/Utility/DiagnosticsRendering.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "llvm/ADT/SmallPtrSet.h"
@@ -897,11 +898,25 @@ bool StackFrameList::IsPreviousFrameHidden(lldb_private::StackFrame &frame) {
return frame_sp->IsHidden();
}
+std::wstring StackFrameList::FrameMarker(lldb::StackFrameSP frame_sp,
+ lldb::StackFrameSP selected_frame_sp) {
+ if (frame_sp == selected_frame_sp) {
+ return TerminalSupportsUnicode() ? L" * " : L"* ";
+ } else if (!TerminalSupportsUnicode()) {
+ return L" ";
+ } else if (IsPreviousFrameHidden(*frame_sp)) {
+ return L" ﹉";
+ } else if (IsNextFrameHidden(*frame_sp)) {
+ return L" ﹍";
+ }
+ return L" ";
+}
+
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_selected_frame) {
size_t num_frames_displayed = 0;
if (num_frames == 0)
@@ -919,30 +934,17 @@ 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::wstring 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;
- }
- std::wstring skipped_frame_marker = L" ";
- if (IsPreviousFrameHidden(*frame_sp))
- skipped_frame_marker = L"﹈";
- else if (IsNextFrameHidden(*frame_sp))
- skipped_frame_marker = L"﹇";
+ if (show_selected_frame)
+ marker = FrameMarker(frame_sp, selected_frame_sp);
+ else
+ marker = FrameMarker(frame_sp, nullptr);
// Hide uninteresting frames unless it's the selected frame.
if (!show_hidden && frame_sp != selected_frame_sp && frame_sp->IsHidden())
@@ -958,7 +960,7 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
if (!frame_sp->GetStatus(strm, show_frame_info,
num_frames_with_source > (first_frame - frame_idx),
- show_unique, marker, skipped_frame_marker))
+ show_unique, marker))
break;
++num_frames_displayed;
}
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index 8c3e19725f8cb..e1b3fde77c40e 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -1817,16 +1817,16 @@ 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;
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_selected_frame);
if (num_frames == 1)
strm.IndentLess();
strm.IndentLess();
>From 90a7ebd314162bff10c14065f4404b320a52e3f3 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 18 Nov 2025 19:38:55 +0100
Subject: [PATCH 3/6] remove unused comments
---
lldb/include/lldb/Target/StackFrame.h | 16 ++--------------
lldb/include/lldb/Target/StackFrameList.h | 6 ++----
lldb/source/Target/StackFrame.cpp | 4 ++--
3 files changed, 6 insertions(+), 20 deletions(-)
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 7cae4d6e69972..2f48d8fb74751 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -335,10 +335,6 @@ class StackFrame : public ExecutionContextScope,
/// \param[in] frame_marker
/// Optional string that will be prepended to the frame output description.
///
- /// \param[in] skipped_frame_marker
- /// Optional string that will be prepended to the first or last non skipped
- /// frame output description.
- ///
/// \return
/// \b true if and only if dumping with the given \p format worked.
bool DumpUsingFormat(Stream &strm,
@@ -357,12 +353,8 @@ class StackFrame : public ExecutionContextScope,
///
/// \param [in] frame_marker
/// Optional string that will be prepended to the frame output description.
- ///
- /// \param[in] skipped_frame_marker
- /// Optional string that will be prepended to the first or last non skipped
- /// frame output description.
void DumpUsingSettingsFormat(Stream *strm, bool show_unique = false,
- const std::wstring frame_marker = L"");
+ const std::wstring &frame_marker = L"");
/// Print a description for this frame using a default format.
///
@@ -395,15 +387,11 @@ class StackFrame : public ExecutionContextScope,
/// \param[in] frame_marker
/// Passed to DumpUsingSettingsFormat() for the frame info printing.
///
- ///
- /// \param[in] skipped_frame_marker
- /// Optional string that will be prepended to the first or last non skipped
- /// frame output description.
/// \return
/// Returns true if successful.
bool GetStatus(Stream &strm, bool show_frame_info, bool show_source,
bool show_unique = false,
- const std::wstring skipped_frame_marker = L"");
+ const std::wstring &frame_marker = L"");
/// 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 953b3c323f609..92905728de69a 100644
--- a/lldb/include/lldb/Target/StackFrameList.h
+++ b/lldb/include/lldb/Target/StackFrameList.h
@@ -49,12 +49,10 @@ class StackFrameList {
/// Resets the selected frame index of this object.
void ClearSelectedFrameIndex();
- /// Return \code true if the next frame is hidden. False otherwise or if it's
- /// the last frame.
+ /// Return \code true if the next frame is hidden.
bool IsNextFrameHidden(lldb_private::StackFrame &frame);
- /// Return \code true if the previous frame is hidden. False otherwise or if
- /// it's the first frame.
+ /// Return \code true if the previous frame is hidden.
bool IsPreviousFrameHidden(lldb_private::StackFrame &frame);
std::wstring FrameMarker(lldb::StackFrameSP frame_sp,
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 303325927aff1..b4d5f7ea28b92 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -1936,7 +1936,7 @@ bool StackFrame::DumpUsingFormat(Stream &strm,
}
void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
- const std::wstring frame_marker) {
+ const std::wstring &frame_marker) {
if (strm == nullptr)
return;
@@ -2038,7 +2038,7 @@ bool StackFrame::HasCachedData() const {
}
bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
- bool show_unique, const std::wstring frame_marker) {
+ bool show_unique, const std::wstring &frame_marker) {
if (show_frame_info) {
strm.Indent();
DumpUsingSettingsFormat(&strm, show_unique, frame_marker);
>From 3c61ed5a8950826fb3ac29b2dc90079b743493f9 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 18 Nov 2025 19:43:56 +0100
Subject: [PATCH 4/6] make APIs use std::string instead of std::wstring
---
lldb/include/lldb/Target/StackFrame.h | 4 ++--
lldb/source/Target/StackFrame.cpp | 9 +++------
lldb/source/Target/StackFrameList.cpp | 5 ++++-
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 2f48d8fb74751..f57c989468afd 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -354,7 +354,7 @@ class StackFrame : public ExecutionContextScope,
/// \param [in] frame_marker
/// Optional string that will be prepended to the frame output description.
void DumpUsingSettingsFormat(Stream *strm, bool show_unique = false,
- const std::wstring &frame_marker = L"");
+ const std::string &frame_marker = "");
/// Print a description for this frame using a default format.
///
@@ -391,7 +391,7 @@ class StackFrame : public ExecutionContextScope,
/// Returns true if successful.
bool GetStatus(Stream &strm, bool show_frame_info, bool show_source,
bool show_unique = false,
- const std::wstring &frame_marker = L"");
+ const std::string &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/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index b4d5f7ea28b92..6fd8e24f43293 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -37,7 +37,6 @@
#include "lldb/ValueObject/ValueObjectConstResult.h"
#include "lldb/ValueObject/ValueObjectMemory.h"
#include "lldb/ValueObject/ValueObjectVariable.h"
-#include "llvm/Support/ConvertUTF.h"
#include "lldb/lldb-enumerations.h"
@@ -1936,7 +1935,7 @@ bool StackFrame::DumpUsingFormat(Stream &strm,
}
void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
- const std::wstring &frame_marker) {
+ const std::string &frame_marker) {
if (strm == nullptr)
return;
@@ -1955,9 +1954,7 @@ void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
}
}
- std::string frame_marker_utf8;
- llvm::convertWideToUTF8(frame_marker, frame_marker_utf8);
- if (!DumpUsingFormat(*strm, frame_format, frame_marker_utf8)) {
+ if (!DumpUsingFormat(*strm, frame_format, frame_marker)) {
Dump(strm, true, false);
strm->EOL();
}
@@ -2038,7 +2035,7 @@ bool StackFrame::HasCachedData() const {
}
bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
- bool show_unique, const std::wstring &frame_marker) {
+ bool show_unique, const std::string &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 97374ee7123f0..b696ff39bbd45 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>
@@ -958,9 +959,11 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
m_thread.GetID(), num_frames_displayed))
break;
+ std::string marker_utf8;
+ llvm::convertWideToUTF8(marker, marker_utf8);
if (!frame_sp->GetStatus(strm, show_frame_info,
num_frames_with_source > (first_frame - frame_idx),
- show_unique, marker))
+ show_unique, marker_utf8))
break;
++num_frames_displayed;
}
>From e9462bed68c3a1555700e40a85b50676e0997b18 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 18 Nov 2025 20:43:12 +0100
Subject: [PATCH 5/6] [lldb] improve the heuristics for checking if a terminal
supports Unicode
---
lldb/include/lldb/Host/Terminal.h | 12 ++++++++++++
.../Host/common/DiagnosticsRendering.cpp | 19 ++-----------------
lldb/source/Host/common/Terminal.cpp | 15 +++++++++++++++
lldb/source/Target/StackFrameList.cpp | 5 ++---
4 files changed, 31 insertions(+), 20 deletions(-)
diff --git a/lldb/include/lldb/Host/Terminal.h b/lldb/include/lldb/Host/Terminal.h
index da0d05e8bd265..3d66515c18812 100644
--- a/lldb/include/lldb/Host/Terminal.h
+++ b/lldb/include/lldb/Host/Terminal.h
@@ -68,6 +68,18 @@ class Terminal {
llvm::Error SetHardwareFlowControl(bool enabled);
+ /// Returns whether or not the current terminal supports Unicode rendering.
+ ///
+ /// The value is cached after the first computation.
+ ///
+ /// On POSIX systems, we check if the LANG environment variable contains the
+ /// substring "UTF-8", case insensitive.
+ ///
+ /// On Windows, we always return true since we use the `WriteConsoleW` API
+ /// internally. Note that the default Windows codepage (437) does not support
+ /// all Unicode characters. This function does not check the codepage.
+ static bool SupportsUnicode();
+
protected:
struct Data;
diff --git a/lldb/source/Host/common/DiagnosticsRendering.cpp b/lldb/source/Host/common/DiagnosticsRendering.cpp
index b37f93aefa51d..006bbe545e9b3 100644
--- a/lldb/source/Host/common/DiagnosticsRendering.cpp
+++ b/lldb/source/Host/common/DiagnosticsRendering.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "lldb/Host/common/DiagnosticsRendering.h"
+#include "lldb/Host/Terminal.h"
#include <cstdint>
using namespace lldb_private;
@@ -102,7 +103,7 @@ void RenderDiagnosticDetails(Stream &stream,
// characters. In the future it might make sense to move this into
// Host so it can be customized for a specific platform.
llvm::StringRef cursor, underline, vbar, joint, hbar, spacer;
- if (TerminalSupportsUnicode()) {
+ if (Terminal::SupportsUnicode()) {
cursor = "˄";
underline = "˜";
vbar = "│";
@@ -231,20 +232,4 @@ void RenderDiagnosticDetails(Stream &stream,
stream << detail.rendered << '\n';
}
}
-
-bool TerminalSupportsUnicode() {
- static std::optional<bool> result;
- if (result)
- return result.value();
-#ifndef _WIN32
- if (const char *lang_var = std::getenv("LANG"))
- result = std::string(lang_var).find("UTF-8");
- else
- result = false;
-#else
- result = std::getenv("WT_SESSION") != nullptr;
-#endif
- return result.value();
-}
-
} // namespace lldb_private
diff --git a/lldb/source/Host/common/Terminal.cpp b/lldb/source/Host/common/Terminal.cpp
index 436dfd8130d9b..dd1dc75133f45 100644
--- a/lldb/source/Host/common/Terminal.cpp
+++ b/lldb/source/Host/common/Terminal.cpp
@@ -400,6 +400,21 @@ llvm::Error Terminal::SetHardwareFlowControl(bool enabled) {
#endif // LLDB_ENABLE_TERMIOS
}
+bool Terminal::SupportsUnicode() {
+ static std::optional<bool> result;
+ if (result)
+ return result.value();
+#ifdef _WIN32
+ return true;
+#else
+ const char *lang_var = std::getenv("LANG");
+ if (!lang_var)
+ return false;
+ result = llvm::StringRef(lang_var).lower().find("utf-8") != std::string::npos;
+#endif
+ return result.value();
+}
+
TerminalState::TerminalState(Terminal term, bool save_process_group)
: m_tty(term) {
Save(term, save_process_group);
diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp
index b696ff39bbd45..e60019307bd18 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -23,7 +23,6 @@
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/Unwind.h"
-#include "lldb/Utility/DiagnosticsRendering.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "llvm/ADT/SmallPtrSet.h"
@@ -902,8 +901,8 @@ bool StackFrameList::IsPreviousFrameHidden(lldb_private::StackFrame &frame) {
std::wstring StackFrameList::FrameMarker(lldb::StackFrameSP frame_sp,
lldb::StackFrameSP selected_frame_sp) {
if (frame_sp == selected_frame_sp) {
- return TerminalSupportsUnicode() ? L" * " : L"* ";
- } else if (!TerminalSupportsUnicode()) {
+ return Terminal::SupportsUnicode() ? L" * " : L"* ";
+ } else if (!Terminal::SupportsUnicode()) {
return L" ";
} else if (IsPreviousFrameHidden(*frame_sp)) {
return L" ﹉";
>From 3ef49dbc1a36af4921d4d78e9c32bca74e0d1085 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Wed, 26 Nov 2025 15:33:42 +0000
Subject: [PATCH 6/6] address comments
---
lldb/include/lldb/Target/StackFrame.h | 5 ++---
lldb/source/Target/StackFrame.cpp | 4 ++--
lldb/source/Target/StackFrameList.cpp | 9 ++++-----
3 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index f57c989468afd..0b8374cba3460 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -354,7 +354,7 @@ class StackFrame : public ExecutionContextScope,
/// \param [in] frame_marker
/// Optional string that will be prepended to the frame output description.
void DumpUsingSettingsFormat(Stream *strm, bool show_unique = false,
- const std::string &frame_marker = "");
+ llvm::StringRef frame_marker = "");
/// Print a description for this frame using a default format.
///
@@ -390,8 +390,7 @@ class StackFrame : public ExecutionContextScope,
/// \return
/// Returns true if successful.
bool GetStatus(Stream &strm, bool show_frame_info, bool show_source,
- bool show_unique = false,
- const std::string &frame_marker = "");
+ bool show_unique = false, 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/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 6fd8e24f43293..4cef2d0aae32e 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -1935,7 +1935,7 @@ bool StackFrame::DumpUsingFormat(Stream &strm,
}
void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique,
- const std::string &frame_marker) {
+ llvm::StringRef frame_marker) {
if (strm == nullptr)
return;
@@ -2035,7 +2035,7 @@ bool StackFrame::HasCachedData() const {
}
bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source,
- bool show_unique, const std::string &frame_marker) {
+ bool show_unique, 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 e60019307bd18..1f731e2fe847b 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -900,15 +900,14 @@ bool StackFrameList::IsPreviousFrameHidden(lldb_private::StackFrame &frame) {
std::wstring StackFrameList::FrameMarker(lldb::StackFrameSP frame_sp,
lldb::StackFrameSP selected_frame_sp) {
- if (frame_sp == selected_frame_sp) {
+ if (frame_sp == selected_frame_sp)
return Terminal::SupportsUnicode() ? L" * " : L"* ";
- } else if (!Terminal::SupportsUnicode()) {
+ else if (!Terminal::SupportsUnicode())
return L" ";
- } else if (IsPreviousFrameHidden(*frame_sp)) {
+ else if (IsPreviousFrameHidden(*frame_sp))
return L" ﹉";
- } else if (IsNextFrameHidden(*frame_sp)) {
+ else if (IsNextFrameHidden(*frame_sp))
return L" ﹍";
- }
return L" ";
}
More information about the lldb-commits
mailing list