diff options
| author | Todd Fiala <todd.fiala@gmail.com> | 2014-09-11 23:29:14 +0000 |
|---|---|---|
| committer | Todd Fiala <todd.fiala@gmail.com> | 2014-09-11 23:29:14 +0000 |
| commit | 511e5cdce4020e221d3a8609492004e908ccfc1a (patch) | |
| tree | 3eb6c795c832cc84797dac0c63a87955458e97d4 /lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp | |
| parent | 3d7260e7b21b6630ac42e1838c582a84c57e8a95 (diff) | |
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
Diffstat (limited to 'lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp')
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp | 224 |
1 files changed, 186 insertions, 38 deletions
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index 1bfeb54c3daf..5de973196de8 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -1216,6 +1216,10 @@ NativeProcessLinux::NativeProcessLinux () : 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 @@ NativeProcessLinux::Launch(LaunchArgs *args) 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 @@ NativeProcessLinux::MonitorCallback(void *callback_baton, { 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 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t *info, lldb::pid_t pid) // 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 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t *info, lldb::pid_t pid) 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 @@ NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool e 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 @@ NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool e __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 @@ NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool e } // 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 @@ NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool e } 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) { |
