Index: lldb.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -85,7 +85,6 @@ 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, ); }; }; @@ -1220,8 +1219,6 @@ 23AB0530199FF639003B8084 /* ProcessFreeBSD.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProcessFreeBSD.h; sourceTree = ""; }; 23AB0531199FF639003B8084 /* ProcessMonitor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ProcessMonitor.cpp; sourceTree = ""; }; 23AB0532199FF639003B8084 /* ProcessMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProcessMonitor.h; sourceTree = ""; }; - 23B6FF4119D38D48004CC8C3 /* ThreadStateCoordinator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ThreadStateCoordinator.h; sourceTree = ""; }; - 23CDD8E819D3E13200461DDC /* ThreadStateCoordinator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadStateCoordinator.cpp; sourceTree = ""; }; 23DDF224196C3EE600BB8417 /* CommandOptionValidators.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommandOptionValidators.cpp; path = source/Interpreter/CommandOptionValidators.cpp; sourceTree = ""; }; 23EDE3301926839700F6A132 /* NativeRegisterContext.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = NativeRegisterContext.cpp; path = source/Host/common/NativeRegisterContext.cpp; sourceTree = ""; }; 23EDE3311926843600F6A132 /* NativeRegisterContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NativeRegisterContext.h; path = include/lldb/Host/common/NativeRegisterContext.h; sourceTree = ""; }; @@ -2915,8 +2912,6 @@ 233B009319610B1F0090E598 /* ProcessMonitor.h */, 233B00A2196113730090E598 /* ProcFileReader.h */, 233B00A1196113730090E598 /* ProcFileReader.cpp */, - 23B6FF4119D38D48004CC8C3 /* ThreadStateCoordinator.h */, - 23CDD8E819D3E13200461DDC /* ThreadStateCoordinator.cpp */, ); path = Linux; sourceTree = ""; @@ -6336,7 +6331,6 @@ 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 */, Index: source/Plugins/Process/Linux/CMakeLists.txt =================================================================== --- source/Plugins/Process/Linux/CMakeLists.txt +++ source/Plugins/Process/Linux/CMakeLists.txt @@ -15,6 +15,5 @@ ProcessLinux.cpp ProcessMonitor.cpp ProcFileReader.cpp - ThreadStateCoordinator.cpp ) Index: source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.h +++ source/Plugins/Process/Linux/NativeProcessLinux.h @@ -15,6 +15,8 @@ #include // C++ Includes +#include +#include #include // Other libraries and framework includes @@ -33,8 +35,6 @@ class Scalar; namespace process_linux { - class ThreadStateCoordinator; - /// @class NativeProcessLinux /// @brief Manages communication with the inferior (debugee) process. /// @@ -187,8 +187,6 @@ std::vector m_mem_region_cache; Mutex m_mem_region_cache_mutex; - std::unique_ptr m_coordinator_up; - // List of thread ids stepping with a breakpoint with the address of // the relevan breakpoint std::map m_threads_stepping_with_breakpoint; @@ -375,6 +373,225 @@ Error RequestThreadStop (const lldb::pid_t pid, const lldb::tid_t tid); + + public: + + // Typedefs. + typedef std::unordered_set ThreadIDSet; + + // Callback/block definitions. + typedef std::function ThreadIDFunction; + typedef std::function LogFunction; + typedef std::function ErrorFunction; + typedef std::function StopThreadFunction; + typedef std::function ResumeThreadFunction; + + // 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 TIDContextMap; + + struct PendingNotification + { + public: + 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; + }; + + // 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(std::unique_ptr &¬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; + std::unique_ptr m_pending_notification_up; + + // Maps known TIDs to ThreadContext. + TIDContextMap m_tid_map; + + bool m_log_event_processing; }; } // namespace process_linux Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -20,6 +20,7 @@ // C++ Includes #include +#include #include // Other libraries and framework includes @@ -49,7 +50,6 @@ #include "NativeThreadLinux.h" #include "ProcFileReader.h" #include "Procfs.h" -#include "ThreadStateCoordinator.h" // System includes - They have to be included after framework includes because they define some // macros which collide with variable names in other modules @@ -153,7 +153,7 @@ return signals; } - ThreadStateCoordinator::LogFunction + NativeProcessLinux::LogFunction GetThreadLoggerFunction () { return [](const char *format, va_list args) @@ -170,7 +170,7 @@ 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 @@ -1632,7 +1632,9 @@ 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) { } @@ -2310,7 +2312,7 @@ new_thread_sp = AddThread(tid); std::static_pointer_cast (new_thread_sp)->SetRunning (); Resume (tid, LLDB_INVALID_SIGNAL_NUMBER); - m_coordinator_up->NotifyThreadCreate (tid, false, CoordinatorErrorHandler); + NotifyThreadCreate (tid, false, CoordinatorErrorHandler); } void @@ -2368,7 +2370,7 @@ 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) @@ -2454,13 +2456,13 @@ } const int signo = static_cast (data); - m_coordinator_up->RequestThreadResume (pid, - [=](lldb::tid_t tid_to_resume, bool supress_signal) - { - std::static_pointer_cast (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 (thread_sp)->SetRunning (); + return Resume (tid_to_resume, (supress_signal) ? LLDB_INVALID_SIGNAL_NUMBER : signo); + }, + CoordinatorErrorHandler); break; } @@ -2505,13 +2507,13 @@ // 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 (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 (thread_sp)->SetRunning (); + return Resume (tid_to_resume, LLDB_INVALID_SIGNAL_NUMBER); + }, + CoordinatorErrorHandler); break; default: @@ -2689,7 +2691,7 @@ // We can now resume the newly created thread. std::static_pointer_cast (thread_sp)->SetRunning (); Resume (pid, LLDB_INVALID_SIGNAL_NUMBER); - m_coordinator_up->NotifyThreadCreate (pid, false, CoordinatorErrorHandler); + NotifyThreadCreate (pid, false, CoordinatorErrorHandler); // Done handling. return; } @@ -2723,7 +2725,7 @@ // 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 { @@ -2773,14 +2775,14 @@ // 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 (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 (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: @@ -3055,17 +3057,17 @@ { // 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 (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 (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; } @@ -3073,23 +3075,23 @@ { // 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 (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 (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; } @@ -4222,19 +4224,19 @@ 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 @@ -4246,7 +4248,7 @@ 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); @@ -4265,14 +4267,11 @@ 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 @@ -4332,3 +4331,561 @@ 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 ("EventRequestResume::%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 ("EventRequestResume::%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 ("EventRequestResume::%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 ("EventRequestResume::%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 ("EventRequestResume::%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 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(std::unique_ptr(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 lock(m_event_mutex); + + if (m_log_event_processing) + { + TSCLog ("NativeProcessLinux::%s about to process event: (triggering_tid: %" PRIu64 ")", + __FUNCTION__, triggering_tid); + } + + DoCallAfterThreadsStop(std::unique_ptr(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 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(std::unique_ptr(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 ("EventCallAfterThreadsStop::%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(std::unique_ptr &¬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 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 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 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 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 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 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 (); +} Index: source/Plugins/Process/Linux/ThreadStateCoordinator.h =================================================================== --- source/Plugins/Process/Linux/ThreadStateCoordinator.h +++ /dev/null @@ -1,253 +0,0 @@ -//===-- ThreadStateCoordinator.h --------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef lldb_ThreadStateCoordinator_h -#define lldb_ThreadStateCoordinator_h - -#include -#include -#include -#include - -#include "lldb/lldb-types.h" - -#include "lldb/Core/Error.h" - -namespace lldb_private { -namespace process_linux { - - class ThreadStateCoordinator - { - public: - - // Typedefs. - typedef std::unordered_set ThreadIDSet; - - // Callback/block definitions. - typedef std::function ThreadIDFunction; - typedef std::function LogFunction; - typedef std::function ErrorFunction; - typedef std::function StopThreadFunction; - typedef std::function 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 TIDContextMap; - - struct PendingNotification - { - public: - 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; - }; - - // 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(std::unique_ptr &¬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; - std::unique_ptr 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 Index: source/Plugins/Process/Linux/ThreadStateCoordinator.cpp =================================================================== --- source/Plugins/Process/Linux/ThreadStateCoordinator.cpp +++ /dev/null @@ -1,590 +0,0 @@ -//===-- ThreadStateCoordinator.cpp ------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - - -#if !defined (__STDC_FORMAT_MACROS) -#define __STDC_FORMAT_MACROS 1 -#endif - -#include - -#include "ThreadStateCoordinator.h" -#include -#include -#include - -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 ("EventRequestResume::%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 ("EventRequestResume::%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 ("EventRequestResume::%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 ("EventRequestResume::%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 ("EventRequestResume::%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 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(std::unique_ptr(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 lock(m_event_mutex); - - if (m_log_event_processing) - { - Log ("ThreadStateCoordinator::%s about to process event: (triggering_tid: %" PRIu64 ")", - __FUNCTION__, triggering_tid); - } - - DoCallAfterThreadsStop(std::unique_ptr(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 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(std::unique_ptr(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 ("EventCallAfterThreadsStop::%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(std::unique_ptr &¬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 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 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 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 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 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 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 (); -} Index: unittests/CMakeLists.txt =================================================================== --- unittests/CMakeLists.txt +++ unittests/CMakeLists.txt @@ -29,5 +29,4 @@ add_subdirectory(Host) add_subdirectory(Interpreter) -add_subdirectory(Plugins) add_subdirectory(Utility) Index: unittests/Plugins/CMakeLists.txt =================================================================== --- unittests/Plugins/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(Process) Index: unittests/Plugins/Process/CMakeLists.txt =================================================================== --- unittests/Plugins/Process/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -if (CMAKE_SYSTEM_NAME MATCHES "Linux") - add_subdirectory(Linux) -endif() Index: unittests/Plugins/Process/Linux/CMakeLists.txt =================================================================== --- unittests/Plugins/Process/Linux/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_lldb_unittest(ProcessLinuxTests - ThreadStateCoordinatorTest.cpp - ) Index: unittests/Plugins/Process/Linux/Makefile =================================================================== --- unittests/Plugins/Process/Linux/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -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 Index: unittests/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp =================================================================== --- unittests/Plugins/Process/Linux/ThreadStateCoordinatorTest.cpp +++ /dev/null @@ -1,714 +0,0 @@ -#include -#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 ()); -}