Add "-k" and "-K" options to the driver, that allow you to register
authorJim Ingham <jingham@apple.com>
Wed, 19 Nov 2014 01:28:13 +0000 (01:28 +0000)
committerJim Ingham <jingham@apple.com>
Wed, 19 Nov 2014 01:28:13 +0000 (01:28 +0000)
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

lldb/tools/driver/Driver.cpp
lldb/tools/driver/Driver.h

index 477b7d6..206a2f6 100644 (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)
-            {
-                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)
+            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)
index c4faac1..5316c57 100644 (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;