From 4add3b13f00e78ed11028440bda8cb1ce00b0939 Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Wed, 19 Nov 2014 01:28:13 +0000 Subject: [PATCH] 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. llvm-svn: 222295 --- lldb/tools/driver/Driver.cpp | 279 ++++++++++++++++++++++++++++--------------- lldb/tools/driver/Driver.h | 12 +- 2 files changed, 193 insertions(+), 98 deletions(-) diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp index 477b7d6..206a2f6 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -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 (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 > *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 > &command_set = before_file ? m_option_data.m_initial_commands : - m_option_data.m_after_file_commands; + std::vector > *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(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) - { - 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(nrwr) == commands_size) + 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()) { - // 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) diff --git a/lldb/tools/driver/Driver.h b/lldb/tools/driver/Driver.h index c4faac1..5316c57 100644 --- a/lldb/tools/driver/Driver.h +++ b/lldb/tools/driver/Driver.h @@ -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 > m_initial_commands; std::vector > m_after_file_commands; + std::vector > m_after_crash_commands; bool m_debug_mode; bool m_source_quietly; bool m_print_version; -- 2.7.4