Index: lldb/include/lldb/Core/Debugger.h =================================================================== --- lldb/include/lldb/Core/Debugger.h +++ lldb/include/lldb/Core/Debugger.h @@ -410,6 +410,8 @@ m_script_interpreters; IOHandlerStack m_io_handler_stack; + std::recursive_mutex m_io_handler_synchronous_mutex; + llvm::StringMap> m_log_streams; std::shared_ptr m_log_callback_stream_sp; ConstString m_instance_name; Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_callback_command_source/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_callback_command_source/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_callback_command_source/TestBreakpointCallbackCommandSource.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_callback_command_source/TestBreakpointCallbackCommandSource.py @@ -0,0 +1,43 @@ +""" +Test completion in our IOHandlers. +""" + +import os + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test.lldbpexpect import PExpectTest + + +class BreakpointCallbackCommandSource(PExpectTest): + + NO_DEBUG_INFO_TESTCASE = True + mydir = TestBase.compute_mydir(__file__) + file_to_source = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'source.lldb') + + # PExpect uses many timeouts internally and doesn't play well + # under ASAN on a loaded machine.. + @skipIfAsan + @skipIfEditlineSupportMissing + def test_breakpoint_callback_command_source(self): + self.build() + exe = self.getBuildArtifact("a.out") + + self.launch(exe) + self.expect_prompt() + self.child.send("b main\n") + self.expect_prompt() + self.child.send("breakpoint command add -s python\n") + self.child.send( + "frame.GetThread().GetProcess().GetTarget().GetDebugger().HandleCommand('command source -s true {}')\n" + .format(self.file_to_source)) + self.child.send("DONE\n") + self.expect_prompt() + self.child.send("run\n") + self.child.expect("Process ([0-9]+) launched") + self.expect_prompt() + self.child.send("script print(foo)\n") + self.expect_prompt() + self.child.expect_exact("95126") + self.quit() Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_callback_command_source/main.c =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_callback_command_source/main.c @@ -0,0 +1,4 @@ +int main (int argc, char const *argv[]) +{ + return 0; +} Index: lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_callback_command_source/source.lldb =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/functionalities/breakpoint/breakpoint_callback_command_source/source.lldb @@ -0,0 +1 @@ +script foo = 95126 Index: lldb/source/Core/Debugger.cpp =================================================================== --- lldb/source/Core/Debugger.cpp +++ lldb/source/Core/Debugger.cpp @@ -895,23 +895,62 @@ } void Debugger::RunIOHandlers() { + IOHandlerSP reader_sp = m_io_handler_stack.Top(); while (true) { - IOHandlerSP reader_sp(m_io_handler_stack.Top()); if (!reader_sp) break; reader_sp->Run(); + { + std::lock_guard guard( + m_io_handler_synchronous_mutex); + + // Remove all input readers that are done from the top of the stack + while (true) { + IOHandlerSP top_reader_sp = m_io_handler_stack.Top(); + if (top_reader_sp && top_reader_sp->GetIsDone()) + PopIOHandler(top_reader_sp); + else + break; + } + reader_sp = m_io_handler_stack.Top(); + } + } + ClearIOHandlers(); +} + +void Debugger::RunIOHandlerSync(const IOHandlerSP &reader_sp) { + std::lock_guard guard(m_io_handler_synchronous_mutex); + + PushIOHandler(reader_sp); + IOHandlerSP top_reader_sp = reader_sp; - // Remove all input readers that are done from the top of the stack + while (top_reader_sp) { + if (!top_reader_sp) + break; + + top_reader_sp->Run(); + + // Don't unwind past the starting point. + if (top_reader_sp.get() == reader_sp.get()) { + if (PopIOHandler(reader_sp)) + break; + } + + // If we pushed new IO handlers, pop them if they're done or restart the + // loop to run them if they're not. while (true) { - IOHandlerSP top_reader_sp = m_io_handler_stack.Top(); - if (top_reader_sp && top_reader_sp->GetIsDone()) + top_reader_sp = m_io_handler_stack.Top(); + if (top_reader_sp && top_reader_sp->GetIsDone()) { PopIOHandler(top_reader_sp); - else + // Don't unwind past the starting point. + if (top_reader_sp.get() == reader_sp.get()) + return; + } else { break; + } } } - ClearIOHandlers(); } bool Debugger::IsTopIOHandler(const lldb::IOHandlerSP &reader_sp) { @@ -950,28 +989,6 @@ PushIOHandler(reader_sp, cancel_top_handler); } -void Debugger::RunIOHandlerSync(const IOHandlerSP &reader_sp) { - PushIOHandler(reader_sp); - - IOHandlerSP top_reader_sp = reader_sp; - while (top_reader_sp) { - top_reader_sp->Run(); - - if (top_reader_sp.get() == reader_sp.get()) { - if (PopIOHandler(reader_sp)) - break; - } - - while (true) { - top_reader_sp = m_io_handler_stack.Top(); - if (top_reader_sp && top_reader_sp->GetIsDone()) - PopIOHandler(top_reader_sp); - else - break; - } - } -} - void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in, StreamFileSP &out, StreamFileSP &err) { // Before an IOHandler runs, it must have in/out/err streams. This function