forked from OSchip/llvm-project
650 lines
25 KiB
C++
650 lines
25 KiB
C++
//===-- REPL.cpp ------------------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// C Includes
|
|
// C++ Includes
|
|
// Other libraries and framework includes
|
|
// Project includes
|
|
#include "lldb/Core/Debugger.h"
|
|
#include "lldb/Core/PluginManager.h"
|
|
#include "lldb/Core/StreamFile.h"
|
|
#include "lldb/Expression/ExpressionVariable.h"
|
|
#include "lldb/Expression/REPL.h"
|
|
#include "lldb/Expression/UserExpression.h"
|
|
#include "lldb/Host/HostInfo.h"
|
|
#include "lldb/Interpreter/CommandInterpreter.h"
|
|
#include "lldb/Interpreter/CommandReturnObject.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Target/Thread.h"
|
|
#include "lldb/Utility/AnsiTerminal.h"
|
|
|
|
using namespace lldb_private;
|
|
|
|
REPL::REPL(LLVMCastKind kind, Target &target) :
|
|
m_target(target),
|
|
m_kind(kind)
|
|
{
|
|
// Make sure all option values have sane defaults
|
|
Debugger &debugger = m_target.GetDebugger();
|
|
CommandInterpreter &ci = debugger.GetCommandInterpreter();
|
|
m_format_options.OptionParsingStarting(ci);
|
|
m_varobj_options.OptionParsingStarting(ci);
|
|
m_command_options.OptionParsingStarting(ci);
|
|
|
|
// Default certain settings for REPL regardless of the global settings.
|
|
m_command_options.unwind_on_error = false;
|
|
m_command_options.ignore_breakpoints = false;
|
|
m_command_options.debug = false;
|
|
}
|
|
|
|
REPL::~REPL() = default;
|
|
|
|
lldb::REPLSP
|
|
REPL::Create(Error &err, lldb::LanguageType language, Debugger *debugger, Target *target, const char *repl_options)
|
|
{
|
|
uint32_t idx = 0;
|
|
lldb::REPLSP ret;
|
|
|
|
while (REPLCreateInstance create_instance = PluginManager::GetREPLCreateCallbackAtIndex(idx++))
|
|
{
|
|
ret = (*create_instance)(err, language, debugger, target, repl_options);
|
|
if (ret)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::string
|
|
REPL::GetSourcePath()
|
|
{
|
|
ConstString file_basename = GetSourceFileBasename();
|
|
|
|
FileSpec tmpdir_file_spec;
|
|
if (HostInfo::GetLLDBPath (lldb::ePathTypeLLDBTempSystemDir, tmpdir_file_spec))
|
|
{
|
|
tmpdir_file_spec.GetFilename().SetCString(file_basename.AsCString());
|
|
m_repl_source_path = tmpdir_file_spec.GetPath();
|
|
}
|
|
else
|
|
{
|
|
tmpdir_file_spec = FileSpec("/tmp", false);
|
|
tmpdir_file_spec.AppendPathComponent(file_basename.AsCString());
|
|
}
|
|
|
|
return tmpdir_file_spec.GetPath();
|
|
}
|
|
|
|
lldb::IOHandlerSP
|
|
REPL::GetIOHandler()
|
|
{
|
|
if (!m_io_handler_sp)
|
|
{
|
|
Debugger &debugger = m_target.GetDebugger();
|
|
m_io_handler_sp.reset (new IOHandlerEditline (debugger,
|
|
IOHandler::Type::REPL,
|
|
"lldb-repl", // Name of input reader for history
|
|
"> ", // prompt
|
|
". ", // Continuation prompt
|
|
true, // Multi-line
|
|
true, // The REPL prompt is always colored
|
|
1, // Line number
|
|
*this));
|
|
|
|
// Don't exit if CTRL+C is pressed
|
|
static_cast<IOHandlerEditline *>(m_io_handler_sp.get())->SetInterruptExits(false);
|
|
|
|
if (m_io_handler_sp->GetIsInteractive() && m_io_handler_sp->GetIsRealTerminal())
|
|
{
|
|
m_indent_str.assign (debugger.GetTabSize(), ' ');
|
|
m_enable_auto_indent = debugger.GetAutoIndent();
|
|
}
|
|
else
|
|
{
|
|
m_indent_str.clear();
|
|
m_enable_auto_indent = false;
|
|
}
|
|
|
|
}
|
|
return m_io_handler_sp;
|
|
}
|
|
|
|
void
|
|
REPL::IOHandlerActivated (IOHandler &io_handler)
|
|
{
|
|
lldb::ProcessSP process_sp = m_target.GetProcessSP();
|
|
if (process_sp && process_sp->IsAlive())
|
|
return;
|
|
lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile());
|
|
error_sp->Printf("REPL requires a running target process.\n");
|
|
io_handler.SetIsDone(true);
|
|
}
|
|
|
|
bool
|
|
REPL::IOHandlerInterrupt (IOHandler &io_handler)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void
|
|
REPL::IOHandlerInputInterrupted (IOHandler &io_handler,
|
|
std::string &line)
|
|
{
|
|
}
|
|
|
|
const char *
|
|
REPL::IOHandlerGetFixIndentationCharacters()
|
|
{
|
|
return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr);
|
|
}
|
|
|
|
ConstString
|
|
REPL::IOHandlerGetControlSequence (char ch)
|
|
{
|
|
if (ch == 'd')
|
|
return ConstString(":quit\n");
|
|
return ConstString();
|
|
}
|
|
|
|
const char *
|
|
REPL::IOHandlerGetCommandPrefix ()
|
|
{
|
|
return ":";
|
|
}
|
|
|
|
const char *
|
|
REPL::IOHandlerGetHelpPrologue ()
|
|
{
|
|
return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter. "
|
|
"Valid statements, expressions, and declarations are immediately compiled and executed.\n\n"
|
|
"The complete set of LLDB debugging commands are also available as described below. Commands "
|
|
"must be prefixed with a colon at the REPL prompt (:quit for example.) Typing just a colon "
|
|
"followed by return will switch to the LLDB prompt.\n\n";
|
|
}
|
|
|
|
bool
|
|
REPL::IOHandlerIsInputComplete (IOHandler &io_handler,
|
|
StringList &lines)
|
|
{
|
|
// Check for meta command
|
|
const size_t num_lines = lines.GetSize();
|
|
if (num_lines == 1)
|
|
{
|
|
const char *first_line = lines.GetStringAtIndex(0);
|
|
if (first_line[0] == ':')
|
|
return true; // Meta command is a single line where that starts with ':'
|
|
}
|
|
|
|
// Check if REPL input is done
|
|
std::string source_string (lines.CopyList());
|
|
return SourceIsComplete(source_string);
|
|
}
|
|
|
|
int
|
|
REPL::CalculateActualIndentation (const StringList &lines)
|
|
{
|
|
std::string last_line = lines[lines.GetSize() - 1];
|
|
|
|
int actual_indent = 0;
|
|
for (char &ch : last_line)
|
|
{
|
|
if (ch != ' ') break;
|
|
++actual_indent;
|
|
}
|
|
|
|
return actual_indent;
|
|
}
|
|
|
|
int
|
|
REPL::IOHandlerFixIndentation (IOHandler &io_handler,
|
|
const StringList &lines,
|
|
int cursor_position)
|
|
{
|
|
if (!m_enable_auto_indent) return 0;
|
|
|
|
if (!lines.GetSize())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int tab_size = io_handler.GetDebugger().GetTabSize();
|
|
|
|
lldb::offset_t desired_indent = GetDesiredIndentation(lines,
|
|
cursor_position,
|
|
tab_size);
|
|
|
|
int actual_indent = REPL::CalculateActualIndentation(lines);
|
|
|
|
if (desired_indent == LLDB_INVALID_OFFSET)
|
|
return 0;
|
|
|
|
return (int)desired_indent - actual_indent;
|
|
}
|
|
|
|
void
|
|
REPL::IOHandlerInputComplete (IOHandler &io_handler, std::string &code)
|
|
{
|
|
lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFile());
|
|
lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile());
|
|
bool extra_line = false;
|
|
bool did_quit = false;
|
|
|
|
if (code.empty())
|
|
{
|
|
m_code.AppendString("");
|
|
static_cast<IOHandlerEditline &>(io_handler).SetBaseLineNumber(m_code.GetSize()+1);
|
|
}
|
|
else
|
|
{
|
|
Debugger &debugger = m_target.GetDebugger();
|
|
CommandInterpreter &ci = debugger.GetCommandInterpreter();
|
|
extra_line = ci.GetSpaceReplPrompts();
|
|
|
|
ExecutionContext exe_ctx (m_target.GetProcessSP()->GetThreadList().GetSelectedThread()->GetSelectedFrame().get());
|
|
|
|
lldb::ProcessSP process_sp(exe_ctx.GetProcessSP());
|
|
|
|
if (code[0] == ':')
|
|
{
|
|
// Meta command
|
|
// Strip the ':'
|
|
code.erase(0, 1);
|
|
if (Args::StripSpaces (code))
|
|
{
|
|
// "lldb" was followed by arguments, so just execute the command dump the results
|
|
|
|
// Turn off prompt on quit in case the user types ":quit"
|
|
const bool saved_prompt_on_quit = ci.GetPromptOnQuit();
|
|
if (saved_prompt_on_quit)
|
|
ci.SetPromptOnQuit(false);
|
|
|
|
// Execute the command
|
|
CommandReturnObject result;
|
|
result.SetImmediateOutputStream(output_sp);
|
|
result.SetImmediateErrorStream(error_sp);
|
|
ci.HandleCommand(code.c_str(), eLazyBoolNo, result);
|
|
|
|
if (saved_prompt_on_quit)
|
|
ci.SetPromptOnQuit(true);
|
|
|
|
if (result.GetStatus() == lldb::eReturnStatusQuit)
|
|
{
|
|
did_quit = true;
|
|
io_handler.SetIsDone(true);
|
|
if (debugger.CheckTopIOHandlerTypes(IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter))
|
|
{
|
|
// We typed "quit" or an alias to quit so we need to check if the
|
|
// command interpreter is above us and tell it that it is done as well
|
|
// so we don't drop back into the command interpreter if we have already
|
|
// quit
|
|
lldb::IOHandlerSP io_handler_sp (ci.GetIOHandler());
|
|
if (io_handler_sp)
|
|
io_handler_sp->SetIsDone(true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ":" was followed by no arguments, so push the LLDB command prompt
|
|
if (debugger.CheckTopIOHandlerTypes(IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter))
|
|
{
|
|
// If the user wants to get back to the command interpreter and the
|
|
// command interpreter is what launched the REPL, then just let the
|
|
// REPL exit and fall back to the command interpreter.
|
|
io_handler.SetIsDone(true);
|
|
}
|
|
else
|
|
{
|
|
// The REPL wasn't launched the by the command interpreter, it is the
|
|
// base IOHandler, so we need to get the command interpreter and
|
|
lldb::IOHandlerSP io_handler_sp (ci.GetIOHandler());
|
|
if (io_handler_sp)
|
|
{
|
|
io_handler_sp->SetIsDone(false);
|
|
debugger.PushIOHandler(ci.GetIOHandler());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Unwind any expression we might have been running in case our REPL
|
|
// expression crashed and the user was looking around
|
|
if (m_dedicated_repl_mode)
|
|
{
|
|
Thread *thread = exe_ctx.GetThreadPtr();
|
|
if (thread && thread->UnwindInnermostExpression().Success())
|
|
{
|
|
thread->SetSelectedFrameByIndex(0, false);
|
|
exe_ctx.SetFrameSP(thread->GetSelectedFrame());
|
|
}
|
|
}
|
|
|
|
const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
|
|
|
|
EvaluateExpressionOptions expr_options;
|
|
expr_options.SetCoerceToId(m_varobj_options.use_objc);
|
|
expr_options.SetUnwindOnError(m_command_options.unwind_on_error);
|
|
expr_options.SetIgnoreBreakpoints (m_command_options.ignore_breakpoints);
|
|
expr_options.SetKeepInMemory(true);
|
|
expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
|
|
expr_options.SetTryAllThreads(m_command_options.try_all_threads);
|
|
expr_options.SetGenerateDebugInfo(true);
|
|
expr_options.SetREPLEnabled (true);
|
|
expr_options.SetColorizeErrors(colorize_err);
|
|
expr_options.SetPoundLine(m_repl_source_path.c_str(), m_code.GetSize() + 1);
|
|
if (m_command_options.timeout > 0)
|
|
expr_options.SetTimeoutUsec(m_command_options.timeout);
|
|
else
|
|
expr_options.SetTimeoutUsec(0);
|
|
|
|
expr_options.SetLanguage(GetLanguage());
|
|
|
|
PersistentExpressionState *persistent_state = m_target.GetPersistentExpressionStateForLanguage(GetLanguage());
|
|
|
|
const size_t var_count_before = persistent_state->GetSize();
|
|
|
|
const char *expr_prefix = nullptr;
|
|
lldb::ValueObjectSP result_valobj_sp;
|
|
Error error;
|
|
lldb::ModuleSP jit_module_sp;
|
|
lldb::ExpressionResults execution_results = UserExpression::Evaluate (exe_ctx,
|
|
expr_options,
|
|
code.c_str(),
|
|
expr_prefix,
|
|
result_valobj_sp,
|
|
error,
|
|
0, // Line offset
|
|
&jit_module_sp);
|
|
|
|
//CommandInterpreter &ci = debugger.GetCommandInterpreter();
|
|
|
|
if (process_sp && process_sp->IsAlive())
|
|
{
|
|
bool add_to_code = true;
|
|
bool handled = false;
|
|
if (result_valobj_sp)
|
|
{
|
|
lldb::Format format = m_format_options.GetFormat();
|
|
|
|
if (result_valobj_sp->GetError().Success())
|
|
{
|
|
handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp);
|
|
}
|
|
else if (result_valobj_sp->GetError().GetError() == UserExpression::kNoResult)
|
|
{
|
|
if (format != lldb::eFormatVoid && debugger.GetNotifyVoid())
|
|
{
|
|
error_sp->PutCString("(void)\n");
|
|
handled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (debugger.GetPrintDecls())
|
|
{
|
|
for (size_t vi = var_count_before, ve = persistent_state->GetSize();
|
|
vi != ve;
|
|
++vi)
|
|
{
|
|
lldb::ExpressionVariableSP persistent_var_sp = persistent_state->GetVariableAtIndex(vi);
|
|
lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject();
|
|
|
|
PrintOneVariable(debugger, output_sp, valobj_sp, persistent_var_sp.get());
|
|
}
|
|
}
|
|
|
|
if (!handled)
|
|
{
|
|
bool useColors = error_sp->GetFile().GetIsTerminalWithColors();
|
|
switch (execution_results)
|
|
{
|
|
case lldb::eExpressionSetupError:
|
|
case lldb::eExpressionParseError:
|
|
add_to_code = false;
|
|
// Fall through
|
|
case lldb::eExpressionDiscarded:
|
|
error_sp->Printf("%s\n", error.AsCString());
|
|
break;
|
|
|
|
case lldb::eExpressionCompleted:
|
|
break;
|
|
case lldb::eExpressionInterrupted:
|
|
if (useColors) {
|
|
error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
|
|
error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
|
|
}
|
|
error_sp->Printf("Execution interrupted. ");
|
|
if (useColors) error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
|
|
error_sp->Printf("Enter code to recover and continue.\nEnter LLDB commands to investigate (type :help for assistance.)\n");
|
|
break;
|
|
|
|
case lldb::eExpressionHitBreakpoint:
|
|
// Breakpoint was hit, drop into LLDB command interpreter
|
|
if (useColors) {
|
|
error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
|
|
error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
|
|
}
|
|
output_sp->Printf("Execution stopped at breakpoint. ");
|
|
if (useColors) error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
|
|
output_sp->Printf("Enter LLDB commands to investigate (type help for assistance.)\n");
|
|
{
|
|
lldb::IOHandlerSP io_handler_sp (ci.GetIOHandler());
|
|
if (io_handler_sp)
|
|
{
|
|
io_handler_sp->SetIsDone(false);
|
|
debugger.PushIOHandler(ci.GetIOHandler());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case lldb::eExpressionTimedOut:
|
|
error_sp->Printf("error: timeout\n");
|
|
if (error.AsCString())
|
|
error_sp->Printf("error: %s\n", error.AsCString());
|
|
break;
|
|
case lldb::eExpressionResultUnavailable:
|
|
// Shoulnd't happen???
|
|
error_sp->Printf("error: could not fetch result -- %s\n", error.AsCString());
|
|
break;
|
|
case lldb::eExpressionStoppedForDebug:
|
|
// Shoulnd't happen???
|
|
error_sp->Printf("error: stopped for debug -- %s\n", error.AsCString());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (add_to_code)
|
|
{
|
|
const uint32_t new_default_line = m_code.GetSize() + 1;
|
|
|
|
m_code.SplitIntoLines(code);
|
|
|
|
// Update our code on disk
|
|
if (!m_repl_source_path.empty())
|
|
{
|
|
lldb_private::File file (m_repl_source_path.c_str(),
|
|
File::eOpenOptionWrite | File::eOpenOptionTruncate | File::eOpenOptionCanCreate,
|
|
lldb::eFilePermissionsFileDefault);
|
|
std::string code (m_code.CopyList());
|
|
code.append(1, '\n');
|
|
size_t bytes_written = code.size();
|
|
file.Write(code.c_str(), bytes_written);
|
|
file.Close();
|
|
|
|
// Now set the default file and line to the REPL source file
|
|
m_target.GetSourceManager().SetDefaultFileAndLine(FileSpec(m_repl_source_path.c_str(), false), new_default_line);
|
|
}
|
|
static_cast<IOHandlerEditline &>(io_handler).SetBaseLineNumber(m_code.GetSize()+1);
|
|
}
|
|
if (extra_line)
|
|
{
|
|
fprintf(output_sp->GetFile().GetStream(), "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Don't complain about the REPL process going away if we are in the process of quitting.
|
|
if (!did_quit && (!process_sp || !process_sp->IsAlive()))
|
|
{
|
|
error_sp->Printf("error: REPL process is no longer alive, exiting REPL\n");
|
|
io_handler.SetIsDone(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
REPL::IOHandlerComplete (IOHandler &io_handler,
|
|
const char *current_line,
|
|
const char *cursor,
|
|
const char *last_char,
|
|
int skip_first_n_matches,
|
|
int max_matches,
|
|
StringList &matches)
|
|
{
|
|
matches.Clear();
|
|
|
|
llvm::StringRef line (current_line, cursor - current_line);
|
|
|
|
// Complete an LLDB command if the first character is a colon...
|
|
if (!line.empty() && line[0] == ':')
|
|
{
|
|
Debugger &debugger = m_target.GetDebugger();
|
|
|
|
// auto complete LLDB commands
|
|
const char *lldb_current_line = line.substr(1).data();
|
|
return debugger.GetCommandInterpreter().HandleCompletion (lldb_current_line,
|
|
cursor,
|
|
last_char,
|
|
skip_first_n_matches,
|
|
max_matches,
|
|
matches);
|
|
}
|
|
|
|
// Strip spaces from the line and see if we had only spaces
|
|
line = line.ltrim();
|
|
if (line.empty())
|
|
{
|
|
// Only spaces on this line, so just indent
|
|
matches.AppendString(m_indent_str);
|
|
return 1;
|
|
}
|
|
|
|
std::string current_code;
|
|
current_code.append(m_code.CopyList());
|
|
|
|
IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler);
|
|
const StringList *current_lines = editline.GetCurrentLines();
|
|
if (current_lines)
|
|
{
|
|
const uint32_t current_line_idx = editline.GetCurrentLineIndex();
|
|
|
|
if (current_line_idx < current_lines->GetSize())
|
|
{
|
|
for (uint32_t i=0; i<current_line_idx; ++i)
|
|
{
|
|
const char *line_cstr = current_lines->GetStringAtIndex(i);
|
|
if (line_cstr)
|
|
{
|
|
current_code.append("\n");
|
|
current_code.append (line_cstr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cursor > current_line)
|
|
{
|
|
current_code.append("\n");
|
|
current_code.append(current_line, cursor - current_line);
|
|
}
|
|
|
|
return CompleteCode(current_code, matches);
|
|
}
|
|
|
|
bool
|
|
QuitCommandOverrideCallback(void *baton, const char **argv)
|
|
{
|
|
Target *target = (Target *)baton;
|
|
lldb::ProcessSP process_sp (target->GetProcessSP());
|
|
if (process_sp)
|
|
{
|
|
process_sp->Destroy(false);
|
|
process_sp->GetTarget().GetDebugger().ClearIOHandlers();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Error
|
|
REPL::RunLoop ()
|
|
{
|
|
Error error;
|
|
|
|
error = DoInitialization();
|
|
m_repl_source_path = GetSourcePath();
|
|
|
|
if (!error.Success())
|
|
return error;
|
|
|
|
Debugger &debugger = m_target.GetDebugger();
|
|
|
|
lldb::IOHandlerSP io_handler_sp (GetIOHandler());
|
|
|
|
FileSpec save_default_file;
|
|
uint32_t save_default_line = 0;
|
|
|
|
if (!m_repl_source_path.empty())
|
|
{
|
|
// Save the current default file and line
|
|
m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file, save_default_line);
|
|
}
|
|
|
|
debugger.PushIOHandler(io_handler_sp);
|
|
|
|
// Check if we are in dedicated REPL mode where LLDB was start with the "--repl" option
|
|
// from the command line. Currently we know this by checking if the debugger already
|
|
// has a IOHandler thread.
|
|
if (!debugger.HasIOHandlerThread())
|
|
{
|
|
// The debugger doesn't have an existing IOHandler thread, so this must be
|
|
// dedicated REPL mode...
|
|
m_dedicated_repl_mode = true;
|
|
debugger.StartIOHandlerThread();
|
|
std::string command_name_str ("quit");
|
|
CommandObject *cmd_obj = debugger.GetCommandInterpreter().GetCommandObjectForCommand(command_name_str);
|
|
if (cmd_obj)
|
|
{
|
|
assert(command_name_str.empty());
|
|
cmd_obj->SetOverrideCallback (QuitCommandOverrideCallback, &m_target);
|
|
}
|
|
}
|
|
|
|
// Wait for the REPL command interpreter to get popped
|
|
io_handler_sp->WaitForPop();
|
|
|
|
if (m_dedicated_repl_mode)
|
|
{
|
|
// If we were in dedicated REPL mode we would have started the
|
|
// IOHandler thread, and we should kill our process
|
|
lldb::ProcessSP process_sp = m_target.GetProcessSP();
|
|
if (process_sp && process_sp->IsAlive())
|
|
process_sp->Destroy(false);
|
|
|
|
// Wait for the IO handler thread to exit (TODO: don't do this if the IO handler thread already exists...)
|
|
debugger.JoinIOHandlerThread();
|
|
}
|
|
|
|
// Restore the default file and line
|
|
if (save_default_file && save_default_line != 0)
|
|
m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file, save_default_line);
|
|
return error;
|
|
}
|