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.

llvm-svn: 128547
diff --git a/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp
index c5909c8..48a57a8 100644
--- a/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp
+++ b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp
@@ -338,20 +338,26 @@
 class ResumeOperation : public Operation
 {
 public:
-    ResumeOperation(lldb::tid_t tid, bool &result) :
-        m_tid(tid), m_result(result) { }
+    ResumeOperation(lldb::tid_t tid, uint32_t signo, bool &result) :
+        m_tid(tid), m_signo(signo), m_result(result) { }
 
     void Execute(ProcessMonitor *monitor);
 
 private:
     lldb::tid_t m_tid;
+    uint32_t m_signo;
     bool &m_result;
 };
 
 void
 ResumeOperation::Execute(ProcessMonitor *monitor)
 {
-    if (ptrace(PTRACE_CONT, m_tid, NULL, NULL))
+    int data = 0;
+
+    if (m_signo != LLDB_INVALID_SIGNAL_NUMBER)
+        data = m_signo;
+
+    if (ptrace(PTRACE_CONT, m_tid, NULL, data))
         m_result = false;
     else
         m_result = true;
@@ -363,20 +369,26 @@
 class SingleStepOperation : public Operation
 {
 public:
-    SingleStepOperation(lldb::tid_t tid, bool &result)
-        : m_tid(tid), m_result(result) { }
+    SingleStepOperation(lldb::tid_t tid, uint32_t signo, bool &result)
+        : m_tid(tid), m_signo(signo), m_result(result) { }
 
     void Execute(ProcessMonitor *monitor);
 
 private:
     lldb::tid_t m_tid;
+    uint32_t m_signo;
     bool &m_result;
 };
 
 void
 SingleStepOperation::Execute(ProcessMonitor *monitor)
 {
-    if (ptrace(PTRACE_SINGLESTEP, m_tid, NULL, NULL))
+    int data = 0;
+
+    if (m_signo != LLDB_INVALID_SIGNAL_NUMBER)
+        data = m_signo;
+
+    if (ptrace(PTRACE_SINGLESTEP, m_tid, NULL, data))
         m_result = false;
     else
         m_result = true;
@@ -457,32 +469,6 @@
         m_result = false;
     else
         m_result = true;
-
-#if 0
-    // First, stop the inferior process.
-    if (kill(pid, SIGSTOP))
-    {
-        m_result = false;
-        return;
-    }
-
-    // Clear any ptrace options.  When PTRACE_O_TRACEEXIT is set, a plain
-    // PTRACE_KILL (or any termination signal) will not truely terminate the
-    // inferior process.  Instead, the process is left in a state of "limbo"
-    // allowing us to interrogate its state.  However in this case we really do
-    // want the process gone.
-    if (ptrace(PTRACE_SETOPTIONS, pid, NULL, 0UL))
-    {
-        m_result = false;
-        return;
-    }
-
-    // Kill it.
-    if (ptrace(PTRACE_KILL, pid, NULL, NULL))
-        m_result = false;
-    else
-        m_result = true;
-#endif
 }
 
 ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor,
@@ -586,12 +572,7 @@
 
 ProcessMonitor::~ProcessMonitor()
 {
-    StopMonitoringChildProcess();
-    StopOperationThread();
-
-    close(m_terminal_fd);
-    close(m_client_fd);
-    close(m_server_fd);
+    StopMonitor();
 }
 
 //------------------------------------------------------------------------------
@@ -768,50 +749,39 @@
     ProcessMessage message;
     ProcessMonitor *monitor = static_cast<ProcessMonitor*>(callback_baton);
     ProcessLinux *process = monitor->m_process;
+    bool stop_monitoring;
+    siginfo_t info;
 
-    switch (signal)
-    {
-    case 0:
-        // No signal.  The child has exited normally.
-        message = ProcessMessage::Exit(pid, status);
-        break;
+    if (!monitor->GetSignalInfo(pid, &info))
+        stop_monitoring = true; // pid is gone.  Bail.
+    else {
+        switch (info.si_signo)
+        {
+        case SIGTRAP:
+            message = MonitorSIGTRAP(monitor, &info, pid);
+            break;
+            
+        default:
+            message = MonitorSignal(monitor, &info, pid);
+            break;
+        }
 
-    case SIGTRAP:
-        // Specially handle SIGTRAP and form the appropriate message.
-        message = MonitorSIGTRAP(monitor, pid);
-        break;
-
-    default:
-        // For all other signals simply notify the process instance.  Note that
-        // the process exit status is set when the signal resulted in
-        // termination.
-        //
-        // FIXME: We need a specialized message to inform the process instance
-        // about "crashes".
-        if (status)
-            message = ProcessMessage::Exit(pid, status);
-        else
-            message = ProcessMessage::Signal(pid, signal);
+        process->SendMessage(message);
+        stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage;
     }
 
-    process->SendMessage(message);
-    bool stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage;
     return stop_monitoring;
 }
 
 ProcessMessage
-ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, lldb::pid_t pid)
+ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor,
+                               const struct siginfo *info, lldb::pid_t pid)
 {
-    siginfo_t info;
     ProcessMessage message;
-    bool status;
 
-    status = monitor->GetSignalInfo(pid, &info);
-    assert(status && "GetSignalInfo failed!");
+    assert(info->si_signo == SIGTRAP && "Unexpected child signal!");
 
-    assert(info.si_signo == SIGTRAP && "Unexpected child signal!");
-
-    switch (info.si_code)
+    switch (info->si_code)
     {
     default:
         assert(false && "Unexpected SIGTRAP code!");
@@ -825,7 +795,7 @@
         unsigned long data = 0;
         if (!monitor->GetEventMessage(pid, &data))
             data = -1;
-        message = ProcessMessage::Exit(pid, (data >> 8));
+        message = ProcessMessage::Limbo(pid, (data >> 8));
         break;
     }
 
@@ -843,6 +813,193 @@
     return message;
 }
 
+ProcessMessage
+ProcessMonitor::MonitorSignal(ProcessMonitor *monitor,
+                              const struct siginfo *info, lldb::pid_t pid)
+{
+    ProcessMessage message;
+    int signo = info->si_signo;
+
+    // POSIX says that process behaviour is undefined after it ignores a SIGFPE,
+    // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a
+    // kill(2) or raise(3).  Similarly for tgkill(2) on Linux.
+    //
+    // IOW, user generated signals never generate what we consider to be a
+    // "crash".
+    //
+    // Similarly, ACK signals generated by this monitor.
+    if (info->si_code == SI_TKILL || info->si_code == SI_USER)
+    {
+        if (info->si_pid == getpid())
+            return ProcessMessage::SignalDelivered(pid, signo);
+        else
+            return ProcessMessage::Signal(pid, signo);
+    }
+
+    if (signo == SIGSEGV) {
+        lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
+        ProcessMessage::CrashReason reason = GetCrashReasonForSIGSEGV(info);
+        return ProcessMessage::Crash(pid, reason, signo, fault_addr);
+    }
+
+    if (signo == SIGILL) {
+        lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
+        ProcessMessage::CrashReason reason = GetCrashReasonForSIGILL(info);
+        return ProcessMessage::Crash(pid, reason, signo, fault_addr);
+    }
+
+    if (signo == SIGFPE) {
+        lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
+        ProcessMessage::CrashReason reason = GetCrashReasonForSIGFPE(info);
+        return ProcessMessage::Crash(pid, reason, signo, fault_addr);
+    }
+
+    if (signo == SIGBUS) {
+        lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr);
+        ProcessMessage::CrashReason reason = GetCrashReasonForSIGBUS(info);
+        return ProcessMessage::Crash(pid, reason, signo, fault_addr);
+    }
+
+    // Everything else is "normal" and does not require any special action on
+    // our part.
+    return ProcessMessage::Signal(pid, signo);
+}
+
+ProcessMessage::CrashReason
+ProcessMonitor::GetCrashReasonForSIGSEGV(const struct siginfo *info)
+{
+    ProcessMessage::CrashReason reason;
+    assert(info->si_signo == SIGSEGV);
+
+    reason = ProcessMessage::eInvalidCrashReason;
+
+    switch (info->si_code) 
+    {
+    default:
+        assert(false && "unexpected si_code for SIGSEGV");
+        break;
+    case SEGV_MAPERR:
+        reason = ProcessMessage::eInvalidAddress;
+        break;
+    case SEGV_ACCERR:
+        reason = ProcessMessage::ePrivilegedAddress;
+        break;
+    }
+        
+    return reason;
+}
+
+ProcessMessage::CrashReason
+ProcessMonitor::GetCrashReasonForSIGILL(const struct siginfo *info)
+{
+    ProcessMessage::CrashReason reason;
+    assert(info->si_signo == SIGILL);
+
+    reason = ProcessMessage::eInvalidCrashReason;
+
+    switch (info->si_code)
+    {
+    default:
+        assert(false && "unexpected si_code for SIGILL");
+        break;
+    case ILL_ILLOPC:
+        reason = ProcessMessage::eIllegalOpcode;
+        break;
+    case ILL_ILLOPN:
+        reason = ProcessMessage::eIllegalOperand;
+        break;
+    case ILL_ILLADR:
+        reason = ProcessMessage::eIllegalAddressingMode;
+        break;
+    case ILL_ILLTRP:
+        reason = ProcessMessage::eIllegalTrap;
+        break;
+    case ILL_PRVOPC:
+        reason = ProcessMessage::ePrivilegedOpcode;
+        break;
+    case ILL_PRVREG:
+        reason = ProcessMessage::ePrivilegedRegister;
+        break;
+    case ILL_COPROC:
+        reason = ProcessMessage::eCoprocessorError;
+        break;
+    case ILL_BADSTK:
+        reason = ProcessMessage::eInternalStackError;
+        break;
+    }
+
+    return reason;
+}
+
+ProcessMessage::CrashReason
+ProcessMonitor::GetCrashReasonForSIGFPE(const struct siginfo *info)
+{
+    ProcessMessage::CrashReason reason;
+    assert(info->si_signo == SIGFPE);
+
+    reason = ProcessMessage::eInvalidCrashReason;
+
+    switch (info->si_code)
+    {
+    default:
+        assert(false && "unexpected si_code for SIGFPE");
+        break;
+    case FPE_INTDIV:
+        reason = ProcessMessage::eIntegerDivideByZero;
+        break;
+    case FPE_INTOVF:
+        reason = ProcessMessage::eIntegerOverflow;
+        break;
+    case FPE_FLTDIV:
+        reason = ProcessMessage::eFloatDivideByZero;
+        break;
+    case FPE_FLTOVF:
+        reason = ProcessMessage::eFloatOverflow;
+        break;
+    case FPE_FLTUND:
+        reason = ProcessMessage::eFloatUnderflow;
+        break;
+    case FPE_FLTRES:
+        reason = ProcessMessage::eFloatInexactResult;
+        break;
+    case FPE_FLTINV:
+        reason = ProcessMessage::eFloatInvalidOperation;
+        break;
+    case FPE_FLTSUB:
+        reason = ProcessMessage::eFloatSubscriptRange;
+        break;
+    }
+
+    return reason;
+}
+
+ProcessMessage::CrashReason
+ProcessMonitor::GetCrashReasonForSIGBUS(const struct siginfo *info)
+{
+    ProcessMessage::CrashReason reason;
+    assert(info->si_signo == SIGBUS);
+
+    reason = ProcessMessage::eInvalidCrashReason;
+
+    switch (info->si_code)
+    {
+    default:
+        assert(false && "unexpected si_code for SIGBUS");
+        break;
+    case BUS_ADRALN:
+        reason = ProcessMessage::eIllegalAlignment;
+        break;
+    case BUS_ADRERR:
+        reason = ProcessMessage::eIllegalAddress;
+        break;
+    case BUS_OBJERR:
+        reason = ProcessMessage::eHardwareError;
+        break;
+    }
+
+    return reason;
+}
+
 void
 ProcessMonitor::ServeOperation(LaunchArgs *args)
 {
@@ -976,19 +1133,19 @@
 }
 
 bool
-ProcessMonitor::Resume(lldb::tid_t tid)
+ProcessMonitor::Resume(lldb::tid_t tid, uint32_t signo)
 {
     bool result;
-    ResumeOperation op(tid, result);
+    ResumeOperation op(tid, signo, result);
     DoOperation(&op);
     return result;
 }
 
 bool
-ProcessMonitor::SingleStep(lldb::tid_t tid)
+ProcessMonitor::SingleStep(lldb::tid_t tid, uint32_t signo)
 {
     bool result;
-    SingleStepOperation op(tid, result);
+    SingleStepOperation op(tid, signo, result);
     DoOperation(&op);
     return result;
 }
@@ -1021,6 +1178,16 @@
 }
 
 bool
+ProcessMonitor::Detach()
+{
+    bool result;
+    KillOperation op(result);
+    DoOperation(&op);
+    StopMonitor();
+    return result;
+}    
+
+bool
 ProcessMonitor::DupDescriptor(const char *path, int fd, int flags)
 {
     int target_fd = open(path, flags);
@@ -1043,3 +1210,23 @@
         m_monitor_thread = LLDB_INVALID_HOST_THREAD;
     }
 }
+
+void
+ProcessMonitor::StopMonitor()
+{
+    StopMonitoringChildProcess();
+    StopOperationThread();
+    CloseFD(m_terminal_fd);
+    CloseFD(m_client_fd);
+    CloseFD(m_server_fd);
+}
+
+void
+ProcessMonitor::CloseFD(int &fd)
+{
+    if (fd != -1)
+    {
+        close(fd);
+        fd = -1;
+    }
+}