forked from OSchip/llvm-project
Added support for the "--repl" argument to LLDB.
This makes LLDB launch and create a REPL, specifying no target so that the REPL can create one for itself. Also added the "--repl-language" option, which specifies the language to use. Plumbed the relevant arguments and errors through the REPL creation mechanism. llvm-svn: 250773
This commit is contained in:
parent
7449ab9834
commit
3e7e915dca
|
@ -329,6 +329,9 @@ public:
|
|||
int &num_errors,
|
||||
bool &quit_requested,
|
||||
bool &stopped_for_crash);
|
||||
|
||||
SBError
|
||||
RunREPL (lldb::LanguageType language, const char *repl_options);
|
||||
private:
|
||||
|
||||
friend class SBCommandInterpreter;
|
||||
|
|
|
@ -375,6 +375,9 @@ public:
|
|||
{
|
||||
return m_event_handler_thread.IsJoinable();
|
||||
}
|
||||
|
||||
Error
|
||||
RunREPL (lldb::LanguageType language, const char *repl_options);
|
||||
|
||||
// This is for use in the command interpreter, when you either want the selected target, or if no target
|
||||
// is present you want to prime the dummy target with entities that will be copied over to new targets.
|
||||
|
|
|
@ -37,8 +37,26 @@ public:
|
|||
|
||||
virtual ~REPL();
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Get a REPL with an (optional) existing target, and (optional) extra arguments for the compiler.
|
||||
///
|
||||
/// @param[out] error
|
||||
/// If this language is supported but the REPL couldn't be created, this error is populated with the reason.
|
||||
///
|
||||
/// @param[in] language
|
||||
/// The language to create a REPL for.
|
||||
///
|
||||
/// @param[in] target
|
||||
/// If provided, the target to put the REPL inside.
|
||||
///
|
||||
/// @param[in] repl_options
|
||||
/// If provided, additional options for the compiler when parsing REPL expressions.
|
||||
///
|
||||
/// @return
|
||||
/// The range of the containing object in the target process.
|
||||
//------------------------------------------------------------------
|
||||
static lldb::REPLSP
|
||||
Create (lldb::LanguageType language, Target *target);
|
||||
Create (Error &Error, lldb::LanguageType language, Target *target, const char *repl_options);
|
||||
|
||||
void
|
||||
SetFormatOptions (const OptionGroupFormat &options)
|
||||
|
|
|
@ -1568,7 +1568,7 @@ public:
|
|||
GetSearchFilterForModuleAndCUList (const FileSpecList *containingModules, const FileSpecList *containingSourceFiles);
|
||||
|
||||
lldb::REPLSP
|
||||
GetREPL (lldb::LanguageType, bool can_create);
|
||||
GetREPL (Error &err, lldb::LanguageType language, const char *repl_options, bool can_create);
|
||||
|
||||
protected:
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace lldb_private
|
|||
typedef lldb::InstrumentationRuntimeType (*InstrumentationRuntimeGetType) ();
|
||||
typedef lldb::InstrumentationRuntimeSP (*InstrumentationRuntimeCreateInstance) (const lldb::ProcessSP &process_sp);
|
||||
typedef lldb::TypeSystemSP (*TypeSystemCreateInstance) (lldb::LanguageType language, Module *module, Target *target);
|
||||
typedef lldb::REPLSP (*REPLCreateInstance) (lldb::LanguageType language, Target *target);
|
||||
typedef lldb::REPLSP (*REPLCreateInstance) (Error &error, lldb::LanguageType language, Target *target, const char *repl_options);
|
||||
typedef void (*TypeSystemEnumerateSupportedLanguages) (std::set<lldb::LanguageType> &languages_for_types, std::set<lldb::LanguageType> &languages_for_expressions);
|
||||
typedef int (*ComparisonFunction)(const void *, const void *);
|
||||
typedef void (*DebuggerInitializeCallback)(Debugger &debugger);
|
||||
|
|
|
@ -377,6 +377,9 @@ public:
|
|||
int &num_errors,
|
||||
bool &quit_requested,
|
||||
bool &stopped_for_crash);
|
||||
|
||||
lldb::SBError
|
||||
RunREPL (lldb::LanguageType language, const char *repl_options);
|
||||
}; // class SBDebugger
|
||||
|
||||
} // namespace lldb
|
||||
|
|
|
@ -1000,6 +1000,17 @@ SBDebugger::RunCommandInterpreter (bool auto_handle_events,
|
|||
}
|
||||
}
|
||||
|
||||
SBError
|
||||
SBDebugger::RunREPL (lldb::LanguageType language, const char *repl_options)
|
||||
{
|
||||
SBError error;
|
||||
if (m_opaque_sp)
|
||||
error.ref() = m_opaque_sp->RunREPL(language, repl_options);
|
||||
else
|
||||
error.SetErrorString ("invalid debugger");
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
SBDebugger::reset (const DebuggerSP &debugger_sp)
|
||||
{
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "lldb/DataFormatters/DataVisualization.h"
|
||||
#include "lldb/DataFormatters/FormatManager.h"
|
||||
#include "lldb/DataFormatters/TypeSummary.h"
|
||||
#include "lldb/Expression/REPL.h"
|
||||
#include "lldb/Host/ConnectionFileDescriptor.h"
|
||||
#include "lldb/Host/HostInfo.h"
|
||||
#include "lldb/Host/Terminal.h"
|
||||
|
@ -44,6 +45,7 @@
|
|||
#include "lldb/Symbol/Symbol.h"
|
||||
#include "lldb/Symbol/VariableList.h"
|
||||
#include "lldb/Target/TargetList.h"
|
||||
#include "lldb/Target/Language.h"
|
||||
#include "lldb/Target/Process.h"
|
||||
#include "lldb/Target/RegisterContext.h"
|
||||
#include "lldb/Target/SectionLoadList.h"
|
||||
|
@ -1798,3 +1800,35 @@ Debugger::GetSelectedOrDummyTarget(bool prefer_dummy)
|
|||
return GetDummyTarget();
|
||||
}
|
||||
|
||||
Error
|
||||
Debugger::RunREPL (LanguageType language, const char *repl_options)
|
||||
{
|
||||
Error err;
|
||||
FileSpec repl_executable;
|
||||
if (language == eLanguageTypeUnknown)
|
||||
{
|
||||
err.SetErrorString ("must specify a language for a REPL"); // TODO make it possible to specify a default language
|
||||
return err;
|
||||
}
|
||||
|
||||
Target *const target = nullptr; // passing in an empty target means the REPL must create one
|
||||
|
||||
REPLSP repl_sp(REPL::Create(err, language, target, repl_options));
|
||||
|
||||
if (!err.Success())
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!repl_sp)
|
||||
{
|
||||
err.SetErrorStringWithFormat("couldn't find a REPL for %s", Language::GetNameForLanguageType(language));
|
||||
return err;
|
||||
}
|
||||
|
||||
repl_sp->SetCompilerOptions(repl_options);
|
||||
repl_sp->RunLoop();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,14 +23,14 @@
|
|||
using namespace lldb_private;
|
||||
|
||||
lldb::REPLSP
|
||||
REPL::Create(lldb::LanguageType language, Target *target)
|
||||
REPL::Create(Error &err, lldb::LanguageType language, Target *target, const char *repl_options)
|
||||
{
|
||||
uint32_t idx = 0;
|
||||
lldb::REPLSP ret;
|
||||
|
||||
while (REPLCreateInstance create_instance = PluginManager::GetREPLCreateCallbackAtIndex(idx++))
|
||||
{
|
||||
ret = (*create_instance)(language, target);
|
||||
ret = (*create_instance)(err, language, target, repl_options);
|
||||
if (ret)
|
||||
{
|
||||
break;
|
||||
|
|
|
@ -213,7 +213,7 @@ Target::GetProcessSP () const
|
|||
}
|
||||
|
||||
lldb::REPLSP
|
||||
Target::GetREPL (lldb::LanguageType language, bool can_create)
|
||||
Target::GetREPL (Error &err, lldb::LanguageType language, const char *repl_options, bool can_create)
|
||||
{
|
||||
if (language == eLanguageTypeUnknown)
|
||||
{
|
||||
|
@ -229,10 +229,11 @@ Target::GetREPL (lldb::LanguageType language, bool can_create)
|
|||
|
||||
if (!can_create)
|
||||
{
|
||||
err.SetErrorStringWithFormat("Couldn't find an existing REPL for %s, and can't create a new one", Language::GetNameForLanguageType(language));
|
||||
return lldb::REPLSP();
|
||||
}
|
||||
|
||||
lldb::REPLSP ret = REPL::Create(language, this);
|
||||
lldb::REPLSP ret = REPL::Create(err, language, this, repl_options);
|
||||
|
||||
if (ret)
|
||||
{
|
||||
|
@ -240,7 +241,12 @@ Target::GetREPL (lldb::LanguageType language, bool can_create)
|
|||
return m_repl_map[language];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
if (err.Success())
|
||||
{
|
||||
err.SetErrorStringWithFormat("Couldn't create a REPL for %s", Language::GetNameForLanguageType(language));
|
||||
}
|
||||
|
||||
return lldb::REPLSP();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "lldb/API/SBDebugger.h"
|
||||
#include "lldb/API/SBEvent.h"
|
||||
#include "lldb/API/SBHostOS.h"
|
||||
#include "lldb/API/SBLanguageRuntime.h"
|
||||
#include "lldb/API/SBListener.h"
|
||||
#include "lldb/API/SBStream.h"
|
||||
#include "lldb/API/SBTarget.h"
|
||||
|
@ -132,6 +133,10 @@ static OptionDefinition g_options[] =
|
|||
"extensions have been implemented." },
|
||||
{ LLDB_3_TO_5, false, "debug" , 'd', no_argument , 0, eArgTypeNone,
|
||||
"Tells the debugger to print out extra information for debugging itself." },
|
||||
{ LLDB_OPT_SET_7, true , "repl" , 'r', optional_argument, 0, eArgTypeNone,
|
||||
"Runs lldb in REPL mode with a stub process." },
|
||||
{ LLDB_OPT_SET_7, true , "repl-language" , 'R', required_argument, 0, eArgTypeNone,
|
||||
"Chooses the language for the REPL." },
|
||||
{ 0, false, NULL , 0 , 0 , 0, eArgTypeNone, NULL }
|
||||
};
|
||||
|
||||
|
@ -408,6 +413,9 @@ Driver::OptionData::OptionData () :
|
|||
m_print_python_path (false),
|
||||
m_print_help (false),
|
||||
m_wait_for(false),
|
||||
m_repl (false),
|
||||
m_repl_lang (eLanguageTypeUnknown),
|
||||
m_repl_options (),
|
||||
m_process_name(),
|
||||
m_process_pid(LLDB_INVALID_PROCESS_ID),
|
||||
m_use_external_editor(false),
|
||||
|
@ -769,6 +777,23 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exiting)
|
|||
optarg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
m_option_data.m_repl = true;
|
||||
if (optarg && optarg[0])
|
||||
m_option_data.m_repl_options = optarg;
|
||||
else
|
||||
m_option_data.m_repl_options.clear();
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
m_option_data.m_repl_lang = SBLanguageRuntime::GetLanguageTypeFromString (optarg);
|
||||
if (m_option_data.m_repl_lang == eLanguageTypeUnknown)
|
||||
{
|
||||
error.SetErrorStringWithFormat ("Unrecognized language name: \"%s\"", optarg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 's':
|
||||
m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterFile, true, error);
|
||||
break;
|
||||
|
@ -1056,96 +1081,114 @@ Driver::MainLoop ()
|
|||
bool handle_events = true;
|
||||
bool spawn_thread = false;
|
||||
|
||||
// Check if we have any data in the commands stream, and if so, save it to a temp file
|
||||
// so we can then run the command interpreter using the file contents.
|
||||
const char *commands_data = commands_stream.GetData();
|
||||
const size_t commands_size = commands_stream.GetSize();
|
||||
|
||||
// The command file might have requested that we quit, this variable will track that.
|
||||
bool quit_requested = false;
|
||||
bool stopped_for_crash = false;
|
||||
if (commands_data && commands_size)
|
||||
if (m_option_data.m_repl)
|
||||
{
|
||||
int initial_commands_fds[2];
|
||||
bool success = true;
|
||||
FILE *commands_file = PrepareCommandsForSourcing (commands_data, commands_size, initial_commands_fds);
|
||||
if (commands_file)
|
||||
const char *repl_options = NULL;
|
||||
if (!m_option_data.m_repl_options.empty())
|
||||
repl_options = m_option_data.m_repl_options.c_str();
|
||||
SBError error (m_debugger.RunREPL(m_option_data.m_repl_lang, repl_options));
|
||||
if (error.Fail())
|
||||
{
|
||||
m_debugger.SetInputFileHandle (commands_file, true);
|
||||
|
||||
// Set the debugger into Sync mode when running the command file. Otherwise command files
|
||||
// that run the target won't run in a sensible way.
|
||||
bool old_async = m_debugger.GetAsync();
|
||||
m_debugger.SetAsync(false);
|
||||
int num_errors;
|
||||
|
||||
SBCommandInterpreterRunOptions options;
|
||||
options.SetStopOnError (true);
|
||||
if (m_option_data.m_batch)
|
||||
options.SetStopOnCrash (true);
|
||||
|
||||
m_debugger.RunCommandInterpreter(handle_events,
|
||||
spawn_thread,
|
||||
options,
|
||||
num_errors,
|
||||
quit_requested,
|
||||
stopped_for_crash);
|
||||
|
||||
if (m_option_data.m_batch && stopped_for_crash && !m_option_data.m_after_crash_commands.empty())
|
||||
{
|
||||
int crash_command_fds[2];
|
||||
SBStream crash_commands_stream;
|
||||
WriteCommandsForSourcing (eCommandPlacementAfterCrash, crash_commands_stream);
|
||||
const char *crash_commands_data = crash_commands_stream.GetData();
|
||||
const size_t crash_commands_size = crash_commands_stream.GetSize();
|
||||
commands_file = PrepareCommandsForSourcing (crash_commands_data, crash_commands_size, crash_command_fds);
|
||||
if (commands_file)
|
||||
{
|
||||
bool local_quit_requested;
|
||||
bool local_stopped_for_crash;
|
||||
m_debugger.SetInputFileHandle (commands_file, true);
|
||||
|
||||
m_debugger.RunCommandInterpreter(handle_events,
|
||||
spawn_thread,
|
||||
options,
|
||||
num_errors,
|
||||
local_quit_requested,
|
||||
local_stopped_for_crash);
|
||||
if (local_quit_requested)
|
||||
quit_requested = true;
|
||||
|
||||
}
|
||||
}
|
||||
m_debugger.SetAsync(old_async);
|
||||
const char *error_cstr = error.GetCString();
|
||||
if (error_cstr && error_cstr[0])
|
||||
fprintf (stderr, "error: %s\n", error_cstr);
|
||||
else
|
||||
fprintf (stderr, "error: %u\n", error.GetError());
|
||||
}
|
||||
else
|
||||
success = false;
|
||||
|
||||
// Close any pipes that we still have ownership of
|
||||
CleanupAfterCommandSourcing(initial_commands_fds);
|
||||
|
||||
// Something went wrong with command pipe
|
||||
if (!success)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Now set the input file handle to STDIN and run the command
|
||||
// interpreter again in interactive mode and let the debugger
|
||||
// take ownership of stdin
|
||||
|
||||
bool go_interactive = true;
|
||||
if (quit_requested)
|
||||
go_interactive = false;
|
||||
else if (m_option_data.m_batch && !stopped_for_crash)
|
||||
go_interactive = false;
|
||||
|
||||
if (go_interactive)
|
||||
else
|
||||
{
|
||||
m_debugger.SetInputFileHandle (stdin, true);
|
||||
m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
|
||||
// Check if we have any data in the commands stream, and if so, save it to a temp file
|
||||
// so we can then run the command interpreter using the file contents.
|
||||
const char *commands_data = commands_stream.GetData();
|
||||
const size_t commands_size = commands_stream.GetSize();
|
||||
|
||||
// The command file might have requested that we quit, this variable will track that.
|
||||
bool quit_requested = false;
|
||||
bool stopped_for_crash = false;
|
||||
if (commands_data && commands_size)
|
||||
{
|
||||
int initial_commands_fds[2];
|
||||
bool success = true;
|
||||
FILE *commands_file = PrepareCommandsForSourcing (commands_data, commands_size, initial_commands_fds);
|
||||
if (commands_file)
|
||||
{
|
||||
m_debugger.SetInputFileHandle (commands_file, true);
|
||||
|
||||
// Set the debugger into Sync mode when running the command file. Otherwise command files
|
||||
// that run the target won't run in a sensible way.
|
||||
bool old_async = m_debugger.GetAsync();
|
||||
m_debugger.SetAsync(false);
|
||||
int num_errors;
|
||||
|
||||
SBCommandInterpreterRunOptions options;
|
||||
options.SetStopOnError (true);
|
||||
if (m_option_data.m_batch)
|
||||
options.SetStopOnCrash (true);
|
||||
|
||||
m_debugger.RunCommandInterpreter(handle_events,
|
||||
spawn_thread,
|
||||
options,
|
||||
num_errors,
|
||||
quit_requested,
|
||||
stopped_for_crash);
|
||||
|
||||
if (m_option_data.m_batch && stopped_for_crash && !m_option_data.m_after_crash_commands.empty())
|
||||
{
|
||||
int crash_command_fds[2];
|
||||
SBStream crash_commands_stream;
|
||||
WriteCommandsForSourcing (eCommandPlacementAfterCrash, crash_commands_stream);
|
||||
const char *crash_commands_data = crash_commands_stream.GetData();
|
||||
const size_t crash_commands_size = crash_commands_stream.GetSize();
|
||||
commands_file = PrepareCommandsForSourcing (crash_commands_data, crash_commands_size, crash_command_fds);
|
||||
if (commands_file)
|
||||
{
|
||||
bool local_quit_requested;
|
||||
bool local_stopped_for_crash;
|
||||
m_debugger.SetInputFileHandle (commands_file, true);
|
||||
|
||||
m_debugger.RunCommandInterpreter(handle_events,
|
||||
spawn_thread,
|
||||
options,
|
||||
num_errors,
|
||||
local_quit_requested,
|
||||
local_stopped_for_crash);
|
||||
if (local_quit_requested)
|
||||
quit_requested = true;
|
||||
|
||||
}
|
||||
}
|
||||
m_debugger.SetAsync(old_async);
|
||||
}
|
||||
else
|
||||
success = false;
|
||||
|
||||
// Close any pipes that we still have ownership of
|
||||
CleanupAfterCommandSourcing(initial_commands_fds);
|
||||
|
||||
// Something went wrong with command pipe
|
||||
if (!success)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Now set the input file handle to STDIN and run the command
|
||||
// interpreter again in interactive mode and let the debugger
|
||||
// take ownership of stdin
|
||||
|
||||
bool go_interactive = true;
|
||||
if (quit_requested)
|
||||
go_interactive = false;
|
||||
else if (m_option_data.m_batch && !stopped_for_crash)
|
||||
go_interactive = false;
|
||||
|
||||
if (go_interactive)
|
||||
{
|
||||
m_debugger.SetInputFileHandle (stdin, true);
|
||||
m_debugger.RunCommandInterpreter(handle_events, spawn_thread);
|
||||
}
|
||||
}
|
||||
|
||||
reset_stdin_termios();
|
||||
|
|
|
@ -105,6 +105,9 @@ public:
|
|||
bool m_print_python_path;
|
||||
bool m_print_help;
|
||||
bool m_wait_for;
|
||||
bool m_repl;
|
||||
lldb::LanguageType m_repl_lang;
|
||||
std::string m_repl_options;
|
||||
std::string m_process_name;
|
||||
lldb::pid_t m_process_pid;
|
||||
bool m_use_external_editor; // FIXME: When we have set/show variables we can remove this from here.
|
||||
|
|
Loading…
Reference in New Issue