llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere) <details> <summary>Changes</summary> 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 --- Patch is 36.09 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/121860.diff 14 Files Affected: - (modified) lldb/docs/use/formatting.rst (+6-2) - (modified) lldb/include/lldb/Core/Debugger.h (+20-1) - (modified) lldb/include/lldb/Core/FormatEntity.h (+4-1) - (added) lldb/include/lldb/Core/Statusline.h (+58) - (modified) lldb/include/lldb/Utility/AnsiTerminal.h (+39) - (modified) lldb/source/Core/CMakeLists.txt (+1) - (modified) lldb/source/Core/CoreProperties.td (+8) - (modified) lldb/source/Core/Debugger.cpp (+68-75) - (modified) lldb/source/Core/FormatEntity.cpp (+56-8) - (added) lldb/source/Core/Statusline.cpp (+161) - (modified) lldb/source/Host/common/Editline.cpp (+3-1) - (modified) lldb/test/API/terminal/TestEditline.py (+16) - (modified) lldb/unittests/Core/FormatEntityTest.cpp (+3) - (modified) lldb/unittests/Utility/AnsiTerminalTest.cpp (+15) ``````````diff diff --git a/lldb/docs/use/formatting.rst b/lldb/docs/use/formatting.rst index 970bacfd8807a7..3b7819d29d0a27 100644 --- a/lldb/docs/use/formatting.rst +++ b/lldb/docs/use/formatting.rst @@ -113,11 +113,11 @@ A complete list of currently supported format string variables is listed below: +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ``module.file.basename`` | The basename of the current module (shared library or executable) | +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``module.file.fullpath`` | The basename of the current module (shared library or executable) | +| ``module.file.fullpath`` | The path of the current module (shared library or executable) | +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ``process.file.basename`` | The basename of the file for the process | +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``process.file.fullpath`` | The fullname of the file for the process | +| ``process.file.fullpath`` | The path of the file for the process | +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ``process.id`` | The process ID native to the system on which the inferior runs. | +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ @@ -141,6 +141,10 @@ A complete list of currently supported format string variables is listed below: +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ``target.arch`` | The architecture of the current target | +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``target.file.basename`` | The basename of the current current target | ++---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ``target.file.fullpath`` | The path of the current current target | ++---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ``script.target:python_func`` | Use a Python function to generate a piece of textual output | +---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ``script.process:python_func`` | Use a Python function to generate a piece of textual output | 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 36f6df4118c21f..51e9ce37e54e79 100644 --- a/lldb/include/lldb/Core/FormatEntity.h +++ b/lldb/include/lldb/Core/FormatEntity.h @@ -67,6 +67,7 @@ struct Entry { ScriptThread, ThreadInfo, TargetArch, + TargetFile, ScriptTarget, ModuleFile, File, @@ -99,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..aeb1ae7e6846df --- /dev/null +++ b/lldb/include/lldb/Core/Statusline.h @@ -0,0 +1,58 @@ +//===-- 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 +// +//===----------------------------------------------------------------------===// +#include "lldb/Core/Debugger.h" +#include "llvm/ADT/SmallVector.h" +#include <string> +#ifndef LLDB_CORE_STATUSBAR_H +#define LLDB_CORE_STATUSBAR_H + +namespace lldb_private { +class Statusline { +public: + Statusline(Debugger &debugger); + ~Statusline(); + + void Enable(); + void Disable(); + + void Clear(); + void Update(); + + void TerminalSizeChanged() { m_terminal_size_has_changed = 1; } + +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); + + // Write at the given column. + void AddAtPosition(uint64_t col, llvm::StringRef str); + + // Clear the statusline (without redrawing the background). + void Reset(); + + bool IsSupported() const; + + lldb::thread_result_t StatuslineThread(); + + Debugger &m_debugger; + + 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 67795971d2ca89..a49865e711e108 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> @@ -171,7 +172,45 @@ inline std::string FormatAnsiTerminalCodes(llvm::StringRef format, } return fmt; } + +inline std::string StripAnsiTerminalCodes(llvm::StringRef str) { + std::string stripped; + while (!str.empty()) { + llvm::StringRef left, right; + + std::tie(left, right) = str.split(ANSI_ESC_START); + stripped += left; + + // ANSI_ESC_START not found. + if (left == str && right.empty()) + break; + + auto end = llvm::StringRef::npos; + for (size_t i = 0; i < right.size(); i++) { + char c = right[i]; + if (c == 'm' || c == 'G') { + end = i; + break; + } + if (isdigit(c) || c == ';') + continue; + + break; + } + + // ANSI_ESC_END not found. + if (end != llvm::StringRef::npos) { + str = right.substr(end + 1); + continue; + } + + stripped += ANSI_ESC_START; + str = right; + } + return stripped; } + +} // namespace ansi } // namespace lldb_private #endif 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 6ceb209269c9e7..0735d9e4360381 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -243,6 +243,11 @@ 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[ePropertyStatuslineFormat].name) { + // Statusline format changed. Redraw the statusline. + if (m_statusline) + m_statusline->Update(); } else if (property_path == g_debugger_properties[ePropertyUseSourceCache].name) { // use-source-cache changed. Wipe out the cache contents if it was @@ -376,6 +381,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 +399,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 +463,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 +1113,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; @@ -1958,6 +1984,12 @@ 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); + + if (m_statusline) + m_statusline->Enable(); + bool done = false; while (!done) { EventSP event_sp; @@ -2016,8 +2048,14 @@ lldb::thread_result_t Debugger::DefaultEventHandler() { if (m_forward_listener_sp) m_forward_listener_sp->AddEvent(event_sp); } + if (m_statusline) + m_statusline->Update(); } } + + if (m_statusline) + m_statusline->Disable(); + return {}; } @@ -2080,84 +2118,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; - } - - // 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(); + // Make a local copy of the incoming pr... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/121860 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits