From 692713d2679675636af1d5fa6e534ce1c34dd20b Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Mon, 6 Oct 2025 14:20:41 -0700 Subject: [PATCH 1/3] [lldb] Support OSC escape codes for native progress This PR adds support for emitting the ConEmu OSC 9;4 sequences to show a GUI native progress bar. There's a limited number of terminal emulators that support this, so for now this requires explicit opt-in through a setting. I'm reusing the existing `show-progress` setting, which became a NOOP with the introduction of the statusline. The option now defaults to off. Implements #160369 --- lldb/include/lldb/Core/Debugger.h | 1 + lldb/include/lldb/Utility/AnsiTerminal.h | 9 +++++ lldb/source/Core/CoreProperties.td | 10 +++-- lldb/source/Core/Debugger.cpp | 49 +++++++++++++++++++----- 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 06136ed40471d..78f1fa6757f9d 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -682,6 +682,7 @@ class Debugger : public std::enable_shared_from_this, lldb::LockableStreamFileSP GetErrorStreamSP() { return m_error_stream_sp; } /// @} + bool IsInteractiveColorTTY(); bool StatuslineSupported(); void PushIOHandler(const lldb::IOHandlerSP &reader_sp, diff --git a/lldb/include/lldb/Utility/AnsiTerminal.h b/lldb/include/lldb/Utility/AnsiTerminal.h index 7db184ad67225..350c1fb145300 100644 --- a/lldb/include/lldb/Utility/AnsiTerminal.h +++ b/lldb/include/lldb/Utility/AnsiTerminal.h @@ -72,6 +72,15 @@ #define ANSI_ESC_START_LEN 2 +// OSC (Operating System Commands) +#define OSC_ESCAPE_START "\033" +#define OSC_ESCAPE_END "\x07" + +#define OSC_PROGRESS_REMOVE OSC_ESCAPE_START "]9;4;0;0" OSC_ESCAPE_END +#define OSC_PROGRESS_SHOW OSC_ESCAPE_START "]9;4;1;%u" OSC_ESCAPE_END +#define OSC_PROGRESS_ERROR OSC_ESCAPE_START "]9;4;2;%u" OSC_ESCAPE_END +#define OSC_PROGRESS_INDETERMINATE OSC_ESCAPE_START "]9;4;3;%u" OSC_ESCAPE_END + #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td index fda34a8ad2630..1be911c291703 100644 --- a/lldb/source/Core/CoreProperties.td +++ b/lldb/source/Core/CoreProperties.td @@ -162,10 +162,12 @@ let Definition = "debugger" in { Global, DefaultTrue, Desc<"Whether to use Ansi color codes or not.">; - def ShowProgress: Property<"show-progress", "Boolean">, - Global, - DefaultTrue, - Desc<"Whether to show progress or not if the debugger's output is an interactive color-enabled terminal.">; + def ShowProgress + : Property<"show-progress", "Boolean">, + Global, + DefaultFalse, + Desc<"Whether to show progress using Operating System Command (OSC) " + "Sequences in supporting terminal emulators.">; def ShowProgressAnsiPrefix: Property<"show-progress-ansi-prefix", "String">, Global, DefaultStringValue<"${ansi.faint}">, diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 568cd9d3d03b6..13fd3a705128e 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -2066,19 +2066,23 @@ void Debugger::CancelForwardEvents(const ListenerSP &listener_sp) { m_forward_listener_sp.reset(); } +bool Debugger::IsInteractiveColorTTY() { + if (lldb::LockableStreamFileSP stream_sp = GetOutputStreamSP()) { + File &file = stream_sp->GetUnlockedFile(); + return file.GetIsInteractive() && file.GetIsRealTerminal() && + file.GetIsTerminalWithColors(); + } + return false; +} + bool Debugger::StatuslineSupported() { // We have trouble with the contol codes on Windows, see // https://github.com/llvm/llvm-project/issues/134846. #ifndef _WIN32 - if (GetShowStatusline()) { - if (lldb::LockableStreamFileSP stream_sp = GetOutputStreamSP()) { - File &file = stream_sp->GetUnlockedFile(); - return file.GetIsInteractive() && file.GetIsRealTerminal() && - file.GetIsTerminalWithColors(); - } - } -#endif + return GetShowStatusline() && IsInteractiveColorTTY(); +#else return false; +#endif } static bool RequiresFollowChildWorkaround(const Process &process) { @@ -2271,10 +2275,11 @@ void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) { ProgressReport progress_report{data->GetID(), data->GetCompleted(), data->GetTotal(), data->GetMessage()}; - // Do some bookkeeping regardless of whether we're going to display - // progress reports. { std::lock_guard guard(m_progress_reports_mutex); + + // Do some bookkeeping regardless of whether we're going to display + // progress reports. auto it = llvm::find_if(m_progress_reports, [&](const auto &report) { return report.id == progress_report.id; }); @@ -2287,6 +2292,30 @@ void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) { } else { m_progress_reports.push_back(progress_report); } + + // Show progress using Operating System Command (OSC) sequences. + if (GetShowProgress() && IsInteractiveColorTTY()) { + if (lldb::LockableStreamFileSP stream_sp = GetOutputStreamSP()) { + + // Clear progress if this was the last progress event. + if (m_progress_reports.empty()) { + stream_sp->Lock() << OSC_PROGRESS_REMOVE; + return; + } + + const ProgressReport &report = m_progress_reports.back(); + + // Show indeterminate progress. + if (report.total == UINT64_MAX) { + stream_sp->Lock() << OSC_PROGRESS_INDETERMINATE; + return; + } + + // Compute and show the progress value (0-100). + const unsigned value = (report.completed / report.total) * 100; + stream_sp->Lock().Printf(OSC_PROGRESS_SHOW, value); + } + } } } From 290d518cb9875ba2ae9341b30a7ab39eda2a0e19 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Tue, 7 Oct 2025 09:12:49 -0700 Subject: [PATCH 2/3] Address David's feedback --- lldb/include/lldb/Utility/AnsiTerminal.h | 2 ++ llvm/docs/ReleaseNotes.md | 3 +++ 2 files changed, 5 insertions(+) diff --git a/lldb/include/lldb/Utility/AnsiTerminal.h b/lldb/include/lldb/Utility/AnsiTerminal.h index 350c1fb145300..41acac74364bb 100644 --- a/lldb/include/lldb/Utility/AnsiTerminal.h +++ b/lldb/include/lldb/Utility/AnsiTerminal.h @@ -73,9 +73,11 @@ #define ANSI_ESC_START_LEN 2 // OSC (Operating System Commands) +// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html #define OSC_ESCAPE_START "\033" #define OSC_ESCAPE_END "\x07" +// https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC #define OSC_PROGRESS_REMOVE OSC_ESCAPE_START "]9;4;0;0" OSC_ESCAPE_END #define OSC_PROGRESS_SHOW OSC_ESCAPE_START "]9;4;1;%u" OSC_ESCAPE_END #define OSC_PROGRESS_ERROR OSC_ESCAPE_START "]9;4;2;%u" OSC_ESCAPE_END diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index 40cddb45df84d..ecd8be9761135 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -166,6 +166,9 @@ Changes to LLDB * LLDB can now set breakpoints, show backtraces, and display variables when debugging Wasm with supported runtimes (WAMR and V8). +* The `show-progress` setting, which became a NOOP with the introduction of the + statusline, now defaults to off and controls using OSC escape codes to show a + native progress bar in supporting terminals like Ghostty and ConEmu. Changes to BOLT --------------------------------- From 961031c47c7076bd677b144bfe630572bb79e07b Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Tue, 7 Oct 2025 12:44:14 -0700 Subject: [PATCH 3/3] Appease Adrian and find a better name --- lldb/include/lldb/Core/Debugger.h | 2 +- lldb/source/Core/Debugger.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 78f1fa6757f9d..ead2ed35fadd4 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -682,7 +682,7 @@ class Debugger : public std::enable_shared_from_this, lldb::LockableStreamFileSP GetErrorStreamSP() { return m_error_stream_sp; } /// @} - bool IsInteractiveColorTTY(); + bool IsEscapeCodeCapableTTY(); bool StatuslineSupported(); void PushIOHandler(const lldb::IOHandlerSP &reader_sp, diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 13fd3a705128e..b37d9d3ed85e3 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -2066,7 +2066,7 @@ void Debugger::CancelForwardEvents(const ListenerSP &listener_sp) { m_forward_listener_sp.reset(); } -bool Debugger::IsInteractiveColorTTY() { +bool Debugger::IsEscapeCodeCapableTTY() { if (lldb::LockableStreamFileSP stream_sp = GetOutputStreamSP()) { File &file = stream_sp->GetUnlockedFile(); return file.GetIsInteractive() && file.GetIsRealTerminal() && @@ -2079,7 +2079,7 @@ bool Debugger::StatuslineSupported() { // We have trouble with the contol codes on Windows, see // https://github.com/llvm/llvm-project/issues/134846. #ifndef _WIN32 - return GetShowStatusline() && IsInteractiveColorTTY(); + return GetShowStatusline() && IsEscapeCodeCapableTTY(); #else return false; #endif @@ -2294,7 +2294,7 @@ void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) { } // Show progress using Operating System Command (OSC) sequences. - if (GetShowProgress() && IsInteractiveColorTTY()) { + if (GetShowProgress() && IsEscapeCodeCapableTTY()) { if (lldb::LockableStreamFileSP stream_sp = GetOutputStreamSP()) { // Clear progress if this was the last progress event.