236124A41986B4E2004EFC37 /* IOObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 236124A21986B4E2004EFC37 /* IOObject.cpp */; };
236124A51986B4E2004EFC37 /* Socket.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 236124A31986B4E2004EFC37 /* Socket.cpp */; };
2377C2F819E613C100737875 /* PipePosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2377C2F719E613C100737875 /* PipePosix.cpp */; };
- 23CDD8FB19D47B3600461DDC /* ThreadStateCoordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23CDD8E819D3E13200461DDC /* ThreadStateCoordinator.cpp */; };
23DDF226196C3EE600BB8417 /* CommandOptionValidators.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23DDF224196C3EE600BB8417 /* CommandOptionValidators.cpp */; };
23EDE33319269E7C00F6A132 /* NativeRegisterContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 23EDE3301926839700F6A132 /* NativeRegisterContext.cpp */; };
23EFE389193D1ABC00E54E54 /* SBTypeEnumMember.h in Headers */ = {isa = PBXBuildFile; fileRef = 23EFE388193D1ABC00E54E54 /* SBTypeEnumMember.h */; settings = {ATTRIBUTES = (Public, ); }; };
23AB0530199FF639003B8084 /* ProcessFreeBSD.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProcessFreeBSD.h; sourceTree = "<group>"; };
23AB0531199FF639003B8084 /* ProcessMonitor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessMonitor.cpp; sourceTree = "<group>"; };
23AB0532199FF639003B8084 /* ProcessMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProcessMonitor.h; sourceTree = "<group>"; };
- 23B6FF4119D38D48004CC8C3 /* ThreadStateCoordinator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ThreadStateCoordinator.h; sourceTree = "<group>"; };
- 23CDD8E819D3E13200461DDC /* ThreadStateCoordinator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadStateCoordinator.cpp; sourceTree = "<group>"; };
23DDF224196C3EE600BB8417 /* CommandOptionValidators.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandOptionValidators.cpp; path = source/Interpreter/CommandOptionValidators.cpp; sourceTree = "<group>"; };
23EDE3301926839700F6A132 /* NativeRegisterContext.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = NativeRegisterContext.cpp; path = source/Host/common/NativeRegisterContext.cpp; sourceTree = "<group>"; };
23EDE3311926843600F6A132 /* NativeRegisterContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NativeRegisterContext.h; path = include/lldb/Host/common/NativeRegisterContext.h; sourceTree = "<group>"; };
233B009319610B1F0090E598 /* ProcessMonitor.h */,
233B00A2196113730090E598 /* ProcFileReader.h */,
233B00A1196113730090E598 /* ProcFileReader.cpp */,
- 23B6FF4119D38D48004CC8C3 /* ThreadStateCoordinator.h */,
- 23CDD8E819D3E13200461DDC /* ThreadStateCoordinator.cpp */,
);
path = Linux;
sourceTree = "<group>";
AF26703B1852D01E00B6CC36 /* QueueList.cpp in Sources */,
267C012B136880DF006E963E /* OptionGroupValueObjectDisplay.cpp in Sources */,
26BCFC521368AE38006DC050 /* OptionGroupFormat.cpp in Sources */,
- 23CDD8FB19D47B3600461DDC /* ThreadStateCoordinator.cpp in Sources */,
AF81DEFA1828A23F0042CF19 /* SystemRuntime.cpp in Sources */,
267C01371368C49C006E963E /* OptionGroupOutputFile.cpp in Sources */,
260E07C6136FA69E00CF21D3 /* OptionGroupUUID.cpp in Sources */,
ProcessLinux.cpp
ProcessMonitor.cpp
ProcFileReader.cpp
- ThreadStateCoordinator.cpp
)
// C++ Includes
#include <fstream>
+#include <sstream>
#include <string>
// Other libraries and framework includes
#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
return signals;
}
- ThreadStateCoordinator::LogFunction
+ NativeProcessLinux::LogFunction
GetThreadLoggerFunction ()
{
return [](const char *format, va_list args)
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
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)
{
}
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
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)
}
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;
}
// 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:
// 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;
}
// 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
{
// 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:
{
// 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;
}
{
// 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;
}
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
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);
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
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 &¬ification_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 ();
+}
#include <signal.h>
// C++ Includes
+#include <mutex>
+#include <unordered_map>
#include <unordered_set>
// Other libraries and framework includes
class Scalar;
namespace process_linux {
- class ThreadStateCoordinator;
-
/// @class NativeProcessLinux
/// @brief Manages communication with the inferior (debugee) process.
///
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;
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 &¬ification_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
+++ /dev/null
-//===-- 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 &¬ification_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 ();
-}
+++ /dev/null
-//===-- 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 &¬ification_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
add_subdirectory(Host)
add_subdirectory(Interpreter)
-add_subdirectory(Plugins)
add_subdirectory(Utility)
+++ /dev/null
-add_subdirectory(Process)
+++ /dev/null
-if (CMAKE_SYSTEM_NAME MATCHES "Linux")
- add_subdirectory(Linux)
-endif()
+++ /dev/null
-add_lldb_unittest(ProcessLinuxTests
- ThreadStateCoordinatorTest.cpp
- )
+++ /dev/null
-THIS_FILE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))/
-
-LEVEL := $(realpath $(THIS_FILE_DIR)../../../../make)
-
-CFLAGS_EXTRAS := -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS
-ENABLE_THREADS := YES
-CXX_SOURCES := $(wildcard *.cpp) \
- $(realpath $(LEVEL)/../../source/Plugins/Process/Linux/ThreadStateCoordinator.cpp) \
- $(realpath $(LEVEL)/../../source/Core/Error.cpp)
-MAKE_DSYM := NO
-
-OS := $(shell uname -s)
-
-# $(info OS $(OS))
-ifeq ($(OS),Linux)
- LD_EXTRAS := -lncurses -ldl
-endif
-
-include $(LEVEL)/Makefile.rules
+++ /dev/null
-#include <limits.h>
-#include "gtest/gtest.h"
-
-#include "lldb/Core/Error.h"
-#include "Plugins/Process/Linux/ThreadStateCoordinator.h"
-
-using namespace lldb_private;
-using namespace process_linux;
-
-namespace
-{
- const ThreadStateCoordinator::ThreadIDSet EMPTY_THREAD_ID_SET;
-
- void
- StdoutLogger (const char *format, va_list args)
- {
- // Print to stdout.
- vprintf (format, args);
- printf ("\n");
- }
-
- // These are single-line macros so that IDE integration of gtest results puts
- // the error markers on the correct failure point within the gtest.
-
-#define ASSERT_PROCESS_EVENT_SUCCEEDED() do { \
-if (HasError ()) { printf ("unexpected error in processing of event, error: %s\n", m_error_string.c_str ()); } \
-ASSERT_EQ (false, HasError ()); } while(0)
-
-#define ASSERT_PROCESS_EVENT_FAILED() ASSERT_EQ (true, HasError ())
-
- class ThreadStateCoordinatorTest: public ::testing::Test
- {
- protected:
- // Constants.
- const lldb::tid_t TRIGGERING_TID = 4105;
- const lldb::tid_t PENDING_STOP_TID = 3;
- const lldb::tid_t PENDING_STOP_TID_02 = 29016;
- const lldb::tid_t NEW_THREAD_TID = 1234;
-
- // Member variables.
- bool m_error_called = false;
- std::string m_error_string;
-
- ThreadStateCoordinator m_coordinator;
-
- bool m_deferred_notification_called;
- lldb::tid_t m_deferred_notification_tid;
-
- ThreadStateCoordinator::ThreadIDSet m_requested_stop_tids;
-
- // Constructors.
- ThreadStateCoordinatorTest () :
- m_error_called (false),
- m_error_string (),
- m_coordinator (StdoutLogger),
- m_deferred_notification_called (false),
- m_deferred_notification_tid (0),
- m_requested_stop_tids ()
- {
- }
-
- // Member functions.
-
- // Error handling.
- ThreadStateCoordinator::ErrorFunction
- GetErrorFunction ()
- {
- return [this] (const std::string &error_string)
- {
- m_error_called = true;
- m_error_string = error_string;
- printf ("received error: %s (test might be expecting)\n", error_string.c_str ());
- };
- }
-
- bool
- HasError () const
- {
- return m_error_called;
- }
-
- // Deferred notification reception.
- ThreadStateCoordinator::ThreadIDFunction
- GetDeferredStopNotificationFunction ()
- {
- return [this] (lldb::tid_t triggered_tid)
- {
- m_deferred_notification_called = true;
- m_deferred_notification_tid = triggered_tid;
- };
- }
-
- bool
- DidFireDeferredNotification () const
- {
- return m_deferred_notification_called;
- }
-
- lldb::tid_t
- GetDeferredNotificationTID () const
- {
- return m_deferred_notification_tid;
- }
-
- // Stop request call reception.
- ThreadStateCoordinator::StopThreadFunction
- GetStopRequestFunction ()
- {
- return [this] (lldb::tid_t stop_tid)
- {
- m_requested_stop_tids.insert (stop_tid);
- return Error();
- };
- }
-
- ThreadStateCoordinator::ThreadIDSet::size_type
- GetRequestedStopCount () const
- {
- return m_requested_stop_tids.size();
- }
-
- ThreadStateCoordinator::ResumeThreadFunction
- GetResumeThreadFunction (lldb::tid_t& resumed_tid, int& resume_call_count)
- {
- return [this, &resumed_tid, &resume_call_count] (lldb::tid_t tid, bool)
- {
- resumed_tid = tid;
- ++resume_call_count;
- return Error();
- };
- }
-
- bool
- DidRequestStopForTid (lldb::tid_t tid)
- {
- return m_requested_stop_tids.find (tid) != m_requested_stop_tids.end ();
- }
-
- // Test state initialization helpers.
- void
- SetupKnownRunningThread (lldb::tid_t tid)
- {
- NotifyThreadCreate (tid, false);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
- }
-
- void
- SetupKnownStoppedThread (lldb::tid_t tid)
- {
- NotifyThreadCreate (tid, true);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
- }
-
- // Convenience wrappers for ThreadStateCoordinator, using defaults for expected arguments
- // that plug into the test case handlers.
- void
- CallAfterThreadsStop (lldb::tid_t deferred_tid,
- const ThreadStateCoordinator::ThreadIDSet &pending_stop_wait_tids)
- {
- m_coordinator.CallAfterThreadsStop (deferred_tid,
- pending_stop_wait_tids,
- GetStopRequestFunction (),
- GetDeferredStopNotificationFunction (),
- GetErrorFunction ());
- }
-
- void
- CallAfterRunningThreadsStop (lldb::tid_t deferred_tid)
- {
- m_coordinator.CallAfterRunningThreadsStop (deferred_tid,
- GetStopRequestFunction (),
- GetDeferredStopNotificationFunction (),
- GetErrorFunction ());
- }
-
- void
- NotifyThreadCreate (lldb::tid_t stopped_tid, bool thread_is_stopped)
- {
- m_coordinator.NotifyThreadCreate (stopped_tid, thread_is_stopped, GetErrorFunction ());
- }
-
- void
- NotifyThreadStop (lldb::tid_t stopped_tid)
- {
- m_coordinator.NotifyThreadStop (stopped_tid, false, GetErrorFunction ());
- }
-
- void
- NotifyThreadDeath (lldb::tid_t tid)
- {
- m_coordinator.NotifyThreadDeath (tid, GetErrorFunction ());
- }
- };
-}
-
-TEST_F (ThreadStateCoordinatorTest, NotifyThreadCreateSignalsErrorOnAlreadyKnownThread)
-{
- // Let the coordinator know about our thread.
- SetupKnownStoppedThread (TRIGGERING_TID);
-
- // Notify the thread was created - again.
- NotifyThreadCreate (TRIGGERING_TID, true);
-
- // This should error out.
- ASSERT_PROCESS_EVENT_FAILED ();
-}
-
-
-TEST_F (ThreadStateCoordinatorTest, NotifyThreadDeathSignalsErrorOnUnknownThread)
-{
- const lldb::tid_t UNKNOWN_TID = 678;
-
- // Notify an unknown thread has died.
- NotifyThreadDeath (UNKNOWN_TID);
-
- // This should error out.
- ASSERT_PROCESS_EVENT_FAILED ();
-}
-
-TEST_F (ThreadStateCoordinatorTest, NotifyThreadStopSignalsErrorOnUnknownThread)
-{
- const lldb::tid_t UNKNOWN_TID = 678;
-
- // Notify an unknown thread has stopped.
- NotifyThreadStop (UNKNOWN_TID);
- ASSERT_PROCESS_EVENT_FAILED ();
-}
-
-TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownDeferredThread)
-{
- const lldb::tid_t UNKNOWN_TRIGGER_TID = 678;
-
- // Defer notify for an unknown thread.
- CallAfterThreadsStop (UNKNOWN_TRIGGER_TID,
- EMPTY_THREAD_ID_SET);
-
- // Shouldn't have fired yet.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Event should fail because trigger tid is unknown.
- ASSERT_PROCESS_EVENT_FAILED ();
-
- // Shouldn't have fired due to error.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, CallAfterTheadsStopSignalsErrorOnUnknownPendingStopThread)
-{
- // Let the coordinator know about our thread.
- SetupKnownStoppedThread (TRIGGERING_TID);
-
- // Defer notify for an unknown thread.
- const lldb::tid_t UNKNOWN_PENDING_STOP_TID = 7890;
- ThreadStateCoordinator::ThreadIDSet pending_stop_tids { UNKNOWN_PENDING_STOP_TID };
-
- CallAfterThreadsStop (TRIGGERING_TID,
- pending_stop_tids);
-
- // Shouldn't have fired yet.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Event should fail because trigger tid is unknown.
- ASSERT_PROCESS_EVENT_FAILED ();
-
- // Shouldn't have triggered deferred notification due to error.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Shouldn't have triggered stop request due to unknown tid.
- ASSERT_EQ (0u, GetRequestedStopCount ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenNoPendingStops)
-{
- // Let the coordinator know about our thread.
- SetupKnownStoppedThread (TRIGGERING_TID);
-
- // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
- CallAfterThreadsStop (TRIGGERING_TID,
- EMPTY_THREAD_ID_SET);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // The trigger should have fired, since there were no threads that needed to first stop.
- ASSERT_EQ (true, DidFireDeferredNotification ());
- ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingStop)
-{
- // Let the coordinator know about our thread.
- SetupKnownStoppedThread (TRIGGERING_TID);
-
- // Let the coordinator know about a currently-running thread we'll wait on.
- SetupKnownRunningThread (PENDING_STOP_TID);
-
- ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };
-
- // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
- CallAfterThreadsStop (TRIGGERING_TID,
- pending_stop_tids);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // The request thread stop should have been called for the pending stop.
- ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
-
- // But we still shouldn't have the deferred signal call go off yet. Need to wait for the stop to be reported.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Now report the that the pending stop occurred.
- NotifyThreadStop (PENDING_STOP_TID);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // Deferred signal notification should have fired now.
- ASSERT_EQ (true, DidFireDeferredNotification ());
- ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingStops)
-{
- // Setup threads.
- SetupKnownStoppedThread (TRIGGERING_TID);
- SetupKnownRunningThread (PENDING_STOP_TID);
- SetupKnownRunningThread (PENDING_STOP_TID_02);
-
- ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID, PENDING_STOP_TID_02 };
-
- // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
- CallAfterThreadsStop (TRIGGERING_TID,
- pending_stop_tids);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // The request thread stops should have been called for the pending stop tids.
- ASSERT_EQ (2u, GetRequestedStopCount ());
- ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
- ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02));
-
- // But we still shouldn't have the deferred signal call go off yet. Need to wait for the stop to be reported.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Report the that the first pending stop occurred.
- NotifyThreadStop (PENDING_STOP_TID);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // Shouldn't take effect until after both pending threads are notified.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Report the that the first pending stop occurred.
- NotifyThreadStop (PENDING_STOP_TID_02);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // Deferred signal notification should have fired now.
- ASSERT_EQ (true, DidFireDeferredNotification ());
- ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenPendingAlreadyStopped)
-{
- // Setup threads.
- SetupKnownStoppedThread (TRIGGERING_TID);
- SetupKnownRunningThread (PENDING_STOP_TID);
-
- ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };
-
- // Tell m_coordinator the pending stop tid is already stopped.
- NotifyThreadStop (PENDING_STOP_TID);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
- CallAfterThreadsStop (TRIGGERING_TID,
- pending_stop_tids);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // The pending stop should *not* fire because the m_coordinator knows it has already stopped.
- ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));
-
- // The deferred signal notification should have fired since all requirements were met.
- ASSERT_EQ (true, DidFireDeferredNotification ());
- ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenTwoPendingOneAlreadyStopped)
-{
- SetupKnownStoppedThread (TRIGGERING_TID);
- SetupKnownRunningThread (PENDING_STOP_TID);
- SetupKnownRunningThread (PENDING_STOP_TID_02);
-
- ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID, PENDING_STOP_TID_02 };
-
- // Tell coordinator the pending stop tid is already stopped.
- NotifyThreadStop (PENDING_STOP_TID);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
- CallAfterThreadsStop (TRIGGERING_TID,
- pending_stop_tids);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // The pending stop should only fire for one of the threads, the one that wasn't already stopped.
- ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02));
- ASSERT_EQ (false, DidRequestStopForTid (PENDING_STOP_TID));
-
- // The deferred signal notification should not yet have fired since all pending thread stops have not yet occurred.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Notify final thread has stopped.
- NotifyThreadStop (PENDING_STOP_TID_02);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // The deferred signal notification should have fired since all requirements were met.
- ASSERT_EQ (true, DidFireDeferredNotification ());
- ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, CallAfterThreadsStopFiresWhenOnePendingThreadDies)
-{
- SetupKnownStoppedThread (TRIGGERING_TID);
- SetupKnownRunningThread (PENDING_STOP_TID);
-
- ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };
-
- // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
- CallAfterThreadsStop (TRIGGERING_TID,
- pending_stop_tids);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // The request thread stop should have been called for the pending stop.
- ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
-
- // But we still shouldn't have the deferred signal call go off yet. Need to wait for the death to be reported.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Now report the that the thread with pending stop dies.
- NotifyThreadDeath (PENDING_STOP_TID);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // Deferred signal notification should have fired now.
- ASSERT_EQ (true, DidFireDeferredNotification ());
- ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, ExistingPendingNotificationRequiresStopFromNewThread)
-{
- SetupKnownStoppedThread (TRIGGERING_TID);
- SetupKnownRunningThread (PENDING_STOP_TID);
-
- ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_STOP_TID };
-
- // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
- CallAfterThreadsStop (TRIGGERING_TID,
- pending_stop_tids);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // Now the request thread stop should have been called for the pending stop.
- ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
-
- // But we still shouldn't have the deferred signal call go off yet.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Indicate a new thread has just been created.
- SetupKnownRunningThread (NEW_THREAD_TID);
-
- // We should have just received a stop request for the new thread id.
- ASSERT_EQ (2u, GetRequestedStopCount ());
- ASSERT_EQ (true, DidRequestStopForTid (NEW_THREAD_TID));
-
- // Now report the original pending tid stopped. This should no longer
- // trigger the pending notification because we should now require the
- // new thread to stop too.
- NotifyThreadStop (PENDING_STOP_TID);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Now notify the new thread stopped.
- NotifyThreadStop (NEW_THREAD_TID);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // Deferred signal notification should have fired now.
- ASSERT_EQ (true, DidFireDeferredNotification ());
- ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSignalsErrorOnUnknownThread)
-{
- const lldb::tid_t UNKNOWN_TID = 411;
-
- // Request a resume.
- lldb::tid_t resumed_tid = 0;
- int resume_call_count = 0;
-
- m_coordinator.RequestThreadResume (UNKNOWN_TID,
- GetResumeThreadFunction(resumed_tid, resume_call_count),
- GetErrorFunction ());
- // Shouldn't be called yet.
- ASSERT_EQ (0, resume_call_count);
-
- // Process next event. This should fail since the coordinator doesn't know about the thread.
- ASSERT_PROCESS_EVENT_FAILED ();
- ASSERT_EQ (0, resume_call_count);
-}
-
-TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeCallsCallbackWhenThreadIsStopped)
-{
- // Initialize thread to be in stopped state.
- SetupKnownStoppedThread (NEW_THREAD_TID);
-
- // Request a resume.
- lldb::tid_t resumed_tid = 0;
- int resume_call_count = 0;
-
- m_coordinator.RequestThreadResume (NEW_THREAD_TID,
- GetResumeThreadFunction(resumed_tid, resume_call_count),
- GetErrorFunction ());
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
- ASSERT_EQ (1, resume_call_count);
- ASSERT_EQ (NEW_THREAD_TID, resumed_tid);
-}
-
-TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSkipsCallbackOnSecondResumeAttempt)
-{
- // Initialize thread to be in stopped state.
- SetupKnownStoppedThread (NEW_THREAD_TID);
-
- // Request a resume.
- lldb::tid_t resumed_tid = 0;
- int resume_call_count = 0;
-
- m_coordinator.RequestThreadResume (NEW_THREAD_TID,
- GetResumeThreadFunction(resumed_tid, resume_call_count),
- GetErrorFunction ());
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
- ASSERT_EQ (1, resume_call_count);
- ASSERT_EQ (NEW_THREAD_TID, resumed_tid);
-
- // Make a second resume request.
- const int initial_resume_call_count = resume_call_count;
- m_coordinator.RequestThreadResume (NEW_THREAD_TID,
- GetResumeThreadFunction(resumed_tid, resume_call_count),
- GetErrorFunction ());
-
- // This should fail since the thread should already be running.
- ASSERT_PROCESS_EVENT_FAILED ();
-
- // And the resume count should not have increased.
- ASSERT_EQ (initial_resume_call_count, resume_call_count);
-}
-
-TEST_F (ThreadStateCoordinatorTest, RequestThreadResumeSignalsErrorOnAlreadyRunningThread)
-{
- const lldb::tid_t TEST_TID = 1234;
- SetupKnownRunningThread (NEW_THREAD_TID);
-
- // Request a resume.
- lldb::tid_t resumed_tid = 0;
- int resume_call_count = 0;
-
- m_coordinator.RequestThreadResume (TEST_TID,
- GetResumeThreadFunction(resumed_tid, resume_call_count),
- GetErrorFunction ());
-
- // Shouldn't be called yet.
- ASSERT_EQ (0, resume_call_count);
-
- // Process next event. Should be an error.
- ASSERT_PROCESS_EVENT_FAILED ();
-
- // The resume request should not have gone off because we think it is already running.
- ASSERT_EQ (0, resume_call_count);
-}
-
-TEST_F (ThreadStateCoordinatorTest, ResumedThreadAlreadyMarkedDoesNotHoldUpPendingStopNotification)
-{
- // We're going to test this scenario:
- // * Deferred notification waiting on two threads, A and B. A and B currently running.
- // * Thread A stops.
- // * Thread A resumes.
- // * Thread B stops.
- //
- // Here we could have forced A to stop again (after the Thread A resumes) because we had a pending stop nofication awaiting
- // all those threads to stop. However, we are going to explicitly not try to restop A - somehow
- // that seems wrong and possibly buggy since for that to happen, we would have intentionally called
- // a resume after the stop. Instead, we'll just log and indicate this looks suspicous. We can revisit
- // that decision after we see if/when that happens.
- const lldb::tid_t PENDING_TID_A = 2;
- const lldb::tid_t PENDING_TID_B = 89;
-
- SetupKnownStoppedThread (TRIGGERING_TID);
- SetupKnownRunningThread (PENDING_TID_A);
- SetupKnownRunningThread (PENDING_TID_B);
-
- ThreadStateCoordinator::ThreadIDSet pending_stop_tids { PENDING_TID_A, PENDING_TID_B };
-
- // Notify we have a trigger that needs to be fired when all threads in the wait tid set have stopped.
- CallAfterThreadsStop (TRIGGERING_TID,
- pending_stop_tids);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // Both TID A and TID B should have had stop requests made.
- ASSERT_EQ (2u, GetRequestedStopCount ());
- ASSERT_EQ (true, DidRequestStopForTid (PENDING_TID_A));
- ASSERT_EQ (true, DidRequestStopForTid (PENDING_TID_B));
-
- // But we still shouldn't have the deferred signal call go off yet.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Report thread A stopped.
- NotifyThreadStop (PENDING_TID_A);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Now report thread A is resuming. Ensure the resume is called.
- lldb::tid_t resumed_tid = 0;
- int resume_call_count = 0;
- m_coordinator.RequestThreadResume (PENDING_TID_A,
- GetResumeThreadFunction(resumed_tid, resume_call_count),
- GetErrorFunction ());
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
- ASSERT_EQ (1, resume_call_count);
- ASSERT_EQ (PENDING_TID_A, resumed_tid);
-
- // Report thread B stopped.
- NotifyThreadStop (PENDING_TID_B);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // After notifying thread b stopped, we now have thread a resumed but thread b stopped.
- // However, since thread a had stopped, we now have had both requirements stopped at some point.
- // For now we'll expect this will fire the pending deferred stop notification.
- ASSERT_EQ (true, DidFireDeferredNotification ());
- ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopFiresWhenNoRunningThreads)
-{
- // Let the coordinator know about our thread.
- SetupKnownStoppedThread (TRIGGERING_TID);
-
- // Notify we have a trigger that needs to be fired when all running threads have stopped.
- CallAfterRunningThreadsStop (TRIGGERING_TID);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // The trigger should have fired, since there were no threads that needed to first stop.
- ASSERT_EQ (true, DidFireDeferredNotification ());
- ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
-
- // And no stop requests should have been made.
- ASSERT_EQ (0u, GetRequestedStopCount ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopRequestsTwoPendingStops)
-{
- // Let the coordinator know about our threads.
- SetupKnownStoppedThread (TRIGGERING_TID);
- SetupKnownRunningThread (PENDING_STOP_TID);
- SetupKnownRunningThread (PENDING_STOP_TID_02);
-
- // Notify we have a trigger that needs to be fired when all running threads have stopped.
- CallAfterRunningThreadsStop (TRIGGERING_TID);
-
- // Notification trigger shouldn't go off yet.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Process next event. This will pick up the call after threads stop event.
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // We should have two stop requests for the two threads currently running.
- ASSERT_EQ (2u, GetRequestedStopCount ());
- ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
- ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID_02));
-
- // But the deferred stop notification should not have fired yet.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Now notify the two threads stopped.
- NotifyThreadStop (PENDING_STOP_TID);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- NotifyThreadStop (PENDING_STOP_TID_02);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // Now the trigger should have fired, since there were no threads that needed to first stop.
- ASSERT_EQ (true, DidFireDeferredNotification ());
- ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
-}
-
-TEST_F (ThreadStateCoordinatorTest, CallAfterRunningThreadsStopRequestsStopTwoOtherThreadsOneRunning)
-{
- // Let the coordinator know about our threads. PENDING_STOP_TID_02 will already be stopped.
- SetupKnownStoppedThread (TRIGGERING_TID);
- SetupKnownRunningThread (PENDING_STOP_TID);
- SetupKnownStoppedThread (PENDING_STOP_TID_02);
-
- // Notify we have a trigger that needs to be fired when all running threads have stopped.
- CallAfterRunningThreadsStop (TRIGGERING_TID);
-
- // Notification trigger shouldn't go off yet.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Process next event. This will pick up the call after threads stop event.
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // We should have two stop requests for the two threads currently running.
- ASSERT_EQ (1u, GetRequestedStopCount ());
- ASSERT_EQ (true, DidRequestStopForTid (PENDING_STOP_TID));
-
- // But the deferred stop notification should not have fired yet.
- ASSERT_EQ (false, DidFireDeferredNotification ());
-
- // Now notify the two threads stopped.
- NotifyThreadStop (PENDING_STOP_TID);
- ASSERT_PROCESS_EVENT_SUCCEEDED ();
-
- // Now the trigger should have fired, since there were no threads that needed to first stop.
- ASSERT_EQ (true, DidFireDeferredNotification ());
- ASSERT_EQ (TRIGGERING_TID, GetDeferredNotificationTID ());
-}