Skip to content

Commit d136fbd

Browse files
authored
[lldb] Rework how we pass the execution context to the statusline (#159887)
Currently, we always pass the "selected" execution context to the statusline. When handling a process or thread event, we can be more precise, and build an execution context from the event data. This PR also adopts the new `StoppedExecutionContext` that was recently introduced.
1 parent bd2dac9 commit d136fbd

File tree

7 files changed

+169
-100
lines changed

7 files changed

+169
-100
lines changed

lldb/include/lldb/Core/Debugger.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,15 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
181181
return m_target_list.GetSelectedTarget();
182182
}
183183

184+
/// Get the execution context representing the selected entities in the
185+
/// selected target.
184186
ExecutionContext GetSelectedExecutionContext();
187+
188+
/// Similar to GetSelectedExecutionContext but returns a
189+
/// ExecutionContextRef, and will hold the dummy target if no target is
190+
/// currently selected.
191+
ExecutionContextRef GetSelectedExecutionContextRef();
192+
185193
/// Get accessor for the target list.
186194
///
187195
/// The target list is part of the global debugger object. This the single
@@ -419,7 +427,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
419427
void CancelInterruptRequest();
420428

421429
/// Redraw the statusline if enabled.
422-
void RedrawStatusline(bool update = true);
430+
void RedrawStatusline(std::optional<ExecutionContextRef> exe_ctx_ref);
423431

424432
/// This is the correct way to query the state of Interruption.
425433
/// If you are on the RunCommandInterpreter thread, it will check the
@@ -701,9 +709,9 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
701709

702710
void HandleBreakpointEvent(const lldb::EventSP &event_sp);
703711

704-
void HandleProcessEvent(const lldb::EventSP &event_sp);
712+
lldb::ProcessSP HandleProcessEvent(const lldb::EventSP &event_sp);
705713

706-
void HandleThreadEvent(const lldb::EventSP &event_sp);
714+
lldb::ThreadSP HandleThreadEvent(const lldb::EventSP &event_sp);
707715

708716
void HandleProgressEvent(const lldb::EventSP &event_sp);
709717

lldb/include/lldb/Core/Statusline.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#ifndef LLDB_CORE_STATUSLINE_H
1010
#define LLDB_CORE_STATUSLINE_H
1111

12+
#include "lldb/Symbol/SymbolContext.h"
13+
#include "lldb/Target/ExecutionContext.h"
1214
#include "lldb/lldb-forward.h"
1315
#include <cstdint>
1416
#include <string>
@@ -19,15 +21,16 @@ class Statusline {
1921
Statusline(Debugger &debugger);
2022
~Statusline();
2123

24+
using Context = std::pair<ExecutionContextRef, SymbolContext>;
25+
2226
/// Reduce the scroll window and draw the statusline.
23-
void Enable();
27+
void Enable(std::optional<ExecutionContextRef> exe_ctx_ref);
2428

2529
/// Hide the statusline and extend the scroll window.
2630
void Disable();
2731

28-
/// Redraw the statusline. If update is false, this will redraw the last
29-
/// string.
30-
void Redraw(bool update = true);
32+
/// Redraw the statusline.
33+
void Redraw(std::optional<ExecutionContextRef> exe_ctx_ref);
3134

3235
/// Inform the statusline that the terminal dimensions have changed.
3336
void TerminalSizeChanged();
@@ -46,7 +49,11 @@ class Statusline {
4649
void UpdateScrollWindow(ScrollWindowMode mode);
4750

4851
Debugger &m_debugger;
49-
std::string m_last_str;
52+
53+
/// Cached copy of the execution context that allows us to redraw the
54+
/// statusline.
55+
ExecutionContextRef m_exe_ctx_ref;
56+
5057
uint64_t m_terminal_width = 0;
5158
uint64_t m_terminal_height = 0;
5259
};

lldb/include/lldb/Target/ExecutionContext.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,21 @@ class ExecutionContextRef {
9292

9393
/// Construct using the target and all the selected items inside of it (the
9494
/// process and its selected thread, and the thread's selected frame). If
95-
/// there is no selected thread, default to the first thread If there is no
95+
/// there is no selected thread, default to the first thread. If there is no
9696
/// selected frame, default to the first frame.
9797
ExecutionContextRef(Target *target, bool adopt_selected);
9898

99+
/// Construct using the process and all the selected items inside of it (
100+
/// the selected thread, and the thread's selected frame). If
101+
/// there is no selected thread, default to the first thread. If there is no
102+
/// selected frame, default to the first frame.
103+
ExecutionContextRef(Process *process, bool adopt_selected);
104+
105+
/// Construct using the thread and all the selected items inside of it ( the
106+
/// selected frame). If there is no selected frame, default to the first
107+
/// frame.
108+
ExecutionContextRef(Thread *thread, bool adopt_selected);
109+
99110
/// Construct using an execution context scope.
100111
///
101112
/// If the ExecutionContextScope object is valid and refers to a frame, make
@@ -199,9 +210,9 @@ class ExecutionContextRef {
199210

200211
void SetTargetPtr(Target *target, bool adopt_selected);
201212

202-
void SetProcessPtr(Process *process);
213+
void SetProcessPtr(Process *process, bool adopt_selected = false);
203214

204-
void SetThreadPtr(Thread *thread);
215+
void SetThreadPtr(Thread *thread, bool adopt_selected = false);
205216

206217
void SetFramePtr(StackFrame *frame);
207218

lldb/source/Core/Debugger.cpp

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -253,16 +253,18 @@ Status Debugger::SetPropertyValue(const ExecutionContext *exe_ctx,
253253
// Statusline setting changed. If we have a statusline instance, update it
254254
// now. Otherwise it will get created in the default event handler.
255255
std::lock_guard<std::mutex> guard(m_statusline_mutex);
256-
if (StatuslineSupported())
256+
if (StatuslineSupported()) {
257257
m_statusline.emplace(*this);
258-
else
258+
m_statusline->Enable(GetSelectedExecutionContextRef());
259+
} else {
259260
m_statusline.reset();
261+
}
260262
} else if (property_path ==
261263
g_debugger_properties[ePropertyStatuslineFormat].name ||
262264
property_path ==
263265
g_debugger_properties[ePropertySeparator].name) {
264266
// Statusline format changed. Redraw the statusline.
265-
RedrawStatusline();
267+
RedrawStatusline(std::nullopt);
266268
} else if (property_path ==
267269
g_debugger_properties[ePropertyUseSourceCache].name) {
268270
// use-source-cache changed. Wipe out the cache contents if it was
@@ -501,7 +503,7 @@ FormatEntity::Entry Debugger::GetStatuslineFormat() const {
501503
bool Debugger::SetStatuslineFormat(const FormatEntity::Entry &format) {
502504
constexpr uint32_t idx = ePropertyStatuslineFormat;
503505
bool ret = SetPropertyAtIndex(idx, format);
504-
RedrawStatusline();
506+
RedrawStatusline(std::nullopt);
505507
return ret;
506508
}
507509

@@ -526,7 +528,7 @@ llvm::StringRef Debugger::GetDisabledAnsiSuffix() const {
526528
bool Debugger::SetSeparator(llvm::StringRef s) {
527529
constexpr uint32_t idx = ePropertySeparator;
528530
bool ret = SetPropertyAtIndex(idx, s);
529-
RedrawStatusline();
531+
RedrawStatusline(std::nullopt);
530532
return ret;
531533
}
532534

@@ -1210,14 +1212,18 @@ void Debugger::RestoreInputTerminalState() {
12101212
{
12111213
std::lock_guard<std::mutex> guard(m_statusline_mutex);
12121214
if (m_statusline)
1213-
m_statusline->Enable();
1215+
m_statusline->Enable(GetSelectedExecutionContext());
12141216
}
12151217
}
12161218

1217-
void Debugger::RedrawStatusline(bool update) {
1219+
void Debugger::RedrawStatusline(
1220+
std::optional<ExecutionContextRef> exe_ctx_ref) {
12181221
std::lock_guard<std::mutex> guard(m_statusline_mutex);
1219-
if (m_statusline)
1220-
m_statusline->Redraw(update);
1222+
1223+
if (!m_statusline)
1224+
return;
1225+
1226+
m_statusline->Redraw(exe_ctx_ref);
12211227
}
12221228

12231229
ExecutionContext Debugger::GetSelectedExecutionContext() {
@@ -1226,6 +1232,13 @@ ExecutionContext Debugger::GetSelectedExecutionContext() {
12261232
return ExecutionContext(exe_ctx_ref);
12271233
}
12281234

1235+
ExecutionContextRef Debugger::GetSelectedExecutionContextRef() {
1236+
if (TargetSP selected_target_sp = GetSelectedTarget())
1237+
return ExecutionContextRef(selected_target_sp.get(),
1238+
/*adopt_selected=*/true);
1239+
return ExecutionContextRef(m_dummy_target_sp.get(), /*adopt_selected=*/false);
1240+
}
1241+
12291242
void Debugger::DispatchInputInterrupt() {
12301243
std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex());
12311244
IOHandlerSP reader_sp(m_io_handler_stack.Top());
@@ -1941,8 +1954,7 @@ void Debugger::FlushProcessOutput(Process &process, bool flush_stdout,
19411954
}
19421955

19431956
// This function handles events that were broadcast by the process.
1944-
void Debugger::HandleProcessEvent(const EventSP &event_sp) {
1945-
using namespace lldb;
1957+
ProcessSP Debugger::HandleProcessEvent(const EventSP &event_sp) {
19461958
const uint32_t event_type = event_sp->GetType();
19471959
ProcessSP process_sp =
19481960
(event_type == Process::eBroadcastBitStructuredData)
@@ -2024,23 +2036,24 @@ void Debugger::HandleProcessEvent(const EventSP &event_sp) {
20242036
if (pop_process_io_handler)
20252037
process_sp->PopProcessIOHandler();
20262038
}
2039+
return process_sp;
20272040
}
20282041

2029-
void Debugger::HandleThreadEvent(const EventSP &event_sp) {
2042+
ThreadSP Debugger::HandleThreadEvent(const EventSP &event_sp) {
20302043
// At present the only thread event we handle is the Frame Changed event, and
20312044
// all we do for that is just reprint the thread status for that thread.
2032-
using namespace lldb;
20332045
const uint32_t event_type = event_sp->GetType();
20342046
const bool stop_format = true;
2047+
ThreadSP thread_sp;
20352048
if (event_type == Thread::eBroadcastBitStackChanged ||
20362049
event_type == Thread::eBroadcastBitThreadSelected) {
2037-
ThreadSP thread_sp(
2038-
Thread::ThreadEventData::GetThreadFromEvent(event_sp.get()));
2050+
thread_sp = Thread::ThreadEventData::GetThreadFromEvent(event_sp.get());
20392051
if (thread_sp) {
20402052
thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1, stop_format,
20412053
/*show_hidden*/ true);
20422054
}
20432055
}
2056+
return thread_sp;
20442057
}
20452058

20462059
bool Debugger::IsForwardingEvents() { return (bool)m_forward_listener_sp; }
@@ -2068,6 +2081,11 @@ bool Debugger::StatuslineSupported() {
20682081
return false;
20692082
}
20702083

2084+
static bool RequiresFollowChildWorkaround(const Process &process) {
2085+
// FIXME: https://github.com/llvm/llvm-project/issues/160216
2086+
return process.GetFollowForkMode() == eFollowChild;
2087+
}
2088+
20712089
lldb::thread_result_t Debugger::DefaultEventHandler() {
20722090
ListenerSP listener_sp(GetListener());
20732091
ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
@@ -2109,28 +2127,37 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
21092127

21102128
if (StatuslineSupported()) {
21112129
std::lock_guard<std::mutex> guard(m_statusline_mutex);
2112-
if (!m_statusline)
2130+
if (!m_statusline) {
21132131
m_statusline.emplace(*this);
2132+
m_statusline->Enable(GetSelectedExecutionContextRef());
2133+
}
21142134
}
21152135

21162136
bool done = false;
21172137
while (!done) {
21182138
EventSP event_sp;
21192139
if (listener_sp->GetEvent(event_sp, std::nullopt)) {
2140+
std::optional<ExecutionContextRef> exe_ctx_ref = std::nullopt;
21202141
if (event_sp) {
21212142
Broadcaster *broadcaster = event_sp->GetBroadcaster();
21222143
if (broadcaster) {
21232144
uint32_t event_type = event_sp->GetType();
21242145
ConstString broadcaster_class(broadcaster->GetBroadcasterClass());
21252146
if (broadcaster_class == broadcaster_class_process) {
2126-
HandleProcessEvent(event_sp);
2147+
if (ProcessSP process_sp = HandleProcessEvent(event_sp))
2148+
if (!RequiresFollowChildWorkaround(*process_sp))
2149+
exe_ctx_ref = ExecutionContextRef(process_sp.get(),
2150+
/*adopt_selected=*/true);
21272151
} else if (broadcaster_class == broadcaster_class_target) {
21282152
if (Breakpoint::BreakpointEventData::GetEventDataFromEvent(
21292153
event_sp.get())) {
21302154
HandleBreakpointEvent(event_sp);
21312155
}
21322156
} else if (broadcaster_class == broadcaster_class_thread) {
2133-
HandleThreadEvent(event_sp);
2157+
if (ThreadSP thread_sp = HandleThreadEvent(event_sp))
2158+
if (!RequiresFollowChildWorkaround(*thread_sp->GetProcess()))
2159+
exe_ctx_ref = ExecutionContextRef(thread_sp.get(),
2160+
/*adopt_selected=*/true);
21342161
} else if (broadcaster == m_command_interpreter_up.get()) {
21352162
if (event_type &
21362163
CommandInterpreter::eBroadcastBitQuitCommandReceived) {
@@ -2168,7 +2195,7 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
21682195
if (m_forward_listener_sp)
21692196
m_forward_listener_sp->AddEvent(event_sp);
21702197
}
2171-
RedrawStatusline();
2198+
RedrawStatusline(exe_ctx_ref);
21722199
}
21732200
}
21742201

lldb/source/Core/IOHandler.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request) {
442442
}
443443

444444
void IOHandlerEditline::RedrawCallback() {
445-
m_debugger.RedrawStatusline(/*update=*/false);
445+
m_debugger.RedrawStatusline(std::nullopt);
446446
}
447447

448448
#endif

lldb/source/Core/Statusline.cpp

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ using namespace lldb_private;
3535

3636
Statusline::Statusline(Debugger &debugger)
3737
: m_debugger(debugger), m_terminal_width(m_debugger.GetTerminalWidth()),
38-
m_terminal_height(m_debugger.GetTerminalHeight()) {
39-
Enable();
40-
}
38+
m_terminal_height(m_debugger.GetTerminalHeight()) {}
4139

4240
Statusline::~Statusline() { Disable(); }
4341

@@ -47,16 +45,16 @@ void Statusline::TerminalSizeChanged() {
4745

4846
UpdateScrollWindow(ResizeStatusline);
4947

50-
// Draw the old statusline.
51-
Redraw(/*update=*/false);
48+
// Redraw the old statusline.
49+
Redraw(std::nullopt);
5250
}
5351

54-
void Statusline::Enable() {
52+
void Statusline::Enable(std::optional<ExecutionContextRef> exe_ctx_ref) {
5553
// Reduce the scroll window to make space for the status bar below.
5654
UpdateScrollWindow(EnableStatusline);
5755

5856
// Draw the statusline.
59-
Redraw(/*update=*/true);
57+
Redraw(exe_ctx_ref);
6058
}
6159

6260
void Statusline::Disable() {
@@ -69,8 +67,6 @@ void Statusline::Draw(std::string str) {
6967
if (!stream_sp)
7068
return;
7169

72-
m_last_str = str;
73-
7470
str = ansi::TrimAndPad(str, m_terminal_width);
7571

7672
LockedStreamFile locked_stream = stream_sp->Lock();
@@ -127,33 +123,32 @@ void Statusline::UpdateScrollWindow(ScrollWindowMode mode) {
127123
m_debugger.RefreshIOHandler();
128124
}
129125

130-
void Statusline::Redraw(bool update) {
131-
if (!update) {
132-
Draw(m_last_str);
133-
return;
134-
}
135-
136-
ExecutionContext exe_ctx = m_debugger.GetSelectedExecutionContext();
137-
138-
// For colors and progress events, the format entity needs access to the
139-
// debugger, which requires a target in the execution context.
140-
if (!exe_ctx.HasTargetScope())
141-
exe_ctx.SetTargetPtr(&m_debugger.GetSelectedOrDummyTarget());
142-
143-
SymbolContext symbol_ctx;
144-
if (ProcessSP process_sp = exe_ctx.GetProcessSP()) {
145-
// Check if the process is stopped, and if it is, make sure it remains
146-
// stopped until we've computed the symbol context.
147-
Process::StopLocker stop_locker;
148-
if (stop_locker.TryLock(&process_sp->GetRunLock())) {
149-
if (auto frame_sp = exe_ctx.GetFrameSP())
150-
symbol_ctx = frame_sp->GetSymbolContext(eSymbolContextEverything);
151-
}
126+
void Statusline::Redraw(std::optional<ExecutionContextRef> exe_ctx_ref) {
127+
// Update the cached execution context.
128+
if (exe_ctx_ref)
129+
m_exe_ctx_ref = *exe_ctx_ref;
130+
131+
// Lock the execution context.
132+
ExecutionContext exe_ctx =
133+
m_exe_ctx_ref.Lock(/*thread_and_frame_only_if_stopped=*/false);
134+
135+
// Compute the symbol context if we're stopped.
136+
SymbolContext sym_ctx;
137+
llvm::Expected<StoppedExecutionContext> stopped_exe_ctx =
138+
GetStoppedExecutionContext(&m_exe_ctx_ref);
139+
if (stopped_exe_ctx) {
140+
// The StoppedExecutionContext only ensures that we hold the run lock.
141+
// The process could be in an exited or unloaded state and have no frame.
142+
if (auto frame_sp = stopped_exe_ctx->GetFrameSP())
143+
sym_ctx = frame_sp->GetSymbolContext(eSymbolContextEverything);
144+
} else {
145+
// We can draw the statusline without being stopped.
146+
llvm::consumeError(stopped_exe_ctx.takeError());
152147
}
153148

154149
StreamString stream;
155150
FormatEntity::Entry format = m_debugger.GetStatuslineFormat();
156-
FormatEntity::Format(format, stream, &symbol_ctx, &exe_ctx, nullptr, nullptr,
151+
FormatEntity::Format(format, stream, &sym_ctx, &exe_ctx, nullptr, nullptr,
157152
false, false);
158153

159154
Draw(stream.GetString().str());

0 commit comments

Comments
 (0)