diff options
Diffstat (limited to 'lldb/source/Plugins')
5 files changed, 229 insertions, 42 deletions
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index 1bfeb54c3da..5de973196de 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) { diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h index c94a78bd2ea..a13570f63ef 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -189,6 +189,11 @@ namespace lldb_private 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 @@ namespace lldb_private 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 8e010a55d85..4158e3d1e14 100644 --- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -259,6 +259,25 @@ NativeThreadLinux::SetStoppedBySignal (uint32_t signo) 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 bed530dbe9f..8a042f22b22 100644 --- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h +++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.h @@ -64,6 +64,12 @@ namespace lldb_private 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 (); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index bb3b32d3c10..e3aadf1bc25 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -3396,10 +3396,8 @@ GDBRemoteCommunicationServer::Handle_interrupt (StringExtractorGDBRemote &packet return SendErrorResponse (0x15); } - // Build the ResumeActionList - stop everything. - lldb_private::ResumeActionList actions (StateType::eStateStopped, 0); - - Error error = m_debugged_process_sp->Resume (actions); + // Interrupt the process. + Error error = m_debugged_process_sp->Interrupt (); if (error.Fail ()) { if (log) |