Add "-k" and "-K" options to the driver, that allow you to register

some commands that will get run if the target crashes.

Also fix the bug where the local .lldbinit file was not getting
sourced before not after the target was created from the file options on the
driver command line.

<rdar://problem/19019843>

llvm-svn: 222295
This commit is contained in:
Jim Ingham 2014-11-19 01:28:13 +00:00
parent 22df0eb2e8
commit 4add3b13f0
2 changed files with 193 additions and 98 deletions

View File

@ -110,6 +110,10 @@ static OptionDefinition g_options[] =
"Tells the debugger to read in and execute the lldb commands in the given file, before any file provided on the command line has been loaded." },
{ LLDB_3_TO_5, false, "one-line-before-file" , 'O', required_argument, 0, eArgTypeNone,
"Tells the debugger to execute this one-line lldb command before any file provided on the command line has been loaded." },
{ LLDB_3_TO_5, false, "one-line-on-crash" , 'k', required_argument, 0, eArgTypeNone,
"When in batch mode, tells the debugger to execute this one-line lldb command if the target crashes." },
{ LLDB_3_TO_5, false, "source-on-crash" , 'K', required_argument, 0, eArgTypeFilename,
"When in batch mode, tells the debugger to source this file of lldb commands if the target crashes." },
{ LLDB_3_TO_5, false, "source-quietly" , 'Q', no_argument , 0, eArgTypeNone,
"Tells the debugger to execute this one-line lldb command before any file provided on the command line has been loaded." },
{ LLDB_3_TO_5, false, "batch" , 'b', no_argument , 0, eArgTypeNone,
@ -402,6 +406,7 @@ Driver::OptionData::OptionData () :
m_crash_log (),
m_initial_commands (),
m_after_file_commands (),
m_after_crash_commands(),
m_debug_mode (false),
m_source_quietly(false),
m_print_version (false),
@ -427,6 +432,15 @@ Driver::OptionData::Clear ()
m_script_lang = lldb::eScriptLanguageDefault;
m_initial_commands.clear ();
m_after_file_commands.clear ();
// If there is a local .lldbinit, source that:
SBFileSpec local_lldbinit("./.lldbinit", true);
if (local_lldbinit.Exists())
{
char path[2048];
local_lldbinit.GetPath(path, 2047);
m_after_file_commands.push_back (std::pair<bool, std::string> (true, path));
}
m_debug_mode = false;
m_source_quietly = false;
m_print_help = false;
@ -436,17 +450,27 @@ Driver::OptionData::Clear ()
m_wait_for = false;
m_process_name.erase();
m_batch = false;
m_after_crash_commands.clear();
m_process_pid = LLDB_INVALID_PROCESS_ID;
}
void
Driver::OptionData::AddInitialCommand (const char *command, bool before_file, bool is_file, SBError &error)
Driver::OptionData::AddInitialCommand (const char *command, CommandPlacement placement, bool is_file, SBError &error)
{
std::vector<std::pair<bool, std::string> > *command_set;
if (before_file)
switch (placement)
{
case eCommandPlacementBeforeFile:
command_set = &(m_initial_commands);
else
break;
case eCommandPlacementAfterFile:
command_set = &(m_after_file_commands);
break;
case eCommandPlacementAfterCrash:
command_set = &(m_after_crash_commands);
break;
}
if (is_file)
{
@ -496,12 +520,23 @@ Driver::GetScriptLanguage() const
}
void
Driver::WriteInitialCommands (bool before_file, SBStream &strm)
Driver::WriteCommandsForSourcing (CommandPlacement placement, SBStream &strm)
{
std::vector<std::pair<bool, std::string> > &command_set = before_file ? m_option_data.m_initial_commands :
m_option_data.m_after_file_commands;
std::vector<std::pair<bool, std::string> > *command_set;
switch (placement)
{
case eCommandPlacementBeforeFile:
command_set = &m_option_data.m_initial_commands;
break;
case eCommandPlacementAfterFile:
command_set = &m_option_data.m_after_file_commands;
break;
case eCommandPlacementAfterCrash:
command_set = &m_option_data.m_after_crash_commands;
break;
}
for (const auto &command_pair : command_set)
for (const auto &command_pair : *command_set)
{
const char *command = command_pair.second.c_str();
if (command_pair.first)
@ -712,6 +747,13 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exiting)
m_option_data.m_source_quietly = true;
break;
case 'k':
m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterCrash, true, error);
break;
case 'K':
m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterCrash, false, error);
break;
case 'n':
m_option_data.m_process_name = optarg;
break;
@ -730,16 +772,16 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exiting)
}
break;
case 's':
m_option_data.AddInitialCommand(optarg, false, true, error);
m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterFile, true, error);
break;
case 'o':
m_option_data.AddInitialCommand(optarg, false, false, error);
m_option_data.AddInitialCommand(optarg, eCommandPlacementAfterFile, false, error);
break;
case 'S':
m_option_data.AddInitialCommand(optarg, true, true, error);
m_option_data.AddInitialCommand(optarg, eCommandPlacementBeforeFile, true, error);
break;
case 'O':
m_option_data.AddInitialCommand(optarg, true, false, error);
m_option_data.AddInitialCommand(optarg, eCommandPlacementBeforeFile, false, error);
break;
default:
m_option_data.m_print_help = true;
@ -822,6 +864,95 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exiting)
return error;
}
static ::FILE *
PrepareCommandsForSourcing (const char *commands_data, size_t commands_size, int fds[2])
{
enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
bool success = true;
::FILE *commands_file = NULL;
fds[0] = -1;
fds[1] = -1;
int err = 0;
#ifdef _WIN32
err = _pipe(fds, commands_size, O_BINARY);
#else
err = pipe(fds);
#endif
if (err == 0)
{
ssize_t nrwr = write(fds[WRITE], commands_data, commands_size);
if (nrwr < 0)
{
fprintf(stderr, "error: write(%i, %p, %zd) failed (errno = %i) "
"when trying to open LLDB commands pipe\n",
fds[WRITE], commands_data, commands_size, errno);
success = false;
}
else if (static_cast<size_t>(nrwr) == commands_size)
{
// Close the write end of the pipe so when we give the read end to
// the debugger/command interpreter it will exit when it consumes all
// of the data
#ifdef _WIN32
_close(fds[WRITE]); fds[WRITE] = -1;
#else
close(fds[WRITE]); fds[WRITE] = -1;
#endif
// Now open the read file descriptor in a FILE * that we can give to
// the debugger as an input handle
commands_file = fdopen(fds[READ], "r");
if (commands_file)
{
fds[READ] = -1; // The FILE * 'commands_file' now owns the read descriptor
// Hand ownership if the FILE * over to the debugger for "commands_file".
}
else
{
fprintf(stderr,
"error: fdopen(%i, \"r\") failed (errno = %i) when "
"trying to open LLDB commands pipe\n",
fds[READ], errno);
success = false;
}
}
}
else
{
fprintf(stderr, "error: can't create pipe file descriptors for LLDB commands\n");
success = false;
}
return commands_file;
}
void
CleanupAfterCommandSourcing (int fds[2])
{
enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
// Close any pipes that we still have ownership of
if ( fds[WRITE] != -1)
{
#ifdef _WIN32
_close(fds[WRITE]); fds[WRITE] = -1;
#else
close(fds[WRITE]); fds[WRITE] = -1;
#endif
}
if ( fds[READ] != -1)
{
#ifdef _WIN32
_close(fds[READ]); fds[READ] = -1;
#else
close(fds[READ]); fds[READ] = -1;
#endif
}
}
void
Driver::MainLoop ()
{
@ -864,7 +995,7 @@ Driver::MainLoop ()
SBStream commands_stream;
// First source in the commands specified to be run before the file arguments are processed.
WriteInitialCommands(true, commands_stream);
WriteCommandsForSourcing(eCommandPlacementBeforeFile, commands_stream);
const size_t num_args = m_option_data.m_args.size();
if (num_args > 0)
@ -914,11 +1045,8 @@ Driver::MainLoop ()
commands_stream.Printf ("process attach --pid %" PRIu64 "\n", m_option_data.m_process_pid);
}
WriteInitialCommands(false, commands_stream);
WriteCommandsForSourcing(eCommandPlacementAfterFile, commands_stream);
// Now that all option parsing is done, we try and parse the .lldbinit
// file in the current working directory
sb_interpreter.SourceInitFileInCurrentWorkingDirectory (result);
if (GetDebugMode())
{
result.PutError(m_debugger.GetErrorFileHandle());
@ -938,104 +1066,63 @@ Driver::MainLoop ()
bool stopped_for_crash = false;
if (commands_data && commands_size)
{
enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
int initial_commands_fds[2];
bool success = true;
int fds[2] = { -1, -1 };
int err = 0;
#ifdef _WIN32
err = _pipe(fds, commands_size, O_BINARY);
#else
err = pipe(fds);
#endif
if (err == 0)
FILE *commands_file = PrepareCommandsForSourcing (commands_data, commands_size, initial_commands_fds);
if (commands_file)
{
ssize_t nrwr = write(fds[WRITE], commands_data, commands_size);
if (nrwr < 0)
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())
{
fprintf(stderr, "error: write(%i, %p, %zd) failed (errno = %i) "
"when trying to open LLDB commands pipe\n",
fds[WRITE], commands_data, commands_size, errno);
success = false;
}
else if (static_cast<size_t>(nrwr) == commands_size)
{
// Close the write end of the pipe so when we give the read end to
// the debugger/command interpreter it will exit when it consumes all
// of the data
#ifdef _WIN32
_close(fds[WRITE]); fds[WRITE] = -1;
#else
close(fds[WRITE]); fds[WRITE] = -1;
#endif
// Now open the read file descriptor in a FILE * that we can give to
// the debugger as an input handle
FILE *commands_file = fdopen(fds[READ], "r");
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)
{
fds[READ] = -1; // The FILE * 'commands_file' now owns the read descriptor
// Hand ownership if the FILE * over to the debugger for "commands_file".
bool local_quit_requested;
bool local_stopped_for_crash;
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);
m_debugger.SetAsync(old_async);
}
else
{
fprintf(stderr,
"error: fdopen(%i, \"r\") failed (errno = %i) when "
"trying to open LLDB commands pipe\n",
fds[READ], errno);
success = false;
local_quit_requested,
local_stopped_for_crash);
if (local_quit_requested)
quit_requested = true;
}
}
else
{
assert(!"partial writes not handled");
success = false;
}
m_debugger.SetAsync(old_async);
}
else
{
fprintf(stderr, "error: can't create pipe file descriptors for LLDB commands\n");
success = false;
}
// Close any pipes that we still have ownership of
if ( fds[WRITE] != -1)
{
#ifdef _WIN32
_close(fds[WRITE]); fds[WRITE] = -1;
#else
close(fds[WRITE]); fds[WRITE] = -1;
#endif
}
if ( fds[READ] != -1)
{
#ifdef _WIN32
_close(fds[READ]); fds[READ] = -1;
#else
close(fds[READ]); fds[READ] = -1;
#endif
}
CleanupAfterCommandSourcing(initial_commands_fds);
// Something went wrong with command pipe
if (!success)

View File

@ -28,6 +28,13 @@ class IOChannel;
class Driver : public lldb::SBBroadcaster
{
public:
typedef enum CommandPlacement
{
eCommandPlacementBeforeFile,
eCommandPlacementAfterFile,
eCommandPlacementAfterCrash,
} CommandPlacement;
Driver ();
virtual
@ -52,7 +59,7 @@ public:
GetScriptLanguage() const;
void
WriteInitialCommands (bool before_file, lldb::SBStream &strm);
WriteCommandsForSourcing (CommandPlacement placement, lldb::SBStream &strm);
bool
GetDebugMode() const;
@ -68,7 +75,7 @@ public:
Clear();
void
AddInitialCommand (const char *command, bool before_file, bool is_file, lldb::SBError &error);
AddInitialCommand (const char *command, CommandPlacement placement, bool is_file, lldb::SBError &error);
//static OptionDefinition m_cmd_option_table[];
@ -78,6 +85,7 @@ public:
std::string m_crash_log;
std::vector<std::pair<bool,std::string> > m_initial_commands;
std::vector<std::pair<bool,std::string> > m_after_file_commands;
std::vector<std::pair<bool,std::string> > m_after_crash_commands;
bool m_debug_mode;
bool m_source_quietly;
bool m_print_version;