You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
9.5 KiB
288 lines
9.5 KiB
4 months ago
|
//===-- ThreadPlan.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 "lldb/Target/ThreadPlan.h"
|
||
|
#include "lldb/Core/Debugger.h"
|
||
|
#include "lldb/Target/Process.h"
|
||
|
#include "lldb/Target/RegisterContext.h"
|
||
|
#include "lldb/Target/Target.h"
|
||
|
#include "lldb/Target/Thread.h"
|
||
|
#include "lldb/Utility/Log.h"
|
||
|
#include "lldb/Utility/State.h"
|
||
|
|
||
|
using namespace lldb;
|
||
|
using namespace lldb_private;
|
||
|
|
||
|
// ThreadPlan constructor
|
||
|
ThreadPlan::ThreadPlan(ThreadPlanKind kind, const char *name, Thread &thread,
|
||
|
Vote stop_vote, Vote run_vote)
|
||
|
: m_process(*thread.GetProcess().get()), m_tid(thread.GetID()),
|
||
|
m_stop_vote(stop_vote), m_run_vote(run_vote),
|
||
|
m_takes_iteration_count(false), m_could_not_resolve_hw_bp(false),
|
||
|
m_thread(&thread), m_kind(kind), m_name(name), m_plan_complete_mutex(),
|
||
|
m_cached_plan_explains_stop(eLazyBoolCalculate), m_plan_complete(false),
|
||
|
m_plan_private(false), m_okay_to_discard(true), m_is_master_plan(false),
|
||
|
m_plan_succeeded(true) {
|
||
|
SetID(GetNextID());
|
||
|
}
|
||
|
|
||
|
// Destructor
|
||
|
ThreadPlan::~ThreadPlan() = default;
|
||
|
|
||
|
Target &ThreadPlan::GetTarget() { return m_process.GetTarget(); }
|
||
|
|
||
|
const Target &ThreadPlan::GetTarget() const { return m_process.GetTarget(); }
|
||
|
|
||
|
Thread &ThreadPlan::GetThread() {
|
||
|
if (m_thread)
|
||
|
return *m_thread;
|
||
|
|
||
|
ThreadSP thread_sp = m_process.GetThreadList().FindThreadByID(m_tid);
|
||
|
m_thread = thread_sp.get();
|
||
|
return *m_thread;
|
||
|
}
|
||
|
|
||
|
bool ThreadPlan::PlanExplainsStop(Event *event_ptr) {
|
||
|
if (m_cached_plan_explains_stop == eLazyBoolCalculate) {
|
||
|
bool actual_value = DoPlanExplainsStop(event_ptr);
|
||
|
m_cached_plan_explains_stop = actual_value ? eLazyBoolYes : eLazyBoolNo;
|
||
|
return actual_value;
|
||
|
} else {
|
||
|
return m_cached_plan_explains_stop == eLazyBoolYes;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ThreadPlan::IsPlanComplete() {
|
||
|
std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex);
|
||
|
return m_plan_complete;
|
||
|
}
|
||
|
|
||
|
void ThreadPlan::SetPlanComplete(bool success) {
|
||
|
std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex);
|
||
|
m_plan_complete = true;
|
||
|
m_plan_succeeded = success;
|
||
|
}
|
||
|
|
||
|
bool ThreadPlan::MischiefManaged() {
|
||
|
std::lock_guard<std::recursive_mutex> guard(m_plan_complete_mutex);
|
||
|
// Mark the plan is complete, but don't override the success flag.
|
||
|
m_plan_complete = true;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
Vote ThreadPlan::ShouldReportStop(Event *event_ptr) {
|
||
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
|
||
|
|
||
|
if (m_stop_vote == eVoteNoOpinion) {
|
||
|
ThreadPlan *prev_plan = GetPreviousPlan();
|
||
|
if (prev_plan) {
|
||
|
Vote prev_vote = prev_plan->ShouldReportStop(event_ptr);
|
||
|
LLDB_LOG(log, "returning previous thread plan vote: {0}", prev_vote);
|
||
|
return prev_vote;
|
||
|
}
|
||
|
}
|
||
|
LLDB_LOG(log, "Returning vote: {0}", m_stop_vote);
|
||
|
return m_stop_vote;
|
||
|
}
|
||
|
|
||
|
Vote ThreadPlan::ShouldReportRun(Event *event_ptr) {
|
||
|
if (m_run_vote == eVoteNoOpinion) {
|
||
|
ThreadPlan *prev_plan = GetPreviousPlan();
|
||
|
if (prev_plan)
|
||
|
return prev_plan->ShouldReportRun(event_ptr);
|
||
|
}
|
||
|
return m_run_vote;
|
||
|
}
|
||
|
|
||
|
bool ThreadPlan::StopOthers() {
|
||
|
ThreadPlan *prev_plan;
|
||
|
prev_plan = GetPreviousPlan();
|
||
|
return (prev_plan == nullptr) ? false : prev_plan->StopOthers();
|
||
|
}
|
||
|
|
||
|
void ThreadPlan::SetStopOthers(bool new_value) {
|
||
|
// SetStopOthers doesn't work up the hierarchy. You have to set the explicit
|
||
|
// ThreadPlan you want to affect.
|
||
|
}
|
||
|
|
||
|
bool ThreadPlan::WillResume(StateType resume_state, bool current_plan) {
|
||
|
m_cached_plan_explains_stop = eLazyBoolCalculate;
|
||
|
|
||
|
if (current_plan) {
|
||
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
|
||
|
|
||
|
if (log) {
|
||
|
RegisterContext *reg_ctx = GetThread().GetRegisterContext().get();
|
||
|
assert(reg_ctx);
|
||
|
addr_t pc = reg_ctx->GetPC();
|
||
|
addr_t sp = reg_ctx->GetSP();
|
||
|
addr_t fp = reg_ctx->GetFP();
|
||
|
LLDB_LOGF(
|
||
|
log,
|
||
|
"%s Thread #%u (0x%p): tid = 0x%4.4" PRIx64 ", pc = 0x%8.8" PRIx64
|
||
|
", sp = 0x%8.8" PRIx64 ", fp = 0x%8.8" PRIx64 ", "
|
||
|
"plan = '%s', state = %s, stop others = %d",
|
||
|
__FUNCTION__, GetThread().GetIndexID(),
|
||
|
static_cast<void *>(&GetThread()), m_tid, static_cast<uint64_t>(pc),
|
||
|
static_cast<uint64_t>(sp), static_cast<uint64_t>(fp), m_name.c_str(),
|
||
|
StateAsCString(resume_state), StopOthers());
|
||
|
}
|
||
|
}
|
||
|
bool success = DoWillResume(resume_state, current_plan);
|
||
|
m_thread = nullptr; // We don't cache the thread pointer over resumes. This
|
||
|
// Thread might go away, and another Thread represent
|
||
|
// the same underlying object on a later stop.
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
lldb::user_id_t ThreadPlan::GetNextID() {
|
||
|
static uint32_t g_nextPlanID = 0;
|
||
|
return ++g_nextPlanID;
|
||
|
}
|
||
|
|
||
|
void ThreadPlan::DidPush() {}
|
||
|
|
||
|
void ThreadPlan::WillPop() {}
|
||
|
|
||
|
bool ThreadPlan::OkayToDiscard() {
|
||
|
return IsMasterPlan() ? m_okay_to_discard : true;
|
||
|
}
|
||
|
|
||
|
lldb::StateType ThreadPlan::RunState() {
|
||
|
if (m_tracer_sp && m_tracer_sp->TracingEnabled() &&
|
||
|
m_tracer_sp->SingleStepEnabled())
|
||
|
return eStateStepping;
|
||
|
else
|
||
|
return GetPlanRunState();
|
||
|
}
|
||
|
|
||
|
bool ThreadPlan::IsUsuallyUnexplainedStopReason(lldb::StopReason reason) {
|
||
|
switch (reason) {
|
||
|
case eStopReasonWatchpoint:
|
||
|
case eStopReasonSignal:
|
||
|
case eStopReasonException:
|
||
|
case eStopReasonExec:
|
||
|
case eStopReasonThreadExiting:
|
||
|
case eStopReasonInstrumentation:
|
||
|
return true;
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ThreadPlanNull
|
||
|
|
||
|
ThreadPlanNull::ThreadPlanNull(Thread &thread)
|
||
|
: ThreadPlan(ThreadPlan::eKindNull, "Null Thread Plan", thread,
|
||
|
eVoteNoOpinion, eVoteNoOpinion) {}
|
||
|
|
||
|
ThreadPlanNull::~ThreadPlanNull() = default;
|
||
|
|
||
|
void ThreadPlanNull::GetDescription(Stream *s, lldb::DescriptionLevel level) {
|
||
|
s->PutCString("Null thread plan - thread has been destroyed.");
|
||
|
}
|
||
|
|
||
|
bool ThreadPlanNull::ValidatePlan(Stream *error) {
|
||
|
#ifdef LLDB_CONFIGURATION_DEBUG
|
||
|
fprintf(stderr,
|
||
|
"error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID());
|
||
|
#else
|
||
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
|
||
|
if (log)
|
||
|
log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID());
|
||
|
#endif
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ThreadPlanNull::ShouldStop(Event *event_ptr) {
|
||
|
#ifdef LLDB_CONFIGURATION_DEBUG
|
||
|
fprintf(stderr,
|
||
|
"error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID());
|
||
|
#else
|
||
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
|
||
|
if (log)
|
||
|
log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID());
|
||
|
#endif
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ThreadPlanNull::WillStop() {
|
||
|
#ifdef LLDB_CONFIGURATION_DEBUG
|
||
|
fprintf(stderr,
|
||
|
"error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID());
|
||
|
#else
|
||
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
|
||
|
if (log)
|
||
|
log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID());
|
||
|
#endif
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ThreadPlanNull::DoPlanExplainsStop(Event *event_ptr) {
|
||
|
#ifdef LLDB_CONFIGURATION_DEBUG
|
||
|
fprintf(stderr,
|
||
|
"error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, GetThread().GetID(), GetThread().GetProtocolID());
|
||
|
#else
|
||
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
|
||
|
if (log)
|
||
|
log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID());
|
||
|
#endif
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// The null plan is never done.
|
||
|
bool ThreadPlanNull::MischiefManaged() {
|
||
|
// The null plan is never done.
|
||
|
#ifdef LLDB_CONFIGURATION_DEBUG
|
||
|
fprintf(stderr,
|
||
|
"error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID());
|
||
|
#else
|
||
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
|
||
|
if (log)
|
||
|
log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID());
|
||
|
#endif
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
lldb::StateType ThreadPlanNull::GetPlanRunState() {
|
||
|
// Not sure what to return here. This is a dead thread.
|
||
|
#ifdef LLDB_CONFIGURATION_DEBUG
|
||
|
fprintf(stderr,
|
||
|
"error: %s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID());
|
||
|
#else
|
||
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
|
||
|
if (log)
|
||
|
log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64
|
||
|
", ptid = 0x%" PRIx64 ")",
|
||
|
LLVM_PRETTY_FUNCTION, m_tid, GetThread().GetProtocolID());
|
||
|
#endif
|
||
|
return eStateRunning;
|
||
|
}
|