summaryrefslogtreecommitdiffstats
path: root/lldb
diff options
context:
space:
mode:
authorAdrian McCarthy <amccarth@google.com>2017-09-21 19:36:52 +0000
committerAdrian McCarthy <amccarth@google.com>2017-09-21 19:36:52 +0000
commit977996d25b1f7ca0d4f4d2c863537a95287f096e (patch)
treec55232715bdbeb5879f4dae4f3cbbbf1babbeb1c /lldb
parentbfca5f433476e54433ec7e0b05a6310b4e0d4692 (diff)
downloadbcm5719-llvm-977996d25b1f7ca0d4f4d2c863537a95287f096e.tar.gz
bcm5719-llvm-977996d25b1f7ca0d4f4d2c863537a95287f096e.zip
[LLDB] Implement interactive command interruption
The core of this change is the new CommandInterpreter::m_command_state, which models the state transitions for interactive commands, including an "interrupted" state transition. In general, command interruption requires cooperation from the code executing the command, which needs to poll for interruption requests through CommandInterpreter::WasInterrupted(). CommandInterpreter::PrintCommandOutput() implements an optionally interruptible printing of the command output, which for large outputs was likely the longest blocking part. (ex. target modules dump symtab on a complex binary could take 10+ minutes) patch by lemo Differential Revision: https://reviews.llvm.org/D37923 llvm-svn: 313904
Diffstat (limited to 'lldb')
-rw-r--r--lldb/include/lldb/API/SBCommandInterpreter.h2
-rw-r--r--lldb/include/lldb/Core/IOHandler.h3
-rw-r--r--lldb/include/lldb/Interpreter/CommandInterpreter.h20
-rw-r--r--lldb/scripts/interface/SBCommandInterpreter.i14
-rw-r--r--lldb/source/API/SBCommandInterpreter.cpp4
-rw-r--r--lldb/source/Commands/CommandObjectTarget.cpp14
-rw-r--r--lldb/source/Core/Debugger.cpp2
-rw-r--r--lldb/source/Interpreter/CommandInterpreter.cpp84
8 files changed, 121 insertions, 22 deletions
diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h
index f684071740e..80f24ceca7b 100644
--- a/lldb/include/lldb/API/SBCommandInterpreter.h
+++ b/lldb/include/lldb/API/SBCommandInterpreter.h
@@ -165,6 +165,8 @@ public:
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
diff --git a/lldb/include/lldb/Core/IOHandler.h b/lldb/include/lldb/Core/IOHandler.h
index ebf56d7908b..e8cfbade5c6 100644
--- a/lldb/include/lldb/Core/IOHandler.h
+++ b/lldb/include/lldb/Core/IOHandler.h
@@ -195,7 +195,7 @@ public:
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 @@ public:
protected:
Completion m_completion; // Support for common builtin completions
- bool m_io_handler_done;
};
//----------------------------------------------------------------------
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index 73bd7d6e622..a7a67d2ca1b 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -242,6 +242,8 @@ public:
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 @@ private:
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<CommandHandlingState> 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
diff --git a/lldb/scripts/interface/SBCommandInterpreter.i b/lldb/scripts/interface/SBCommandInterpreter.i
index c427d38b14d..255e8ba4496 100644
--- a/lldb/scripts/interface/SBCommandInterpreter.i
+++ b/lldb/scripts/interface/SBCommandInterpreter.i
@@ -125,18 +125,18 @@ public:
{
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 @@ public:
lldb::SBProcess
GetProcess ();
-
+
lldb::SBDebugger
GetDebugger ();
@@ -209,10 +209,12 @@ public:
int match_start_point,
int max_return_elements,
lldb::SBStringList &matches);
-
+
bool
IsActive ();
+ bool
+ WasInterrupted () const;
};
} // namespace lldb
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index aa4953999b8..c9bb8f65fa2 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -161,6 +161,10 @@ bool SBCommandInterpreter::IsActive() {
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()
diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp
index bb1b3f8ae7d..4eaec5b4cea 100644
--- a/lldb/source/Commands/CommandObjectTarget.cpp
+++ b/lldb/source/Commands/CommandObjectTarget.cpp
@@ -2053,6 +2053,8 @@ protected:
result.GetOutputStream().EOL();
result.GetOutputStream().EOL();
}
+ if (m_interpreter.WasInterrupted())
+ break;
num_dumped++;
DumpModuleSymtab(
m_interpreter, result.GetOutputStream(),
@@ -2081,6 +2083,8 @@ protected:
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 @@ protected:
" 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 @@ protected:
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 @@ protected:
" 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 @@ protected:
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 @@ protected:
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),
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index d42e4df56d8..a4d78151c75 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -1170,7 +1170,7 @@ TestPromptFormats (StackFrame *frame)
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}"
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 5efc6d1db27..884c6dcd869 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -546,7 +546,7 @@ void CommandInterpreter::LoadCommandDictionary() {
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 @@ bool CommandInterpreter::AddCommand(llvm::StringRef name,
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 @@ bool CommandInterpreter::AddUserCommand(llvm::StringRef name,
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 @@ CommandInterpreter::AddAlias(llvm::StringRef alias_name,
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<CommandAlias> command_alias_up(
new CommandAlias(*this, command_obj_sp, args_string, alias_name));
@@ -1839,7 +1839,7 @@ int CommandInterpreter::HandleCompletion(
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 @@ size_t CommandInterpreter::GetProcessOutput() {
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 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
line.c_str());
}
+ StartHandlingCommand();
+
lldb_private::CommandReturnObject result;
HandleCommand(line.c_str(), eLazyBoolCalculate, result);
@@ -2710,18 +2763,20 @@ void CommandInterpreter::IOHandlerInputComplete(IOHandler &io_handler,
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 @@ bool CommandInterpreter::IOHandlerInterrupt(IOHandler &io_handler) {
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 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line,
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());
}
OpenPOWER on IntegriCloud