Skip to content

Commit ceef605

Browse files
committed
Add a Debugger interruption mechanism in conjunction with the
Command Interpreter mechanism. Differential Revision: https://reviews.llvm.org/D145136 (cherry picked from commit fe61b38)
1 parent 4e70baf commit ceef605

File tree

16 files changed

+616
-20
lines changed

16 files changed

+616
-20
lines changed

lldb/include/lldb/API/SBCommandInterpreter.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,20 @@ class SBCommandInterpreter {
235235
lldb::SBStringList &matches,
236236
lldb::SBStringList &descriptions);
237237

238+
/// Returns whether an interrupt flag was raised either by the SBDebugger -
239+
/// when the function is not running on the RunCommandInterpreter thread, or
240+
/// by SBCommandInterpreter::InterruptCommand if it is. If your code is doing
241+
/// interruptible work, check this API periodically, and interrupt if it
242+
/// returns true.
238243
bool WasInterrupted() const;
244+
245+
/// Interrupts the command currently executing in the RunCommandInterpreter
246+
/// thread.
247+
///
248+
/// \return
249+
/// \b true if there was a command in progress to recieve the interrupt.
250+
/// \b false if there's no command currently in flight.
251+
bool InterruptCommand();
239252

240253
// Catch commands before they execute by registering a callback that will get
241254
// called when the command gets executed. This allows GUI or command line

lldb/include/lldb/API/SBDebugger.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ class LLDB_API SBDebugger {
177177
lldb::SBCommandInterpreter GetCommandInterpreter();
178178

179179
void HandleCommand(const char *command);
180+
181+
void RequestInterrupt();
182+
void CancelInterruptRequest();
183+
bool InterruptRequested();
180184

181185
lldb::SBListener GetListener();
182186

lldb/include/lldb/Core/Debugger.h

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,48 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
373373
bool IsHandlingEvents() const { return m_event_handler_thread.IsJoinable(); }
374374

375375
Status RunREPL(lldb::LanguageType language, const char *repl_options);
376+
377+
/// Interruption in LLDB:
378+
///
379+
/// This is a voluntary interruption mechanism, not preemptive. Parts of lldb
380+
/// that do work that can be safely interrupted call
381+
/// Debugger::InterruptRequested and if that returns true, they should return
382+
/// at a safe point, shortcutting the rest of the work they were to do.
383+
///
384+
/// lldb clients can both offer a CommandInterpreter (through
385+
/// RunCommandInterpreter) and use the SB API's for their own purposes, so it
386+
/// is convenient to separate "interrupting the CommandInterpreter execution"
387+
/// and interrupting the work it is doing with the SB API's. So there are two
388+
/// ways to cause an interrupt:
389+
/// * CommandInterpreter::InterruptCommand: Interrupts the command currently
390+
/// running in the command interpreter IOHandler thread
391+
/// * Debugger::RequestInterrupt: Interrupts are active on anything but the
392+
/// CommandInterpreter thread till CancelInterruptRequest is called.
393+
///
394+
/// Since the two checks are mutually exclusive, however, it's also convenient
395+
/// to have just one function to check the interrupt state.
396+
397+
398+
/// Bump the "interrupt requested" count on the debugger to support
399+
/// cooperative interruption. If this is non-zero, InterruptRequested will
400+
/// return true. Interruptible operations are expected to query the
401+
/// InterruptRequested API periodically, and interrupt what they were doing
402+
/// if it returns \b true.
403+
///
404+
void RequestInterrupt();
405+
406+
/// Decrement the "interrupt requested" counter.
407+
void CancelInterruptRequest();
408+
409+
/// This is the correct way to query the state of Interruption.
410+
/// If you are on the RunCommandInterpreter thread, it will check the
411+
/// command interpreter state, and if it is on another thread it will
412+
/// check the debugger Interrupt Request state.
413+
///
414+
/// \return
415+
/// A boolean value, if \b true an interruptible operation should interrupt
416+
/// itself.
417+
bool InterruptRequested();
376418

377419
bool REPLIsActive() { return m_io_handler_stack.REPLIsActive(); }
378420

@@ -492,13 +534,19 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
492534

493535
bool PopIOHandler(const lldb::IOHandlerSP &reader_sp);
494536

495-
bool HasIOHandlerThread();
537+
bool HasIOHandlerThread() const;
496538

497539
bool StartIOHandlerThread();
498540

499541
void StopIOHandlerThread();
542+
543+
// Sets the IOHandler thread to the new_thread, and returns
544+
// the previous IOHandler thread.
545+
HostThread SetIOHandlerThread(HostThread &new_thread);
500546

501547
void JoinIOHandlerThread();
548+
549+
bool IsIOHandlerThreadCurrentThread() const;
502550

503551
lldb::thread_result_t IOHandlerThread();
504552

@@ -578,6 +626,9 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
578626
llvm::once_flag m_clear_once;
579627
lldb::TargetSP m_dummy_target_sp;
580628

629+
uint32_t m_interrupt_requested = 0; ///< Tracks interrupt requests
630+
std::mutex m_interrupt_mutex;
631+
581632
// Events for m_sync_broadcaster
582633
enum {
583634
eBroadcastBitEventThreadIsListening = (1 << 0),

lldb/include/lldb/Interpreter/CommandInterpreter.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ class CommandInterpreter : public Broadcaster,
354354
CommandReturnObject &result,
355355
bool force_repeat_command = false);
356356

357-
bool WasInterrupted() const;
357+
bool InterruptCommand();
358358

359359
/// Execute a list of commands in sequence.
360360
///
@@ -639,6 +639,10 @@ class CommandInterpreter : public Broadcaster,
639639
protected:
640640
friend class Debugger;
641641

642+
// This checks just the RunCommandInterpreter interruption state. It is only
643+
// meant to be used in Debugger::InterruptRequested
644+
bool WasInterrupted() const;
645+
642646
// IOHandlerDelegate functions
643647
void IOHandlerInputComplete(IOHandler &io_handler,
644648
std::string &line) override;
@@ -701,7 +705,6 @@ class CommandInterpreter : public Broadcaster,
701705

702706
void StartHandlingCommand();
703707
void FinishHandlingCommand();
704-
bool InterruptCommand();
705708

706709
Debugger &m_debugger; // The debugger session that this interpreter is
707710
// associated with

lldb/source/API/SBCommandInterpreter.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,13 @@ bool SBCommandInterpreter::IsActive() {
140140
bool SBCommandInterpreter::WasInterrupted() const {
141141
LLDB_INSTRUMENT_VA(this);
142142

143-
return (IsValid() ? m_opaque_ptr->WasInterrupted() : false);
143+
return (IsValid() ? m_opaque_ptr->GetDebugger().InterruptRequested() : false);
144+
}
145+
146+
bool SBCommandInterpreter::InterruptCommand() {
147+
LLDB_INSTRUMENT_VA(this);
148+
149+
return (IsValid() ? m_opaque_ptr->InterruptCommand() : false);
144150
}
145151

146152
const char *SBCommandInterpreter::GetIOHandlerControlSequence(char ch) {

lldb/source/API/SBDebugger.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,3 +1673,24 @@ SBDebugger::LoadTraceFromFile(SBError &error,
16731673
LLDB_INSTRUMENT_VA(this, error, trace_description_file);
16741674
return SBTrace::LoadTraceFromFile(error, *this, trace_description_file);
16751675
}
1676+
1677+
void SBDebugger::RequestInterrupt() {
1678+
LLDB_INSTRUMENT_VA(this);
1679+
1680+
if (m_opaque_sp)
1681+
m_opaque_sp->RequestInterrupt();
1682+
}
1683+
void SBDebugger::CancelInterruptRequest() {
1684+
LLDB_INSTRUMENT_VA(this);
1685+
1686+
if (m_opaque_sp)
1687+
m_opaque_sp->CancelInterruptRequest();
1688+
}
1689+
1690+
bool SBDebugger::InterruptRequested() {
1691+
LLDB_INSTRUMENT_VA(this);
1692+
1693+
if (m_opaque_sp)
1694+
return m_opaque_sp->InterruptRequested();
1695+
return false;
1696+
}

lldb/source/API/SBFrame.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "Utils.h"
1818
#include "lldb/Core/Address.h"
19+
#include "lldb/Core/Debugger.h"
1920
#include "lldb/Core/StreamFile.h"
2021
#include "lldb/Core/StructuredDataImpl.h"
2122
#include "lldb/Core/ValueObjectRegister.h"
@@ -814,6 +815,7 @@ SBValueList SBFrame::GetVariables(const lldb::SBVariablesOptions &options) {
814815
if (stop_locker.TryLock(&process->GetRunLock())) {
815816
frame = exe_ctx.GetFramePtr();
816817
if (frame) {
818+
Debugger &dbg = process->GetTarget().GetDebugger();
817819
VariableList *variable_list = nullptr;
818820
Status var_error;
819821
variable_list = frame->GetVariableList(true, &var_error);
@@ -823,6 +825,11 @@ SBValueList SBFrame::GetVariables(const lldb::SBVariablesOptions &options) {
823825
const size_t num_variables = variable_list->GetSize();
824826
if (num_variables) {
825827
for (const VariableSP &variable_sp : *variable_list) {
828+
if (dbg.InterruptRequested()) {
829+
Log *log = GetLog(LLDBLog::Host);
830+
LLDB_LOG(log, "Interrupted SBFrame::GetVariables");
831+
return {};
832+
}
826833
if (variable_sp) {
827834
bool add_variable = false;
828835
switch (variable_sp->GetScope()) {

lldb/source/Commands/CommandObjectTarget.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2002,7 +2002,7 @@ class CommandObjectTargetModulesDumpSymtab
20022002
result.GetOutputStream().EOL();
20032003
result.GetOutputStream().EOL();
20042004
}
2005-
if (m_interpreter.WasInterrupted())
2005+
if (GetDebugger().InterruptRequested())
20062006
break;
20072007
num_dumped++;
20082008
DumpModuleSymtab(m_interpreter, result.GetOutputStream(),
@@ -2029,7 +2029,7 @@ class CommandObjectTargetModulesDumpSymtab
20292029
result.GetOutputStream().EOL();
20302030
result.GetOutputStream().EOL();
20312031
}
2032-
if (m_interpreter.WasInterrupted())
2032+
if (GetDebugger().InterruptRequested())
20332033
break;
20342034
num_dumped++;
20352035
DumpModuleSymtab(m_interpreter, result.GetOutputStream(),
@@ -2090,7 +2090,7 @@ class CommandObjectTargetModulesDumpSections
20902090
result.GetOutputStream().Format("Dumping sections for {0} modules.\n",
20912091
num_modules);
20922092
for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
2093-
if (m_interpreter.WasInterrupted())
2093+
if (GetDebugger().InterruptRequested())
20942094
break;
20952095
num_dumped++;
20962096
DumpModuleSections(
@@ -2108,7 +2108,7 @@ class CommandObjectTargetModulesDumpSections
21082108
FindModulesByName(target, arg_cstr, module_list, true);
21092109
if (num_matches > 0) {
21102110
for (size_t i = 0; i < num_matches; ++i) {
2111-
if (m_interpreter.WasInterrupted())
2111+
if (GetDebugger().InterruptRequested())
21122112
break;
21132113
Module *module = module_list.GetModulePointerAtIndex(i);
21142114
if (module) {
@@ -2222,7 +2222,7 @@ class CommandObjectTargetModulesDumpClangAST
22222222
result.GetOutputStream().Format("Dumping clang ast for {0} modules.\n",
22232223
num_modules);
22242224
for (ModuleSP module_sp : module_list.ModulesNoLocking()) {
2225-
if (m_interpreter.WasInterrupted())
2225+
if (GetDebugger().InterruptRequested())
22262226
break;
22272227
if (SymbolFile *sf = module_sp->GetSymbolFile())
22282228
sf->DumpClangAST(result.GetOutputStream());
@@ -2247,7 +2247,7 @@ class CommandObjectTargetModulesDumpClangAST
22472247
}
22482248

22492249
for (size_t i = 0; i < num_matches; ++i) {
2250-
if (m_interpreter.WasInterrupted())
2250+
if (GetDebugger().InterruptRequested())
22512251
break;
22522252
Module *m = module_list.GetModulePointerAtIndex(i);
22532253
if (SymbolFile *sf = m->GetSymbolFile())
@@ -2296,7 +2296,7 @@ class CommandObjectTargetModulesDumpSymfile
22962296
result.GetOutputStream().Format(
22972297
"Dumping debug symbols for {0} modules.\n", num_modules);
22982298
for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
2299-
if (m_interpreter.WasInterrupted())
2299+
if (GetDebugger().InterruptRequested())
23002300
break;
23012301
if (DumpModuleSymbolFile(result.GetOutputStream(), module_sp.get()))
23022302
num_dumped++;
@@ -2312,7 +2312,7 @@ class CommandObjectTargetModulesDumpSymfile
23122312
FindModulesByName(target, arg_cstr, module_list, true);
23132313
if (num_matches > 0) {
23142314
for (size_t i = 0; i < num_matches; ++i) {
2315-
if (m_interpreter.WasInterrupted())
2315+
if (GetDebugger().InterruptRequested())
23162316
break;
23172317
Module *module = module_list.GetModulePointerAtIndex(i);
23182318
if (module) {
@@ -2379,7 +2379,7 @@ class CommandObjectTargetModulesDumpLineTable
23792379
if (target_modules.GetSize() > 0) {
23802380
uint32_t num_dumped = 0;
23812381
for (ModuleSP module_sp : target_modules.ModulesNoLocking()) {
2382-
if (m_interpreter.WasInterrupted())
2382+
if (GetDebugger().InterruptRequested())
23832383
break;
23842384
if (DumpCompileUnitLineTable(
23852385
m_interpreter, result.GetOutputStream(), module_sp.get(),

lldb/source/Commands/CommandObjectThread.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads {
227227
thread->GetIndexID());
228228
return false;
229229
}
230-
if (m_options.m_extended_backtrace) {
230+
if (m_options.m_extended_backtrace && !GetDebugger().InterruptRequested()) {
231231
DoExtendedBacktrace(thread, result);
232232
}
233233

lldb/source/Core/Debugger.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,29 @@ StreamSP Debugger::GetAsyncErrorStream() {
12361236
return std::make_shared<StreamAsynchronousIO>(*this, false, GetUseColor());
12371237
}
12381238

1239+
void Debugger::RequestInterrupt() {
1240+
std::lock_guard<std::mutex> guard(m_interrupt_mutex);
1241+
m_interrupt_requested++;
1242+
}
1243+
1244+
void Debugger::CancelInterruptRequest() {
1245+
std::lock_guard<std::mutex> guard(m_interrupt_mutex);
1246+
if (m_interrupt_requested > 0)
1247+
m_interrupt_requested--;
1248+
}
1249+
1250+
bool Debugger::InterruptRequested() {
1251+
// This is the one we should call internally. This will return true either
1252+
// if there's a debugger interrupt and we aren't on the IOHandler thread,
1253+
// or if we are on the IOHandler thread and there's a CommandInterpreter
1254+
// interrupt.
1255+
if (!IsIOHandlerThreadCurrentThread()) {
1256+
std::lock_guard<std::mutex> guard(m_interrupt_mutex);
1257+
return m_interrupt_requested != 0;
1258+
}
1259+
return GetCommandInterpreter().WasInterrupted();
1260+
}
1261+
12391262
size_t Debugger::GetNumDebuggers() {
12401263
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
12411264
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
@@ -1973,7 +1996,15 @@ void Debugger::HandleDiagnosticEvent(const lldb::EventSP &event_sp) {
19731996
data->Dump(stream.get());
19741997
}
19751998

1976-
bool Debugger::HasIOHandlerThread() { return m_io_handler_thread.IsJoinable(); }
1999+
bool Debugger::HasIOHandlerThread() const {
2000+
return m_io_handler_thread.IsJoinable();
2001+
}
2002+
2003+
HostThread Debugger::SetIOHandlerThread(HostThread &new_thread) {
2004+
HostThread old_host = m_io_handler_thread;
2005+
m_io_handler_thread = new_thread;
2006+
return old_host;
2007+
}
19772008

19782009
bool Debugger::StartIOHandlerThread() {
19792010
if (!m_io_handler_thread.IsJoinable()) {
@@ -2005,6 +2036,12 @@ void Debugger::JoinIOHandlerThread() {
20052036
}
20062037
}
20072038

2039+
bool Debugger::IsIOHandlerThreadCurrentThread() const {
2040+
if (!HasIOHandlerThread())
2041+
return false;
2042+
return m_io_handler_thread.EqualsThread(Host::GetCurrentThread());
2043+
}
2044+
20082045
Target &Debugger::GetSelectedOrDummyTarget(bool prefer_dummy) {
20092046
if (!prefer_dummy) {
20102047
if (TargetSP target = m_target_list.GetSelectedTarget())

0 commit comments

Comments
 (0)