llvm-project/lldb/source/Target/ThreadList.cpp
Jason Molenda fb36a54ef6
[lldb] Rename formatv verbose log call, misc log cleanups [NFC] (#186951)
lldb had three preprocessor defines for logging,

LLDB_LOG  - formatv style argument
LLDB_LOGF - printf style argument
LLDB_LOGV - formatv style argument, only when verbose enabled

If you weren't looking at Log.h and the definition of these three, and
wanted to log something with formatv, it was easy to use LLDB_LOGV by
accident. We just had a situation where an important log statement
wasn't logging and it turned out to be this. This is fragile if you
aren't looking at the header directly, so I'd like to make this more
explicit. My proposal:

LLDB_LOG  - formatv style argument
LLDB_LOG_VERBOSE - formatv style argument, only when verbose enabled 
LLDB_LOGF - printf style argument
LLDB_LOGF_VERBOSE - printf style argument, only when verbose enabled

The new fouth one is to remove several places where we do `if (log &&
log->GetVerbose()) LLDB_LOGF (...)` in the sources today, and make both
styles consistent.

This PR implements that change, mechanically changing all LLDB_LOGV's to
LLDB_LOG_VERBOSE.

It also updates many of the `if (log && log->GetVerbose()) LLDB_LOGF`'s.
Some uses of this conditional expression do extra calculations in
addition to logging, and so those were left as-is so we're not doing
throwaway work when running without verbose logging.

There were many instances throughout lldb where callers are still doing
`if (log) LLDB_LOG*(...)`, a remnant of when all calls were to the `Log`
object's `Printf()` method, and you had to check if your local Log*
pointer was non-nullptr before calling the method. I removed those,
again keeping ones where work for logging is done in the block of code.

The code changes are all mechanical and uninteresting, but the question
of whether this naming change is widely agreed on is maybe worth
discussing.
2026-03-18 16:31:33 -07:00

970 lines
35 KiB
C++

//===-- ThreadList.cpp ----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include <cstdlib>
#include <algorithm>
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadList.h"
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanStepOverBreakpoint.h"
#include "lldb/Utility/LLDBAssert.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/State.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
using namespace lldb;
using namespace lldb_private;
ThreadList::ThreadList(Process &process)
: ThreadCollection(), m_process(process), m_stop_id(0),
m_selected_tid(LLDB_INVALID_THREAD_ID) {}
ThreadList::ThreadList(const ThreadList &rhs)
: ThreadCollection(), m_process(rhs.m_process), m_stop_id(rhs.m_stop_id),
m_selected_tid() {
// Use the assignment operator since it uses the mutex
*this = rhs;
}
const ThreadList &ThreadList::operator=(const ThreadList &rhs) {
if (this != &rhs) {
// We only allow assignments between thread lists describing the same
// process. Same process implies same mutex, which means it's enough to lock
// just the current object.
assert(&m_process == &rhs.m_process);
assert(&GetMutex() == &rhs.GetMutex());
std::lock_guard<std::recursive_mutex> guard(GetMutex());
m_stop_id = rhs.m_stop_id;
m_threads = rhs.m_threads;
m_selected_tid = rhs.m_selected_tid;
}
return *this;
}
ThreadList::~ThreadList() {
// Clear the thread list. Clear will take the mutex lock which will ensure
// that if anyone is using the list they won't get it removed while using it.
Clear();
}
lldb::ThreadSP ThreadList::GetExpressionExecutionThread() {
if (m_expression_tid_stack.empty())
return GetSelectedThread();
ThreadSP expr_thread_sp = FindThreadByID(m_expression_tid_stack.back());
if (expr_thread_sp)
return expr_thread_sp;
else
return GetSelectedThread();
}
void ThreadList::PushExpressionExecutionThread(lldb::tid_t tid) {
m_expression_tid_stack.push_back(tid);
}
void ThreadList::PopExpressionExecutionThread(lldb::tid_t tid) {
assert(m_expression_tid_stack.back() == tid);
m_expression_tid_stack.pop_back();
}
uint32_t ThreadList::GetStopID() const { return m_stop_id; }
void ThreadList::SetStopID(uint32_t stop_id) { m_stop_id = stop_id; }
uint32_t ThreadList::GetSize(bool can_update) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
if (can_update)
m_process.UpdateThreadListIfNeeded();
return m_threads.size();
}
ThreadSP ThreadList::GetThreadAtIndex(uint32_t idx, bool can_update) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
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) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
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::FindThreadByProtocolID(lldb::tid_t tid, bool can_update) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
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]->GetProtocolID() == tid) {
thread_sp = m_threads[idx];
break;
}
}
return thread_sp;
}
ThreadSP ThreadList::RemoveThreadByID(lldb::tid_t tid, bool can_update) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
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];
m_threads.erase(m_threads.begin() + idx);
break;
}
}
return thread_sp;
}
ThreadSP ThreadList::RemoveThreadByProtocolID(lldb::tid_t tid,
bool can_update) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
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]->GetProtocolID() == tid) {
thread_sp = m_threads[idx];
m_threads.erase(m_threads.begin() + idx);
break;
}
}
return thread_sp;
}
ThreadSP ThreadList::GetThreadSPForThreadPtr(Thread *thread_ptr) {
ThreadSP thread_sp;
if (thread_ptr) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
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) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
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) {
// Running events should never stop, obviously...
Log *log = GetLog(LLDBLog::Step);
// The ShouldStop method of the threads can do a whole lot of work, figuring
// out whether the thread plan conditions are met. So we don't want to keep
// the ThreadList locked the whole time we are doing this.
// FIXME: It is possible that running code could cause new threads
// to be created. If that happens, we will miss asking them whether they
// should stop. This is not a big deal since we haven't had a chance to hang
// any interesting operations on those threads yet.
collection threads_copy;
{
// Scope for locker
std::lock_guard<std::recursive_mutex> guard(GetMutex());
m_process.UpdateThreadListIfNeeded();
for (lldb::ThreadSP thread_sp : m_threads) {
// This is an optimization... If we didn't let a thread run in between
// the previous stop and this one, we shouldn't have to consult it for
// ShouldStop. So just leave it off the list we are going to inspect.
// If the thread didn't run but had work to do before declaring a public
// stop, then also include it.
// On Linux, if a thread-specific conditional breakpoint was hit, it won't
// necessarily be the thread that hit the breakpoint itself that
// evaluates the conditional expression, so the thread that hit the
// breakpoint could still be asked to stop, even though it hasn't been
// allowed to run since the previous stop.
if (thread_sp->GetTemporaryResumeState() != eStateSuspended ||
thread_sp->IsStillAtLastBreakpointHit()
|| thread_sp->ShouldRunBeforePublicStop())
threads_copy.push_back(thread_sp);
}
// It is possible the threads we were allowing to run all exited and then
// maybe the user interrupted or something, then fall back on looking at
// all threads:
if (threads_copy.size() == 0)
threads_copy = m_threads;
}
collection::iterator pos, end = threads_copy.end();
if (log) {
log->PutCString("");
LLDB_LOGF(log,
"ThreadList::%s: %" PRIu64 " threads, %" PRIu64
" unsuspended threads",
__FUNCTION__, (uint64_t)m_threads.size(),
(uint64_t)threads_copy.size());
}
bool did_anybody_stop_for_a_reason = false;
// If the event is an Interrupt event, then we're going to stop no matter
// what. Otherwise, presume we won't stop.
bool should_stop = false;
if (Process::ProcessEventData::GetInterruptedFromEvent(event_ptr)) {
LLDB_LOGF(
log, "ThreadList::%s handling interrupt event, should stop set to true",
__FUNCTION__);
should_stop = true;
}
// Now we run through all the threads and get their stop info's. We want to
// make sure to do this first before we start running the ShouldStop, because
// one thread's ShouldStop could destroy information (like deleting a thread
// specific breakpoint another thread had stopped at) which could lead us to
// compute the StopInfo incorrectly. We don't need to use it here, we just
// want to make sure it gets computed.
for (pos = threads_copy.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
thread_sp->GetStopInfo();
}
// If a thread needs to finish some job that can be done just on this thread
// before broadcastion the stop, it will signal that by returning true for
// ShouldRunBeforePublicStop. This variable gathers the results from that.
bool a_thread_needs_to_run = false;
for (pos = threads_copy.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
// We should never get a stop for which no thread had a stop reason, but
// sometimes we do see this - for instance when we first connect to a
// remote stub. In that case we should stop, since we can't figure out the
// right thing to do and stopping gives the user control over what to do in
// this instance.
//
// Note, this causes a problem when you have a thread specific breakpoint,
// and a bunch of threads hit the breakpoint, but not the thread which we
// are waiting for. All the threads that are not "supposed" to hit the
// breakpoint are marked as having no stop reason, which is right, they
// should not show a stop reason. But that triggers this code and causes
// us to stop seemingly for no reason.
//
// Since the only way we ever saw this error was on first attach, I'm only
// going to trigger set did_anybody_stop_for_a_reason to true unless this
// is the first stop.
//
// If this becomes a problem, we'll have to have another StopReason like
// "StopInfoHidden" which will look invalid everywhere but at this check.
if (thread_sp->GetProcess()->GetStopID() > 1)
did_anybody_stop_for_a_reason = true;
else
did_anybody_stop_for_a_reason |= thread_sp->ThreadStoppedForAReason();
const bool thread_should_stop = thread_sp->ShouldStop(event_ptr);
if (thread_should_stop)
should_stop |= true;
else {
bool this_thread_forces_run = thread_sp->ShouldRunBeforePublicStop();
a_thread_needs_to_run |= this_thread_forces_run;
if (this_thread_forces_run)
LLDB_LOG(log,
"ThreadList::{0} thread: {1:x}, "
"says it needs to run before public stop.",
__FUNCTION__, thread_sp->GetID());
}
}
if (a_thread_needs_to_run) {
should_stop = false;
} else if (!should_stop && !did_anybody_stop_for_a_reason) {
should_stop = true;
LLDB_LOGF(log,
"ThreadList::%s we stopped but no threads had a stop reason, "
"overriding should_stop and stopping.",
__FUNCTION__);
}
LLDB_LOGF(log, "ThreadList::%s overall should_stop = %i", __FUNCTION__,
should_stop);
if (should_stop) {
for (pos = threads_copy.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
thread_sp->WillStop();
}
}
return should_stop;
}
Vote ThreadList::ShouldReportStop(Event *event_ptr) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
Vote result = eVoteNoOpinion;
m_process.UpdateThreadListIfNeeded();
collection::iterator pos, end = m_threads.end();
Log *log = GetLog(LLDBLog::Step);
LLDB_LOGF(log, "ThreadList::%s %" PRIu64 " threads", __FUNCTION__,
(uint64_t)m_threads.size());
// 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. The exception is if a thread has work it needs to force before
// a public stop, which overrides everyone else's opinion:
for (pos = m_threads.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
if (thread_sp->ShouldRunBeforePublicStop()) {
LLDB_LOG(log, "Thread {0:x} has private business to complete, overrode "
"the should report stop.", thread_sp->GetID());
result = eVoteNo;
break;
}
const Vote vote = thread_sp->ShouldReportStop(event_ptr);
switch (vote) {
case eVoteNoOpinion:
continue;
case eVoteYes:
result = eVoteYes;
break;
case eVoteNo:
if (result == eVoteNoOpinion) {
result = eVoteNo;
} else {
LLDB_LOG(log,
"Thread {0:x} voted {1}, but lost out because result was {2}",
thread_sp->GetID(), vote, result);
}
break;
}
}
LLDB_LOG(log, "Returning {0}", result);
return result;
}
void ThreadList::SetShouldReportStop(Vote vote) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
m_process.UpdateThreadListIfNeeded();
collection::iterator pos, end = m_threads.end();
for (pos = m_threads.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
thread_sp->SetShouldReportStop(vote);
}
}
Vote ThreadList::ShouldReportRun(Event *event_ptr) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
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.
Log *log = GetLog(LLDBLog::Step);
for (pos = m_threads.begin(); pos != end; ++pos) {
if ((*pos)->GetResumeState() != eStateSuspended) {
switch ((*pos)->ShouldReportRun(event_ptr)) {
case eVoteNoOpinion:
continue;
case eVoteYes:
if (result == eVoteNoOpinion)
result = eVoteYes;
break;
case eVoteNo:
LLDB_LOGF(log,
"ThreadList::ShouldReportRun() thread %d (0x%4.4" PRIx64
") says don't report.",
(*pos)->GetIndexID(), (*pos)->GetID());
result = eVoteNo;
break;
}
}
}
return result;
}
void ThreadList::Clear() {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
m_stop_id = 0;
m_threads.clear();
m_selected_tid = LLDB_INVALID_THREAD_ID;
}
void ThreadList::Destroy() {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
const uint32_t num_threads = m_threads.size();
for (uint32_t idx = 0; idx < num_threads; ++idx) {
m_threads[idx]->DestroyThread();
}
}
void ThreadList::RefreshStateAfterStop() {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
m_process.UpdateThreadListIfNeeded();
LLDB_LOGF_VERBOSE(
GetLog(LLDBLog::Step),
"Turning off notification of new threads while single stepping "
"a thread.");
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.
std::lock_guard<std::recursive_mutex> guard(GetMutex());
collection::iterator pos, end = m_threads.end();
for (pos = m_threads.begin(); pos != end; ++pos)
(*pos)->DiscardThreadPlans(true);
}
bool ThreadList::WillResume(RunDirection &direction) {
// 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.
std::lock_guard<std::recursive_mutex> guard(GetMutex());
m_process.UpdateThreadListIfNeeded();
collection::iterator pos, end = m_threads.end();
// Clear tracking state from the previous stop and pop any leftover
// StepOverBreakpoint plans. This gives us a clean slate: plans will be
// recreated fresh by SetupToStepOverBreakpointIfNeeded below, and the
// batching logic will recompute deferred state from scratch.
m_threads_stepping_over_bp.clear();
for (const auto &thread_sp : m_threads) {
ThreadPlan *plan = thread_sp->GetCurrentPlan();
if (plan && plan->GetKind() == ThreadPlan::eKindStepOverBreakpoint) {
auto *bp_plan = static_cast<ThreadPlanStepOverBreakpoint *>(plan);
// Only pop plans created by our batching logic (deferred plans).
// Plans from the single-thread path must not be popped, as doing so
// would change the StopOthers scan result and cause other threads
// to lose their breakpoint stop reason.
if (bp_plan->GetDeferReenableBreakpointSite()) {
// Suppress the re-enable side effect in DidPop(), the breakpoint
// may still be disabled from the previous batch, and we don't want
// to toggle it. The new plans will handle re-enable correctly.
bp_plan->SetReenabledBreakpointSite();
thread_sp->DiscardPlan();
}
}
}
// Go through the threads and see if any thread wants to run just itself.
// if so then pick one and run it.
// Collect threads for batched vCont for multiple threads at the same
// breakpoint.
llvm::SmallVector<ThreadSP> batched_step_threads;
ThreadList run_me_only_list(m_process);
run_me_only_list.SetStopID(m_process.GetStopID());
// One or more threads might want to "Stop Others". We want to handle all
// those requests first. And if there is a thread that wanted to "resume
// before a public stop", let it get the first crack:
// There are two special kinds of thread that have priority for "StopOthers":
// a "ShouldRunBeforePublicStop thread, or the currently selected thread. If
// we find one satisfying that critereon, put it here.
ThreadSP thread_to_run;
for (pos = m_threads.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
if (thread_sp->GetResumeState() != eStateSuspended &&
thread_sp->GetCurrentPlan()->StopOthers()) {
if (thread_sp->IsOperatingSystemPluginThread() &&
!thread_sp->GetBackingThread())
continue;
// You can't say "stop others" and also want yourself to be suspended.
assert(thread_sp->GetCurrentPlan()->RunState() != eStateSuspended);
run_me_only_list.AddThread(thread_sp);
if (thread_sp == GetSelectedThread())
thread_to_run = thread_sp;
if (thread_sp->ShouldRunBeforePublicStop()) {
// This takes precedence, so if we find one of these, service it:
thread_to_run = thread_sp;
break;
}
}
}
if (run_me_only_list.GetSize(false) > 0 && !thread_to_run) {
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);
}
}
if (thread_to_run != nullptr) {
direction = thread_to_run->GetCurrentPlan()->GetDirection();
} else {
direction = m_process.GetBaseDirection();
}
// Give all the threads that are likely to run a last chance to set up their
// state before we negotiate who is actually going to get a chance to run...
// Don't set to resume suspended threads, and if any thread wanted to stop
// others, only call setup on the threads that request StopOthers...
if (thread_to_run != nullptr) {
// See if any thread wants to run stopping others. If it does, then we
// won't setup the other threads for resume, since they aren't going to get
// a chance to run. This is necessary because the SetupForResume might add
// "StopOthers" plans which would then get to be part of the who-gets-to-run
// negotiation, but they're coming in after the fact, and the threads that
// are already set up should take priority.
if (thread_to_run->SetupToStepOverBreakpointIfNeeded(direction)) {
// We only need to step over breakpoints when running forward, and the
// step-over-breakpoint plan itself wants to run forward, so this
// keeps our desired direction.
assert(thread_to_run->GetCurrentPlan()->GetDirection() == direction);
}
} else {
// Pre-scan to find all threads that need to step over a breakpoint,
// and group them by breakpoint address. This optimization allows us to
// step multiple threads over the same breakpoint with minimal breakpoint
// swaps, only the last thread in each group will re-enable the breakpoint.
llvm::DenseMap<lldb::addr_t, llvm::SmallVector<ThreadSP>> breakpoint_groups;
bool found_run_before_public_stop = false;
for (pos = m_threads.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
if (thread_sp->GetResumeState() != eStateSuspended) {
if (thread_sp->IsOperatingSystemPluginThread() &&
!thread_sp->GetBackingThread())
continue;
if (thread_sp->SetupToStepOverBreakpointIfNeeded(direction)) {
// We only need to step over breakpoints when running forward, and the
// step-over-breakpoint plan itself wants to run forward, so this
// keeps our desired direction.
assert(thread_sp->GetCurrentPlan()->GetDirection() == direction);
// You can't say "stop others" and also want yourself to be suspended.
assert(thread_sp->GetCurrentPlan()->RunState() != eStateSuspended);
// Get the breakpoint address from the step-over-breakpoint plan.
ThreadPlan *current_plan = thread_sp->GetCurrentPlan();
if (current_plan &&
current_plan->GetKind() == ThreadPlan::eKindStepOverBreakpoint) {
ThreadPlanStepOverBreakpoint *bp_plan =
static_cast<ThreadPlanStepOverBreakpoint *>(current_plan);
lldb::addr_t bp_addr = bp_plan->GetBreakpointLoadAddress();
breakpoint_groups[bp_addr].push_back(thread_sp);
}
thread_to_run = thread_sp;
if (thread_sp->ShouldRunBeforePublicStop()) {
// This takes precedence, so if we find one of these, service it:
found_run_before_public_stop = true;
break;
}
}
}
}
// Only apply batching optimization if we have a complete picture of
// breakpoint groups. If a ShouldRunBeforePublicStop thread caused the
// scan to exit early, the groups are incomplete and the priority thread
// must run solo. Deferred state will be cleaned up on next WillResume().
if (!found_run_before_public_stop) {
// For each group of threads at the same breakpoint, register them with
// ThreadList and set them to use deferred re-enable. The breakpoint will
// only be re-enabled when ALL threads have finished stepping over it.
// Also collect threads for batched vCont if multiple threads at same BP.
for (auto &group : breakpoint_groups) {
lldb::addr_t bp_addr = group.first;
llvm::SmallVector<ThreadSP> &threads = group.second;
if (threads.size() > 1) {
// Use tracking since multiple threads are stepping over the same
// breakpoint.
for (ThreadSP &thread_sp : threads) {
// Register this thread as stepping over the breakpoint.
RegisterThreadSteppingOverBreakpoint(bp_addr, thread_sp->GetID());
// Set the plan to defer re-enabling (use callback instead).
ThreadPlan *plan = thread_sp->GetCurrentPlan();
// Verify the plan is actually a StepOverBreakpoint plan.
if (plan &&
plan->GetKind() == ThreadPlan::eKindStepOverBreakpoint) {
ThreadPlanStepOverBreakpoint *bp_plan =
static_cast<ThreadPlanStepOverBreakpoint *>(plan);
bp_plan->SetDeferReenableBreakpointSite(true);
}
}
// Pick the largest group for batched vCont.
if (threads.size() > batched_step_threads.size())
batched_step_threads = threads;
}
// Keeps default behavior for a single thread at breakpoint.
}
// If we found a batch, use the first thread as thread_to_run.
if (!batched_step_threads.empty())
thread_to_run = batched_step_threads[0];
}
}
if (thread_to_run != nullptr) {
LLDB_LOGF_VERBOSE(GetLog(LLDBLog::Step),
"Turning on notification of new threads while single "
"stepping a thread.");
m_process.StartNoticingNewThreads();
} else {
LLDB_LOGF_VERBOSE(GetLog(LLDBLog::Step),
"Turning off notification of new threads while single "
"stepping a thread.");
m_process.StopNoticingNewThreads();
}
bool need_to_resume = true;
if (!batched_step_threads.empty()) {
// Batched stepping: all threads in the batch step together,
// all other threads stay suspended.
llvm::DenseSet<lldb::tid_t> batch_tids;
for (ThreadSP &thread_sp : batched_step_threads)
batch_tids.insert(thread_sp->GetID());
for (const auto &thread_sp : m_threads) {
if (batch_tids.count(thread_sp->GetID()) > 0) {
// This thread is in the batch, let it step.
if (!thread_sp->ShouldResume(thread_sp->GetCurrentPlan()->RunState()))
need_to_resume = false;
} else {
// Suspend it since it's not in the batch.
thread_sp->ShouldResume(eStateSuspended);
}
}
} else if (thread_to_run == nullptr) {
// Everybody runs as they wish:
for (pos = m_threads.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
StateType run_state;
if (thread_sp->GetResumeState() != eStateSuspended)
run_state = thread_sp->GetCurrentPlan()->RunState();
else
run_state = eStateSuspended;
if (!thread_sp->ShouldResume(run_state))
need_to_resume = false;
}
if (need_to_resume) {
// Ensure all threads are running in the right direction
for (pos = m_threads.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
while (thread_sp->GetCurrentPlan()->GetDirection() != direction) {
// This can't discard the base plan because its direction is
// m_process.GetBaseDirection() i.e. `direction`.
thread_sp->DiscardPlan();
}
}
}
} else {
for (pos = m_threads.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
if (thread_sp == thread_to_run) {
// Note, a thread might be able to fulfil it's plan w/o actually
// resuming. An example of this is a step that changes the current
// inlined function depth w/o moving the PC. Check that here:
if (!thread_sp->ShouldResume(thread_sp->GetCurrentPlan()->RunState()))
need_to_resume = false;
} else
thread_sp->ShouldResume(eStateSuspended);
}
}
return need_to_resume;
}
void ThreadList::DidResume() {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
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->GetTemporaryResumeState() != eStateSuspended)
thread_sp->DidResume();
}
}
void ThreadList::DidStop() {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
collection::iterator pos, end = m_threads.end();
for (pos = m_threads.begin(); pos != end; ++pos) {
// Notify threads that the process just stopped. Note, this currently
// assumes that all threads in the list stop when the process stops. In
// the future we will want to support a debugging model where some threads
// continue to run while others are stopped. We either need to handle that
// somehow here or create a special thread list containing only threads
// which will stop in the code that calls this method (currently
// Process::SetPrivateState).
ThreadSP thread_sp(*pos);
if (StateIsRunningState(thread_sp->GetState()))
thread_sp->DidStop();
}
}
ThreadSP ThreadList::GetSelectedThread() {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
ThreadSP thread_sp = FindThreadByID(m_selected_tid);
if (!thread_sp.get()) {
if (m_threads.size() == 0)
return thread_sp;
m_selected_tid = m_threads[0]->GetID();
thread_sp = m_threads[0];
}
return thread_sp;
}
bool ThreadList::SetSelectedThreadByID(lldb::tid_t tid, bool notify) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
ThreadSP selected_thread_sp(FindThreadByID(tid));
if (selected_thread_sp) {
m_selected_tid = tid;
selected_thread_sp->SetDefaultFileAndLineToSelectedFrame();
} else
m_selected_tid = LLDB_INVALID_THREAD_ID;
if (notify)
NotifySelectedThreadChanged(m_selected_tid);
return m_selected_tid != LLDB_INVALID_THREAD_ID;
}
bool ThreadList::SetSelectedThreadByIndexID(uint32_t index_id, bool notify) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
ThreadSP selected_thread_sp(FindThreadByIndexID(index_id));
if (selected_thread_sp.get()) {
m_selected_tid = selected_thread_sp->GetID();
selected_thread_sp->SetDefaultFileAndLineToSelectedFrame();
} else
m_selected_tid = LLDB_INVALID_THREAD_ID;
if (notify)
NotifySelectedThreadChanged(m_selected_tid);
return m_selected_tid != LLDB_INVALID_THREAD_ID;
}
void ThreadList::NotifySelectedThreadChanged(lldb::tid_t tid) {
ThreadSP selected_thread_sp(FindThreadByID(tid));
if (selected_thread_sp->EventTypeHasListeners(
Thread::eBroadcastBitThreadSelected)) {
auto data_sp =
std::make_shared<Thread::ThreadEventData>(selected_thread_sp);
selected_thread_sp->BroadcastEvent(Thread::eBroadcastBitThreadSelected,
data_sp);
}
}
void ThreadList::Update(ThreadList &rhs) {
if (this != &rhs) {
// We only allow assignments between thread lists describing the same
// process. Same process implies same mutex, which means it's enough to lock
// just the current object.
assert(&m_process == &rhs.m_process);
assert(&GetMutex() == &rhs.GetMutex());
std::lock_guard<std::recursive_mutex> guard(GetMutex());
m_stop_id = rhs.m_stop_id;
m_threads.swap(rhs.m_threads);
m_selected_tid = rhs.m_selected_tid;
// Now we look for threads that we are done with and make sure to clear
// them up as much as possible so anyone with a shared pointer will still
// have a reference, but the thread won't be of much use. Using
// std::weak_ptr for all backward references (such as a thread to a
// process) will eventually solve this issue for us, but for now, we need
// to work around the issue
collection::iterator rhs_pos, rhs_end = rhs.m_threads.end();
for (rhs_pos = rhs.m_threads.begin(); rhs_pos != rhs_end; ++rhs_pos) {
// If this thread has already been destroyed, we don't need to look for
// it to destroy it again.
if (!(*rhs_pos)->IsValid())
continue;
const lldb::tid_t tid = (*rhs_pos)->GetID();
bool thread_is_alive = false;
const uint32_t num_threads = m_threads.size();
for (uint32_t idx = 0; idx < num_threads; ++idx) {
ThreadSP backing_thread = m_threads[idx]->GetBackingThread();
if (m_threads[idx]->GetID() == tid ||
(backing_thread && backing_thread->GetID() == tid)) {
thread_is_alive = true;
break;
}
}
if (!thread_is_alive) {
(*rhs_pos)->DestroyThread();
}
}
}
}
void ThreadList::Flush() {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
collection::iterator pos, end = m_threads.end();
for (pos = m_threads.begin(); pos != end; ++pos)
(*pos)->Flush();
}
std::recursive_mutex &ThreadList::GetMutex() const {
return m_process.m_thread_mutex;
}
ThreadList::ExpressionExecutionThreadPusher::ExpressionExecutionThreadPusher(
lldb::ThreadSP thread_sp)
: m_thread_list(nullptr), m_tid(LLDB_INVALID_THREAD_ID) {
if (thread_sp) {
m_tid = thread_sp->GetID();
m_thread_list = &thread_sp->GetProcess()->GetThreadList();
m_thread_list->PushExpressionExecutionThread(m_tid);
}
}
void ThreadList::RegisterThreadSteppingOverBreakpoint(addr_t breakpoint_addr,
tid_t tid) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
m_threads_stepping_over_bp[breakpoint_addr].insert(tid);
Log *log = GetLog(LLDBLog::Step);
LLDB_LOGF(
log,
"ThreadList::%s: Registered thread 0x%" PRIx64
" stepping over breakpoint at 0x%" PRIx64 " (now %zu threads)",
__FUNCTION__, tid, breakpoint_addr,
static_cast<size_t>(m_threads_stepping_over_bp[breakpoint_addr].size()));
}
void ThreadList::ThreadFinishedSteppingOverBreakpoint(addr_t breakpoint_addr,
tid_t tid) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
Log *log = GetLog(LLDBLog::Step);
auto it = m_threads_stepping_over_bp.find(breakpoint_addr);
if (it == m_threads_stepping_over_bp.end()) {
// No threads registered for this breakpoint, re-enable directly.
LLDB_LOGF(log,
"ThreadList::%s: Thread 0x%" PRIx64
" finished stepping over breakpoint at 0x%" PRIx64
" but no threads were registered, re-enabling directly",
__FUNCTION__, tid, breakpoint_addr);
if (BreakpointSiteSP bp_site_sp =
m_process.GetBreakpointSiteList().FindByAddress(breakpoint_addr))
m_process.EnableBreakpointSite(bp_site_sp.get());
return;
}
// Remove this thread from the set.
it->second.erase(tid);
LLDB_LOGF(log,
"ThreadList::%s: Thread 0x%" PRIx64
" finished stepping over breakpoint at 0x%" PRIx64
" (%zu threads remaining)",
__FUNCTION__, tid, breakpoint_addr,
static_cast<size_t>(it->second.size()));
// If no more threads are stepping over this breakpoint, re-enable it.
if (it->second.empty()) {
LLDB_LOGF(log,
"ThreadList::%s: All threads finished stepping over breakpoint "
"at 0x%" PRIx64 ", re-enabling breakpoint",
__FUNCTION__, breakpoint_addr);
if (BreakpointSiteSP bp_site_sp =
m_process.GetBreakpointSiteList().FindByAddress(breakpoint_addr))
m_process.EnableBreakpointSite(bp_site_sp.get());
// Clean up the entry.
m_threads_stepping_over_bp.erase(it);
}
}