linux: initial support for 'real' signal handling

This patch upgrades the Linux process plugin to handle a larger range of signal
events.  For example, we can detect when the inferior has "crashed" and why,
interrupt a running process, deliver an arbitrary signal, and so on.



git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@128547 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Plugins/Process/Linux/ProcessLinux.cpp b/source/Plugins/Process/Linux/ProcessLinux.cpp
index b3405dd..f915888 100644
--- a/source/Plugins/Process/Linux/ProcessLinux.cpp
+++ b/source/Plugins/Process/Linux/ProcessLinux.cpp
@@ -72,7 +72,9 @@
 ProcessLinux::ProcessLinux(Target& target, Listener &listener)
     : Process(target, listener),
       m_monitor(NULL),
-      m_module(NULL)
+      m_module(NULL),
+      m_in_limbo(false),
+      m_exit_now(false)
 {
     // FIXME: Putting this code in the ctor and saving the byte order in a
     // member variable is a hack to avoid const qual issues in GetByteOrder.
@@ -147,11 +149,20 @@
 Error
 ProcessLinux::DoResume()
 {
-    assert(GetPrivateState() == eStateStopped && "Bad state for DoResume!");
+    StateType state = GetPrivateState();
 
-    // Set our state to running.  This ensures inferior threads do not post a
-    // state change first.
-    SetPrivateState(eStateRunning);
+    assert(state == eStateStopped || state == eStateCrashed);
+
+    // We are about to resume a thread that will cause the process to exit so
+    // set our exit status now.  Do not change our state if the inferior
+    // crashed.
+    if (state == eStateStopped) 
+    {
+        if (m_in_limbo)
+            SetExitStatus(m_exit_status, NULL);
+        else
+            SetPrivateState(eStateRunning);
+    }
 
     bool did_resume = false;
     uint32_t thread_count = m_thread_list.GetSize(false);
@@ -182,7 +193,23 @@
 Error
 ProcessLinux::DoHalt(bool &caused_stop)
 {
-    return Error(1, eErrorTypeGeneric);
+    Error error;
+
+    if (IsStopped())
+    {
+        caused_stop = false;
+    }
+    else if (kill(GetID(), SIGSTOP))
+    {
+        caused_stop = false;
+        error.SetErrorToErrno();
+    }
+    else
+    {
+        caused_stop = true;
+    }
+
+    return error;
 }
 
 Error
@@ -194,7 +221,12 @@
 Error
 ProcessLinux::DoSignal(int signal)
 {
-    return Error(1, eErrorTypeGeneric);
+    Error error;
+
+    if (kill(GetID(), signal))
+        error.SetErrorToErrno();
+
+    return error;
 }
 
 Error
@@ -204,42 +236,19 @@
 
     if (!HasExited())
     {
-        // Shut down the private state thread as we will synchronize with events
-        // ourselves.  Discard all current thread plans.
-        PausePrivateStateThread();
-        GetThreadList().DiscardThreadPlans();
+        // Drive the exit event to completion (do not keep the inferior in
+        // limbo).
+        m_exit_now = true;
 
-        // Bringing the inferior into limbo will be caught by our monitor
-        // thread, in turn updating the process state.
-        if (!m_monitor->BringProcessIntoLimbo())
+        if (kill(m_monitor->GetPID(), SIGKILL) && error.Success())
         {
-            error.SetErrorToGenericError();
-            error.SetErrorString("Process termination failed.");
+            error.SetErrorToErrno();
             return error;
         }
 
-        // Wait for the event to arrive.  This is guaranteed to be an exit event.
-        StateType state;
-        EventSP event;
-        do {
-            TimeValue timeout_time;
-            timeout_time = TimeValue::Now();
-            timeout_time.OffsetWithSeconds(2);
-            state = WaitForStateChangedEventsPrivate(&timeout_time, event);
-        } while (state != eStateExited && state != eStateInvalid);
-
-        // Check if we timed out waiting for the exit event to arrive.
-        if (state == eStateInvalid)
-            error.SetErrorString("ProcessLinux::DoDestroy timed out.");
-
-        // Restart standard event handling and send the process the final kill,
-        // driving it out of limbo.
-        ResumePrivateStateThread();
+        SetPrivateState(eStateExited);
     }
 
-    if (kill(m_monitor->GetPID(), SIGKILL) && error.Success())
-        error.SetErrorToErrno();
-
     return error;
 }
 
@@ -251,18 +260,41 @@
     switch (message.GetKind())
     {
     default:
-        SetPrivateState(eStateStopped);
+        assert(false && "Unexpected process message!");
         break;
 
     case ProcessMessage::eInvalidMessage:
         return;
 
+    case ProcessMessage::eLimboMessage:
+        m_in_limbo = true;
+        m_exit_status = message.GetExitStatus();
+        if (m_exit_now)
+        {
+            SetPrivateState(eStateExited);
+            m_monitor->Detach();
+        }
+        else
+            SetPrivateState(eStateStopped);
+        break;
+
     case ProcessMessage::eExitMessage:
-        SetExitStatus(message.GetExitStatus(), NULL);
+        m_exit_status = message.GetExitStatus();
+        SetExitStatus(m_exit_status, NULL);
+        break;
+
+    case ProcessMessage::eTraceMessage:
+    case ProcessMessage::eBreakpointMessage:
+        SetPrivateState(eStateStopped);
         break;
 
     case ProcessMessage::eSignalMessage:
-        SetExitStatus(-1, NULL);
+    case ProcessMessage::eSignalDeliveredMessage:
+        SetPrivateState(eStateStopped);
+        break;
+
+    case ProcessMessage::eCrashMessage:
+        SetPrivateState(eStateCrashed);
         break;
     }
 
@@ -278,30 +310,12 @@
 
     ProcessMessage &message = m_message_queue.front();
 
-    // Resolve the thread this message corresponds to.
+    // Resolve the thread this message corresponds to and pass it along.
     lldb::tid_t tid = message.GetTID();
     LinuxThread *thread = static_cast<LinuxThread*>(
         GetThreadList().FindThreadByID(tid, false).get());
 
-    switch (message.GetKind())
-    {
-    default:
-        assert(false && "Unexpected message kind!");
-        break;
-
-    case ProcessMessage::eExitMessage:
-    case ProcessMessage::eSignalMessage:
-        thread->ExitNotify();
-        break;
-
-    case ProcessMessage::eTraceMessage:
-        thread->TraceNotify();
-        break;
-
-    case ProcessMessage::eBreakpointMessage:
-        thread->BreakNotify();
-        break;
-    }
+    thread->Notify(message);
 
     m_message_queue.pop();
 }
@@ -432,6 +446,11 @@
     return GetSTDOUT(buf, len, error);
 }
 
+UnixSignals &
+ProcessLinux::GetUnixSignals()
+{
+    return m_linux_signals;
+}
 
 //------------------------------------------------------------------------------
 // ProcessInterface protocol.
@@ -482,8 +501,6 @@
     default:
         break;
 
-    case eStateUnloaded:
-    case eStateCrashed:
     case eStateDetached:
     case eStateExited:
         return true;
@@ -491,3 +508,20 @@
 
     return false;
 }
+
+bool
+ProcessLinux::IsStopped()
+{
+    switch (GetPrivateState())
+    {
+    default:
+        break;
+
+    case eStateStopped:
+    case eStateCrashed:
+    case eStateSuspended:
+        return true;
+    }
+
+    return false;
+}