Index: include/lldb/Core/IOHandler.h =================================================================== --- include/lldb/Core/IOHandler.h +++ include/lldb/Core/IOHandler.h @@ -195,7 +195,7 @@ enum class Completion { None, LLDBCommand, Expression }; IOHandlerDelegate(Completion completion = Completion::None) - : m_completion(completion), m_io_handler_done(false) {} + : m_completion(completion) {} virtual ~IOHandlerDelegate() = default; @@ -296,7 +296,6 @@ protected: Completion m_completion; // Support for common builtin completions - bool m_io_handler_done; }; //---------------------------------------------------------------------- Index: include/lldb/Interpreter/CommandInterpreter.h =================================================================== --- include/lldb/Interpreter/CommandInterpreter.h +++ include/lldb/Interpreter/CommandInterpreter.h @@ -242,6 +242,8 @@ bool repeat_on_empty_command = true, bool no_context_switching = false); + bool WasInterrupted() const; + //------------------------------------------------------------------ /// Execute a list of commands in sequence. /// @@ -522,6 +524,23 @@ StringList &commands_help, CommandObject::CommandMap &command_map); + // An interruptible wrapper around the stream output + void PrintCommandOutput(Stream &stream, llvm::StringRef str, + bool interruptible); + + // A very simple state machine which models the command handling transitions + enum class CommandHandlingState { + eIdle, + eInProgress, + eInterrupted, + }; + + std::atomic m_command_state {CommandHandlingState::eIdle}; + + void StartHandlingCommand(); + void FinishHandlingCommand(); + bool InterruptCommand(); + Debugger &m_debugger; // The debugger session that this interpreter is // associated with ExecutionContextRef m_exe_ctx_ref; // The current execution context to use Index: source/Commands/CommandObjectTarget.cpp =================================================================== --- source/Commands/CommandObjectTarget.cpp +++ source/Commands/CommandObjectTarget.cpp @@ -2053,6 +2053,9 @@ result.GetOutputStream().EOL(); result.GetOutputStream().EOL(); } + if (m_interpreter.WasInterrupted()) { + break; + } num_dumped++; DumpModuleSymtab( m_interpreter, result.GetOutputStream(), @@ -2081,6 +2084,9 @@ result.GetOutputStream().EOL(); result.GetOutputStream().EOL(); } + if (m_interpreter.WasInterrupted()) { + break; + } num_dumped++; DumpModuleSymtab(m_interpreter, result.GetOutputStream(), module, m_options.m_sort_order); @@ -2146,6 +2152,9 @@ " modules.\n", (uint64_t)num_modules); for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) { + if (m_interpreter.WasInterrupted()) { + break; + } num_dumped++; DumpModuleSections( m_interpreter, result.GetOutputStream(), @@ -2167,6 +2176,9 @@ FindModulesByName(target, arg_cstr, module_list, true); if (num_matches > 0) { for (size_t i = 0; i < num_matches; ++i) { + if (m_interpreter.WasInterrupted()) { + break; + } Module *module = module_list.GetModulePointerAtIndex(i); if (module) { num_dumped++; @@ -2239,6 +2251,9 @@ " modules.\n", (uint64_t)num_modules); for (uint32_t image_idx = 0; image_idx < num_modules; ++image_idx) { + if (m_interpreter.WasInterrupted()) { + break; + } if (DumpModuleSymbolVendor( result.GetOutputStream(), target_modules.GetModulePointerAtIndexUnlocked(image_idx))) @@ -2260,6 +2275,9 @@ FindModulesByName(target, arg_cstr, module_list, true); if (num_matches > 0) { for (size_t i = 0; i < num_matches; ++i) { + if (m_interpreter.WasInterrupted()) { + break; + } Module *module = module_list.GetModulePointerAtIndex(i); if (module) { if (DumpModuleSymbolVendor(result.GetOutputStream(), module)) @@ -2327,6 +2345,9 @@ if (num_modules > 0) { uint32_t num_dumped = 0; for (uint32_t i = 0; i < num_modules; ++i) { + if (m_interpreter.WasInterrupted()) { + break; + } if (DumpCompileUnitLineTable( m_interpreter, result.GetOutputStream(), target_modules.GetModulePointerAtIndexUnlocked(i), Index: source/Interpreter/CommandInterpreter.cpp =================================================================== --- source/Interpreter/CommandInterpreter.cpp +++ source/Interpreter/CommandInterpreter.cpp @@ -2677,6 +2677,58 @@ return total_bytes; } +void CommandInterpreter::StartHandlingCommand() { + auto prev_state = m_command_state.exchange(CommandHandlingState::eInProgress); + assert(prev_state == CommandHandlingState::eIdle); +} + +void CommandInterpreter::FinishHandlingCommand() { + auto prev_state = m_command_state.exchange(CommandHandlingState::eIdle); + assert(prev_state != CommandHandlingState::eIdle); +} + +bool CommandInterpreter::InterruptCommand() { + auto in_progress = CommandHandlingState::eInProgress; + return m_command_state.compare_exchange_strong( + in_progress, CommandHandlingState::eInterrupted); +} + +bool CommandInterpreter::WasInterrupted() const { + return m_command_state == CommandHandlingState::eInterrupted; +} + +void CommandInterpreter::PrintCommandOutput(Stream &stream, llvm::StringRef str, + bool interruptible) { + if (str.empty()) { + return; + } + + if (interruptible) { + // Split the output into lines and poll for interrupt requests + const char *data = str.data(); + size_t size = str.size(); + while (size > 0 && !WasInterrupted()) { + size_t chunk_size = 0; + for (; chunk_size < size; ++chunk_size) { + assert(data[chunk_size] != '\0'); + if (data[chunk_size] == '\n') { + ++chunk_size; + break; + } + } + chunk_size = stream.Write(data, chunk_size); + assert(size >= chunk_size); + data += chunk_size; + size -= chunk_size; + } + if (size > 0) { + stream.Printf("\n... Interrupted.\n"); + } + } else { + stream.PutCString(str); + } +} + void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler, std::string &line) { const bool is_interactive = io_handler.GetIsInteractive(); @@ -2700,6 +2752,8 @@ line.c_str()); } + StartHandlingCommand(); + lldb_private::CommandReturnObject result; HandleCommand(line.c_str(), eLazyBoolCalculate, result); @@ -2710,18 +2764,20 @@ if (!result.GetImmediateOutputStream()) { llvm::StringRef output = result.GetOutputData(); - if (!output.empty()) - io_handler.GetOutputStreamFile()->PutCString(output); + PrintCommandOutput(*io_handler.GetOutputStreamFile(), output, + is_interactive); } // Now emit the command error text from the command we just executed if (!result.GetImmediateErrorStream()) { llvm::StringRef error = result.GetErrorData(); - if (!error.empty()) - io_handler.GetErrorStreamFile()->PutCString(error); + PrintCommandOutput(*io_handler.GetErrorStreamFile(), error, + is_interactive); } } + FinishHandlingCommand(); + switch (result.GetStatus()) { case eReturnStatusInvalid: case eReturnStatusSuccessFinishNoResult: @@ -2777,6 +2833,10 @@ ExecutionContext exe_ctx(GetExecutionContext()); Process *process = exe_ctx.GetProcessPtr(); + if (InterruptCommand()) { + return true; + } + if (process) { StateType state = process->GetState(); if (StateIsRunningState(state)) {