summaryrefslogtreecommitdiffstats
path: root/lldb/source/Plugins/Process/Linux
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Plugins/Process/Linux')
-rw-r--r--lldb/source/Plugins/Process/Linux/CMakeLists.txt1
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp697
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeProcessLinux.h226
-rw-r--r--lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp590
-rw-r--r--lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h253
5 files changed, 849 insertions, 918 deletions
diff --git a/lldb/source/Plugins/Process/Linux/CMakeLists.txt b/lldb/source/Plugins/Process/Linux/CMakeLists.txt
index c0899cee18e..f3f5576c58b 100644
--- a/lldb/source/Plugins/Process/Linux/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/Linux/CMakeLists.txt
@@ -15,6 +15,5 @@ add_lldb_library(lldbPluginProcessLinux
ProcessLinux.cpp
ProcessMonitor.cpp
ProcFileReader.cpp
- ThreadStateCoordinator.cpp
)
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index d46505edc2a..b57e30d3c2c 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -20,6 +20,7 @@
// C++ Includes
#include <fstream>
+#include <sstream>
#include <string>
// Other libraries and framework includes
@@ -49,7 +50,6 @@
#include "NativeThreadLinux.h"
#include "ProcFileReader.h"
#include "Procfs.h"
-#include "ThreadStateCoordinator.h"
// System includes - They have to be included after framework includes because they define some
// macros which collide with variable names in other modules
@@ -153,7 +153,7 @@ namespace
return signals;
}
- ThreadStateCoordinator::LogFunction
+ NativeProcessLinux::LogFunction
GetThreadLoggerFunction ()
{
return [](const char *format, va_list args)
@@ -170,7 +170,7 @@ namespace
Log *const log = GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD);
if (log)
log->Printf ("NativeProcessLinux::%s %s", __FUNCTION__, error_message.c_str ());
- assert (false && "ThreadStateCoordinator error reported");
+ assert (false && "NativeProcessLinux error reported");
}
Error
@@ -1673,7 +1673,9 @@ NativeProcessLinux::NativeProcessLinux () :
m_supports_mem_region (eLazyBoolCalculate),
m_mem_region_cache (),
m_mem_region_cache_mutex (),
- m_coordinator_up (new ThreadStateCoordinator (GetThreadLoggerFunction ()))
+ m_log_function (GetThreadLoggerFunction()),
+ m_tid_map (),
+ m_log_event_processing (false)
{
}
@@ -2351,7 +2353,7 @@ NativeProcessLinux::WaitForNewThread(::pid_t tid)
new_thread_sp = AddThread(tid);
std::static_pointer_cast<NativeThreadLinux> (new_thread_sp)->SetRunning ();
Resume (tid, LLDB_INVALID_SIGNAL_NUMBER);
- m_coordinator_up->NotifyThreadCreate (tid, false, CoordinatorErrorHandler);
+ NotifyThreadCreate (tid, false, CoordinatorErrorHandler);
}
void
@@ -2407,7 +2409,7 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t *info, lldb::pid_t pid)
log->Printf ("NativeProcessLinux::%s() received exec event, code = %d", __FUNCTION__, info->si_code ^ SIGTRAP);
// The thread state coordinator needs to reset due to the exec.
- m_coordinator_up->ResetForExec ();
+ ResetForExec ();
// Remove all but the main thread here. Linux fork creates a new process which only copies the main thread. Mutexes are in undefined state.
if (log)
@@ -2493,13 +2495,13 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t *info, lldb::pid_t pid)
}
const int signo = static_cast<int> (data);
- m_coordinator_up->RequestThreadResume (pid,
- [=](lldb::tid_t tid_to_resume, bool supress_signal)
- {
- std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetRunning ();
- return Resume (tid_to_resume, (supress_signal) ? LLDB_INVALID_SIGNAL_NUMBER : signo);
- },
- CoordinatorErrorHandler);
+ RequestThreadResume (pid,
+ [=](lldb::tid_t tid_to_resume, bool supress_signal)
+ {
+ std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetRunning ();
+ return Resume (tid_to_resume, (supress_signal) ? LLDB_INVALID_SIGNAL_NUMBER : signo);
+ },
+ CoordinatorErrorHandler);
break;
}
@@ -2544,13 +2546,13 @@ NativeProcessLinux::MonitorSIGTRAP(const siginfo_t *info, lldb::pid_t pid)
// Ignore these signals until we know more about them.
- m_coordinator_up->RequestThreadResume (pid,
- [=](lldb::tid_t tid_to_resume, bool supress_signal)
- {
- std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetRunning ();
- return Resume (tid_to_resume, LLDB_INVALID_SIGNAL_NUMBER);
- },
- CoordinatorErrorHandler);
+ RequestThreadResume (pid,
+ [=](lldb::tid_t tid_to_resume, bool supress_signal)
+ {
+ std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetRunning ();
+ return Resume (tid_to_resume, LLDB_INVALID_SIGNAL_NUMBER);
+ },
+ CoordinatorErrorHandler);
break;
default:
@@ -2728,7 +2730,7 @@ NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool e
// We can now resume the newly created thread.
std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetRunning ();
Resume (pid, LLDB_INVALID_SIGNAL_NUMBER);
- m_coordinator_up->NotifyThreadCreate (pid, false, CoordinatorErrorHandler);
+ NotifyThreadCreate (pid, false, CoordinatorErrorHandler);
// Done handling.
return;
}
@@ -2762,7 +2764,7 @@ NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool e
// stop signal as 0 to let lldb know this isn't the important stop.
linux_thread_sp->SetStoppedBySignal (0);
SetCurrentThreadID (thread_sp->GetID ());
- m_coordinator_up->NotifyThreadStop (thread_sp->GetID (), true, CoordinatorErrorHandler);
+ NotifyThreadStop (thread_sp->GetID (), true, CoordinatorErrorHandler);
}
else
{
@@ -2812,14 +2814,14 @@ NativeProcessLinux::MonitorSignal(const siginfo_t *info, lldb::pid_t pid, bool e
// Resume this thread to get the group-stop mechanism to fire off the true group stops.
// This thread will get stopped again as part of the group-stop completion.
- m_coordinator_up->RequestThreadResume (pid,
- [=](lldb::tid_t tid_to_resume, bool supress_signal)
- {
- std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetRunning ();
- // Pass this signal number on to the inferior to handle.
- return Resume (tid_to_resume, (supress_signal) ? LLDB_INVALID_SIGNAL_NUMBER : signo);
- },
- CoordinatorErrorHandler);
+ RequestThreadResume (pid,
+ [=](lldb::tid_t tid_to_resume, bool supress_signal)
+ {
+ std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetRunning ();
+ // Pass this signal number on to the inferior to handle.
+ return Resume (tid_to_resume, (supress_signal) ? LLDB_INVALID_SIGNAL_NUMBER : signo);
+ },
+ CoordinatorErrorHandler);
}
break;
case SIGSEGV:
@@ -3092,17 +3094,17 @@ NativeProcessLinux::Resume (const ResumeActionList &resume_actions)
{
// Run the thread, possibly feeding it the signal.
const int signo = action->signal;
- m_coordinator_up->RequestThreadResumeAsNeeded (thread_sp->GetID (),
- [=](lldb::tid_t tid_to_resume, bool supress_signal)
- {
- std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetRunning ();
- // Pass this signal number on to the inferior to handle.
- const auto resume_result = Resume (tid_to_resume, (signo > 0 && !supress_signal) ? signo : LLDB_INVALID_SIGNAL_NUMBER);
- if (resume_result.Success())
- SetState(eStateRunning, true);
- return resume_result;
- },
- CoordinatorErrorHandler);
+ RequestThreadResumeAsNeeded (thread_sp->GetID (),
+ [=](lldb::tid_t tid_to_resume, bool supress_signal)
+ {
+ std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetRunning ();
+ // Pass this signal number on to the inferior to handle.
+ const auto resume_result = Resume (tid_to_resume, (signo > 0 && !supress_signal) ? signo : LLDB_INVALID_SIGNAL_NUMBER);
+ if (resume_result.Success())
+ SetState(eStateRunning, true);
+ return resume_result;
+ },
+ CoordinatorErrorHandler);
break;
}
@@ -3110,23 +3112,23 @@ NativeProcessLinux::Resume (const ResumeActionList &resume_actions)
{
// Request the step.
const int signo = action->signal;
- m_coordinator_up->RequestThreadResume (thread_sp->GetID (),
- [=](lldb::tid_t tid_to_step, bool supress_signal)
- {
- std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetStepping ();
-
- Error step_result;
- if (software_single_step)
- step_result = Resume (tid_to_step, (signo > 0 && !supress_signal) ? signo : LLDB_INVALID_SIGNAL_NUMBER);
- else
- step_result = SingleStep (tid_to_step,(signo > 0 && !supress_signal) ? signo : LLDB_INVALID_SIGNAL_NUMBER);
-
- assert (step_result.Success() && "SingleStep() failed");
- if (step_result.Success())
- SetState(eStateStepping, true);
- return step_result;
- },
- CoordinatorErrorHandler);
+ RequestThreadResume (thread_sp->GetID (),
+ [=](lldb::tid_t tid_to_step, bool supress_signal)
+ {
+ std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetStepping ();
+
+ Error step_result;
+ if (software_single_step)
+ step_result = Resume (tid_to_step, (signo > 0 && !supress_signal) ? signo : LLDB_INVALID_SIGNAL_NUMBER);
+ else
+ step_result = SingleStep (tid_to_step,(signo > 0 && !supress_signal) ? signo : LLDB_INVALID_SIGNAL_NUMBER);
+
+ assert (step_result.Success() && "SingleStep() failed");
+ if (step_result.Success())
+ SetState(eStateStepping, true);
+ return step_result;
+ },
+ CoordinatorErrorHandler);
stepping = true;
break;
}
@@ -4242,19 +4244,19 @@ void
NativeProcessLinux::NotifyThreadCreateStopped (lldb::tid_t tid)
{
const bool is_stopped = true;
- m_coordinator_up->NotifyThreadCreate (tid, is_stopped, CoordinatorErrorHandler);
+ NotifyThreadCreate (tid, is_stopped, CoordinatorErrorHandler);
}
void
NativeProcessLinux::NotifyThreadDeath (lldb::tid_t tid)
{
- m_coordinator_up->NotifyThreadDeath (tid, CoordinatorErrorHandler);
+ NotifyThreadDeath (tid, CoordinatorErrorHandler);
}
void
NativeProcessLinux::NotifyThreadStop (lldb::tid_t tid)
{
- m_coordinator_up->NotifyThreadStop (tid, false, CoordinatorErrorHandler);
+ NotifyThreadStop (tid, false, CoordinatorErrorHandler);
}
void
@@ -4266,7 +4268,7 @@ NativeProcessLinux::CallAfterRunningThreadsStop (lldb::tid_t tid,
log->Printf("NativeProcessLinux::%s tid %" PRIu64, __FUNCTION__, tid);
const lldb::pid_t pid = GetID ();
- m_coordinator_up->CallAfterRunningThreadsStop (tid,
+ CallAfterRunningThreadsStop (tid,
[=](lldb::tid_t request_stop_tid)
{
return RequestThreadStop(pid, request_stop_tid);
@@ -4285,14 +4287,11 @@ NativeProcessLinux::CallAfterRunningThreadsStopWithSkipTID (lldb::tid_t deferred
log->Printf("NativeProcessLinux::%s deferred_signal_tid %" PRIu64 ", skip_stop_request_tid %" PRIu64, __FUNCTION__, deferred_signal_tid, skip_stop_request_tid);
const lldb::pid_t pid = GetID ();
- m_coordinator_up->CallAfterRunningThreadsStopWithSkipTIDs (deferred_signal_tid,
- skip_stop_request_tid != LLDB_INVALID_THREAD_ID ? ThreadStateCoordinator::ThreadIDSet {skip_stop_request_tid} : ThreadStateCoordinator::ThreadIDSet (),
- [=](lldb::tid_t request_stop_tid)
- {
- return RequestThreadStop(pid, request_stop_tid);
- },
- call_after_function,
- CoordinatorErrorHandler);
+ CallAfterRunningThreadsStopWithSkipTIDs (deferred_signal_tid,
+ skip_stop_request_tid != LLDB_INVALID_THREAD_ID ? NativeProcessLinux::ThreadIDSet {skip_stop_request_tid} : NativeProcessLinux::ThreadIDSet (),
+ [=](lldb::tid_t request_stop_tid) { return RequestThreadStop(pid, request_stop_tid); },
+ call_after_function,
+ CoordinatorErrorHandler);
}
Error
@@ -4352,3 +4351,561 @@ NativeProcessLinux::GetLoadedModuleFileSpec(const char* module_path, FileSpec& f
return Error("Module file (%s) not found in /proc/%" PRIu64 "/maps file!",
module_file_spec.GetFilename().AsCString(), GetID());
}
+
+void
+NativeProcessLinux::DoResume(
+ lldb::tid_t tid,
+ ResumeThreadFunction request_thread_resume_function,
+ ErrorFunction error_function,
+ bool error_when_already_running)
+{
+ // Ensure we know about the thread.
+ auto find_it = m_tid_map.find (tid);
+ if (find_it == m_tid_map.end ())
+ {
+ // We don't know about this thread. This is an error condition.
+ std::ostringstream error_message;
+ error_message << "error: tid " << tid << " asked to resume but tid is unknown";
+ error_function (error_message.str ());
+ return;
+ }
+ auto& context = find_it->second;
+ // Tell the thread to resume if we don't already think it is running.
+ const bool is_stopped = context.m_state == ThreadState::Stopped;
+ if (!is_stopped)
+ {
+ // It's not an error, just a log, if the error_when_already_running flag is not set.
+ // This covers cases where, for instance, we're just trying to resume all threads
+ // from the user side.
+ if (!error_when_already_running)
+ {
+ TSCLog ("NativeProcessLinux::%s tid %" PRIu64 " optional resume skipped since it is already running",
+ __FUNCTION__,
+ tid);
+ }
+ else
+ {
+ // Skip the resume call - we have tracked it to be running. And we unconditionally
+ // expected to resume this thread. Flag this as an error.
+ std::ostringstream error_message;
+ error_message << "error: tid " << tid << " asked to resume but we think it is already running";
+ error_function (error_message.str ());
+ }
+
+ // Error or not, we're done.
+ return;
+ }
+
+ // Before we do the resume below, first check if we have a pending
+ // stop notification this is currently or was previously waiting for
+ // this thread to stop. This is potentially a buggy situation since
+ // we're ostensibly waiting for threads to stop before we send out the
+ // pending notification, and here we are resuming one before we send
+ // out the pending stop notification.
+ if (m_pending_notification_up)
+ {
+ if (m_pending_notification_up->wait_for_stop_tids.count (tid) > 0)
+ {
+ TSCLog ("NativeProcessLinux::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that is actively waiting for this thread to stop. Valid sequence of events?", __FUNCTION__, tid, m_pending_notification_up->triggering_tid);
+ }
+ else if (m_pending_notification_up->original_wait_for_stop_tids.count (tid) > 0)
+ {
+ TSCLog ("NativeProcessLinux::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that hasn't fired yet and this is one of the threads we had been waiting on (and already marked satisfied for this tid). Valid sequence of events?", __FUNCTION__, tid, m_pending_notification_up->triggering_tid);
+ for (auto tid : m_pending_notification_up->wait_for_stop_tids)
+ {
+ TSCLog ("NativeProcessLinux::%s tid %" PRIu64 " deferred stop notification still waiting on tid %" PRIu64,
+ __FUNCTION__,
+ m_pending_notification_up->triggering_tid,
+ tid);
+ }
+ }
+ }
+
+ // Request a resume. We expect this to be synchronous and the system
+ // to reflect it is running after this completes.
+ const auto error = request_thread_resume_function (tid, false);
+ if (error.Success ())
+ {
+ // Now mark it is running.
+ context.m_state = ThreadState::Running;
+ context.m_request_resume_function = request_thread_resume_function;
+ }
+ else
+ {
+ TSCLog ("NativeProcessLinux::%s failed to resume thread tid %" PRIu64 ": %s",
+ __FUNCTION__, tid, error.AsCString ());
+ }
+
+ return;
+}
+
+//===----------------------------------------------------------------------===//
+
+void
+NativeProcessLinux::CallAfterThreadsStop (const lldb::tid_t triggering_tid,
+ const ThreadIDSet &wait_for_stop_tids,
+ const StopThreadFunction &request_thread_stop_function,
+ const ThreadIDFunction &call_after_function,
+ const ErrorFunction &error_function)
+{
+ std::lock_guard<std::mutex> lock(m_event_mutex);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s about to process event: (triggering_tid: %" PRIu64 ", wait_for_stop_tids.size(): %zd)",
+ __FUNCTION__, triggering_tid, wait_for_stop_tids.size());
+ }
+
+ DoCallAfterThreadsStop(PendingNotificationUP(new PendingNotification(
+ triggering_tid, wait_for_stop_tids, request_thread_stop_function,
+ call_after_function, error_function)));
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s event processing done", __FUNCTION__);
+ }
+}
+
+void
+NativeProcessLinux::CallAfterRunningThreadsStop (const lldb::tid_t triggering_tid,
+ const StopThreadFunction &request_thread_stop_function,
+ const ThreadIDFunction &call_after_function,
+ const ErrorFunction &error_function)
+{
+ std::lock_guard<std::mutex> lock(m_event_mutex);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s about to process event: (triggering_tid: %" PRIu64 ")",
+ __FUNCTION__, triggering_tid);
+ }
+
+ DoCallAfterThreadsStop(PendingNotificationUP(new PendingNotification(
+ triggering_tid,
+ request_thread_stop_function,
+ call_after_function,
+ error_function)));
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s event processing done", __FUNCTION__);
+ }
+}
+
+void
+NativeProcessLinux::CallAfterRunningThreadsStopWithSkipTIDs (lldb::tid_t triggering_tid,
+ const ThreadIDSet &skip_stop_request_tids,
+ const StopThreadFunction &request_thread_stop_function,
+ const ThreadIDFunction &call_after_function,
+ const ErrorFunction &error_function)
+{
+ std::lock_guard<std::mutex> lock(m_event_mutex);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s about to process event: (triggering_tid: %" PRIu64 ", skip_stop_request_tids.size(): %zd)",
+ __FUNCTION__, triggering_tid, skip_stop_request_tids.size());
+ }
+
+ DoCallAfterThreadsStop(PendingNotificationUP(new PendingNotification(
+ triggering_tid,
+ request_thread_stop_function,
+ call_after_function,
+ skip_stop_request_tids,
+ error_function)));
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s event processing done", __FUNCTION__);
+ }
+}
+
+void
+NativeProcessLinux::SignalIfRequirementsSatisfied()
+{
+ if (m_pending_notification_up && m_pending_notification_up->wait_for_stop_tids.empty ())
+ {
+ m_pending_notification_up->call_after_function(m_pending_notification_up->triggering_tid);
+ m_pending_notification_up.reset();
+ }
+}
+
+bool
+NativeProcessLinux::RequestStopOnAllSpecifiedThreads()
+{
+ // Request a stop for all the thread stops that need to be stopped
+ // and are not already known to be stopped. Keep a list of all the
+ // threads from which we still need to hear a stop reply.
+
+ ThreadIDSet sent_tids;
+ for (auto tid : m_pending_notification_up->wait_for_stop_tids)
+ {
+ // Validate we know about all tids for which we must first receive a stop before
+ // triggering the deferred stop notification.
+ auto find_it = m_tid_map.find (tid);
+ if (find_it == m_tid_map.end ())
+ {
+ // This is an error. We shouldn't be asking for waiting pids that aren't known.
+ // NOTE: we may be stripping out the specification of wait tids and handle this
+ // automatically, in which case this state can never occur.
+ std::ostringstream error_message;
+ error_message << "error: deferred notification for tid " << m_pending_notification_up->triggering_tid << " specified an unknown/untracked pending stop tid " << m_pending_notification_up->triggering_tid;
+ m_pending_notification_up->error_function (error_message.str ());
+
+ // Bail out here.
+ return false;
+ }
+
+ // If the pending stop thread is currently running, we need to send it a stop request.
+ auto& context = find_it->second;
+ if (context.m_state == ThreadState::Running)
+ {
+ RequestThreadStop (tid, context);
+ sent_tids.insert (tid);
+ }
+ }
+ // We only need to wait for the sent_tids - so swap our wait set
+ // to the sent tids. The rest are already stopped and we won't
+ // be receiving stop notifications for them.
+ m_pending_notification_up->wait_for_stop_tids.swap (sent_tids);
+
+ // Succeeded, keep running.
+ return true;
+}
+
+void
+NativeProcessLinux::RequestStopOnAllRunningThreads()
+{
+ // Request a stop for all the thread stops that need to be stopped
+ // and are not already known to be stopped. Keep a list of all the
+ // threads from which we still need to hear a stop reply.
+
+ ThreadIDSet sent_tids;
+ for (auto it = m_tid_map.begin(); it != m_tid_map.end(); ++it)
+ {
+ // We only care about threads not stopped.
+ const bool running = it->second.m_state == ThreadState::Running;
+ if (running)
+ {
+ const lldb::tid_t tid = it->first;
+
+ // Request this thread stop if the tid stop request is not explicitly ignored.
+ const bool skip_stop_request = m_pending_notification_up->skip_stop_request_tids.count (tid) > 0;
+ if (!skip_stop_request)
+ RequestThreadStop (tid, it->second);
+
+ // Even if we skipped sending the stop request for other reasons (like stepping),
+ // we still need to wait for that stepping thread to notify completion/stop.
+ sent_tids.insert (tid);
+ }
+ }
+
+ // Set the wait list to the set of tids for which we requested stops.
+ m_pending_notification_up->wait_for_stop_tids.swap (sent_tids);
+}
+
+void
+NativeProcessLinux::RequestThreadStop (lldb::tid_t tid, ThreadContext& context)
+{
+ const auto error = m_pending_notification_up->request_thread_stop_function (tid);
+ if (error.Success ())
+ context.m_stop_requested = true;
+ else
+ {
+ TSCLog ("NativeProcessLinux::%s failed to request thread stop tid %" PRIu64 ": %s",
+ __FUNCTION__, tid, error.AsCString ());
+ }
+}
+
+
+void
+NativeProcessLinux::ThreadDidStop (lldb::tid_t tid, bool initiated_by_llgs, const ErrorFunction &error_function)
+{
+ // Ensure we know about the thread.
+ auto find_it = m_tid_map.find (tid);
+ if (find_it == m_tid_map.end ())
+ {
+ // We don't know about this thread. This is an error condition.
+ std::ostringstream error_message;
+ error_message << "error: tid " << tid << " asked to stop but tid is unknown";
+ error_function (error_message.str ());
+ return;
+ }
+
+ // Update the global list of known thread states. This one is definitely stopped.
+ auto& context = find_it->second;
+ const auto stop_was_requested = context.m_stop_requested;
+ context.m_state = ThreadState::Stopped;
+ context.m_stop_requested = false;
+
+ // If we have a pending notification, remove this from the set.
+ if (m_pending_notification_up)
+ {
+ m_pending_notification_up->wait_for_stop_tids.erase(tid);
+ SignalIfRequirementsSatisfied();
+ }
+
+ if (initiated_by_llgs && context.m_request_resume_function && !stop_was_requested)
+ {
+ // We can end up here if stop was initiated by LLGS but by this time a
+ // thread stop has occurred - maybe initiated by another event.
+ TSCLog ("Resuming thread %" PRIu64 " since stop wasn't requested", tid);
+ const auto error = context.m_request_resume_function (tid, true);
+ if (error.Success ())
+ {
+ context.m_state = ThreadState::Running;
+ }
+ else
+ {
+ TSCLog ("NativeProcessLinux::%s failed to resume thread tid %" PRIu64 ": %s",
+ __FUNCTION__, tid, error.AsCString ());
+ }
+ }
+}
+
+void
+NativeProcessLinux::DoCallAfterThreadsStop(PendingNotificationUP &&notification_up)
+{
+ // Validate we know about the deferred trigger thread.
+ if (!IsKnownThread (notification_up->triggering_tid))
+ {
+ // We don't know about this thread. This is an error condition.
+ std::ostringstream error_message;
+ error_message << "error: deferred notification tid " << notification_up->triggering_tid << " is unknown";
+ notification_up->error_function (error_message.str ());
+
+ // We bail out here.
+ return;
+ }
+
+ if (m_pending_notification_up)
+ {
+ // Yikes - we've already got a pending signal notification in progress.
+ // Log this info. We lose the pending notification here.
+ TSCLog ("NativeProcessLinux::%s dropping existing pending signal notification for tid %" PRIu64 ", to be replaced with signal for tid %" PRIu64,
+ __FUNCTION__,
+ m_pending_notification_up->triggering_tid,
+ notification_up->triggering_tid);
+ }
+ m_pending_notification_up = std::move(notification_up);
+
+ if (m_pending_notification_up->request_stop_on_all_unstopped_threads)
+ RequestStopOnAllRunningThreads();
+ else
+ {
+ if (!RequestStopOnAllSpecifiedThreads())
+ return;
+ }
+
+ if (m_pending_notification_up->wait_for_stop_tids.empty ())
+ {
+ // We're not waiting for any threads. Fire off the deferred signal delivery event.
+ m_pending_notification_up->call_after_function(m_pending_notification_up->triggering_tid);
+ m_pending_notification_up.reset();
+ }
+}
+
+void
+NativeProcessLinux::ThreadWasCreated (lldb::tid_t tid, bool is_stopped, const ErrorFunction &error_function)
+{
+ // Ensure we don't already know about the thread.
+ auto find_it = m_tid_map.find (tid);
+ if (find_it != m_tid_map.end ())
+ {
+ // We already know about this thread. This is an error condition.
+ std::ostringstream error_message;
+ error_message << "error: notified tid " << tid << " created but we already know about this thread";
+ error_function (error_message.str ());
+ return;
+ }
+
+ // Add the new thread to the stop map.
+ ThreadContext ctx;
+ ctx.m_state = (is_stopped) ? ThreadState::Stopped : ThreadState::Running;
+ m_tid_map[tid] = std::move(ctx);
+
+ if (m_pending_notification_up && !is_stopped)
+ {
+ // We will need to wait for this new thread to stop as well before firing the
+ // notification.
+ m_pending_notification_up->wait_for_stop_tids.insert(tid);
+ m_pending_notification_up->request_thread_stop_function(tid);
+ }
+}
+
+void
+NativeProcessLinux::ThreadDidDie (lldb::tid_t tid, const ErrorFunction &error_function)
+{
+ // Ensure we know about the thread.
+ auto find_it = m_tid_map.find (tid);
+ if (find_it == m_tid_map.end ())
+ {
+ // We don't know about this thread. This is an error condition.
+ std::ostringstream error_message;
+ error_message << "error: notified tid " << tid << " died but tid is unknown";
+ error_function (error_message.str ());
+ return;
+ }
+
+ // Update the global list of known thread states. While this one is stopped, it is also dead.
+ // So stop tracking it. We assume the user of this coordinator will not keep trying to add
+ // dependencies on a thread after it is known to be dead.
+ m_tid_map.erase (find_it);
+
+ // If we have a pending notification, remove this from the set.
+ if (m_pending_notification_up)
+ {
+ m_pending_notification_up->wait_for_stop_tids.erase(tid);
+ SignalIfRequirementsSatisfied();
+ }
+}
+
+void
+NativeProcessLinux::TSCLog (const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+
+ m_log_function (format, args);
+
+ va_end (args);
+}
+
+void
+NativeProcessLinux::NotifyThreadStop (lldb::tid_t tid,
+ bool initiated_by_llgs,
+ const ErrorFunction &error_function)
+{
+ std::lock_guard<std::mutex> lock(m_event_mutex);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s about to process event: (tid: %" PRIu64 ", %sinitiated by llgs)",
+ __FUNCTION__, tid, initiated_by_llgs?"":"not ");
+ }
+
+ ThreadDidStop (tid, initiated_by_llgs, error_function);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s event processing done", __FUNCTION__);
+ }
+}
+
+void
+NativeProcessLinux::RequestThreadResume (lldb::tid_t tid,
+ const ResumeThreadFunction &request_thread_resume_function,
+ const ErrorFunction &error_function)
+{
+ std::lock_guard<std::mutex> lock(m_event_mutex);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s about to process event: (tid: %" PRIu64 ")",
+ __FUNCTION__, tid);
+ }
+
+ DoResume(tid, request_thread_resume_function, error_function, true);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s event processing done", __FUNCTION__);
+ }
+}
+
+void
+NativeProcessLinux::RequestThreadResumeAsNeeded (lldb::tid_t tid,
+ const ResumeThreadFunction &request_thread_resume_function,
+ const ErrorFunction &error_function)
+{
+ std::lock_guard<std::mutex> lock(m_event_mutex);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s about to process event: (tid: %" PRIu64 ")",
+ __FUNCTION__, tid);
+ }
+
+ DoResume (tid, request_thread_resume_function, error_function, false);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s event processing done", __FUNCTION__);
+ }
+}
+
+void
+NativeProcessLinux::NotifyThreadCreate (lldb::tid_t tid,
+ bool is_stopped,
+ const ErrorFunction &error_function)
+{
+ std::lock_guard<std::mutex> lock(m_event_mutex);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s about to process event: (tid: %" PRIu64 ", is %sstopped)",
+ __FUNCTION__, tid, is_stopped?"":"not ");
+ }
+
+ ThreadWasCreated (tid, is_stopped, error_function);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s event processing done", __FUNCTION__);
+ }
+}
+
+void
+NativeProcessLinux::NotifyThreadDeath (lldb::tid_t tid,
+ const ErrorFunction &error_function)
+{
+ std::lock_guard<std::mutex> lock(m_event_mutex);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s about to process event: (tid: %" PRIu64 ")", __FUNCTION__, tid);
+ }
+
+ ThreadDidDie(tid, error_function);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s event processing done", __FUNCTION__);
+ }
+}
+
+void
+NativeProcessLinux::ResetForExec ()
+{
+ std::lock_guard<std::mutex> lock(m_event_mutex);
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s about to process event", __FUNCTION__);
+ }
+
+ // Clear the pending notification if there was one.
+ m_pending_notification_up.reset ();
+
+ // Clear the stop map - we no longer know anything about any thread state.
+ // The caller is expected to reset thread states for all threads, and we
+ // will assume anything we haven't heard about is running and requires a
+ // stop.
+ m_tid_map.clear ();
+
+ if (m_log_event_processing)
+ {
+ TSCLog ("NativeProcessLinux::%s event processing done", __FUNCTION__);
+ }
+}
+void
+NativeProcessLinux::LogEnableEventProcessing (bool enabled)
+{
+ m_log_event_processing = enabled;
+}
+
+bool
+NativeProcessLinux::IsKnownThread (lldb::tid_t tid) const
+{
+ return m_tid_map.find (tid) != m_tid_map.end ();
+}
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
index a35f0a731b7..886d077aac7 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -15,6 +15,8 @@
#include <signal.h>
// C++ Includes
+#include <mutex>
+#include <unordered_map>
#include <unordered_set>
// Other libraries and framework includes
@@ -33,8 +35,6 @@ namespace lldb_private {
class Scalar;
namespace process_linux {
- class ThreadStateCoordinator;
-
/// @class NativeProcessLinux
/// @brief Manages communication with the inferior (debugee) process.
///
@@ -190,8 +190,6 @@ namespace process_linux {
std::vector<MemoryRegionInfo> m_mem_region_cache;
Mutex m_mem_region_cache_mutex;
- std::unique_ptr<ThreadStateCoordinator> m_coordinator_up;
-
// List of thread ids stepping with a breakpoint with the address of
// the relevan breakpoint
std::map<lldb::tid_t, lldb::addr_t> m_threads_stepping_with_breakpoint;
@@ -374,6 +372,226 @@ namespace process_linux {
Error
RequestThreadStop (const lldb::pid_t pid, const lldb::tid_t tid);
+
+
+ public:
+ // Typedefs.
+ typedef std::unordered_set<lldb::tid_t> ThreadIDSet;
+
+ // Callback/block definitions.
+ typedef std::function<void (lldb::tid_t tid)> ThreadIDFunction;
+ typedef std::function<void (const char *format, va_list args)> LogFunction;
+ typedef std::function<void (const std::string &error_message)> ErrorFunction;
+ typedef std::function<Error (lldb::tid_t tid)> StopThreadFunction;
+ typedef std::function<Error (lldb::tid_t tid, bool supress_signal)> ResumeThreadFunction;
+
+ private:
+ // Notify the coordinator when a thread is created and/or starting to be
+ // tracked. is_stopped should be true if the thread is currently stopped;
+ // otherwise, it should be set false if it is already running. Will
+ // call the error function if the thread id is already tracked.
+ void
+ NotifyThreadCreate (lldb::tid_t tid,
+ bool is_stopped,
+ const ErrorFunction &error_function);
+
+ // Notify the coordinator when a previously-existing thread should no
+ // longer be tracked. The error_function will trigger if the thread
+ // is not being tracked.
+ void
+ NotifyThreadDeath (lldb::tid_t tid,
+ const ErrorFunction &error_function);
+
+
+ // This method is the main purpose of the class: triggering a deferred
+ // action after a given set of threads stop. The triggering_tid is the
+ // thread id passed to the call_after_function. The error_function will
+ // be fired if either the triggering tid or any of the wait_for_stop_tids
+ // are unknown at the time the method is processed.
+ void
+ CallAfterThreadsStop (lldb::tid_t triggering_tid,
+ const ThreadIDSet &wait_for_stop_tids,
+ const StopThreadFunction &request_thread_stop_function,
+ const ThreadIDFunction &call_after_function,
+ const ErrorFunction &error_function);
+
+ // This method is the main purpose of the class: triggering a deferred
+ // action after all non-stopped threads stop. The triggering_tid is the
+ // thread id passed to the call_after_function. The error_function will
+ // be fired if the triggering tid is unknown at the time of execution.
+ void
+ CallAfterRunningThreadsStop (lldb::tid_t triggering_tid,
+ const StopThreadFunction &request_thread_stop_function,
+ const ThreadIDFunction &call_after_function,
+ const ErrorFunction &error_function);
+
+ // This method is the main purpose of the class: triggering a deferred
+ // action after all non-stopped threads stop. The triggering_tid is the
+ // thread id passed to the call_after_function. The error_function will
+ // be fired if the triggering tid is unknown at the time of execution.
+ // This variant will send stop requests to all non-stopped threads except
+ // for any contained in skip_stop_request_tids.
+ void
+ CallAfterRunningThreadsStopWithSkipTIDs (lldb::tid_t triggering_tid,
+ const ThreadIDSet &skip_stop_request_tids,
+ const StopThreadFunction &request_thread_stop_function,
+ const ThreadIDFunction &call_after_function,
+ const ErrorFunction &error_function);
+
+ // Notify the thread stopped. Will trigger error at time of execution if we
+ // already think it is stopped.
+ void
+ NotifyThreadStop (lldb::tid_t tid,
+ bool initiated_by_llgs,
+ const ErrorFunction &error_function);
+
+ // Request that the given thread id should have the request_thread_resume_function
+ // called. Will trigger the error_function if the thread is thought to be running
+ // already at that point. This call signals an error if the thread resume is for
+ // a thread that is already in a running state.
+ void
+ RequestThreadResume (lldb::tid_t tid,
+ const ResumeThreadFunction &request_thread_resume_function,
+ const ErrorFunction &error_function);
+
+ // Request that the given thread id should have the request_thread_resume_function
+ // called. Will trigger the error_function if the thread is thought to be running
+ // already at that point. This call ignores threads that are already running and
+ // does not trigger an error in that case.
+ void
+ RequestThreadResumeAsNeeded (lldb::tid_t tid,
+ const ResumeThreadFunction &request_thread_resume_function,
+ const ErrorFunction &error_function);
+
+ // Indicate the calling process did an exec and that the thread state
+ // should be 100% cleared.
+ void
+ ResetForExec ();
+
+ // Enable/disable verbose logging of event processing.
+ void
+ LogEnableEventProcessing (bool enabled);
+
+ private:
+
+ enum class ThreadState
+ {
+ Running,
+ Stopped
+ };
+
+ struct ThreadContext
+ {
+ ThreadState m_state;
+ bool m_stop_requested = false;
+ ResumeThreadFunction m_request_resume_function;
+ };
+ typedef std::unordered_map<lldb::tid_t, ThreadContext> TIDContextMap;
+
+ struct PendingNotification
+ {
+ PendingNotification (lldb::tid_t triggering_tid,
+ const ThreadIDSet &wait_for_stop_tids,
+ const StopThreadFunction &request_thread_stop_function,
+ const ThreadIDFunction &call_after_function,
+ const ErrorFunction &error_function):
+ triggering_tid (triggering_tid),
+ wait_for_stop_tids (wait_for_stop_tids),
+ original_wait_for_stop_tids (wait_for_stop_tids),
+ request_thread_stop_function (request_thread_stop_function),
+ call_after_function (call_after_function),
+ error_function (error_function),
+ request_stop_on_all_unstopped_threads (false),
+ skip_stop_request_tids ()
+ {
+ }
+
+ PendingNotification (lldb::tid_t triggering_tid,
+ const StopThreadFunction &request_thread_stop_function,
+ const ThreadIDFunction &call_after_function,
+ const ErrorFunction &error_function) :
+ triggering_tid (triggering_tid),
+ wait_for_stop_tids (),
+ original_wait_for_stop_tids (),
+ request_thread_stop_function (request_thread_stop_function),
+ call_after_function (call_after_function),
+ error_function (error_function),
+ request_stop_on_all_unstopped_threads (true),
+ skip_stop_request_tids ()
+ {
+ }
+
+ PendingNotification (lldb::tid_t triggering_tid,
+ const StopThreadFunction &request_thread_stop_function,
+ const ThreadIDFunction &call_after_function,
+ const ThreadIDSet &skip_stop_request_tids,
+ const ErrorFunction &error_function) :
+ triggering_tid (triggering_tid),
+ wait_for_stop_tids (),
+ original_wait_for_stop_tids (),
+ request_thread_stop_function (request_thread_stop_function),
+ call_after_function (call_after_function),
+ error_function (error_function),
+ request_stop_on_all_unstopped_threads (true),
+ skip_stop_request_tids (skip_stop_request_tids)
+ {
+ }
+
+ const lldb::tid_t triggering_tid;
+ ThreadIDSet wait_for_stop_tids;
+ const ThreadIDSet original_wait_for_stop_tids;
+ StopThreadFunction request_thread_stop_function;
+ ThreadIDFunction call_after_function;
+ ErrorFunction error_function;
+ const bool request_stop_on_all_unstopped_threads;
+ ThreadIDSet skip_stop_request_tids;
+ };
+ typedef std::unique_ptr<PendingNotification> PendingNotificationUP;
+
+ // Fire pending notification if no pending thread stops remain.
+ void SignalIfRequirementsSatisfied();
+
+ bool
+ RequestStopOnAllSpecifiedThreads();
+
+ void
+ RequestStopOnAllRunningThreads();
+
+ void
+ RequestThreadStop (lldb::tid_t tid, ThreadContext& context);
+
+ std::mutex m_event_mutex; // Serializes execution of ProcessEvent. XXX
+
+ void
+ ThreadDidStop (lldb::tid_t tid, bool initiated_by_llgs, const ErrorFunction &error_function);
+
+ void
+ DoResume(lldb::tid_t tid, ResumeThreadFunction request_thread_resume_function,
+ ErrorFunction error_function, bool error_when_already_running);
+
+ void
+ DoCallAfterThreadsStop(PendingNotificationUP &&notification_up);
+
+ void
+ ThreadWasCreated (lldb::tid_t tid, bool is_stopped, const ErrorFunction &error_function);
+
+ void
+ ThreadDidDie (lldb::tid_t tid, const ErrorFunction &error_function);
+
+ bool
+ IsKnownThread(lldb::tid_t tid) const;
+
+ void
+ TSCLog (const char *format, ...);
+
+ // Member variables.
+ LogFunction m_log_function;
+ PendingNotificationUP m_pending_notification_up;
+
+ // Maps known TIDs to ThreadContext.
+ TIDContextMap m_tid_map;
+
+ bool m_log_event_processing;
};
} // namespace process_linux
diff --git a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp
deleted file mode 100644
index 1d7affc3c46..00000000000
--- a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.cpp
+++ /dev/null
@@ -1,590 +0,0 @@
-//===-- ThreadStateCoordinator.cpp ------------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-
-#if !defined (__STDC_FORMAT_MACROS)
-#define __STDC_FORMAT_MACROS 1
-#endif
-
-#include <inttypes.h>
-
-#include "ThreadStateCoordinator.h"
-#include <memory>
-#include <cstdarg>
-#include <sstream>
-
-using namespace lldb_private;
-using namespace lldb_private::process_linux;
-
-//===----------------------------------------------------------------------===//
-
-void
-ThreadStateCoordinator::DoResume(
- lldb::tid_t tid,
- ResumeThreadFunction request_thread_resume_function,
- ErrorFunction error_function,
- bool error_when_already_running)
-{
- // Ensure we know about the thread.
- auto find_it = m_tid_map.find (tid);
- if (find_it == m_tid_map.end ())
- {
- // We don't know about this thread. This is an error condition.
- std::ostringstream error_message;
- error_message << "error: tid " << tid << " asked to resume but tid is unknown";
- error_function (error_message.str ());
- return;
- }
- auto& context = find_it->second;
- // Tell the thread to resume if we don't already think it is running.
- const bool is_stopped = context.m_state == ThreadState::Stopped;
- if (!is_stopped)
- {
- // It's not an error, just a log, if the error_when_already_running flag is not set.
- // This covers cases where, for instance, we're just trying to resume all threads
- // from the user side.
- if (!error_when_already_running)
- {
- Log ("ThreadStateCoordinator::%s tid %" PRIu64 " optional resume skipped since it is already running",
- __FUNCTION__,
- tid);
- }
- else
- {
- // Skip the resume call - we have tracked it to be running. And we unconditionally
- // expected to resume this thread. Flag this as an error.
- std::ostringstream error_message;
- error_message << "error: tid " << tid << " asked to resume but we think it is already running";
- error_function (error_message.str ());
- }
-
- // Error or not, we're done.
- return;
- }
-
- // Before we do the resume below, first check if we have a pending
- // stop notification this is currently or was previously waiting for
- // this thread to stop. This is potentially a buggy situation since
- // we're ostensibly waiting for threads to stop before we send out the
- // pending notification, and here we are resuming one before we send
- // out the pending stop notification.
- if (m_pending_notification_up)
- {
- if (m_pending_notification_up->wait_for_stop_tids.count (tid) > 0)
- {
- Log ("ThreadStateCoordinator::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that is actively waiting for this thread to stop. Valid sequence of events?", __FUNCTION__, tid, m_pending_notification_up->triggering_tid);
- }
- else if (m_pending_notification_up->original_wait_for_stop_tids.count (tid) > 0)
- {
- Log ("ThreadStateCoordinator::%s about to resume tid %" PRIu64 " per explicit request but we have a pending stop notification (tid %" PRIu64 ") that hasn't fired yet and this is one of the threads we had been waiting on (and already marked satisfied for this tid). Valid sequence of events?", __FUNCTION__, tid, m_pending_notification_up->triggering_tid);
- for (auto tid : m_pending_notification_up->wait_for_stop_tids)
- {
- Log ("ThreadStateCoordinator::%s tid %" PRIu64 " deferred stop notification still waiting on tid %" PRIu64,
- __FUNCTION__,
- m_pending_notification_up->triggering_tid,
- tid);
- }
- }
- }
-
- // Request a resume. We expect this to be synchronous and the system
- // to reflect it is running after this completes.
- const auto error = request_thread_resume_function (tid, false);
- if (error.Success ())
- {
- // Now mark it is running.
- context.m_state = ThreadState::Running;
- context.m_request_resume_function = request_thread_resume_function;
- }
- else
- {
- Log ("ThreadStateCoordinator::%s failed to resume thread tid %" PRIu64 ": %s",
- __FUNCTION__, tid, error.AsCString ());
- }
-
- return;
-}
-
-//===----------------------------------------------------------------------===//
-
-ThreadStateCoordinator::ThreadStateCoordinator (const LogFunction &log_function) :
- m_log_function (log_function),
- m_tid_map (),
- m_log_event_processing (false)
-{
-}
-
-void
-ThreadStateCoordinator::CallAfterThreadsStop (const lldb::tid_t triggering_tid,
- const ThreadIDSet &wait_for_stop_tids,
- const StopThreadFunction &request_thread_stop_function,
- const ThreadIDFunction &call_after_function,
- const ErrorFunction &error_function)
-{
- std::lock_guard<std::mutex> lock(m_event_mutex);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s about to process event: (triggering_tid: %" PRIu64 ", wait_for_stop_tids.size(): %zd)",
- __FUNCTION__, triggering_tid, wait_for_stop_tids.size());
- }
-
- DoCallAfterThreadsStop(PendingNotificationUP(new PendingNotification(
- triggering_tid, wait_for_stop_tids, request_thread_stop_function,
- call_after_function, error_function)));
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__);
- }
-}
-
-void
-ThreadStateCoordinator::CallAfterRunningThreadsStop (const lldb::tid_t triggering_tid,
- const StopThreadFunction &request_thread_stop_function,
- const ThreadIDFunction &call_after_function,
- const ErrorFunction &error_function)
-{
- std::lock_guard<std::mutex> lock(m_event_mutex);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s about to process event: (triggering_tid: %" PRIu64 ")",
- __FUNCTION__, triggering_tid);
- }
-
- DoCallAfterThreadsStop(PendingNotificationUP(new PendingNotification(
- triggering_tid,
- request_thread_stop_function,
- call_after_function,
- error_function)));
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__);
- }
-}
-
-void
-ThreadStateCoordinator::CallAfterRunningThreadsStopWithSkipTIDs (lldb::tid_t triggering_tid,
- const ThreadIDSet &skip_stop_request_tids,
- const StopThreadFunction &request_thread_stop_function,
- const ThreadIDFunction &call_after_function,
- const ErrorFunction &error_function)
-{
- std::lock_guard<std::mutex> lock(m_event_mutex);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s about to process event: (triggering_tid: %" PRIu64 ", skip_stop_request_tids.size(): %zd)",
- __FUNCTION__, triggering_tid, skip_stop_request_tids.size());
- }
-
- DoCallAfterThreadsStop(PendingNotificationUP(new PendingNotification(
- triggering_tid,
- request_thread_stop_function,
- call_after_function,
- skip_stop_request_tids,
- error_function)));
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__);
- }
-}
-
-void
-ThreadStateCoordinator::SignalIfRequirementsSatisfied()
-{
- if (m_pending_notification_up && m_pending_notification_up->wait_for_stop_tids.empty ())
- {
- m_pending_notification_up->call_after_function(m_pending_notification_up->triggering_tid);
- m_pending_notification_up.reset();
- }
-}
-
-bool
-ThreadStateCoordinator::RequestStopOnAllSpecifiedThreads()
-{
- // Request a stop for all the thread stops that need to be stopped
- // and are not already known to be stopped. Keep a list of all the
- // threads from which we still need to hear a stop reply.
-
- ThreadIDSet sent_tids;
- for (auto tid : m_pending_notification_up->wait_for_stop_tids)
- {
- // Validate we know about all tids for which we must first receive a stop before
- // triggering the deferred stop notification.
- auto find_it = m_tid_map.find (tid);
- if (find_it == m_tid_map.end ())
- {
- // This is an error. We shouldn't be asking for waiting pids that aren't known.
- // NOTE: we may be stripping out the specification of wait tids and handle this
- // automatically, in which case this state can never occur.
- std::ostringstream error_message;
- error_message << "error: deferred notification for tid " << m_pending_notification_up->triggering_tid << " specified an unknown/untracked pending stop tid " << m_pending_notification_up->triggering_tid;
- m_pending_notification_up->error_function (error_message.str ());
-
- // Bail out here.
- return false;
- }
-
- // If the pending stop thread is currently running, we need to send it a stop request.
- auto& context = find_it->second;
- if (context.m_state == ThreadState::Running)
- {
- RequestThreadStop (tid, context);
- sent_tids.insert (tid);
- }
- }
- // We only need to wait for the sent_tids - so swap our wait set
- // to the sent tids. The rest are already stopped and we won't
- // be receiving stop notifications for them.
- m_pending_notification_up->wait_for_stop_tids.swap (sent_tids);
-
- // Succeeded, keep running.
- return true;
-}
-
-void
-ThreadStateCoordinator::RequestStopOnAllRunningThreads()
-{
- // Request a stop for all the thread stops that need to be stopped
- // and are not already known to be stopped. Keep a list of all the
- // threads from which we still need to hear a stop reply.
-
- ThreadIDSet sent_tids;
- for (auto it = m_tid_map.begin(); it != m_tid_map.end(); ++it)
- {
- // We only care about threads not stopped.
- const bool running = it->second.m_state == ThreadState::Running;
- if (running)
- {
- const lldb::tid_t tid = it->first;
-
- // Request this thread stop if the tid stop request is not explicitly ignored.
- const bool skip_stop_request = m_pending_notification_up->skip_stop_request_tids.count (tid) > 0;
- if (!skip_stop_request)
- RequestThreadStop (tid, it->second);
-
- // Even if we skipped sending the stop request for other reasons (like stepping),
- // we still need to wait for that stepping thread to notify completion/stop.
- sent_tids.insert (tid);
- }
- }
-
- // Set the wait list to the set of tids for which we requested stops.
- m_pending_notification_up->wait_for_stop_tids.swap (sent_tids);
-}
-
-void
-ThreadStateCoordinator::RequestThreadStop (lldb::tid_t tid, ThreadContext& context)
-{
- const auto error = m_pending_notification_up->request_thread_stop_function (tid);
- if (error.Success ())
- context.m_stop_requested = true;
- else
- {
- Log ("ThreadStateCoordinator::%s failed to request thread stop tid %" PRIu64 ": %s",
- __FUNCTION__, tid, error.AsCString ());
- }
-}
-
-
-void
-ThreadStateCoordinator::ThreadDidStop (lldb::tid_t tid, bool initiated_by_llgs, const ErrorFunction &error_function)
-{
- // Ensure we know about the thread.
- auto find_it = m_tid_map.find (tid);
- if (find_it == m_tid_map.end ())
- {
- // We don't know about this thread. This is an error condition.
- std::ostringstream error_message;
- error_message << "error: tid " << tid << " asked to stop but tid is unknown";
- error_function (error_message.str ());
- return;
- }
-
- // Update the global list of known thread states. This one is definitely stopped.
- auto& context = find_it->second;
- const auto stop_was_requested = context.m_stop_requested;
- context.m_state = ThreadState::Stopped;
- context.m_stop_requested = false;
-
- // If we have a pending notification, remove this from the set.
- if (m_pending_notification_up)
- {
- m_pending_notification_up->wait_for_stop_tids.erase(tid);
- SignalIfRequirementsSatisfied();
- }
-
- if (initiated_by_llgs && context.m_request_resume_function && !stop_was_requested)
- {
- // We can end up here if stop was initiated by LLGS but by this time a
- // thread stop has occurred - maybe initiated by another event.
- Log ("Resuming thread %" PRIu64 " since stop wasn't requested", tid);
- const auto error = context.m_request_resume_function (tid, true);
- if (error.Success ())
- {
- context.m_state = ThreadState::Running;
- }
- else
- {
- Log ("ThreadStateCoordinator::%s failed to resume thread tid %" PRIu64 ": %s",
- __FUNCTION__, tid, error.AsCString ());
- }
- }
-}
-
-void
-ThreadStateCoordinator::DoCallAfterThreadsStop(PendingNotificationUP &&notification_up)
-{
- // Validate we know about the deferred trigger thread.
- if (!IsKnownThread (notification_up->triggering_tid))
- {
- // We don't know about this thread. This is an error condition.
- std::ostringstream error_message;
- error_message << "error: deferred notification tid " << notification_up->triggering_tid << " is unknown";
- notification_up->error_function (error_message.str ());
-
- // We bail out here.
- return;
- }
-
- if (m_pending_notification_up)
- {
- // Yikes - we've already got a pending signal notification in progress.
- // Log this info. We lose the pending notification here.
- Log ("ThreadStateCoordinator::%s dropping existing pending signal notification for tid %" PRIu64 ", to be replaced with signal for tid %" PRIu64,
- __FUNCTION__,
- m_pending_notification_up->triggering_tid,
- notification_up->triggering_tid);
- }
- m_pending_notification_up = std::move(notification_up);
-
- if (m_pending_notification_up->request_stop_on_all_unstopped_threads)
- RequestStopOnAllRunningThreads();
- else
- {
- if (!RequestStopOnAllSpecifiedThreads())
- return;
- }
-
- if (m_pending_notification_up->wait_for_stop_tids.empty ())
- {
- // We're not waiting for any threads. Fire off the deferred signal delivery event.
- m_pending_notification_up->call_after_function(m_pending_notification_up->triggering_tid);
- m_pending_notification_up.reset();
- }
-}
-
-void
-ThreadStateCoordinator::ThreadWasCreated (lldb::tid_t tid, bool is_stopped, const ErrorFunction &error_function)
-{
- // Ensure we don't already know about the thread.
- auto find_it = m_tid_map.find (tid);
- if (find_it != m_tid_map.end ())
- {
- // We already know about this thread. This is an error condition.
- std::ostringstream error_message;
- error_message << "error: notified tid " << tid << " created but we already know about this thread";
- error_function (error_message.str ());
- return;
- }
-
- // Add the new thread to the stop map.
- ThreadContext ctx;
- ctx.m_state = (is_stopped) ? ThreadState::Stopped : ThreadState::Running;
- m_tid_map[tid] = std::move(ctx);
-
- if (m_pending_notification_up && !is_stopped)
- {
- // We will need to wait for this new thread to stop as well before firing the
- // notification.
- m_pending_notification_up->wait_for_stop_tids.insert(tid);
- m_pending_notification_up->request_thread_stop_function(tid);
- }
-}
-
-void
-ThreadStateCoordinator::ThreadDidDie (lldb::tid_t tid, const ErrorFunction &error_function)
-{
- // Ensure we know about the thread.
- auto find_it = m_tid_map.find (tid);
- if (find_it == m_tid_map.end ())
- {
- // We don't know about this thread. This is an error condition.
- std::ostringstream error_message;
- error_message << "error: notified tid " << tid << " died but tid is unknown";
- error_function (error_message.str ());
- return;
- }
-
- // Update the global list of known thread states. While this one is stopped, it is also dead.
- // So stop tracking it. We assume the user of this coordinator will not keep trying to add
- // dependencies on a thread after it is known to be dead.
- m_tid_map.erase (find_it);
-
- // If we have a pending notification, remove this from the set.
- if (m_pending_notification_up)
- {
- m_pending_notification_up->wait_for_stop_tids.erase(tid);
- SignalIfRequirementsSatisfied();
- }
-}
-
-void
-ThreadStateCoordinator::Log (const char *format, ...)
-{
- va_list args;
- va_start (args, format);
-
- m_log_function (format, args);
-
- va_end (args);
-}
-
-void
-ThreadStateCoordinator::NotifyThreadStop (lldb::tid_t tid,
- bool initiated_by_llgs,
- const ErrorFunction &error_function)
-{
- std::lock_guard<std::mutex> lock(m_event_mutex);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s about to process event: (tid: %" PRIu64 ", %sinitiated by llgs)",
- __FUNCTION__, tid, initiated_by_llgs?"":"not ");
- }
-
- ThreadDidStop (tid, initiated_by_llgs, error_function);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__);
- }
-}
-
-void
-ThreadStateCoordinator::RequestThreadResume (lldb::tid_t tid,
- const ResumeThreadFunction &request_thread_resume_function,
- const ErrorFunction &error_function)
-{
- std::lock_guard<std::mutex> lock(m_event_mutex);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s about to process event: (tid: %" PRIu64 ")",
- __FUNCTION__, tid);
- }
-
- DoResume(tid, request_thread_resume_function, error_function, true);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__);
- }
-}
-
-void
-ThreadStateCoordinator::RequestThreadResumeAsNeeded (lldb::tid_t tid,
- const ResumeThreadFunction &request_thread_resume_function,
- const ErrorFunction &error_function)
-{
- std::lock_guard<std::mutex> lock(m_event_mutex);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s about to process event: (tid: %" PRIu64 ")",
- __FUNCTION__, tid);
- }
-
- DoResume (tid, request_thread_resume_function, error_function, false);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__);
- }
-}
-
-void
-ThreadStateCoordinator::NotifyThreadCreate (lldb::tid_t tid,
- bool is_stopped,
- const ErrorFunction &error_function)
-{
- std::lock_guard<std::mutex> lock(m_event_mutex);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s about to process event: (tid: %" PRIu64 ", is %sstopped)",
- __FUNCTION__, tid, is_stopped?"":"not ");
- }
-
- ThreadWasCreated (tid, is_stopped, error_function);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__);
- }
-}
-
-void
-ThreadStateCoordinator::NotifyThreadDeath (lldb::tid_t tid,
- const ErrorFunction &error_function)
-{
- std::lock_guard<std::mutex> lock(m_event_mutex);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s about to process event: (tid: %" PRIu64 ")", __FUNCTION__, tid);
- }
-
- ThreadDidDie(tid, error_function);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__);
- }
-}
-
-void
-ThreadStateCoordinator::ResetForExec ()
-{
- std::lock_guard<std::mutex> lock(m_event_mutex);
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s about to process event", __FUNCTION__);
- }
-
- // Clear the pending notification if there was one.
- m_pending_notification_up.reset ();
-
- // Clear the stop map - we no longer know anything about any thread state.
- // The caller is expected to reset thread states for all threads, and we
- // will assume anything we haven't heard about is running and requires a
- // stop.
- m_tid_map.clear ();
-
- if (m_log_event_processing)
- {
- Log ("ThreadStateCoordinator::%s event processing done", __FUNCTION__);
- }
-}
-void
-ThreadStateCoordinator::LogEnableEventProcessing (bool enabled)
-{
- m_log_event_processing = enabled;
-}
-
-bool
-ThreadStateCoordinator::IsKnownThread (lldb::tid_t tid) const
-{
- return m_tid_map.find (tid) != m_tid_map.end ();
-}
diff --git a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h b/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h
deleted file mode 100644
index 6ad87fc2855..00000000000
--- a/lldb/source/Plugins/Process/Linux/ThreadStateCoordinator.h
+++ /dev/null
@@ -1,253 +0,0 @@
-//===-- ThreadStateCoordinator.h --------------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef lldb_ThreadStateCoordinator_h
-#define lldb_ThreadStateCoordinator_h
-
-#include <functional>
-#include <mutex>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "lldb/lldb-types.h"
-
-#include "lldb/Core/Error.h"
-
-namespace lldb_private {
-namespace process_linux {
-
- class ThreadStateCoordinator
- {
- public:
-
- // Typedefs.
- typedef std::unordered_set<lldb::tid_t> ThreadIDSet;
-
- // Callback/block definitions.
- typedef std::function<void (lldb::tid_t tid)> ThreadIDFunction;
- typedef std::function<void (const char *format, va_list args)> LogFunction;
- typedef std::function<void (const std::string &error_message)> ErrorFunction;
- typedef std::function<Error (lldb::tid_t tid)> StopThreadFunction;
- typedef std::function<Error (lldb::tid_t tid, bool supress_signal)> ResumeThreadFunction;
-
- // Constructors.
- ThreadStateCoordinator (const LogFunction &log_function);
-
- // Notify the coordinator when a thread is created and/or starting to be
- // tracked. is_stopped should be true if the thread is currently stopped;
- // otherwise, it should be set false if it is already running. Will
- // call the error function if the thread id is already tracked.
- void
- NotifyThreadCreate (lldb::tid_t tid,
- bool is_stopped,
- const ErrorFunction &error_function);
-
- // Notify the coordinator when a previously-existing thread should no
- // longer be tracked. The error_function will trigger if the thread
- // is not being tracked.
- void
- NotifyThreadDeath (lldb::tid_t tid,
- const ErrorFunction &error_function);
-
-
- // This method is the main purpose of the class: triggering a deferred
- // action after a given set of threads stop. The triggering_tid is the
- // thread id passed to the call_after_function. The error_function will
- // be fired if either the triggering tid or any of the wait_for_stop_tids
- // are unknown at the time the method is processed.
- void
- CallAfterThreadsStop (lldb::tid_t triggering_tid,
- const ThreadIDSet &wait_for_stop_tids,
- const StopThreadFunction &request_thread_stop_function,
- const ThreadIDFunction &call_after_function,
- const ErrorFunction &error_function);
-
- // This method is the main purpose of the class: triggering a deferred
- // action after all non-stopped threads stop. The triggering_tid is the
- // thread id passed to the call_after_function. The error_function will
- // be fired if the triggering tid is unknown at the time of execution.
- void
- CallAfterRunningThreadsStop (lldb::tid_t triggering_tid,
- const StopThreadFunction &request_thread_stop_function,
- const ThreadIDFunction &call_after_function,
- const ErrorFunction &error_function);
-
- // This method is the main purpose of the class: triggering a deferred
- // action after all non-stopped threads stop. The triggering_tid is the
- // thread id passed to the call_after_function. The error_function will
- // be fired if the triggering tid is unknown at the time of execution.
- // This variant will send stop requests to all non-stopped threads except
- // for any contained in skip_stop_request_tids.
- void
- CallAfterRunningThreadsStopWithSkipTIDs (lldb::tid_t triggering_tid,
- const ThreadIDSet &skip_stop_request_tids,
- const StopThreadFunction &request_thread_stop_function,
- const ThreadIDFunction &call_after_function,
- const ErrorFunction &error_function);
-
- // Notify the thread stopped. Will trigger error at time of execution if we
- // already think it is stopped.
- void
- NotifyThreadStop (lldb::tid_t tid,
- bool initiated_by_llgs,
- const ErrorFunction &error_function);
-
- // Request that the given thread id should have the request_thread_resume_function
- // called. Will trigger the error_function if the thread is thought to be running
- // already at that point. This call signals an error if the thread resume is for
- // a thread that is already in a running state.
- void
- RequestThreadResume (lldb::tid_t tid,
- const ResumeThreadFunction &request_thread_resume_function,
- const ErrorFunction &error_function);
-
- // Request that the given thread id should have the request_thread_resume_function
- // called. Will trigger the error_function if the thread is thought to be running
- // already at that point. This call ignores threads that are already running and
- // does not trigger an error in that case.
- void
- RequestThreadResumeAsNeeded (lldb::tid_t tid,
- const ResumeThreadFunction &request_thread_resume_function,
- const ErrorFunction &error_function);
-
- // Indicate the calling process did an exec and that the thread state
- // should be 100% cleared.
- void
- ResetForExec ();
-
- // Enable/disable verbose logging of event processing.
- void
- LogEnableEventProcessing (bool enabled);
-
- private:
-
- enum class ThreadState
- {
- Running,
- Stopped
- };
-
- struct ThreadContext
- {
- ThreadState m_state;
- bool m_stop_requested = false;
- ResumeThreadFunction m_request_resume_function;
- };
- typedef std::unordered_map<lldb::tid_t, ThreadContext> TIDContextMap;
-
- struct PendingNotification
- {
- PendingNotification (lldb::tid_t triggering_tid,
- const ThreadIDSet &wait_for_stop_tids,
- const StopThreadFunction &request_thread_stop_function,
- const ThreadIDFunction &call_after_function,
- const ErrorFunction &error_function):
- triggering_tid (triggering_tid),
- wait_for_stop_tids (wait_for_stop_tids),
- original_wait_for_stop_tids (wait_for_stop_tids),
- request_thread_stop_function (request_thread_stop_function),
- call_after_function (call_after_function),
- error_function (error_function),
- request_stop_on_all_unstopped_threads (false),
- skip_stop_request_tids ()
- {
- }
-
- PendingNotification (lldb::tid_t triggering_tid,
- const StopThreadFunction &request_thread_stop_function,
- const ThreadIDFunction &call_after_function,
- const ErrorFunction &error_function) :
- triggering_tid (triggering_tid),
- wait_for_stop_tids (),
- original_wait_for_stop_tids (),
- request_thread_stop_function (request_thread_stop_function),
- call_after_function (call_after_function),
- error_function (error_function),
- request_stop_on_all_unstopped_threads (true),
- skip_stop_request_tids ()
- {
- }
-
- PendingNotification (lldb::tid_t triggering_tid,
- const StopThreadFunction &request_thread_stop_function,
- const ThreadIDFunction &call_after_function,
- const ThreadIDSet &skip_stop_request_tids,
- const ErrorFunction &error_function) :
- triggering_tid (triggering_tid),
- wait_for_stop_tids (),
- original_wait_for_stop_tids (),
- request_thread_stop_function (request_thread_stop_function),
- call_after_function (call_after_function),
- error_function (error_function),
- request_stop_on_all_unstopped_threads (true),
- skip_stop_request_tids (skip_stop_request_tids)
- {
- }
-
- const lldb::tid_t triggering_tid;
- ThreadIDSet wait_for_stop_tids;
- const ThreadIDSet original_wait_for_stop_tids;
- StopThreadFunction request_thread_stop_function;
- ThreadIDFunction call_after_function;
- ErrorFunction error_function;
- const bool request_stop_on_all_unstopped_threads;
- ThreadIDSet skip_stop_request_tids;
- };
- typedef std::unique_ptr<PendingNotification> PendingNotificationUP;
-
- // Fire pending notification if no pending thread stops remain.
- void SignalIfRequirementsSatisfied();
-
- bool
- RequestStopOnAllSpecifiedThreads();
-
- void
- RequestStopOnAllRunningThreads();
-
- void
- RequestThreadStop (lldb::tid_t tid, ThreadContext& context);
-
- std::mutex m_event_mutex; // Serializes execution of TSC operations.
-
- void
- ThreadDidStop (lldb::tid_t tid, bool initiated_by_llgs, const ErrorFunction &error_function);
-
- void
- DoResume(lldb::tid_t tid, ResumeThreadFunction request_thread_resume_function,
- ErrorFunction error_function, bool error_when_already_running);
-
- void
- DoCallAfterThreadsStop(PendingNotificationUP &&notification_up);
-
- void
- ThreadWasCreated (lldb::tid_t tid, bool is_stopped, const ErrorFunction &error_function);
-
- void
- ThreadDidDie (lldb::tid_t tid, const ErrorFunction &error_function);
-
- bool
- IsKnownThread(lldb::tid_t tid) const;
-
- void
- Log (const char *format, ...);
-
- // Member variables.
- LogFunction m_log_function;
- PendingNotificationUP m_pending_notification_up;
-
- // Maps known TIDs to ThreadContext.
- TIDContextMap m_tid_map;
-
- bool m_log_event_processing;
- };
-
-} // namespace process_linux
-} // namespace lldb_private
-
-#endif
OpenPOWER on IntegriCloud