1//===-- Statusline.cpp ---------------------------------------------------===//
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#include "lldb/Core/Statusline.h"
10#include "lldb/Core/Debugger.h"
11#include "lldb/Core/FormatEntity.h"
12#include "lldb/Host/StreamFile.h"
13#include "lldb/Interpreter/CommandInterpreter.h"
14#include "lldb/Symbol/SymbolContext.h"
15#include "lldb/Target/Process.h"
16#include "lldb/Target/StackFrame.h"
17#include "lldb/Utility/AnsiTerminal.h"
18#include "lldb/Utility/StreamString.h"
19#include "llvm/ADT/StringRef.h"
20#include "llvm/Support/Locale.h"
21
22#define ESCAPE "\x1b"
23#define ANSI_NORMAL ESCAPE "[0m"
24#define ANSI_SAVE_CURSOR ESCAPE "7"
25#define ANSI_RESTORE_CURSOR ESCAPE "8"
26#define ANSI_CLEAR_BELOW ESCAPE "[J"
27#define ANSI_SET_SCROLL_ROWS ESCAPE "[1;%ur"
28#define ANSI_TO_START_OF_ROW ESCAPE "[%u;1f"
29#define ANSI_REVERSE_VIDEO ESCAPE "[7m"
30#define ANSI_UP_ROWS ESCAPE "[%dA"
31
32using namespace lldb;
33using namespace lldb_private;
34
35Statusline::Statusline(Debugger &debugger)
36 : m_debugger(debugger), m_terminal_width(m_debugger.GetTerminalWidth()),
37 m_terminal_height(m_debugger.GetTerminalHeight()) {
38 Enable();
39}
40
41Statusline::~Statusline() { Disable(); }
42
43void Statusline::TerminalSizeChanged() {
44 UpdateTerminalProperties();
45
46 // This definitely isn't signal safe, but the best we can do, until we
47 // have proper signal-catching thread.
48 Redraw(/*update=*/false);
49}
50
51void Statusline::Enable() {
52 // Reduce the scroll window to make space for the status bar below.
53 UpdateScrollWindow(mode: EnableStatusline);
54
55 // Draw the statusline.
56 Redraw(/*update=*/true);
57}
58
59void Statusline::Disable() {
60 // Extend the scroll window to cover the status bar.
61 UpdateScrollWindow(mode: DisableStatusline);
62}
63
64void Statusline::Draw(std::string str) {
65 lldb::LockableStreamFileSP stream_sp = m_debugger.GetOutputStreamSP();
66 if (!stream_sp)
67 return;
68
69 m_last_str = str;
70
71 str = ansi::TrimAndPad(str, visible_length: m_terminal_width);
72
73 LockedStreamFile locked_stream = stream_sp->Lock();
74 locked_stream << ANSI_SAVE_CURSOR;
75 locked_stream.Printf(ANSI_TO_START_OF_ROW,
76 static_cast<unsigned>(m_terminal_height));
77
78 // Use "reverse video" to make sure the statusline has a background. Only do
79 // this when colors are disabled, and rely on the statusline format otherwise.
80 if (!m_debugger.GetUseColor())
81 locked_stream << ANSI_REVERSE_VIDEO;
82
83 locked_stream << str;
84 locked_stream << ANSI_NORMAL;
85 locked_stream << ANSI_RESTORE_CURSOR;
86}
87
88void Statusline::UpdateTerminalProperties() {
89 UpdateScrollWindow(mode: DisableStatusline);
90 m_terminal_width = m_debugger.GetTerminalWidth();
91 m_terminal_height = m_debugger.GetTerminalHeight();
92 UpdateScrollWindow(mode: EnableStatusline);
93}
94
95void Statusline::UpdateScrollWindow(ScrollWindowMode mode) {
96 assert(m_terminal_width != 0 && m_terminal_height != 0);
97
98 lldb::LockableStreamFileSP stream_sp = m_debugger.GetOutputStreamSP();
99 if (!stream_sp)
100 return;
101
102 const unsigned scroll_height =
103 (mode == DisableStatusline) ? m_terminal_height : m_terminal_height - 1;
104
105 LockedStreamFile locked_stream = stream_sp->Lock();
106 locked_stream << ANSI_SAVE_CURSOR;
107 locked_stream.Printf(ANSI_SET_SCROLL_ROWS, scroll_height);
108 locked_stream << ANSI_RESTORE_CURSOR;
109 switch (mode) {
110 case EnableStatusline:
111 // Move everything on the screen up.
112 locked_stream.Printf(ANSI_UP_ROWS, 1);
113 locked_stream << '\n';
114 break;
115 case DisableStatusline:
116 // Clear the screen below to hide the old statusline.
117 locked_stream << ANSI_CLEAR_BELOW;
118 break;
119 }
120}
121
122void Statusline::Redraw(bool update) {
123 if (!update) {
124 Draw(str: m_last_str);
125 return;
126 }
127
128 ExecutionContext exe_ctx = m_debugger.GetSelectedExecutionContext();
129
130 // For colors and progress events, the format entity needs access to the
131 // debugger, which requires a target in the execution context.
132 if (!exe_ctx.HasTargetScope())
133 exe_ctx.SetTargetPtr(&m_debugger.GetSelectedOrDummyTarget());
134
135 SymbolContext symbol_ctx;
136 if (ProcessSP process_sp = exe_ctx.GetProcessSP()) {
137 // Check if the process is stopped, and if it is, make sure it remains
138 // stopped until we've computed the symbol context.
139 Process::StopLocker stop_locker;
140 if (stop_locker.TryLock(lock: &process_sp->GetRunLock())) {
141 if (auto frame_sp = exe_ctx.GetFrameSP())
142 symbol_ctx = frame_sp->GetSymbolContext(resolve_scope: eSymbolContextEverything);
143 }
144 }
145
146 StreamString stream;
147 FormatEntity::Entry format = m_debugger.GetStatuslineFormat();
148 FormatEntity::Format(entry: format, s&: stream, sc: &symbol_ctx, exe_ctx: &exe_ctx, addr: nullptr, valobj: nullptr,
149 function_changed: false, initial_function: false);
150
151 Draw(str: stream.GetString().str());
152}
153

source code of lldb/source/Core/Statusline.cpp