Skip to content

Commit 3ef617c

Browse files
authored
Merge pull request #6516 from jimingham/swift-debugger-interrupt
Add a Debugger interruption mechanism in conjunction with the
2 parents 4d3ba69 + d20dcb0 commit 3ef617c

File tree

20 files changed

+749
-20
lines changed

20 files changed

+749
-20
lines changed

lldb/bindings/interface/SBCommandInterpreter.i

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ public:
174174

175175
bool
176176
WasInterrupted () const;
177+
178+
bool
179+
InterruptCommand();
177180
};
178181

179182
} // namespace lldb

lldb/bindings/interface/SBDebugger.i

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,10 @@ public:
263263
void
264264
HandleCommand (const char *command);
265265

266+
void RequestInterrupt();
267+
void CancelInterruptRequest();
268+
bool InterruptRequested();
269+
266270
lldb::SBListener
267271
GetListener ();
268272

lldb/bindings/python/static-binding/LLDBWrapPython.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15861,6 +15861,33 @@ SWIGINTERN PyObject *_wrap_SBCommandInterpreter_WasInterrupted(PyObject *self, P
1586115861
}
1586215862

1586315863

15864+
SWIGINTERN PyObject *_wrap_SBCommandInterpreter_InterruptCommand(PyObject *self, PyObject *args) {
15865+
PyObject *resultobj = 0;
15866+
lldb::SBCommandInterpreter *arg1 = (lldb::SBCommandInterpreter *) 0 ;
15867+
void *argp1 = 0 ;
15868+
int res1 = 0 ;
15869+
PyObject *swig_obj[1] ;
15870+
bool result;
15871+
15872+
if (!args) SWIG_fail;
15873+
swig_obj[0] = args;
15874+
res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_lldb__SBCommandInterpreter, 0 | 0 );
15875+
if (!SWIG_IsOK(res1)) {
15876+
SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SBCommandInterpreter_InterruptCommand" "', argument " "1"" of type '" "lldb::SBCommandInterpreter *""'");
15877+
}
15878+
arg1 = reinterpret_cast< lldb::SBCommandInterpreter * >(argp1);
15879+
{
15880+
SWIG_PYTHON_THREAD_BEGIN_ALLOW;
15881+
result = (bool)(arg1)->InterruptCommand();
15882+
SWIG_PYTHON_THREAD_END_ALLOW;
15883+
}
15884+
resultobj = SWIG_From_bool(static_cast< bool >(result));
15885+
return resultobj;
15886+
fail:
15887+
return NULL;
15888+
}
15889+
15890+
1586415891
SWIGINTERN PyObject *SBCommandInterpreter_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
1586515892
PyObject *obj;
1586615893
if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL;
@@ -23013,6 +23040,85 @@ SWIGINTERN PyObject *_wrap_SBDebugger_HandleCommand(PyObject *self, PyObject *ar
2301323040
}
2301423041

2301523042

23043+
SWIGINTERN PyObject *_wrap_SBDebugger_RequestInterrupt(PyObject *self, PyObject *args) {
23044+
PyObject *resultobj = 0;
23045+
lldb::SBDebugger *arg1 = (lldb::SBDebugger *) 0 ;
23046+
void *argp1 = 0 ;
23047+
int res1 = 0 ;
23048+
PyObject *swig_obj[1] ;
23049+
23050+
if (!args) SWIG_fail;
23051+
swig_obj[0] = args;
23052+
res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_lldb__SBDebugger, 0 | 0 );
23053+
if (!SWIG_IsOK(res1)) {
23054+
SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SBDebugger_RequestInterrupt" "', argument " "1"" of type '" "lldb::SBDebugger *""'");
23055+
}
23056+
arg1 = reinterpret_cast< lldb::SBDebugger * >(argp1);
23057+
{
23058+
SWIG_PYTHON_THREAD_BEGIN_ALLOW;
23059+
(arg1)->RequestInterrupt();
23060+
SWIG_PYTHON_THREAD_END_ALLOW;
23061+
}
23062+
resultobj = SWIG_Py_Void();
23063+
return resultobj;
23064+
fail:
23065+
return NULL;
23066+
}
23067+
23068+
23069+
SWIGINTERN PyObject *_wrap_SBDebugger_CancelInterruptRequest(PyObject *self, PyObject *args) {
23070+
PyObject *resultobj = 0;
23071+
lldb::SBDebugger *arg1 = (lldb::SBDebugger *) 0 ;
23072+
void *argp1 = 0 ;
23073+
int res1 = 0 ;
23074+
PyObject *swig_obj[1] ;
23075+
23076+
if (!args) SWIG_fail;
23077+
swig_obj[0] = args;
23078+
res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_lldb__SBDebugger, 0 | 0 );
23079+
if (!SWIG_IsOK(res1)) {
23080+
SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SBDebugger_CancelInterruptRequest" "', argument " "1"" of type '" "lldb::SBDebugger *""'");
23081+
}
23082+
arg1 = reinterpret_cast< lldb::SBDebugger * >(argp1);
23083+
{
23084+
SWIG_PYTHON_THREAD_BEGIN_ALLOW;
23085+
(arg1)->CancelInterruptRequest();
23086+
SWIG_PYTHON_THREAD_END_ALLOW;
23087+
}
23088+
resultobj = SWIG_Py_Void();
23089+
return resultobj;
23090+
fail:
23091+
return NULL;
23092+
}
23093+
23094+
23095+
SWIGINTERN PyObject *_wrap_SBDebugger_InterruptRequested(PyObject *self, PyObject *args) {
23096+
PyObject *resultobj = 0;
23097+
lldb::SBDebugger *arg1 = (lldb::SBDebugger *) 0 ;
23098+
void *argp1 = 0 ;
23099+
int res1 = 0 ;
23100+
PyObject *swig_obj[1] ;
23101+
bool result;
23102+
23103+
if (!args) SWIG_fail;
23104+
swig_obj[0] = args;
23105+
res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_lldb__SBDebugger, 0 | 0 );
23106+
if (!SWIG_IsOK(res1)) {
23107+
SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SBDebugger_InterruptRequested" "', argument " "1"" of type '" "lldb::SBDebugger *""'");
23108+
}
23109+
arg1 = reinterpret_cast< lldb::SBDebugger * >(argp1);
23110+
{
23111+
SWIG_PYTHON_THREAD_BEGIN_ALLOW;
23112+
result = (bool)(arg1)->InterruptRequested();
23113+
SWIG_PYTHON_THREAD_END_ALLOW;
23114+
}
23115+
resultobj = SWIG_From_bool(static_cast< bool >(result));
23116+
return resultobj;
23117+
fail:
23118+
return NULL;
23119+
}
23120+
23121+
2301623122
SWIGINTERN PyObject *_wrap_SBDebugger_GetListener(PyObject *self, PyObject *args) {
2301723123
PyObject *resultobj = 0;
2301823124
lldb::SBDebugger *arg1 = (lldb::SBDebugger *) 0 ;
@@ -84396,6 +84502,7 @@ static PyMethodDef SwigMethods[] = {
8439684502
{ "SBCommandInterpreter_HandleCompletionWithDescriptions", _wrap_SBCommandInterpreter_HandleCompletionWithDescriptions, METH_VARARGS, "SBCommandInterpreter_HandleCompletionWithDescriptions(SBCommandInterpreter self, char const * current_line, uint32_t cursor_pos, int match_start_point, int max_return_elements, SBStringList matches, SBStringList descriptions) -> int"},
8439784503
{ "SBCommandInterpreter_IsActive", _wrap_SBCommandInterpreter_IsActive, METH_O, "SBCommandInterpreter_IsActive(SBCommandInterpreter self) -> bool"},
8439884504
{ "SBCommandInterpreter_WasInterrupted", _wrap_SBCommandInterpreter_WasInterrupted, METH_O, "SBCommandInterpreter_WasInterrupted(SBCommandInterpreter self) -> bool"},
84505+
{ "SBCommandInterpreter_InterruptCommand", _wrap_SBCommandInterpreter_InterruptCommand, METH_O, "SBCommandInterpreter_InterruptCommand(SBCommandInterpreter self) -> bool"},
8439984506
{ "SBCommandInterpreter_swigregister", SBCommandInterpreter_swigregister, METH_O, NULL},
8440084507
{ "SBCommandInterpreter_swiginit", SBCommandInterpreter_swiginit, METH_VARARGS, NULL},
8440184508
{ "new_SBCommandInterpreterRunOptions", _wrap_new_SBCommandInterpreterRunOptions, METH_NOARGS, "new_SBCommandInterpreterRunOptions() -> SBCommandInterpreterRunOptions"},
@@ -84641,6 +84748,9 @@ static PyMethodDef SwigMethods[] = {
8464184748
{ "SBDebugger_GetErrorFile", _wrap_SBDebugger_GetErrorFile, METH_O, "SBDebugger_GetErrorFile(SBDebugger self) -> SBFile"},
8464284749
{ "SBDebugger_GetCommandInterpreter", _wrap_SBDebugger_GetCommandInterpreter, METH_O, "SBDebugger_GetCommandInterpreter(SBDebugger self) -> SBCommandInterpreter"},
8464384750
{ "SBDebugger_HandleCommand", _wrap_SBDebugger_HandleCommand, METH_VARARGS, "SBDebugger_HandleCommand(SBDebugger self, char const * command)"},
84751+
{ "SBDebugger_RequestInterrupt", _wrap_SBDebugger_RequestInterrupt, METH_O, "SBDebugger_RequestInterrupt(SBDebugger self)"},
84752+
{ "SBDebugger_CancelInterruptRequest", _wrap_SBDebugger_CancelInterruptRequest, METH_O, "SBDebugger_CancelInterruptRequest(SBDebugger self)"},
84753+
{ "SBDebugger_InterruptRequested", _wrap_SBDebugger_InterruptRequested, METH_O, "SBDebugger_InterruptRequested(SBDebugger self) -> bool"},
8464484754
{ "SBDebugger_GetListener", _wrap_SBDebugger_GetListener, METH_O, "SBDebugger_GetListener(SBDebugger self) -> SBListener"},
8464584755
{ "SBDebugger_HandleProcessEvent", _wrap_SBDebugger_HandleProcessEvent, METH_VARARGS, "\n"
8464684756
"SBDebugger_HandleProcessEvent(SBDebugger self, SBProcess process, SBEvent event, SBFile out, SBFile err)\n"

lldb/bindings/python/static-binding/lldb.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3241,6 +3241,10 @@ def WasInterrupted(self):
32413241
r"""WasInterrupted(SBCommandInterpreter self) -> bool"""
32423242
return _lldb.SBCommandInterpreter_WasInterrupted(self)
32433243

3244+
def InterruptCommand(self):
3245+
r"""InterruptCommand(SBCommandInterpreter self) -> bool"""
3246+
return _lldb.SBCommandInterpreter_InterruptCommand(self)
3247+
32443248
# Register SBCommandInterpreter in _lldb:
32453249
_lldb.SBCommandInterpreter_swigregister(SBCommandInterpreter)
32463250
class SBCommandInterpreterRunOptions(object):
@@ -4352,6 +4356,18 @@ def HandleCommand(self, command):
43524356
r"""HandleCommand(SBDebugger self, char const * command)"""
43534357
return _lldb.SBDebugger_HandleCommand(self, command)
43544358

4359+
def RequestInterrupt(self):
4360+
r"""RequestInterrupt(SBDebugger self)"""
4361+
return _lldb.SBDebugger_RequestInterrupt(self)
4362+
4363+
def CancelInterruptRequest(self):
4364+
r"""CancelInterruptRequest(SBDebugger self)"""
4365+
return _lldb.SBDebugger_CancelInterruptRequest(self)
4366+
4367+
def InterruptRequested(self):
4368+
r"""InterruptRequested(SBDebugger self) -> bool"""
4369+
return _lldb.SBDebugger_InterruptRequested(self)
4370+
43554371
def GetListener(self):
43564372
r"""GetListener(SBDebugger self) -> SBListener"""
43574373
return _lldb.SBDebugger_GetListener(self)

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+
}

0 commit comments

Comments
 (0)