[Lldb-commits] [lldb] [lldb] Implement a statusline in LLDB (PR #121860)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Thu Jan 23 09:52:22 PST 2025
https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/121860
>From b0cf540a08958ead1c7b9e38f30a2722fe780592 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 17 Jan 2025 17:10:36 -0800
Subject: [PATCH 1/3] [lldb] Implement a statusline in LLDB
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add a statusline to command-line LLDB to display progress events and
other information related to the current state of the debugger. The
statusline is a dedicated area displayed the bottom of the screen. The
contents of the status line are configurable through a setting
consisting of LLDB’s format strings.
The statusline is configurable through the `statusline-format` setting.
The default configuration shows the target name, the current file, the
stop reason and the current progress event.
```
(lldb) settings show statusline-format
statusline-format (format-string) = "${ansi.bg.cyan}${ansi.fg.black}{${target.file.basename}}{ | ${line.file.basename}:${line.number}:${line.column}}{ | ${thread.stop-reason}}{ | {${progress.count} }${progress.message}}"
```
The statusline is enabled by default, but can be disabled with the
following setting:
```
(lldb) settings set show-statusline false
```
The statusline supersedes the current progress reporting implementation.
Consequently, the following settings no longer have any effect (but
continue to exist):
```
show-progress -- Whether to show progress or not if the debugger's output is an interactive color-enabled terminal.
show-progress-ansi-prefix -- When displaying progress in a color-enabled terminal, use the ANSI terminal code specified in this format immediately before the progress message.
show-progress-ansi-suffix -- When displaying progress in a color-enabled terminal, use the ANSI terminal code specified in this format immediately after the progress message.
```
RFC: https://discourse.llvm.org/t/rfc-lldb-statusline/83948
---
lldb/include/lldb/Core/Debugger.h | 21 ++-
lldb/include/lldb/Core/FormatEntity.h | 4 +-
lldb/include/lldb/Core/Statusline.h | 63 +++++++++
lldb/include/lldb/Utility/AnsiTerminal.h | 1 +
lldb/source/Core/CMakeLists.txt | 1 +
lldb/source/Core/CoreProperties.td | 8 ++
lldb/source/Core/Debugger.cpp | 148 ++++++++++----------
lldb/source/Core/FormatEntity.cpp | 46 ++++++-
lldb/source/Core/Statusline.cpp | 165 +++++++++++++++++++++++
9 files changed, 373 insertions(+), 84 deletions(-)
create mode 100644 lldb/include/lldb/Core/Statusline.h
create mode 100644 lldb/source/Core/Statusline.cpp
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index 70f4c4216221c6..a4da5fd44c17fe 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -19,6 +19,7 @@
#include "lldb/Core/FormatEntity.h"
#include "lldb/Core/IOHandler.h"
#include "lldb/Core/SourceManager.h"
+#include "lldb/Core/Statusline.h"
#include "lldb/Core/UserSettingsController.h"
#include "lldb/Host/HostThread.h"
#include "lldb/Host/StreamFile.h"
@@ -308,6 +309,10 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
bool SetShowProgress(bool show_progress);
+ bool GetShowStatusline() const;
+
+ const FormatEntity::Entry *GetStatuslineFormat() const;
+
llvm::StringRef GetShowProgressAnsiPrefix() const;
llvm::StringRef GetShowProgressAnsiSuffix() const;
@@ -604,6 +609,14 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
return m_source_file_cache;
}
+ struct ProgressReport {
+ uint64_t id;
+ uint64_t completed;
+ uint64_t total;
+ std::string message;
+ };
+ std::optional<ProgressReport> GetCurrentProgressReport() const;
+
protected:
friend class CommandInterpreter;
friend class REPL;
@@ -728,7 +741,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
IOHandlerStack m_io_handler_stack;
std::recursive_mutex m_io_handler_synchronous_mutex;
- std::optional<uint64_t> m_current_event_id;
+ std::optional<Statusline> m_statusline;
llvm::StringMap<std::weak_ptr<LogHandler>> m_stream_handlers;
std::shared_ptr<CallbackLogHandler> m_callback_handler_sp;
@@ -745,6 +758,12 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
lldb::TargetSP m_dummy_target_sp;
Diagnostics::CallbackID m_diagnostics_callback_id;
+ /// Bookkeeping for command line progress events.
+ /// @{
+ llvm::SmallVector<ProgressReport, 4> m_progress_reports;
+ mutable std::mutex m_progress_reports_mutex;
+ /// @}
+
std::mutex m_destroy_callback_mutex;
lldb::callback_token_t m_destroy_callback_next_token = 0;
struct DestroyCallbackInfo {
diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h
index c9d5af1f31673b..51e9ce37e54e79 100644
--- a/lldb/include/lldb/Core/FormatEntity.h
+++ b/lldb/include/lldb/Core/FormatEntity.h
@@ -100,7 +100,9 @@ struct Entry {
LineEntryColumn,
LineEntryStartAddress,
LineEntryEndAddress,
- CurrentPCArrow
+ CurrentPCArrow,
+ ProgressCount,
+ ProgressMessage,
};
struct Definition {
diff --git a/lldb/include/lldb/Core/Statusline.h b/lldb/include/lldb/Core/Statusline.h
new file mode 100644
index 00000000000000..162b6ad998df7d
--- /dev/null
+++ b/lldb/include/lldb/Core/Statusline.h
@@ -0,0 +1,63 @@
+//===-- Statusline.h -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_CORE_STATUSBAR_H
+#define LLDB_CORE_STATUSBAR_H
+
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/StringRef.h"
+#include <csignal>
+#include <string>
+
+namespace lldb_private {
+class Statusline {
+public:
+ Statusline(Debugger &debugger);
+ ~Statusline();
+
+ /// Reduce the scroll window and draw the statusline.
+ void Enable();
+
+ /// Hide the statusline and extend the scroll window.
+ void Disable();
+
+ /// Redraw the statusline. If update is false, this will redraw the last
+ /// string.
+ void Redraw(bool update = true);
+
+ /// Inform the statusline that the terminal dimensions have changed.
+ void TerminalSizeChanged();
+
+private:
+ /// Draw the statusline with the given text.
+ void Draw(llvm::StringRef msg);
+
+ /// Update terminal dimensions.
+ void UpdateTerminalProperties();
+
+ /// Set the scroll window to the given height.
+ void SetScrollWindow(uint64_t height);
+
+ /// Clear the statusline (without redrawing the background).
+ void Reset();
+
+ /// Return true if the output is an interactive terminal with colors.
+ bool IsSupported() const;
+
+ Debugger &m_debugger;
+ std::string m_last_str;
+
+ volatile std::sig_atomic_t m_terminal_size_has_changed = 1;
+ uint64_t m_terminal_width = 0;
+ uint64_t m_terminal_height = 0;
+ uint64_t m_scroll_height = 0;
+
+ static constexpr llvm::StringLiteral k_ansi_suffix = "${ansi.normal}";
+};
+} // namespace lldb_private
+#endif // LLDB_CORE_STATUSBAR_H
diff --git a/lldb/include/lldb/Utility/AnsiTerminal.h b/lldb/include/lldb/Utility/AnsiTerminal.h
index 1939c49c7b859c..031433272e85dd 100644
--- a/lldb/include/lldb/Utility/AnsiTerminal.h
+++ b/lldb/include/lldb/Utility/AnsiTerminal.h
@@ -73,6 +73,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Regex.h"
#include <string>
diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt
index 6d14f7a87764e0..5d4576837dbe61 100644
--- a/lldb/source/Core/CMakeLists.txt
+++ b/lldb/source/Core/CMakeLists.txt
@@ -46,6 +46,7 @@ add_lldb_library(lldbCore
Opcode.cpp
PluginManager.cpp
Progress.cpp
+ Statusline.cpp
RichManglingContext.cpp
SearchFilter.cpp
Section.cpp
diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td
index d3816c3070bbc5..0c6f93cb23e456 100644
--- a/lldb/source/Core/CoreProperties.td
+++ b/lldb/source/Core/CoreProperties.td
@@ -172,6 +172,14 @@ let Definition = "debugger" in {
Global,
DefaultStringValue<"${ansi.normal}">,
Desc<"When displaying progress in a color-enabled terminal, use the ANSI terminal code specified in this format immediately after the progress message.">;
+ def ShowStatusline: Property<"show-statusline", "Boolean">,
+ Global,
+ DefaultTrue,
+ Desc<"Whether to show a statusline at the bottom of the terminal.">;
+ def StatuslineFormat: Property<"statusline-format", "FormatEntity">,
+ Global,
+ DefaultStringValue<"${ansi.bg.blue}${ansi.fg.black}{${target.file.basename}}{ | ${line.file.basename}:${line.number}:${line.column}}{ | ${thread.stop-reason}}{ | {${progress.count} }${progress.message}}">,
+ Desc<"List of statusline format entities.">;
def UseSourceCache: Property<"use-source-cache", "Boolean">,
Global,
DefaultTrue,
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 2df2aeb20aa26a..59f76f7779961f 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -243,6 +243,19 @@ Status Debugger::SetPropertyValue(const ExecutionContext *exe_ctx,
// Prompt colors changed. Ping the prompt so it can reset the ansi
// terminal codes.
SetPrompt(GetPrompt());
+ } else if (property_path ==
+ g_debugger_properties[ePropertyShowStatusline].name) {
+ // Statusline setting changed. If we have a statusline instance, update it
+ // now. Otherwise it will get created in the default event handler.
+ if (GetShowStatusline())
+ m_statusline.emplace(*this);
+ else
+ m_statusline.reset();
+ } else if (property_path ==
+ g_debugger_properties[ePropertyStatuslineFormat].name) {
+ // Statusline format changed. Redraw the statusline.
+ if (m_statusline)
+ m_statusline->Redraw();
} else if (property_path ==
g_debugger_properties[ePropertyUseSourceCache].name) {
// use-source-cache changed. Wipe out the cache contents if it was
@@ -376,6 +389,8 @@ bool Debugger::SetTerminalWidth(uint64_t term_width) {
if (auto handler_sp = m_io_handler_stack.Top())
handler_sp->TerminalSizeChanged();
+ if (m_statusline)
+ m_statusline->TerminalSizeChanged();
return success;
}
@@ -392,6 +407,8 @@ bool Debugger::SetTerminalHeight(uint64_t term_height) {
if (auto handler_sp = m_io_handler_stack.Top())
handler_sp->TerminalSizeChanged();
+ if (m_statusline)
+ m_statusline->TerminalSizeChanged();
return success;
}
@@ -454,6 +471,17 @@ llvm::StringRef Debugger::GetShowProgressAnsiSuffix() const {
idx, g_debugger_properties[idx].default_cstr_value);
}
+bool Debugger::GetShowStatusline() const {
+ const uint32_t idx = ePropertyShowStatusline;
+ return GetPropertyAtIndexAs<bool>(
+ idx, g_debugger_properties[idx].default_uint_value != 0);
+}
+
+const FormatEntity::Entry *Debugger::GetStatuslineFormat() const {
+ constexpr uint32_t idx = ePropertyStatuslineFormat;
+ return GetPropertyAtIndexAs<const FormatEntity::Entry *>(idx);
+}
+
bool Debugger::GetUseAutosuggestion() const {
const uint32_t idx = ePropertyShowAutosuggestion;
return GetPropertyAtIndexAs<bool>(
@@ -1093,12 +1121,18 @@ void Debugger::SetErrorFile(FileSP file_sp) {
}
void Debugger::SaveInputTerminalState() {
+ if (m_statusline)
+ m_statusline->Disable();
int fd = GetInputFile().GetDescriptor();
if (fd != File::kInvalidDescriptor)
m_terminal_state.Save(fd, true);
}
-void Debugger::RestoreInputTerminalState() { m_terminal_state.Restore(); }
+void Debugger::RestoreInputTerminalState() {
+ m_terminal_state.Restore();
+ if (m_statusline)
+ m_statusline->Enable();
+}
ExecutionContext Debugger::GetSelectedExecutionContext() {
bool adopt_selected = true;
@@ -1959,6 +1993,9 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
// are now listening to all required events so no events get missed
m_sync_broadcaster.BroadcastEvent(eBroadcastBitEventThreadIsListening);
+ if (!m_statusline && GetShowStatusline())
+ m_statusline.emplace(*this);
+
bool done = false;
while (!done) {
EventSP event_sp;
@@ -2017,8 +2054,14 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
if (m_forward_listener_sp)
m_forward_listener_sp->AddEvent(event_sp);
}
+ if (m_statusline)
+ m_statusline->Redraw();
}
}
+
+ if (m_statusline)
+ m_statusline.reset();
+
return {};
}
@@ -2081,84 +2124,39 @@ void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) {
if (!data)
return;
- // Do some bookkeeping for the current event, regardless of whether we're
- // going to show the progress.
- const uint64_t id = data->GetID();
- if (m_current_event_id) {
- Log *log = GetLog(LLDBLog::Events);
- if (log && log->GetVerbose()) {
- StreamString log_stream;
- log_stream.AsRawOstream()
- << static_cast<void *>(this) << " Debugger(" << GetID()
- << ")::HandleProgressEvent( m_current_event_id = "
- << *m_current_event_id << ", data = { ";
- data->Dump(&log_stream);
- log_stream << " } )";
- log->PutString(log_stream.GetString());
- }
- if (id != *m_current_event_id)
- return;
- if (data->GetCompleted() == data->GetTotal())
- m_current_event_id.reset();
- } else {
- m_current_event_id = id;
- }
+ // Make a local copy of the incoming progress report that we'll store.
+ ProgressReport progress_report{data->GetID(), data->GetCompleted(),
+ data->GetTotal(), data->GetMessage()};
- // Decide whether we actually are going to show the progress. This decision
- // can change between iterations so check it inside the loop.
- if (!GetShowProgress())
- return;
-
- // Determine whether the current output file is an interactive terminal with
- // color support. We assume that if we support ANSI escape codes we support
- // vt100 escape codes.
- File &file = GetOutputFile();
- if (!file.GetIsInteractive() || !file.GetIsTerminalWithColors())
- return;
-
- StreamSP output = GetAsyncOutputStream();
-
- // Print over previous line, if any.
- output->Printf("\r");
-
- if (data->GetCompleted() == data->GetTotal()) {
- // Clear the current line.
- output->Printf("\x1B[2K");
- output->Flush();
- return;
+ // Do some bookkeeping regardless of whether we're going to display
+ // progress reports.
+ {
+ std::lock_guard<std::mutex> guard(m_progress_reports_mutex);
+ auto it = std::find_if(
+ m_progress_reports.begin(), m_progress_reports.end(),
+ [&](const auto &report) { return report.id == progress_report.id; });
+ if (it != m_progress_reports.end()) {
+ const bool complete = data->GetCompleted() == data->GetTotal();
+ if (complete)
+ m_progress_reports.erase(it);
+ else
+ *it = progress_report;
+ } else {
+ m_progress_reports.push_back(progress_report);
+ }
}
- // Trim the progress message if it exceeds the window's width and print it.
- std::string message = data->GetMessage();
- if (data->IsFinite())
- message = llvm::formatv("[{0}/{1}] {2}", data->GetCompleted(),
- data->GetTotal(), message)
- .str();
-
- // Trim the progress message if it exceeds the window's width and print it.
- const uint32_t term_width = GetTerminalWidth();
- const uint32_t ellipsis = 3;
- if (message.size() + ellipsis >= term_width)
- message.resize(term_width - ellipsis);
-
- const bool use_color = GetUseColor();
- llvm::StringRef ansi_prefix = GetShowProgressAnsiPrefix();
- if (!ansi_prefix.empty())
- output->Printf(
- "%s", ansi::FormatAnsiTerminalCodes(ansi_prefix, use_color).c_str());
-
- output->Printf("%s...", message.c_str());
-
- llvm::StringRef ansi_suffix = GetShowProgressAnsiSuffix();
- if (!ansi_suffix.empty())
- output->Printf(
- "%s", ansi::FormatAnsiTerminalCodes(ansi_suffix, use_color).c_str());
-
- // Clear until the end of the line.
- output->Printf("\x1B[K\r");
+ // Redraw the statusline if enabled.
+ if (m_statusline)
+ m_statusline->Redraw();
+}
- // Flush the output.
- output->Flush();
+std::optional<Debugger::ProgressReport>
+Debugger::GetCurrentProgressReport() const {
+ std::lock_guard<std::mutex> guard(m_progress_reports_mutex);
+ if (m_progress_reports.empty())
+ return std::nullopt;
+ return m_progress_reports.back();
}
void Debugger::HandleDiagnosticEvent(const lldb::EventSP &event_sp) {
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index fb7043ac74b8dd..5729af6752d37f 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -166,6 +166,10 @@ constexpr Definition g_target_child_entries[] = {
Entry::DefinitionWithChildren("file", EntryType::TargetFile,
g_file_child_entries)};
+constexpr Definition g_progress_child_entries[] = {
+ Definition("count", EntryType::ProgressCount),
+ Definition("message", EntryType::ProgressMessage)};
+
#define _TO_STR2(_val) #_val
#define _TO_STR(_val) _TO_STR2(_val)
@@ -259,7 +263,10 @@ constexpr Definition g_top_level_entries[] = {
Entry::DefinitionWithChildren("target", EntryType::Invalid,
g_target_child_entries),
Entry::DefinitionWithChildren("var", EntryType::Variable,
- g_var_child_entries, true)};
+ g_var_child_entries, true),
+ Entry::DefinitionWithChildren("progress", EntryType::Invalid,
+ g_progress_child_entries),
+};
constexpr Definition g_root = Entry::DefinitionWithChildren(
"<root>", EntryType::Root, g_top_level_entries);
@@ -358,6 +365,8 @@ const char *FormatEntity::Entry::TypeToCString(Type t) {
ENUM_TO_CSTR(LineEntryStartAddress);
ENUM_TO_CSTR(LineEntryEndAddress);
ENUM_TO_CSTR(CurrentPCArrow);
+ ENUM_TO_CSTR(ProgressCount);
+ ENUM_TO_CSTR(ProgressMessage);
}
return "???";
}
@@ -1198,13 +1207,13 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
// FormatEntity::Entry::Definition encoding
return false;
case Entry::Type::EscapeCode:
- if (exe_ctx) {
- if (Target *target = exe_ctx->GetTargetPtr()) {
- Debugger &debugger = target->GetDebugger();
- if (debugger.GetUseColor()) {
- s.PutCString(entry.string);
- }
+ if (Target *target = Target::GetTargetFromContexts(exe_ctx, sc)) {
+ Debugger &debugger = target->GetDebugger();
+ if (debugger.GetUseColor()) {
+ s.PutCString(entry.string);
}
+ } else {
+ assert(false);
}
// Always return true, so colors being disabled is transparent.
return true;
@@ -1912,7 +1921,30 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
return true;
}
return false;
+
+ case Entry::Type::ProgressCount:
+ if (Target *target = Target::GetTargetFromContexts(exe_ctx, sc)) {
+ Debugger &debugger = target->GetDebugger();
+ if (auto progress = debugger.GetCurrentProgressReport()) {
+ if (progress->total != UINT64_MAX) {
+ s.Printf("[%llu/%llu]", progress->completed, progress->total);
+ return true;
+ }
+ }
+ }
+ return false;
+
+ case Entry::Type::ProgressMessage:
+ if (Target *target = Target::GetTargetFromContexts(exe_ctx, sc)) {
+ Debugger &debugger = target->GetDebugger();
+ if (auto progress = debugger.GetCurrentProgressReport()) {
+ s.PutCString(progress->message);
+ return true;
+ }
+ }
+ return false;
}
+
return false;
}
diff --git a/lldb/source/Core/Statusline.cpp b/lldb/source/Core/Statusline.cpp
new file mode 100644
index 00000000000000..a4d72a333565a4
--- /dev/null
+++ b/lldb/source/Core/Statusline.cpp
@@ -0,0 +1,165 @@
+//===-- Statusline.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Statusline.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/FormatEntity.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Utility/AnsiTerminal.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+#include "llvm/Support/Locale.h"
+
+#include <sys/ioctl.h>
+#include <termios.h>
+
+#define ESCAPE "\x1b"
+#define ANSI_SAVE_CURSOR ESCAPE "7"
+#define ANSI_RESTORE_CURSOR ESCAPE "8"
+#define ANSI_CLEAR_BELOW ESCAPE "[J"
+#define ANSI_CLEAR_LINE "\r\x1B[2K"
+#define ANSI_SET_SCROLL_ROWS ESCAPE "[0;%ur"
+#define ANSI_TO_START_OF_ROW ESCAPE "[%u;0f"
+#define ANSI_UP_ROWS ESCAPE "[%dA"
+#define ANSI_DOWN_ROWS ESCAPE "[%dB"
+#define ANSI_FORWARD_COLS ESCAPE "\033[%dC"
+#define ANSI_BACKWARD_COLS ESCAPE "\033[%dD"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static size_t ColumnWidth(llvm::StringRef str) {
+ std::string stripped = ansi::StripAnsiTerminalCodes(str);
+ return llvm::sys::locale::columnWidth(stripped);
+}
+
+Statusline::Statusline(Debugger &debugger) : m_debugger(debugger) { Enable(); }
+
+Statusline::~Statusline() { Disable(); }
+
+void Statusline::TerminalSizeChanged() {
+ m_terminal_size_has_changed = 1;
+
+ // FIXME: This probably isn't safe?
+ Redraw(/*update=*/false);
+}
+
+bool Statusline::IsSupported() const {
+ File &file = m_debugger.GetOutputFile();
+ return file.GetIsInteractive() && file.GetIsTerminalWithColors();
+}
+
+void Statusline::Enable() {
+ if (!IsSupported())
+ return;
+
+ UpdateTerminalProperties();
+
+ // Reduce the scroll window to make space for the status bar below.
+ SetScrollWindow(m_terminal_height - 1);
+
+ // Draw the statusline.
+ Redraw();
+}
+
+void Statusline::Disable() {
+ UpdateTerminalProperties();
+
+ // Extend the scroll window to cover the status bar.
+ SetScrollWindow(m_terminal_height);
+}
+
+void Statusline::Draw(llvm::StringRef str) {
+ UpdateTerminalProperties();
+
+ m_last_str = str;
+
+ const size_t ellipsis = 3;
+ const size_t column_width = ColumnWidth(str);
+
+ if (column_width + ellipsis >= m_terminal_width)
+ str = str.substr(0, m_terminal_width - ellipsis);
+
+ StreamFile &out = m_debugger.GetOutputStream();
+ out << ANSI_SAVE_CURSOR;
+ out.Printf(ANSI_TO_START_OF_ROW, static_cast<unsigned>(m_terminal_height));
+ out << ANSI_CLEAR_LINE;
+ out << str;
+ if (m_terminal_width > column_width)
+ out << std::string(m_terminal_width - column_width, ' ');
+ out << ansi::FormatAnsiTerminalCodes(k_ansi_suffix);
+ out << ANSI_RESTORE_CURSOR;
+}
+
+void Statusline::Reset() {
+ StreamFile &out = m_debugger.GetOutputStream();
+ out << ANSI_SAVE_CURSOR;
+ out.Printf(ANSI_TO_START_OF_ROW, static_cast<unsigned>(m_terminal_height));
+ out << ANSI_CLEAR_LINE;
+ out << ANSI_RESTORE_CURSOR;
+}
+
+void Statusline::UpdateTerminalProperties() {
+ if (m_terminal_size_has_changed == 0)
+ return;
+
+ // Clear the previous statusline using the previous dimensions.
+ Reset();
+
+ m_terminal_width = m_debugger.GetTerminalWidth();
+ m_terminal_height = m_debugger.GetTerminalHeight();
+
+ // Set the scroll window based on the new terminal height.
+ SetScrollWindow(m_terminal_height - 1);
+
+ // Clear the flag.
+ m_terminal_size_has_changed = 0;
+}
+
+void Statusline::SetScrollWindow(uint64_t height) {
+ StreamFile &out = m_debugger.GetOutputStream();
+ out << '\n';
+ out << ANSI_SAVE_CURSOR;
+ out.Printf(ANSI_SET_SCROLL_ROWS, static_cast<unsigned>(height));
+ out << ANSI_RESTORE_CURSOR;
+ out.Printf(ANSI_UP_ROWS, 1);
+ out << ANSI_CLEAR_BELOW;
+ out.Flush();
+
+ m_scroll_height = height;
+}
+
+void Statusline::Redraw(bool update) {
+ if (!update) {
+ Draw(m_last_str);
+ return;
+ }
+
+ StreamString stream;
+ ExecutionContext exe_ctx =
+ m_debugger.GetCommandInterpreter().GetExecutionContext();
+
+ // For colors and progress events, the format entity needs access to the
+ // debugger, which requires a target in the execution context.
+ if (!exe_ctx.HasTargetScope())
+ exe_ctx.SetTargetPtr(&m_debugger.GetSelectedOrDummyTarget());
+
+ SymbolContext symbol_ctx;
+ if (auto frame_sp = exe_ctx.GetFrameSP())
+ symbol_ctx = frame_sp->GetSymbolContext(eSymbolContextEverything);
+
+ if (auto *format = m_debugger.GetStatuslineFormat())
+ FormatEntity::Format(*format, stream, &symbol_ctx, &exe_ctx, nullptr,
+ nullptr, false, false);
+
+ Draw(stream.GetString());
+}
>From f8342170cf00152126eb9079ddc5f90654bceed4 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Wed, 22 Jan 2025 16:45:11 -0800
Subject: [PATCH 2/3] Address Alex' feedback
---
lldb/include/lldb/Core/Statusline.h | 6 +++---
lldb/source/Core/Statusline.cpp | 6 ++----
2 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/lldb/include/lldb/Core/Statusline.h b/lldb/include/lldb/Core/Statusline.h
index 162b6ad998df7d..1bff3af23b6a3c 100644
--- a/lldb/include/lldb/Core/Statusline.h
+++ b/lldb/include/lldb/Core/Statusline.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLDB_CORE_STATUSBAR_H
-#define LLDB_CORE_STATUSBAR_H
+#ifndef LLDB_CORE_STATUSLINE_H
+#define LLDB_CORE_STATUSLINE_H
#include "lldb/lldb-forward.h"
#include "llvm/ADT/StringRef.h"
@@ -60,4 +60,4 @@ class Statusline {
static constexpr llvm::StringLiteral k_ansi_suffix = "${ansi.normal}";
};
} // namespace lldb_private
-#endif // LLDB_CORE_STATUSBAR_H
+#endif // LLDB_CORE_STATUSLINE_H
diff --git a/lldb/source/Core/Statusline.cpp b/lldb/source/Core/Statusline.cpp
index a4d72a333565a4..7e8f0707d4bdaf 100644
--- a/lldb/source/Core/Statusline.cpp
+++ b/lldb/source/Core/Statusline.cpp
@@ -19,9 +19,6 @@
#include "lldb/Utility/StreamString.h"
#include "llvm/Support/Locale.h"
-#include <sys/ioctl.h>
-#include <termios.h>
-
#define ESCAPE "\x1b"
#define ANSI_SAVE_CURSOR ESCAPE "7"
#define ANSI_RESTORE_CURSOR ESCAPE "8"
@@ -49,7 +46,8 @@ Statusline::~Statusline() { Disable(); }
void Statusline::TerminalSizeChanged() {
m_terminal_size_has_changed = 1;
- // FIXME: This probably isn't safe?
+ // FIXME: This code gets called from a signal handler. It's probably not safe
+ // to redraw the statusline, even without recomputing it?
Redraw(/*update=*/false);
}
>From e2e834435ca2a4d13e3c33ae655f41faed7d25f6 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Thu, 23 Jan 2025 09:52:08 -0800
Subject: [PATCH 3/3] Remove debugging assert
---
lldb/source/Core/FormatEntity.cpp | 2 --
1 file changed, 2 deletions(-)
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index 5729af6752d37f..16b064a3e15ace 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -1212,8 +1212,6 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
if (debugger.GetUseColor()) {
s.PutCString(entry.string);
}
- } else {
- assert(false);
}
// Always return true, so colors being disabled is transparent.
return true;
More information about the lldb-commits
mailing list