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:
Sean Callanan 2015-10-20 00:23:46 +00:00
parent 7449ab9834
commit 3e7e915dca
12 changed files with 217 additions and 93 deletions

View File

@ -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;

View File

@ -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.

View File

@ -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)

View File

@ -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:
//------------------------------------------------------------------

View File

@ -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);

View File

@ -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

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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();

View File

@ -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.