diff options
Diffstat (limited to 'lldb/source/Target/ThreadList.cpp')
-rw-r--r-- | lldb/source/Target/ThreadList.cpp | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp new file mode 100644 index 00000000000..6bc22712d10 --- /dev/null +++ b/lldb/source/Target/ThreadList.cpp @@ -0,0 +1,460 @@ +//===-- ThreadList.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include <stdlib.h> + +#include <algorithm> + +#include "lldb/Target/ThreadList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +ThreadList::ThreadList (Process *process) : + m_process (process), + m_stop_id (0), + m_threads(), + m_threads_mutex (Mutex::eMutexTypeRecursive), + m_current_tid (LLDB_INVALID_THREAD_ID) +{ +} + +ThreadList::ThreadList (const ThreadList &rhs) : + m_process (), + m_stop_id (), + m_threads (), + m_threads_mutex (Mutex::eMutexTypeRecursive), + m_current_tid () +{ + // Use the assignment operator since it uses the mutex + *this = rhs; +} + +const ThreadList& +ThreadList::operator = (const ThreadList& rhs) +{ + if (this != &rhs) + { + // Lock both mutexes to make sure neither side changes anyone on us + // while the assignement occurs + Mutex::Locker locker_this(m_threads_mutex); + Mutex::Locker locker_rhs(rhs.m_threads_mutex); + m_process = rhs.m_process; + m_stop_id = rhs.m_stop_id; + m_threads = rhs.m_threads; + m_current_tid = rhs.m_current_tid; + } + return *this; +} + + +ThreadList::~ThreadList() +{ +} + + +uint32_t +ThreadList::GetStopID () const +{ + return m_stop_id; +} + +void +ThreadList::SetStopID (uint32_t stop_id) +{ + m_stop_id = stop_id; +} + + +void +ThreadList::AddThread (ThreadSP &thread_sp) +{ + Mutex::Locker locker(m_threads_mutex); + m_threads.push_back(thread_sp); +} + +uint32_t +ThreadList::GetSize (bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + if (can_update) + m_process->UpdateThreadListIfNeeded(); + return m_threads.size(); +} + +ThreadSP +ThreadList::GetThreadAtIndex (uint32_t idx, bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + if (idx < m_threads.size()) + thread_sp = m_threads[idx]; + return thread_sp; +} + +ThreadSP +ThreadList::FindThreadByID (lldb::tid_t tid, bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetID() == tid) + { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +ThreadSP +ThreadList::GetThreadSPForThreadPtr (Thread *thread_ptr) +{ + ThreadSP thread_sp; + if (thread_ptr) + { + Mutex::Locker locker(m_threads_mutex); + + uint32_t idx = 0; + const uint32_t num_threads = m_threads.size(); + for (idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx].get() == thread_ptr) + { + thread_sp = m_threads[idx]; + break; + } + } + } + return thread_sp; +} + + + +ThreadSP +ThreadList::FindThreadByIndexID (uint32_t index_id, bool can_update) +{ + Mutex::Locker locker(m_threads_mutex); + + if (can_update) + m_process->UpdateThreadListIfNeeded(); + + ThreadSP thread_sp; + const uint32_t num_threads = m_threads.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + if (m_threads[idx]->GetIndexID() == index_id) + { + thread_sp = m_threads[idx]; + break; + } + } + return thread_sp; +} + +bool +ThreadList::ShouldStop (Event *event_ptr) +{ + Mutex::Locker locker(m_threads_mutex); + + // Running events should never stop, obviously... + + + bool should_stop = false; + m_process->UpdateThreadListIfNeeded(); + + collection::iterator pos, end = m_threads.end(); + + // Run through the threads and ask whether we should stop. Don't ask + // suspended threads, however, it makes more sense for them to preserve their + // state across the times the process runs but they don't get a chance to. + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if ((thread_sp->ThreadStoppedForAReason()) + && (thread_sp->GetResumeState () != eStateSuspended)) + { + should_stop |= thread_sp->ShouldStop(event_ptr); + } + } + if (should_stop) + { + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + thread_sp->WillStop (); + } + } + + return should_stop; +} + +Vote +ThreadList::ShouldReportStop (Event *event_ptr) +{ + Vote result = eVoteNoOpinion; + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + + // Run through the threads and ask whether we should report this event. + // For stopping, a YES vote wins over everything. A NO vote wins over NO opinion. + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp->ThreadStoppedForAReason() && (thread_sp->GetResumeState () != eStateSuspended)) + { + switch (thread_sp->ShouldReportStop (event_ptr)) + { + case eVoteNoOpinion: + continue; + case eVoteYes: + result = eVoteYes; + break; + case eVoteNo: + if (result == eVoteNoOpinion) + result = eVoteNo; + break; + } + } + } + return result; +} + +Vote +ThreadList::ShouldReportRun (Event *event_ptr) +{ + Vote result = eVoteNoOpinion; + m_process->UpdateThreadListIfNeeded(); + collection::iterator pos, end = m_threads.end(); + + // Run through the threads and ask whether we should report this event. + // The rule is NO vote wins over everything, a YES vote wins over no opinion. + + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp->GetResumeState () != eStateSuspended) + + switch (thread_sp->ShouldReportRun (event_ptr)) + { + case eVoteNoOpinion: + continue; + case eVoteYes: + if (result == eVoteNoOpinion) + result = eVoteYes; + break; + case eVoteNo: + result = eVoteNo; + break; + } + } + return result; +} + +void +ThreadList::Clear() +{ + m_stop_id = 0; + m_threads.clear(); + m_current_tid = LLDB_INVALID_THREAD_ID; +} + +void +ThreadList::RefreshStateAfterStop () +{ + Mutex::Locker locker(m_threads_mutex); + + m_process->UpdateThreadListIfNeeded(); + + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->RefreshStateAfterStop (); +} + +void +ThreadList::DiscardThreadPlans () +{ + // You don't need to update the thread list here, because only threads + // that you currently know about have any thread plans. + Mutex::Locker locker(m_threads_mutex); + + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->DiscardThreadPlans (true); + +} + +bool +ThreadList::WillResume () +{ + // Run through the threads and perform their momentary actions. + // But we only do this for threads that are running, user suspended + // threads stay where they are. + bool success = true; + + Mutex::Locker locker(m_threads_mutex); + m_process->UpdateThreadListIfNeeded(); + + collection::iterator pos, end = m_threads.end(); + + // Give all the threads a last chance to set up their state before we + // negotiate who is actually going to get a chance to run... + + for (pos = m_threads.begin(); pos != end; ++pos) + (*pos)->SetupForResume (); + + // Now go through the threads and see if any thread wants to run just itself. + // if so then pick one and run it. + ThreadList run_me_only_list (m_process); + + run_me_only_list.SetStopID(m_process->GetStopID()); + + ThreadSP immediate_thread_sp; + bool run_only_current_thread = false; + + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp->GetCurrentPlan()->IsImmediate()) + { + // We first do all the immediate plans, so if we find one, set + // immediate_thread_sp and break out, and we'll pick it up first thing + // when we're negotiating which threads get to run. + immediate_thread_sp = thread_sp; + break; + } + else if (thread_sp->GetResumeState() != eStateSuspended && + thread_sp->GetCurrentPlan()->StopOthers()) + { + // You can't say "stop others" and also want yourself to be suspended. + assert (thread_sp->GetCurrentPlan()->RunState() != eStateSuspended); + + if (thread_sp == GetCurrentThread()) + { + run_only_current_thread = true; + run_me_only_list.Clear(); + run_me_only_list.AddThread (thread_sp); + break; + } + + run_me_only_list.AddThread (thread_sp); + } + + } + + if (immediate_thread_sp) + { + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp.get() == immediate_thread_sp.get()) + thread_sp->WillResume(thread_sp->GetCurrentPlan()->RunState()); + else + thread_sp->WillResume (eStateSuspended); + } + } + else if (run_me_only_list.GetSize (false) == 0) + { + // Everybody runs as they wish: + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + thread_sp->WillResume(thread_sp->GetCurrentPlan()->RunState()); + } + } + else + { + ThreadSP thread_to_run; + + if (run_only_current_thread) + { + thread_to_run = GetCurrentThread(); + } + else if (run_me_only_list.GetSize (false) == 1) + { + thread_to_run = run_me_only_list.GetThreadAtIndex (0); + } + else + { + int random_thread = (int) + ((run_me_only_list.GetSize (false) * (double) rand ()) / (RAND_MAX + 1.0)); + thread_to_run = run_me_only_list.GetThreadAtIndex (random_thread); + } + + for (pos = m_threads.begin(); pos != end; ++pos) + { + ThreadSP thread_sp(*pos); + if (thread_sp == thread_to_run) + thread_sp->WillResume(thread_sp->GetCurrentPlan()->RunState()); + else + thread_sp->WillResume (eStateSuspended); + } + } + + return success; +} + +void +ThreadList::DidResume () +{ + collection::iterator pos, end = m_threads.end(); + for (pos = m_threads.begin(); pos != end; ++pos) + { + // Don't clear out threads that aren't going to get a chance to run, rather + // leave their state for the next time around. + ThreadSP thread_sp(*pos); + if (thread_sp->GetResumeState() != eStateSuspended) + thread_sp->DidResume (); + } +} + +ThreadSP +ThreadList::GetCurrentThread () +{ + Mutex::Locker locker(m_threads_mutex); + return FindThreadByID(m_current_tid); +} + +bool +ThreadList::SetCurrentThreadByID (lldb::tid_t tid) +{ + Mutex::Locker locker(m_threads_mutex); + if (FindThreadByID(tid).get()) + m_current_tid = tid; + else + m_current_tid = LLDB_INVALID_THREAD_ID; + + return m_current_tid != LLDB_INVALID_THREAD_ID; +} + +bool +ThreadList::SetCurrentThreadByIndexID (uint32_t index_id) +{ + Mutex::Locker locker(m_threads_mutex); + ThreadSP thread_sp (FindThreadByIndexID(index_id)); + if (thread_sp.get()) + m_current_tid = thread_sp->GetID(); + else + m_current_tid = LLDB_INVALID_THREAD_ID; + + return m_current_tid != LLDB_INVALID_THREAD_ID; +} + |