This adds a "batch mode" to lldb kinda like the gdb batch mode.  It will quit the debugger
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
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index bc8d6d3..4e65c4f 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -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 @@
 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 @@
                      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 @@
     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,
diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index a655950..4b00f19 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -986,7 +986,8 @@
                                    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 @@
         interp.RunCommandInterpreter(auto_handle_events, spawn_thread, options.ref());
         num_errors = interp.GetNumErrors();
         quit_requested = interp.GetQuitRequested();
+        stopped_for_crash = interp.GetStoppedForCrash();
     }
 }
 
diff --git a/lldb/source/API/SBExecutionContext.cpp b/lldb/source/API/SBExecutionContext.cpp
index d1c21ec..dc20c60 100644
--- a/lldb/source/API/SBExecutionContext.cpp
+++ b/lldb/source/API/SBExecutionContext.cpp
@@ -69,6 +69,12 @@
     return *this;
 }
 
+ExecutionContextRef *
+SBExecutionContext::get () const
+{
+    return m_exe_ctx_sp.get();
+}
+
 SBTarget
 SBExecutionContext::GetTarget () const
 {
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 28eafcb..b281bf1 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -121,7 +121,8 @@
     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 @@
                 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 @@
                 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 @@
                                                               *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 @@
             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 @@
     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