This adds a "batch mode" to lldb kinda like the gdb batch mode. It will quit the...
authorJim Ingham <jingham@apple.com>
Tue, 14 Oct 2014 01:20:07 +0000 (01:20 +0000)
committerJim Ingham <jingham@apple.com>
Tue, 14 Oct 2014 01:20:07 +0000 (01:20 +0000)
after all the commands have been executed except if one of the commands was an execution control
command that stopped because of a signal or exception.

Also adds a variant of SBCommandInterpreter::HandleCommand that takes an SBExecutionContext.  That
way you can run an lldb command targeted at a particular target, thread or process w/o having to
select same before running the command.

Also exposes CommandInterpreter::HandleCommandsFromFile to the SBCommandInterpreter API, since that
seemed generally useful.

llvm-svn: 219654

14 files changed:
lldb/include/lldb/API/SBCommandInterpreter.h
lldb/include/lldb/API/SBDebugger.h
lldb/include/lldb/API/SBExecutionContext.h
lldb/include/lldb/API/SBFileSpec.h
lldb/include/lldb/Interpreter/CommandInterpreter.h
lldb/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme
lldb/scripts/Python/interface/SBCommandInterpreter.i
lldb/scripts/Python/interface/SBDebugger.i
lldb/source/API/SBCommandInterpreter.cpp
lldb/source/API/SBDebugger.cpp
lldb/source/API/SBExecutionContext.cpp
lldb/source/Interpreter/CommandInterpreter.cpp
lldb/tools/driver/Driver.cpp
lldb/tools/driver/Driver.h

index 6f9295b..947e391 100644 (file)
@@ -18,6 +18,8 @@ namespace lldb {
 class SBCommandInterpreterRunOptions
 {
 friend class SBDebugger;
+friend class SBCommandInterpreter;
+
 public:
     SBCommandInterpreterRunOptions();
     ~SBCommandInterpreterRunOptions();
@@ -138,6 +140,15 @@ public:
     lldb::ReturnStatus
     HandleCommand (const char *command_line, lldb::SBCommandReturnObject &result, bool add_to_history = false);
 
+    lldb::ReturnStatus
+    HandleCommand (const char *command_line, SBExecutionContext &exe_ctx, SBCommandReturnObject &result, bool add_to_history = false);
+
+    void
+    HandleCommandsFromFile (lldb::SBFileSpec &file,
+                            lldb::SBExecutionContext &override_context,
+                            lldb::SBCommandInterpreterRunOptions &options,
+                            lldb::SBCommandReturnObject result);
+
     // The pointer based interface is not useful in SWIG, since the cursor & last_char arguments are string pointers INTO current_line
     // and you can't do that in a scripting language interface in general...
     
index de7d1a1..6e43e12 100644 (file)
@@ -327,7 +327,8 @@ public:
                            bool spawn_thread,
                            SBCommandInterpreterRunOptions &options,
                            int  &num_errors,
-                           bool &quit_requested);
+                           bool &quit_requested,
+                           bool &stopped_for_crash);
 private:
 
     friend class SBCommandInterpreter;
index e5cac1a..9d88913 100644 (file)
@@ -20,6 +20,8 @@ namespace lldb {
     
 class SBExecutionContext
 {
+friend class SBCommandInterpreter;
+
 public:
     SBExecutionContext();
     
index d262b98..1eee3d1 100644 (file)
@@ -63,6 +63,7 @@ public:
 private:
     friend class SBAttachInfo;
     friend class SBBlock;
+    friend class SBCommandInterpreter;
     friend class SBCompileUnit;
     friend class SBDeclaration;
     friend class SBFileSpecList;
index 7047f21..ddc4dba 100644 (file)
@@ -628,6 +628,12 @@ public:
     {
         return m_quit_requested;
     }
+
+    bool
+    GetStoppedForCrash () const
+    {
+        return m_stopped_for_crash;
+    }
     
 protected:
     friend class Debugger;
@@ -685,6 +691,7 @@ private:
     std::vector<uint32_t> m_command_source_flags;
     uint32_t m_num_errors;
     bool m_quit_requested;
+    bool m_stopped_for_crash;
     
 };
 
index 34560fe..bd7c34c 100644 (file)
@@ -84,7 +84,7 @@
    <LaunchAction
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
+      launchStyle = "1"
       useCustomWorkingDirectory = "NO"
       customWorkingDirectory = "/Volumes/work/gclayton/Documents/devb/attach"
       buildConfiguration = "Debug"
index 8560380..0a795ad 100644 (file)
@@ -182,6 +182,15 @@ public:
     lldb::ReturnStatus
     HandleCommand (const char *command_line, lldb::SBCommandReturnObject &result, bool add_to_history = false);
 
+    lldb::ReturnStatus
+    HandleCommand (const char *command_line, SBExecutionContext &exe_ctx, SBCommandReturnObject &result, bool add_to_history = false);
+
+    void
+    HandleCommandsFromFile (lldb::SBFileSpec &file,
+                            lldb::SBExecutionContext &override_context,
+                            lldb::SBCommandInterpreterRunOptions &options,
+                            lldb::SBCommandReturnObject result);
+
     int
     HandleCompletion (const char *current_line,
                       uint32_t cursor_pos,
index fb63fc3..48e0d5d 100644 (file)
@@ -369,7 +369,8 @@ public:
                            bool spawn_thread,
                            SBCommandInterpreterRunOptions &options,
                            int  &num_errors,
-                           bool &quit_requested);
+                           bool &quit_requested,
+                           bool &stopped_for_crash);
 }; // class SBDebugger
 
 } // namespace lldb
index bc8d6d3..4e65c4f 100644 (file)
@@ -20,6 +20,7 @@
 #include "lldb/API/SBBroadcaster.h"
 #include "lldb/API/SBCommandReturnObject.h"
 #include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBExecutionContext.h"
 #include "lldb/API/SBProcess.h"
 #include "lldb/API/SBTarget.h"
 #include "lldb/API/SBListener.h"
@@ -222,6 +223,13 @@ SBCommandInterpreter::GetIOHandlerControlSequence(char ch)
 lldb::ReturnStatus
 SBCommandInterpreter::HandleCommand (const char *command_line, SBCommandReturnObject &result, bool add_to_history)
 {
+    SBExecutionContext sb_exe_ctx;
+    return HandleCommand (command_line, sb_exe_ctx, result, add_to_history);
+}
+
+lldb::ReturnStatus
+SBCommandInterpreter::HandleCommand (const char *command_line, SBExecutionContext &override_context, SBCommandReturnObject &result, bool add_to_history)
+{
     Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
 
     if (log)
@@ -229,11 +237,21 @@ SBCommandInterpreter::HandleCommand (const char *command_line, SBCommandReturnOb
                      static_cast<void*>(m_opaque_ptr), command_line,
                      static_cast<void*>(result.get()), add_to_history);
 
+    ExecutionContext ctx, *ctx_ptr;
+    if (override_context.get())
+    {
+        ctx = override_context.get()->Lock(true);
+        ctx_ptr = &ctx;
+    }
+    else
+       ctx_ptr = nullptr;
+
+
     result.Clear();
     if (command_line && m_opaque_ptr)
     {
         result.ref().SetInteractive(false);
-        m_opaque_ptr->HandleCommand (command_line, add_to_history ? eLazyBoolYes : eLazyBoolNo, result.ref());
+        m_opaque_ptr->HandleCommand (command_line, add_to_history ? eLazyBoolYes : eLazyBoolNo, result.ref(), ctx_ptr);
     }
     else
     {
@@ -256,6 +274,54 @@ SBCommandInterpreter::HandleCommand (const char *command_line, SBCommandReturnOb
     return result.GetStatus();
 }
 
+void
+SBCommandInterpreter::HandleCommandsFromFile (lldb::SBFileSpec &file,
+                                              lldb::SBExecutionContext &override_context,
+                                              lldb::SBCommandInterpreterRunOptions &options,
+                                              lldb::SBCommandReturnObject result)
+{
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
+
+    if (log)
+    {
+        SBStream s;
+        file.GetDescription (s);
+        log->Printf ("SBCommandInterpreter(%p)::HandleCommandsFromFile (file=\"%s\", SBCommandReturnObject(%p))",
+                     static_cast<void*>(m_opaque_ptr), s.GetData(),
+                     static_cast<void*>(result.get()));
+    }
+
+    if (!m_opaque_ptr)
+    {
+        result->AppendError ("SBCommandInterpreter is not valid.");
+        result->SetStatus (eReturnStatusFailed);
+        return;
+    }
+
+    if (!file.IsValid())
+    {
+        SBStream s;
+        file.GetDescription (s);
+        result->AppendErrorWithFormat ("File is not valid: %s.", s.GetData());
+        result->SetStatus (eReturnStatusFailed);
+    }
+
+    FileSpec tmp_spec = file.ref();
+    ExecutionContext ctx, *ctx_ptr;
+    if (override_context.get())
+    {
+        ctx = override_context.get()->Lock(true);
+        ctx_ptr = &ctx;
+    }
+    else
+       ctx_ptr = nullptr;
+
+
+    m_opaque_ptr->HandleCommandsFromFile (tmp_spec, ctx_ptr, options.ref(), result.ref());
+
+}
+
+
 int
 SBCommandInterpreter::HandleCompletion (const char *current_line,
                                         const char *cursor,
index a655950..4b00f19 100644 (file)
@@ -986,7 +986,8 @@ SBDebugger::RunCommandInterpreter (bool auto_handle_events,
                                    bool spawn_thread,
                                    SBCommandInterpreterRunOptions &options,
                                    int  &num_errors,
-                                   bool &quit_requested)
+                                   bool &quit_requested,
+                                   bool &stopped_for_crash)
 
 {
     if (m_opaque_sp)
@@ -995,6 +996,7 @@ SBDebugger::RunCommandInterpreter (bool auto_handle_events,
         interp.RunCommandInterpreter(auto_handle_events, spawn_thread, options.ref());
         num_errors = interp.GetNumErrors();
         quit_requested = interp.GetQuitRequested();
+        stopped_for_crash = interp.GetStoppedForCrash();
     }
 }
 
index d1c21ec..dc20c60 100644 (file)
@@ -69,6 +69,12 @@ SBExecutionContext::operator = (const lldb::SBExecutionContext &rhs)
     return *this;
 }
 
+ExecutionContextRef *
+SBExecutionContext::get () const
+{
+    return m_exe_ctx_sp.get();
+}
+
 SBTarget
 SBExecutionContext::GetTarget () const
 {
index 28eafcb..b281bf1 100644 (file)
@@ -121,7 +121,8 @@ CommandInterpreter::CommandInterpreter
     m_truncation_warning(eNoTruncation),
     m_command_source_depth (0),
     m_num_errors(0),
-    m_quit_requested(false)
+    m_quit_requested(false),
+    m_stopped_for_crash(false)
 
 {
     debugger.SetScriptLanguage (script_language);
@@ -2568,6 +2569,42 @@ CommandInterpreter::HandleCommands (const StringList &commands,
                 return;
             }
         }
+
+        // Also check for "stop on crash here:
+        bool should_stop = false;
+        if (tmp_result.GetDidChangeProcessState() && options.GetStopOnCrash())
+        {
+            TargetSP target_sp (m_debugger.GetTargetList().GetSelectedTarget());
+            if (target_sp)
+            {
+                ProcessSP process_sp (target_sp->GetProcessSP());
+                if (process_sp)
+                {
+                    for (ThreadSP thread_sp : process_sp->GetThreadList().Threads())
+                    {
+                        StopReason reason = thread_sp->GetStopReason();
+                        if (reason == eStopReasonSignal || reason == eStopReasonException || reason == eStopReasonInstrumentation)
+                        {
+                            should_stop = true;
+                            break;
+                        }
+                    }
+                }
+            }
+            if (should_stop)
+            {
+                if (idx != num_lines - 1)
+                    result.AppendErrorWithFormat("Aborting reading of commands after command #%" PRIu64 ": '%s' stopped with a signal or exception.\n",
+                                                 (uint64_t)idx + 1, cmd);
+                else
+                    result.AppendMessageWithFormat("Command #%" PRIu64 " '%s' stopped with a signal or exception.\n", (uint64_t)idx + 1, cmd);
+                    
+                result.SetStatus(tmp_result.GetStatus());
+                m_debugger.SetAsyncExecution (old_async_execution);
+
+                return;
+            }
+        }
         
     }
     
@@ -2639,6 +2676,19 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file,
                 flags |= eHandleCommandFlagStopOnError;
             }
 
+            if (options.GetStopOnCrash())
+            {
+                if (m_command_source_flags.empty())
+                {
+                    // Echo command by default
+                    flags |= eHandleCommandFlagStopOnCrash;
+                }
+                else if (m_command_source_flags.back() & eHandleCommandFlagStopOnCrash)
+                {
+                    flags |= eHandleCommandFlagStopOnCrash;
+                }
+            }
+
             if (options.m_echo_commands == eLazyBoolCalculate)
             {
                 if (m_command_source_flags.empty())
@@ -2694,7 +2744,7 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file,
                                                               *this));
             const bool old_async_execution = debugger.GetAsyncExecution();
             
-            // Set synchronous execution if we not stopping when we continue
+            // Set synchronous execution if we are not stopping on continue
             if ((flags & eHandleCommandFlagStopOnContinue) == 0)
                 debugger.SetAsyncExecution (false);
 
@@ -3069,6 +3119,50 @@ CommandInterpreter::IOHandlerInputComplete (IOHandler &io_handler, std::string &
             io_handler.SetIsDone(true);
             break;
     }
+
+    // Finally, if we're going to stop on crash, check that here:
+    if (!m_quit_requested
+        && result.GetDidChangeProcessState()
+        && io_handler.GetFlags().Test(eHandleCommandFlagStopOnCrash))
+    {
+        bool should_stop = false;
+        TargetSP target_sp (m_debugger.GetTargetList().GetSelectedTarget());
+        if (target_sp)
+        {
+            ProcessSP process_sp (target_sp->GetProcessSP());
+            if (process_sp)
+            {
+                for (ThreadSP thread_sp : process_sp->GetThreadList().Threads())
+                {
+                    StopReason reason = thread_sp->GetStopReason();
+                    if (reason == eStopReasonSignal || reason == eStopReasonException || reason == eStopReasonInstrumentation)
+                    {
+                        // If we are printing results, we ought to show the resaon why we are stopping here:
+                        if (io_handler.GetFlags().Test(eHandleCommandFlagPrintResult))
+                        {
+                            if (!result.GetImmediateOutputStream())
+                            {
+                                const uint32_t start_frame = 0;
+                                const uint32_t num_frames = 1;
+                                const uint32_t num_frames_with_source = 1;
+                                thread_sp->GetStatus (*io_handler.GetOutputStreamFile().get(),
+                                                      start_frame,
+                                                      num_frames,
+                                                      num_frames_with_source);
+                            }
+                        }
+                        should_stop = true;
+                        break;
+                    }
+                }
+            }
+        }
+        if (should_stop)
+        {
+            io_handler.SetIsDone(true);
+            m_stopped_for_crash = true;
+        }
+    }
 }
 
 bool
@@ -3155,6 +3249,7 @@ CommandInterpreter::RunCommandInterpreter(bool auto_handle_events,
     const bool multiple_lines = false;
     m_num_errors = 0;
     m_quit_requested = false;
+    m_stopped_for_crash = false;
     
     // Always re-create the IOHandlerEditline in case the input
     // changed. The old instance might have had a non-interactive
index e4fdc77..cec56b4 100644 (file)
@@ -109,7 +109,10 @@ static OptionDefinition g_options[] =
     { 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, "source-quietly"          , 'Q', no_argument      , 0,  eArgTypeNone,
-        "Tells the debugger suppress output from commands provided in the -s, -S, -O and -o commands." },
+        "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,
+        "Tells the debugger to running the commands from -s, -S, -o & -O, and then quit.  However if any run command stopped due to a signal or crash, "
+        "the debugger will return to the interactive prompt at the place of the crash." },
     { LLDB_3_TO_5,       false, "editor"         , 'e', no_argument      , 0,  eArgTypeNone,
         "Tells the debugger to open source files using the host's \"external editor\" mechanism." },
     { LLDB_3_TO_5,       false, "no-lldbinit"    , 'x', no_argument      , 0,  eArgTypeNone,
@@ -406,6 +409,7 @@ Driver::OptionData::OptionData () :
     m_process_name(),
     m_process_pid(LLDB_INVALID_PROCESS_ID),
     m_use_external_editor(false),
+    m_batch(false),
     m_seen_options()
 {
 }
@@ -429,6 +433,7 @@ Driver::OptionData::Clear ()
     m_use_external_editor = false;
     m_wait_for = false;
     m_process_name.erase();
+    m_batch = false;
     m_process_pid = LLDB_INVALID_PROCESS_ID;
 }
 
@@ -641,6 +646,10 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exiting)
                         m_option_data.m_print_python_path = true;
                         break;
 
+                    case 'b':
+                        m_option_data.m_batch = true;
+                        break;
+
                     case 'c':
                         {
                             SBFileSpec file(optarg);
@@ -924,6 +933,7 @@ Driver::MainLoop ()
 
     // 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)
     {
         enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
@@ -973,8 +983,15 @@ Driver::MainLoop ()
                     
                     SBCommandInterpreterRunOptions options;
                     options.SetStopOnError (true);
-                    
-                    m_debugger.RunCommandInterpreter(handle_events, spawn_thread, options, num_errors, quit_requested);
+                    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
@@ -1030,7 +1047,13 @@ Driver::MainLoop ()
     // interpreter again in interactive mode and let the debugger
     // take ownership of stdin
 
-    if (!quit_requested)
+    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);
index 8b6c6ee..c4faac1 100644 (file)
@@ -87,6 +87,7 @@ public:
         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.
+        bool m_batch;
         typedef std::set<char> OptionSet;
         OptionSet m_seen_options;
     };