llgs: fix Ctrl-C inferior interrupt handling to do the right thing.

* Sends a SIGSTOP to the process.
* Fixes busted SIGSTOP handling.  Now builds a list of non-stopped
  that we wait for the PTRACE group-stop for.  When the final must-stop
  tid gets its group stop, we propagate the process state change.
  Only the signal receiving the notification of the pending SIGSTOP
  is marked with the SIGSTOP signal.  All the rest, if they weren't
  already stopped, are marked as stopped with signal 0.
* Fixes a few broken tests.
* Marks the Linux test I added earlier as expect-pass (no longer XFAIL).

Implements fix for http://llvm.org/bugs/show_bug.cgi?id=20908.

llvm-svn: 217647
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index 1bfeb54..5de9731 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -1216,6 +1216,10 @@
     m_operation_done (),
     m_wait_for_stop_tids (),
     m_wait_for_stop_tids_mutex (),
+    m_wait_for_group_stop_tids (),
+    m_group_stop_signal_tid (LLDB_INVALID_THREAD_ID),
+    m_group_stop_signal (LLDB_INVALID_SIGNAL_NUMBER),
+    m_wait_for_group_stop_tids_mutex (),
     m_supports_mem_region (eLazyBoolCalculate),
     m_mem_region_cache (),
     m_mem_region_cache_mutex ()
@@ -1495,7 +1499,7 @@
             if (log)
             {
                 const int error_code = errno;
-                log->Printf ("NativeProcessLinux::%s inferior setpgid() failed, errno=%d (%s), continuing with existing proccess group %" PRIu64,
+                log->Printf ("NativeProcessLinux::%s inferior setpgid() failed, errno=%d (%s), continuing with existing process group %" PRIu64,
                         __FUNCTION__,
                         error_code,
                         strerror (error_code),
@@ -1949,32 +1953,7 @@
     {
         if (ptrace_err == EINVAL)
         {
-            // This is the first part of the Linux ptrace group-stop mechanism.
-            // (The other thing it can conceivably be is a call on a pid that no
-            // longer exists for some reason).
-            // The tracer (i.e. NativeProcessLinux) is expected to inject the signal
-            // into the tracee (i.e. inferior) at this point.
-            if (log)
-                log->Printf ("NativeProcessLinux::%s resuming from group-stop", __FUNCTION__);
-
-            // The inferior process is in 'group-stop', so deliver the stopping signal.
-            const bool signal_delivered = process->Resume (pid, info.si_signo);
-            if (log)
-                log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " group-stop signal delivery of signal 0x%x (%s) - %s", __FUNCTION__, pid, info.si_signo, GetUnixSignals ().GetSignalAsCString (info.si_signo), signal_delivered ? "success" : "failed");
-
-            if (signal_delivered)
-            {
-                // All is well.
-                stop_monitoring = false;
-            }
-            else
-            {
-                if (log)
-                    log->Printf ("NativeProcessLinux::%s pid %" PRIu64 " something looks horribly wrong - like the process we're monitoring died.  Stop monitoring it.", __FUNCTION__, pid);
-
-                // Stop monitoring now.
-                return true;
-            }
+            process->OnGroupStop (pid);
         }
         else
         {
@@ -2061,12 +2040,12 @@
         // If we don't track the thread yet: create it, mark as stopped.
         // If we do track it, this is the wait we needed.  Now resume the new thread.
         // In all cases, resume the current (i.e. main process) thread.
-        bool already_tracked = false;
-        thread_sp = GetOrCreateThread (tid, already_tracked);
+        bool created_now = false;
+        thread_sp = GetOrCreateThread (tid, created_now);
         assert (thread_sp.get() && "failed to get or create the tracking data for newly created inferior thread");
 
         // If the thread was already tracked, it means the created thread already received its SI_USER notification of creation.
-        if (already_tracked)
+        if (!created_now)
         {
             // FIXME loops like we want to stop all theads here.
             // StopAllThreads
@@ -2275,7 +2254,12 @@
 void
 NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool exited)
 {
-    int signo = info->si_signo;
+    assert (info && "null info");
+    if (!info)
+        return;
+
+    const int signo = info->si_signo;
+    const bool is_from_llgs = info->si_pid == getpid ();
 
     Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
 
@@ -2306,7 +2290,7 @@
                             signo,
                             (info->si_code == SI_TKILL ? "SI_TKILL" : "SI_USER"),
                             info->si_pid,
-                            (info->si_pid == getpid ()) ? "is monitor" : "is not monitor",
+                            is_from_llgs ? "from llgs" : "not from llgs",
                             pid);
     }
 
@@ -2320,12 +2304,12 @@
                      __FUNCTION__, GetID (), pid);
 
         // Did we already create the thread?
-        bool already_tracked = false;
-        thread_sp = GetOrCreateThread (pid, already_tracked);
+        bool created_now = false;
+        thread_sp = GetOrCreateThread (pid, created_now);
         assert (thread_sp.get() && "failed to get or create the tracking data for newly created inferior thread");
 
         // If the thread was already tracked, it means the main thread already received its SIGTRAP for the create.
-        if (already_tracked)
+        if (!created_now)
         {
             // We can now resume this thread up since it is fully created.
             reinterpret_cast<NativeThreadLinux*> (thread_sp.get ())->SetRunning ();
@@ -2343,7 +2327,7 @@
     }
 
     // Check for thread stop notification.
-    if ((info->si_pid == getpid ()) && (info->si_code == SI_TKILL) && (signo == SIGSTOP))
+    if (is_from_llgs && (info->si_code == SI_TKILL) && (signo == SIGSTOP))
     {
         // This is a tgkill()-based stop.
         if (thread_sp)
@@ -2451,13 +2435,177 @@
         }
         break;
 
+    case SIGSTOP:
+        {
+            if (log)
+            {
+                if (is_from_llgs)
+                    log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " received SIGSTOP from llgs, most likely an interrupt", __FUNCTION__, GetID (), pid);
+                else
+                    log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " received SIGSTOP from outside of debugger", __FUNCTION__, GetID (), pid);
+            }
+
+            // Save group stop tids to wait for.
+            SetGroupStopTids (pid, SIGSTOP);
+            // Fall through to deliver signal to thread.
+            // This will trigger a group stop sequence, after which we'll notify the process that everything stopped.
+        }
+
     default:
-        if (log)
-            log->Printf ("NativeProcessLinux::%s unhandled signal %s (%d)", __FUNCTION__, GetUnixSignals ().GetSignalAsCString (signo), signo);
+        {
+            if (log)
+                log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " resuming thread with signal %s (%d)", __FUNCTION__, GetID (), pid, GetUnixSignals().GetSignalAsCString (signo), signo);
+
+            // Pass the signal on to the inferior.
+            const bool resume_success = Resume (pid, signo);
+
+            if (log)
+                log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " resume %s", __FUNCTION__, GetID (), pid, resume_success ? "SUCCESS" : "FAILURE");
+
+        }
         break;
     }
 }
 
+void
+NativeProcessLinux::SetGroupStopTids (lldb::tid_t signaled_thread_tid, int signo)
+{
+    Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+
+    // Lock 1 - thread lock.
+    {
+        Mutex::Locker locker (m_threads_mutex);
+        // Lock 2 - group stop tids
+        {
+            Mutex::Locker locker (m_wait_for_group_stop_tids_mutex);
+            if (log)
+                log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " loading up known threads in set%s",
+                             __FUNCTION__,
+                             GetID (),
+                             signaled_thread_tid,
+                             m_wait_for_group_stop_tids.empty () ? " (currently empty)"
+                                : "(group_stop_tids not empty?!?)");
+
+            // Add all known threads not already stopped into the wait for group-stop tids.
+            for (auto thread_sp : m_threads)
+            {
+                int unused_signo = LLDB_INVALID_SIGNAL_NUMBER;
+                if (thread_sp && !((NativeThreadLinux*)thread_sp.get())->IsStopped (&unused_signo))
+                {
+                    // Wait on this thread for a group stop before we notify the delegate about the process state change.
+                    m_wait_for_group_stop_tids.insert (thread_sp->GetID ());
+                }
+            }
+
+            m_group_stop_signal_tid = signaled_thread_tid;
+            m_group_stop_signal = signo;
+        }
+    }
+}
+
+void
+NativeProcessLinux::OnGroupStop (lldb::tid_t tid)
+{
+    Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+    bool should_tell_delegate = false;
+
+    // Lock 1 - thread lock.
+    {
+        Mutex::Locker locker (m_threads_mutex);
+        // Lock 2 - group stop tids
+        {
+            Mutex::Locker locker (m_wait_for_group_stop_tids_mutex);
+
+            // Remove this thread from the set.
+            auto remove_result = m_wait_for_group_stop_tids.erase (tid);
+            if (log)
+                log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " tried to remove tid from group-stop set: %s",
+                             __FUNCTION__,
+                             GetID (),
+                             tid,
+                             remove_result > 0 ? "SUCCESS" : "FAILURE");
+
+            // Grab the thread metadata for this thread.
+            NativeThreadProtocolSP thread_sp = GetThreadByIDUnlocked (tid);
+            if (thread_sp)
+            {
+                NativeThreadLinux *const linux_thread = static_cast<NativeThreadLinux*> (thread_sp.get ());
+                if (thread_sp->GetID () == m_group_stop_signal_tid)
+                {
+                    linux_thread->SetStoppedBySignal (m_group_stop_signal);
+                    if (log)
+                        log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " set group stop tid to state 'stopped by signal %d'",
+                                     __FUNCTION__,
+                                     GetID (),
+                                     tid,
+                                     m_group_stop_signal);
+                }
+                else
+                {
+                    int stopping_signal = LLDB_INVALID_SIGNAL_NUMBER;
+                    if (linux_thread->IsStopped (&stopping_signal))
+                    {
+                        if (log)
+                            log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " thread is already stopped with signal %d, not clearing",
+                                         __FUNCTION__,
+                                         GetID (),
+                                         tid,
+                                         stopping_signal);
+
+                    }
+                    else
+                    {
+                        linux_thread->SetStoppedBySignal (0);
+                        if (log)
+                            log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " set stopped by signal with signal 0 (i.e. debugger-initiated stop)",
+                                         __FUNCTION__,
+                                         GetID (),
+                                         tid);
+
+                    }
+                }
+            }
+            else
+            {
+                if (log)
+                    log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " WARNING failed to find thread metadata for tid",
+                                 __FUNCTION__,
+                                 GetID (),
+                                 tid);
+
+            }
+
+            // If there are no more threads we're waiting on for group stop, signal the process.
+            if (m_wait_for_group_stop_tids.empty ())
+            {
+                if (log)
+                    log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " done waiting for group stop, will notify delegate of process state change",
+                                 __FUNCTION__,
+                                 GetID (),
+                                 tid);
+
+                SetCurrentThreadID (m_group_stop_signal_tid);
+
+                // Tell the delegate about the stop event, after we release our mutexes.
+                should_tell_delegate = true;
+            }
+        }
+    }
+
+    // If we're ready to broadcast the process event change, do it now that we're no longer
+    // holding any locks.  Note this does introduce a potential race, we should think about
+    // adding a notification queue.
+    if (should_tell_delegate)
+    {
+        if (log)
+            log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " done waiting for group stop, notifying delegate of process state change",
+                         __FUNCTION__,
+                         GetID (),
+                         tid);
+        SetState (StateType::eStateStopped, true);
+    }
+}
+
 Error
 NativeProcessLinux::Resume (const ResumeActionList &resume_actions)
 {
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
index c94a78b..a13570f 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -189,6 +189,11 @@
         std::unordered_set<lldb::tid_t> m_wait_for_stop_tids;
         lldb_private::Mutex m_wait_for_stop_tids_mutex;
 
+        std::unordered_set<lldb::tid_t> m_wait_for_group_stop_tids;
+        lldb::tid_t m_group_stop_signal_tid;
+        int m_group_stop_signal;
+        lldb_private::Mutex m_wait_for_group_stop_tids_mutex;
+
         lldb_private::LazyBool m_supports_mem_region;
         std::vector<MemoryRegionInfo> m_mem_region_cache;
         lldb_private::Mutex m_mem_region_cache_mutex;
@@ -375,6 +380,17 @@
         bool
         SingleStep(lldb::tid_t tid, uint32_t signo);
 
+        /// Safely mark all existing threads as waiting for group stop.
+        /// When the final group stop comes in from the set of group stop threads,
+        /// we'll mark the current thread as signaled_thread_tid and set its stop
+        /// reason as the given signo.  All other threads from group stop notification
+        /// will have thread stop reason marked as signaled with no signo.
+        void
+        SetGroupStopTids (lldb::tid_t signaled_thread_tid, int signo);
+
+        void
+        OnGroupStop (lldb::tid_t tid);
+
         lldb_private::Error
         Detach(lldb::tid_t tid);
     };
diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
index 8e010a5..4158e3d 100644
--- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
@@ -259,6 +259,25 @@
     m_stop_info.details.signal.signo = signo;
 }
 
+bool
+NativeThreadLinux::IsStopped (int *signo)
+{
+    if (!StateIsStoppedState (m_state, false))
+        return false;
+
+    // If we are stopped by a signal, return the signo.
+    if (signo &&
+        m_state == StateType::eStateStopped &&
+        m_stop_info.reason == StopReason::eStopReasonSignal)
+    {
+        *signo = m_stop_info.details.signal.signo;
+    }
+
+    // Regardless, we are stopped.
+    return true;
+}
+
+
 void
 NativeThreadLinux::SetStoppedByExec ()
 {
diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
index bed530d..8a042f2 100644
--- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
@@ -64,6 +64,12 @@
         void
         SetStoppedBySignal (uint32_t signo);
 
+        /// Return true if the thread is stopped.
+        /// If stopped by a signal, indicate the signo in the signo argument.
+        /// Otherwise, return LLDB_INVALID_SIGNAL_NUMBER.
+        bool
+        IsStopped (int *signo);
+
         void
         SetStoppedByExec ();