Index: lldb/trunk/include/lldb/API/SBCommandInterpreter.h =================================================================== --- lldb/trunk/include/lldb/API/SBCommandInterpreter.h +++ lldb/trunk/include/lldb/API/SBCommandInterpreter.h @@ -165,6 +165,8 @@ int match_start_point, int max_return_elements, lldb::SBStringList &matches); + bool WasInterrupted() const; + // Catch commands before they execute by registering a callback that will // get called when the command gets executed. This allows GUI or command // line interfaces to intercept a command and stop it from happening Index: lldb/trunk/include/lldb/Core/IOHandler.h =================================================================== --- lldb/trunk/include/lldb/Core/IOHandler.h +++ lldb/trunk/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: lldb/trunk/include/lldb/Interpreter/CommandInterpreter.h =================================================================== --- lldb/trunk/include/lldb/Interpreter/CommandInterpreter.h +++ lldb/trunk/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,24 @@ 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: lldb/trunk/scripts/interface/SBCommandInterpreter.i =================================================================== --- lldb/trunk/scripts/interface/SBCommandInterpreter.i +++ lldb/trunk/scripts/interface/SBCommandInterpreter.i @@ -125,18 +125,18 @@ { eBroadcastBitThreadShouldExit = (1 << 0), eBroadcastBitResetPrompt = (1 << 1), - eBroadcastBitQuitCommandReceived = (1 << 2), // User entered quit + eBroadcastBitQuitCommandReceived = (1 << 2), // User entered quit eBroadcastBitAsynchronousOutputData = (1 << 3), eBroadcastBitAsynchronousErrorData = (1 << 4) }; SBCommandInterpreter (const lldb::SBCommandInterpreter &rhs); - + ~SBCommandInterpreter (); - static const char * + static const char * GetArgumentTypeAsCString (const lldb::CommandArgumentType arg_type); - + static const char * GetArgumentDescriptionAsCString (const lldb::CommandArgumentType arg_type); @@ -181,7 +181,7 @@ lldb::SBProcess GetProcess (); - + lldb::SBDebugger GetDebugger (); @@ -209,10 +209,12 @@ int match_start_point, int max_return_elements, lldb::SBStringList &matches); - + bool IsActive (); + bool + WasInterrupted () const; }; } // namespace lldb Index: lldb/trunk/source/API/SBCommandInterpreter.cpp =================================================================== --- lldb/trunk/source/API/SBCommandInterpreter.cpp +++ lldb/trunk/source/API/SBCommandInterpreter.cpp @@ -161,6 +161,10 @@ return (IsValid() ? m_opaque_ptr->IsActive() : false); } +bool SBCommandInterpreter::WasInterrupted() const { + return (IsValid() ? m_opaque_ptr->WasInterrupted() : false); +} + const char *SBCommandInterpreter::GetIOHandlerControlSequence(char ch) { return (IsValid() ? m_opaque_ptr->GetDebugger() Index: lldb/trunk/source/Commands/CommandObjectTarget.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectTarget.cpp +++ lldb/trunk/source/Commands/CommandObjectTarget.cpp @@ -2053,6 +2053,8 @@ result.GetOutputStream().EOL(); result.GetOutputStream().EOL(); } + if (m_interpreter.WasInterrupted()) + break; num_dumped++; DumpModuleSymtab( m_interpreter, result.GetOutputStream(), @@ -2081,6 +2083,8 @@ 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 +2150,8 @@ " 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 +2173,8 @@ 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 +2247,8 @@ " 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 +2270,8 @@ 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 +2339,8 @@ 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: lldb/trunk/source/Core/Debugger.cpp =================================================================== --- lldb/trunk/source/Core/Debugger.cpp +++ lldb/trunk/source/Core/Debugger.cpp @@ -1170,7 +1170,7 @@ return; StreamString s; - const char *prompt_format = + const char *prompt_format = "{addr = '${addr}'\n}" "{addr-file-or-load = '${addr-file-or-load}'\n}" "{current-pc-arrow = '${current-pc-arrow}'\n}" Index: lldb/trunk/source/Interpreter/CommandInterpreter.cpp =================================================================== --- lldb/trunk/source/Interpreter/CommandInterpreter.cpp +++ lldb/trunk/source/Interpreter/CommandInterpreter.cpp @@ -546,7 +546,7 @@ char buffer[1024]; int num_printed = snprintf(buffer, 1024, "%s %s", break_regexes[i][1], "-o"); - assert(num_printed < 1024); + lldbassert(num_printed < 1024); UNUSED_IF_ASSERT_DISABLED(num_printed); success = tbreak_regex_cmd_ap->AddRegexCommand(break_regexes[i][0], buffer); @@ -891,8 +891,8 @@ const lldb::CommandObjectSP &cmd_sp, bool can_replace) { if (cmd_sp.get()) - assert((this == &cmd_sp->GetCommandInterpreter()) && - "tried to add a CommandObject from a different interpreter"); + lldbassert((this == &cmd_sp->GetCommandInterpreter()) && + "tried to add a CommandObject from a different interpreter"); if (name.empty()) return false; @@ -913,8 +913,8 @@ const lldb::CommandObjectSP &cmd_sp, bool can_replace) { if (cmd_sp.get()) - assert((this == &cmd_sp->GetCommandInterpreter()) && - "tried to add a CommandObject from a different interpreter"); + lldbassert((this == &cmd_sp->GetCommandInterpreter()) && + "tried to add a CommandObject from a different interpreter"); if (!name.empty()) { // do not allow replacement of internal commands @@ -1062,8 +1062,8 @@ lldb::CommandObjectSP &command_obj_sp, llvm::StringRef args_string) { if (command_obj_sp.get()) - assert((this == &command_obj_sp->GetCommandInterpreter()) && - "tried to add a CommandObject from a different interpreter"); + lldbassert((this == &command_obj_sp->GetCommandInterpreter()) && + "tried to add a CommandObject from a different interpreter"); std::unique_ptr command_alias_up( new CommandAlias(*this, command_obj_sp, args_string, alias_name)); @@ -1839,7 +1839,7 @@ matches.Clear(); // Only max_return_elements == -1 is supported at present: - assert(max_return_elements == -1); + lldbassert(max_return_elements == -1); bool word_complete; num_command_matches = HandleCompletionMatches( parsed_line, cursor_index, cursor_char_position, match_start_point, @@ -2677,6 +2677,57 @@ return total_bytes; } +void CommandInterpreter::StartHandlingCommand() { + auto prev_state = m_command_state.exchange(CommandHandlingState::eInProgress); + lldbassert(prev_state == CommandHandlingState::eIdle); +} + +void CommandInterpreter::FinishHandlingCommand() { + auto prev_state = m_command_state.exchange(CommandHandlingState::eIdle); + lldbassert(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) { + lldbassert(data[chunk_size] != '\0'); + if (data[chunk_size] == '\n') { + ++chunk_size; + break; + } + } + chunk_size = stream.Write(data, chunk_size); + lldbassert(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 +2751,8 @@ line.c_str()); } + StartHandlingCommand(); + lldb_private::CommandReturnObject result; HandleCommand(line.c_str(), eLazyBoolCalculate, result); @@ -2710,18 +2763,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 +2832,9 @@ ExecutionContext exe_ctx(GetExecutionContext()); Process *process = exe_ctx.GetProcessPtr(); + if (InterruptCommand()) + return true; + if (process) { StateType state = process->GetState(); if (StateIsRunningState(state)) { @@ -2998,7 +3056,7 @@ result.AppendRawError(error_msg.GetString()); } else { // We didn't have only one match, otherwise we wouldn't get here. - assert(num_matches == 0); + lldbassert(num_matches == 0); result.AppendErrorWithFormat("'%s' is not a valid command.\n", next_word.c_str()); }