Skip to content

Commit 199a171

Browse files
authored
Merge pull request #10379 from swiftlang/statusline
Cherrypick LLDB statusline
2 parents c3429d5 + e827c44 commit 199a171

File tree

21 files changed

+630
-154
lines changed

21 files changed

+630
-154
lines changed

lldb/docs/use/formatting.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,11 @@ A complete list of currently supported format string variables is listed below:
113113
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
114114
| ``module.file.basename`` | The basename of the current module (shared library or executable) |
115115
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
116-
| ``module.file.fullpath`` | The basename of the current module (shared library or executable) |
116+
| ``module.file.fullpath`` | The path of the current module (shared library or executable) |
117117
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
118118
| ``process.file.basename`` | The basename of the file for the process |
119119
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
120-
| ``process.file.fullpath`` | The fullname of the file for the process |
120+
| ``process.file.fullpath`` | The path of the file for the process |
121121
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
122122
| ``process.id`` | The process ID native to the system on which the inferior runs. |
123123
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
@@ -141,6 +141,10 @@ A complete list of currently supported format string variables is listed below:
141141
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
142142
| ``target.arch`` | The architecture of the current target |
143143
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
144+
| ``target.file.basename`` | The basename of the current target |
145+
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
146+
| ``target.file.fullpath`` | The path of the current target |
147+
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
144148
| ``script.target:python_func`` | Use a Python function to generate a piece of textual output |
145149
+---------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
146150
| ``script.process:python_func`` | Use a Python function to generate a piece of textual output |

lldb/include/lldb/Core/Debugger.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "lldb/Core/FormatEntity.h"
2020
#include "lldb/Core/IOHandler.h"
2121
#include "lldb/Core/SourceManager.h"
22+
#include "lldb/Core/Statusline.h"
2223
#include "lldb/Core/UserSettingsController.h"
2324
#include "lldb/Host/HostThread.h"
2425
#include "lldb/Host/StreamFile.h"
@@ -307,6 +308,10 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
307308

308309
bool SetShowProgress(bool show_progress);
309310

311+
bool GetShowStatusline() const;
312+
313+
const FormatEntity::Entry *GetStatuslineFormat() const;
314+
310315
llvm::StringRef GetShowProgressAnsiPrefix() const;
311316

312317
llvm::StringRef GetShowProgressAnsiSuffix() const;
@@ -420,6 +425,9 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
420425
/// Decrement the "interrupt requested" counter.
421426
void CancelInterruptRequest();
422427

428+
/// Redraw the statusline if enabled.
429+
void RedrawStatusline(bool update = true);
430+
423431
/// This is the correct way to query the state of Interruption.
424432
/// If you are on the RunCommandInterpreter thread, it will check the
425433
/// command interpreter state, and if it is on another thread it will
@@ -607,12 +615,21 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
607615
return m_source_file_cache;
608616
}
609617

618+
struct ProgressReport {
619+
uint64_t id;
620+
uint64_t completed;
621+
uint64_t total;
622+
std::string message;
623+
};
624+
std::optional<ProgressReport> GetCurrentProgressReport() const;
625+
610626
protected:
611627
friend class CommandInterpreter;
612628
friend class SwiftREPL;
613629
friend class REPL;
614630
friend class Progress;
615631
friend class ProgressManager;
632+
friend class Statusline;
616633

617634
/// Report progress events.
618635
///
@@ -665,6 +682,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
665682
lldb::LockableStreamFileSP GetErrorStreamSP() { return m_error_stream_sp; }
666683
/// @}
667684

685+
bool StatuslineSupported();
686+
668687
void PushIOHandler(const lldb::IOHandlerSP &reader_sp,
669688
bool cancel_top_handler = true);
670689

@@ -741,7 +760,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
741760
IOHandlerStack m_io_handler_stack;
742761
std::recursive_mutex m_io_handler_synchronous_mutex;
743762

744-
std::optional<uint64_t> m_current_event_id;
763+
std::optional<Statusline> m_statusline;
745764

746765
llvm::StringMap<std::weak_ptr<LogHandler>> m_stream_handlers;
747766
std::shared_ptr<CallbackLogHandler> m_callback_handler_sp;
@@ -758,6 +777,12 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
758777
lldb::TargetSP m_dummy_target_sp;
759778
Diagnostics::CallbackID m_diagnostics_callback_id;
760779

780+
/// Bookkeeping for command line progress events.
781+
/// @{
782+
llvm::SmallVector<ProgressReport, 4> m_progress_reports;
783+
mutable std::mutex m_progress_reports_mutex;
784+
/// @}
785+
761786
std::mutex m_destroy_callback_mutex;
762787
lldb::callback_token_t m_destroy_callback_next_token = 0;
763788
struct DestroyCallbackInfo {

lldb/include/lldb/Core/FormatEntity.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ struct Entry {
6767
ScriptThread,
6868
ThreadInfo,
6969
TargetArch,
70+
TargetFile,
7071
ScriptTarget,
7172
ModuleFile,
7273
File,
@@ -99,7 +100,9 @@ struct Entry {
99100
LineEntryColumn,
100101
LineEntryStartAddress,
101102
LineEntryEndAddress,
102-
CurrentPCArrow
103+
CurrentPCArrow,
104+
ProgressCount,
105+
ProgressMessage,
103106
};
104107

105108
struct Definition {

lldb/include/lldb/Core/IOHandler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,8 @@ class IOHandlerEditline : public IOHandler {
406406
std::optional<std::string> SuggestionCallback(llvm::StringRef line);
407407

408408
void AutoCompleteCallback(CompletionRequest &request);
409+
410+
void RedrawCallback();
409411
#endif
410412

411413
protected:

lldb/include/lldb/Core/Statusline.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===-- Statusline.h -----------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_CORE_STATUSLINE_H
10+
#define LLDB_CORE_STATUSLINE_H
11+
12+
#include "lldb/lldb-forward.h"
13+
#include "llvm/ADT/StringRef.h"
14+
#include <csignal>
15+
#include <cstdint>
16+
#include <string>
17+
18+
namespace lldb_private {
19+
class Statusline {
20+
public:
21+
Statusline(Debugger &debugger);
22+
~Statusline();
23+
24+
/// Reduce the scroll window and draw the statusline.
25+
void Enable();
26+
27+
/// Hide the statusline and extend the scroll window.
28+
void Disable();
29+
30+
/// Redraw the statusline. If update is false, this will redraw the last
31+
/// string.
32+
void Redraw(bool update = true);
33+
34+
/// Inform the statusline that the terminal dimensions have changed.
35+
void TerminalSizeChanged();
36+
37+
protected:
38+
/// Pad and trim the given string to fit to the given width.
39+
static std::string TrimAndPad(std::string str, size_t width);
40+
41+
private:
42+
/// Draw the statusline with the given text.
43+
void Draw(std::string msg);
44+
45+
/// Update terminal dimensions.
46+
void UpdateTerminalProperties();
47+
48+
enum ScrollWindowMode {
49+
ScrollWindowExtend,
50+
ScrollWindowShrink,
51+
};
52+
53+
/// Set the scroll window for the given mode.
54+
void UpdateScrollWindow(ScrollWindowMode mode);
55+
56+
/// Clear the statusline (without redrawing the background).
57+
void Reset();
58+
59+
Debugger &m_debugger;
60+
std::string m_last_str;
61+
62+
volatile std::sig_atomic_t m_terminal_size_has_changed = 1;
63+
uint64_t m_terminal_width = 0;
64+
uint64_t m_terminal_height = 0;
65+
};
66+
} // namespace lldb_private
67+
#endif // LLDB_CORE_STATUSLINE_H

lldb/include/lldb/Host/Editline.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ using SuggestionCallbackType =
102102

103103
using CompleteCallbackType = llvm::unique_function<void(CompletionRequest &)>;
104104

105+
using RedrawCallbackType = llvm::unique_function<void()>;
106+
105107
/// Status used to decide when and how to start editing another line in
106108
/// multi-line sessions.
107109
enum class EditorStatus {
@@ -194,6 +196,11 @@ class Editline {
194196
m_suggestion_callback = std::move(callback);
195197
}
196198

199+
/// Register a callback for redrawing the statusline.
200+
void SetRedrawCallback(RedrawCallbackType callback) {
201+
m_redraw_callback = std::move(callback);
202+
}
203+
197204
/// Register a callback for the tab key
198205
void SetAutoCompleteCallback(CompleteCallbackType callback) {
199206
m_completion_callback = std::move(callback);
@@ -409,6 +416,7 @@ class Editline {
409416

410417
CompleteCallbackType m_completion_callback;
411418
SuggestionCallbackType m_suggestion_callback;
419+
RedrawCallbackType m_redraw_callback;
412420

413421
bool m_color;
414422
std::string m_prompt_ansi_prefix;

lldb/include/lldb/Utility/AnsiTerminal.h

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,12 @@
7070
#define ANSI_1_CTRL(ctrl1) "\033["##ctrl1 ANSI_ESC_END
7171
#define ANSI_2_CTRL(ctrl1, ctrl2) "\033["##ctrl1 ";"##ctrl2 ANSI_ESC_END
7272

73+
#define ANSI_ESC_START_LEN 2
74+
7375
#include "llvm/ADT/ArrayRef.h"
7476
#include "llvm/ADT/STLExtras.h"
7577
#include "llvm/ADT/StringRef.h"
78+
#include "llvm/Support/Locale.h"
7679

7780
#include <string>
7881

@@ -172,28 +175,89 @@ inline std::string FormatAnsiTerminalCodes(llvm::StringRef format,
172175
return fmt;
173176
}
174177

178+
inline std::tuple<llvm::StringRef, llvm::StringRef, llvm::StringRef>
179+
FindNextAnsiSequence(llvm::StringRef str) {
180+
llvm::StringRef left;
181+
llvm::StringRef right = str;
182+
183+
while (!right.empty()) {
184+
const size_t start = right.find(ANSI_ESC_START);
185+
186+
// ANSI_ESC_START not found.
187+
if (start == llvm::StringRef::npos)
188+
return {str, {}, {}};
189+
190+
// Split the string around the current ANSI_ESC_START.
191+
left = str.take_front(left.size() + start);
192+
llvm::StringRef escape = right.substr(start);
193+
right = right.substr(start + ANSI_ESC_START_LEN + 1);
194+
195+
const size_t end = right.find_first_not_of("0123456789;");
196+
197+
// ANSI_ESC_END found.
198+
if (end < right.size() && (right[end] == 'm' || right[end] == 'G'))
199+
return {left, escape.take_front(ANSI_ESC_START_LEN + 1 + end + 1),
200+
right.substr(end + 1)};
201+
202+
// Maintain the invariant that str == left + right at the start of the loop.
203+
left = str.take_front(left.size() + ANSI_ESC_START_LEN + 1);
204+
}
205+
206+
return {str, {}, {}};
207+
}
208+
175209
inline std::string StripAnsiTerminalCodes(llvm::StringRef str) {
176210
std::string stripped;
177211
while (!str.empty()) {
178-
llvm::StringRef left, right;
179-
180-
std::tie(left, right) = str.split(ANSI_ESC_START);
212+
auto [left, escape, right] = FindNextAnsiSequence(str);
181213
stripped += left;
214+
str = right;
215+
}
216+
return stripped;
217+
}
182218

183-
// ANSI_ESC_START not found.
184-
if (left == str && right.empty())
185-
break;
219+
inline std::string TrimAndPad(llvm::StringRef str, size_t visible_length,
220+
char padding = ' ') {
221+
std::string result;
222+
result.reserve(visible_length);
223+
size_t result_visibile_length = 0;
186224

187-
size_t end = right.find_first_not_of("0123456789;");
188-
if (end < right.size() && (right[end] == 'm' || right[end] == 'G')) {
189-
str = right.substr(end + 1);
190-
} else {
191-
// ANSI_ESC_END not found.
192-
stripped += ANSI_ESC_START;
193-
str = right;
225+
// Trim the string to the given visible length.
226+
while (!str.empty()) {
227+
auto [left, escape, right] = FindNextAnsiSequence(str);
228+
str = right;
229+
230+
// Compute the length of the string without escape codes. If it fits, append
231+
// it together with the invisible escape code.
232+
size_t column_width = llvm::sys::locale::columnWidth(left);
233+
if (result_visibile_length + column_width <= visible_length) {
234+
result.append(left).append(escape);
235+
result_visibile_length += column_width;
236+
continue;
237+
}
238+
239+
// The string might contain unicode which means it's not safe to truncate.
240+
// Repeatedly trim the string until it its valid unicode and fits.
241+
llvm::StringRef trimmed = left;
242+
while (!trimmed.empty()) {
243+
// This relies on columnWidth returning -2 for invalid/partial unicode
244+
// characters, which after conversion to size_t will be larger than the
245+
// visible width.
246+
column_width = llvm::sys::locale::columnWidth(trimmed);
247+
if (result_visibile_length + column_width <= visible_length) {
248+
result.append(trimmed);
249+
result_visibile_length += column_width;
250+
break;
251+
}
252+
trimmed = trimmed.drop_back();
194253
}
195254
}
196-
return stripped;
255+
256+
// Pad the string.
257+
if (result_visibile_length < visible_length)
258+
result.append(visible_length - result_visibile_length, padding);
259+
260+
return result;
197261
}
198262

199263
} // namespace ansi

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,10 @@ def setUpCommands(cls):
771771
'settings set symbols.clang-modules-cache-path "{}"'.format(
772772
configuration.lldb_module_cache_dir
773773
),
774+
# Disable colors by default.
775+
"settings set use-color false",
776+
# Disable the statusline by default.
777+
"settings set show-statusline false",
774778
# Enable the swift metadata cache in order to speed up tests.
775779
"settings set symbols.enable-swift-metadata-cache true",
776780
'settings set symbols.swift-metadata-cache-path "{}"'.format(
@@ -779,7 +783,6 @@ def setUpCommands(cls):
779783
# Enable expensive validations in TypeSystemSwiftTypeRef.
780784
"settings set symbols.swift-validate-typesystem true",
781785
"settings set symbols.swift-typesystem-compiler-fallback true",
782-
"settings set use-color false",
783786
]
784787

785788
# Set any user-overridden settings.

lldb/source/Core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ add_lldb_library(lldbCore
4747
Opcode.cpp
4848
PluginManager.cpp
4949
Progress.cpp
50+
Statusline.cpp
5051
RichManglingContext.cpp
5152
SearchFilter.cpp
5253
Section.cpp

lldb/source/Core/CoreProperties.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,14 @@ let Definition = "debugger" in {
225225
Global,
226226
DefaultStringValue<"${ansi.normal}">,
227227
Desc<"When displaying progress in a color-enabled terminal, use the ANSI terminal code specified in this format immediately after the progress message.">;
228+
def ShowStatusline: Property<"show-statusline", "Boolean">,
229+
Global,
230+
DefaultTrue,
231+
Desc<"Whether to show a statusline at the bottom of the terminal.">;
232+
def StatuslineFormat: Property<"statusline-format", "FormatEntity">,
233+
Global,
234+
DefaultStringValue<"${ansi.negative}{${target.file.basename}}{ | ${line.file.basename}:${line.number}:${line.column}}{ | ${thread.stop-reason}}{ | {${progress.count} }${progress.message}}">,
235+
Desc<"The default statusline format string.">;
228236
def UseSourceCache: Property<"use-source-cache", "Boolean">,
229237
Global,
230238
DefaultTrue,

0 commit comments

Comments
 (0)