When a user holds an SBFrame reference and then triggers an inferior function call (via expression evaluation or GetExtendedBacktraceThread), variables in that frame become inaccessible with "register fp is not available" errors. This happens because inferior function calls execute through ThreadPlanCallFunction, which calls ClearStackFrames() during cleanup to invalidate the unwinder state. ExecutionContextRef objects in the old SBFrames were tracking StackFrameLists via weak_ptr, which became stale when ClearStackFrames() created new instances. The fix uses stable StackFrameList identifiers that persist across ClearStackFrames(): - ID = 0: Normal unwinder frames (constant across all instances) - ID = sequential counter: Scripted frame provider instances ExecutionContextRef now stores the frame list ID instead of a weak_ptr, allowing it to resolve to the current StackFrameList with fresh unwinder state after an inferior function call completes. The Thread object preserves the provider chain configuration (m_provider_chain_ids and m_next_provider_id) across ClearStackFrames() so that recreated StackFrameLists get the same IDs. When providers need to be recreated, GetStackFrameList() rebuilds them from the persisted configuration. This commit also fixes a deadlock when Python scripted frame providers call back into LLDB during frame fetching. The m_list_mutex is now released before calling GetFrameAtIndex() on the Python scripted frame provider to prevent same-thread deadlock. A dedicated m_unwinder_frames_sp member ensures GetFrameListByIdentifier(0) always returns the current unwinder frames, and proper cleanup in DestroyThread() and ClearStackFrames() to prevent modules from lingering after a Thread (and its StackFrameLists) gets destroyed. Added test validates that variables remain accessible after GetExtendedBacktraceThread triggers an inferior function call to fetch libdispatch Queue Info. rdar://167027676 Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
703 lines
21 KiB
C++
703 lines
21 KiB
C++
//===-- ExecutionContext.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/ExecutionContext.h"
|
|
#include "lldb/Target/ExecutionContextScope.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/StackFrame.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Target/Thread.h"
|
|
#include "lldb/Utility/LLDBLog.h"
|
|
#include "lldb/Utility/State.h"
|
|
#include <mutex>
|
|
|
|
using namespace lldb_private;
|
|
|
|
ExecutionContext::ExecutionContext()
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {}
|
|
|
|
ExecutionContext::ExecutionContext(const ExecutionContext &rhs) = default;
|
|
|
|
ExecutionContext::ExecutionContext(const lldb::TargetSP &target_sp,
|
|
bool get_process)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
if (target_sp)
|
|
SetContext(target_sp, get_process);
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(const lldb::ProcessSP &process_sp)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
if (process_sp)
|
|
SetContext(process_sp);
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(const lldb::ThreadSP &thread_sp)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
if (thread_sp)
|
|
SetContext(thread_sp);
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(const lldb::StackFrameSP &frame_sp)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
if (frame_sp)
|
|
SetContext(frame_sp);
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(const lldb::TargetWP &target_wp,
|
|
bool get_process)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
lldb::TargetSP target_sp(target_wp.lock());
|
|
if (target_sp)
|
|
SetContext(target_sp, get_process);
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(const lldb::ProcessWP &process_wp)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
lldb::ProcessSP process_sp(process_wp.lock());
|
|
if (process_sp)
|
|
SetContext(process_sp);
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(const lldb::ThreadWP &thread_wp)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
lldb::ThreadSP thread_sp(thread_wp.lock());
|
|
if (thread_sp)
|
|
SetContext(thread_sp);
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(const lldb::StackFrameWP &frame_wp)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
lldb::StackFrameSP frame_sp(frame_wp.lock());
|
|
if (frame_sp)
|
|
SetContext(frame_sp);
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(Target *t,
|
|
bool fill_current_process_thread_frame)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
if (t) {
|
|
m_target_sp = t->shared_from_this();
|
|
if (fill_current_process_thread_frame) {
|
|
m_process_sp = t->GetProcessSP();
|
|
if (m_process_sp) {
|
|
m_thread_sp = m_process_sp->GetThreadList().GetSelectedThread();
|
|
if (m_thread_sp)
|
|
m_frame_sp =
|
|
m_thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(Process *process, Thread *thread,
|
|
StackFrame *frame)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
if (process) {
|
|
m_process_sp = process->shared_from_this();
|
|
m_target_sp = process->GetTarget().shared_from_this();
|
|
}
|
|
if (thread)
|
|
m_thread_sp = thread->shared_from_this();
|
|
if (frame)
|
|
m_frame_sp = frame->shared_from_this();
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(const ExecutionContextRef &exe_ctx_ref)
|
|
: m_target_sp(exe_ctx_ref.GetTargetSP()),
|
|
m_process_sp(exe_ctx_ref.GetProcessSP()),
|
|
m_thread_sp(exe_ctx_ref.GetThreadSP()),
|
|
m_frame_sp(exe_ctx_ref.GetFrameSP()) {}
|
|
|
|
ExecutionContext::ExecutionContext(const ExecutionContextRef *exe_ctx_ref_ptr,
|
|
bool thread_and_frame_only_if_stopped)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
if (exe_ctx_ref_ptr) {
|
|
m_target_sp = exe_ctx_ref_ptr->GetTargetSP();
|
|
m_process_sp = exe_ctx_ref_ptr->GetProcessSP();
|
|
if (!thread_and_frame_only_if_stopped ||
|
|
(m_process_sp && StateIsStoppedState(m_process_sp->GetState(), true))) {
|
|
m_thread_sp = exe_ctx_ref_ptr->GetThreadSP();
|
|
m_frame_sp = exe_ctx_ref_ptr->GetFrameSP();
|
|
}
|
|
}
|
|
}
|
|
|
|
llvm::Expected<StoppedExecutionContext>
|
|
lldb_private::GetStoppedExecutionContext(
|
|
const lldb::ExecutionContextRefSP &exe_ctx_ref_ptr) {
|
|
return GetStoppedExecutionContext(exe_ctx_ref_ptr.get());
|
|
}
|
|
|
|
llvm::Expected<StoppedExecutionContext>
|
|
lldb_private::GetStoppedExecutionContext(
|
|
const ExecutionContextRef *exe_ctx_ref_ptr) {
|
|
if (!exe_ctx_ref_ptr)
|
|
return llvm::createStringError(
|
|
"StoppedExecutionContext created with an empty ExecutionContextRef");
|
|
|
|
lldb::TargetSP target_sp = exe_ctx_ref_ptr->GetTargetSP();
|
|
if (!target_sp)
|
|
return llvm::createStringError(
|
|
"StoppedExecutionContext created with a null target");
|
|
|
|
auto api_lock =
|
|
std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
|
|
|
|
auto process_sp = exe_ctx_ref_ptr->GetProcessSP();
|
|
if (!process_sp)
|
|
return llvm::createStringError(
|
|
"StoppedExecutionContext created with a null process");
|
|
|
|
ProcessRunLock::ProcessRunLocker stop_locker;
|
|
if (!stop_locker.TryLock(&process_sp->GetRunLock()))
|
|
return llvm::createStringError(
|
|
"attempted to create a StoppedExecutionContext with a running process");
|
|
|
|
auto thread_sp = exe_ctx_ref_ptr->GetThreadSP();
|
|
auto frame_sp = exe_ctx_ref_ptr->GetFrameSP();
|
|
|
|
if (!frame_sp && exe_ctx_ref_ptr->m_frame_list_id) {
|
|
return llvm::createStringError(
|
|
"attempted to create a StoppedExecutionContext but "
|
|
"ScriptedFrameProvider (name = %s - id = %u) is no longer available",
|
|
exe_ctx_ref_ptr->m_frame_list_id->first.GetName().str().c_str(),
|
|
exe_ctx_ref_ptr->m_frame_list_id->second);
|
|
}
|
|
|
|
return StoppedExecutionContext(target_sp, process_sp, thread_sp, frame_sp,
|
|
std::move(api_lock), std::move(stop_locker));
|
|
}
|
|
|
|
std::unique_lock<std::recursive_mutex> StoppedExecutionContext::AllowResume() {
|
|
Clear();
|
|
m_stop_locker = ProcessRunLock::ProcessRunLocker();
|
|
return std::move(m_api_lock);
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(ExecutionContextScope *exe_scope_ptr)
|
|
: m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
|
|
if (exe_scope_ptr)
|
|
exe_scope_ptr->CalculateExecutionContext(*this);
|
|
}
|
|
|
|
ExecutionContext::ExecutionContext(ExecutionContextScope &exe_scope_ref) {
|
|
exe_scope_ref.CalculateExecutionContext(*this);
|
|
}
|
|
|
|
void ExecutionContext::Clear() {
|
|
m_target_sp.reset();
|
|
m_process_sp.reset();
|
|
m_thread_sp.reset();
|
|
m_frame_sp.reset();
|
|
}
|
|
|
|
ExecutionContext::~ExecutionContext() = default;
|
|
|
|
uint32_t ExecutionContext::GetAddressByteSize() const {
|
|
if (m_target_sp && m_target_sp->GetArchitecture().IsValid())
|
|
return m_target_sp->GetArchitecture().GetAddressByteSize();
|
|
if (m_process_sp)
|
|
return m_process_sp->GetAddressByteSize();
|
|
return sizeof(void *);
|
|
}
|
|
|
|
lldb::ByteOrder ExecutionContext::GetByteOrder() const {
|
|
if (m_target_sp && m_target_sp->GetArchitecture().IsValid())
|
|
return m_target_sp->GetArchitecture().GetByteOrder();
|
|
if (m_process_sp)
|
|
return m_process_sp->GetByteOrder();
|
|
return endian::InlHostByteOrder();
|
|
}
|
|
|
|
RegisterContext *ExecutionContext::GetRegisterContext() const {
|
|
if (m_frame_sp)
|
|
return m_frame_sp->GetRegisterContext().get();
|
|
else if (m_thread_sp)
|
|
return m_thread_sp->GetRegisterContext().get();
|
|
return nullptr;
|
|
}
|
|
|
|
Target *ExecutionContext::GetTargetPtr() const {
|
|
if (m_target_sp)
|
|
return m_target_sp.get();
|
|
if (m_process_sp)
|
|
return &m_process_sp->GetTarget();
|
|
return nullptr;
|
|
}
|
|
|
|
Process *ExecutionContext::GetProcessPtr() const {
|
|
if (m_process_sp)
|
|
return m_process_sp.get();
|
|
if (m_target_sp)
|
|
return m_target_sp->GetProcessSP().get();
|
|
return nullptr;
|
|
}
|
|
|
|
ExecutionContextScope *ExecutionContext::GetBestExecutionContextScope() const {
|
|
if (m_frame_sp)
|
|
return m_frame_sp.get();
|
|
if (m_thread_sp)
|
|
return m_thread_sp.get();
|
|
if (m_process_sp)
|
|
return m_process_sp.get();
|
|
return m_target_sp.get();
|
|
}
|
|
|
|
Target &ExecutionContext::GetTargetRef() const {
|
|
assert(m_target_sp);
|
|
return *m_target_sp;
|
|
}
|
|
|
|
Process &ExecutionContext::GetProcessRef() const {
|
|
assert(m_process_sp);
|
|
return *m_process_sp;
|
|
}
|
|
|
|
Thread &ExecutionContext::GetThreadRef() const {
|
|
assert(m_thread_sp);
|
|
return *m_thread_sp;
|
|
}
|
|
|
|
StackFrame &ExecutionContext::GetFrameRef() const {
|
|
assert(m_frame_sp);
|
|
return *m_frame_sp;
|
|
}
|
|
|
|
void ExecutionContext::SetTargetSP(const lldb::TargetSP &target_sp) {
|
|
m_target_sp = target_sp;
|
|
}
|
|
|
|
void ExecutionContext::SetProcessSP(const lldb::ProcessSP &process_sp) {
|
|
m_process_sp = process_sp;
|
|
}
|
|
|
|
void ExecutionContext::SetThreadSP(const lldb::ThreadSP &thread_sp) {
|
|
m_thread_sp = thread_sp;
|
|
}
|
|
|
|
void ExecutionContext::SetFrameSP(const lldb::StackFrameSP &frame_sp) {
|
|
m_frame_sp = frame_sp;
|
|
}
|
|
|
|
void ExecutionContext::SetTargetPtr(Target *target) {
|
|
if (target)
|
|
m_target_sp = target->shared_from_this();
|
|
else
|
|
m_target_sp.reset();
|
|
}
|
|
|
|
void ExecutionContext::SetProcessPtr(Process *process) {
|
|
if (process)
|
|
m_process_sp = process->shared_from_this();
|
|
else
|
|
m_process_sp.reset();
|
|
}
|
|
|
|
void ExecutionContext::SetThreadPtr(Thread *thread) {
|
|
if (thread)
|
|
m_thread_sp = thread->shared_from_this();
|
|
else
|
|
m_thread_sp.reset();
|
|
}
|
|
|
|
void ExecutionContext::SetFramePtr(StackFrame *frame) {
|
|
if (frame)
|
|
m_frame_sp = frame->shared_from_this();
|
|
else
|
|
m_frame_sp.reset();
|
|
}
|
|
|
|
void ExecutionContext::SetContext(const lldb::TargetSP &target_sp,
|
|
bool get_process) {
|
|
m_target_sp = target_sp;
|
|
if (get_process && target_sp)
|
|
m_process_sp = target_sp->GetProcessSP();
|
|
else
|
|
m_process_sp.reset();
|
|
m_thread_sp.reset();
|
|
m_frame_sp.reset();
|
|
}
|
|
|
|
void ExecutionContext::SetContext(const lldb::ProcessSP &process_sp) {
|
|
m_process_sp = process_sp;
|
|
if (process_sp)
|
|
m_target_sp = process_sp->GetTarget().shared_from_this();
|
|
else
|
|
m_target_sp.reset();
|
|
m_thread_sp.reset();
|
|
m_frame_sp.reset();
|
|
}
|
|
|
|
void ExecutionContext::SetContext(const lldb::ThreadSP &thread_sp) {
|
|
m_frame_sp.reset();
|
|
m_thread_sp = thread_sp;
|
|
if (thread_sp) {
|
|
m_process_sp = thread_sp->GetProcess();
|
|
if (m_process_sp)
|
|
m_target_sp = m_process_sp->GetTarget().shared_from_this();
|
|
else
|
|
m_target_sp.reset();
|
|
} else {
|
|
m_target_sp.reset();
|
|
m_process_sp.reset();
|
|
}
|
|
}
|
|
|
|
void ExecutionContext::SetContext(const lldb::StackFrameSP &frame_sp) {
|
|
m_frame_sp = frame_sp;
|
|
if (frame_sp) {
|
|
m_thread_sp = frame_sp->CalculateThread();
|
|
if (m_thread_sp) {
|
|
m_process_sp = m_thread_sp->GetProcess();
|
|
if (m_process_sp)
|
|
m_target_sp = m_process_sp->GetTarget().shared_from_this();
|
|
else
|
|
m_target_sp.reset();
|
|
} else {
|
|
m_target_sp.reset();
|
|
m_process_sp.reset();
|
|
}
|
|
} else {
|
|
m_target_sp.reset();
|
|
m_process_sp.reset();
|
|
m_thread_sp.reset();
|
|
}
|
|
}
|
|
|
|
ExecutionContext &ExecutionContext::operator=(const ExecutionContext &rhs) {
|
|
if (this != &rhs) {
|
|
m_target_sp = rhs.m_target_sp;
|
|
m_process_sp = rhs.m_process_sp;
|
|
m_thread_sp = rhs.m_thread_sp;
|
|
m_frame_sp = rhs.m_frame_sp;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool ExecutionContext::operator==(const ExecutionContext &rhs) const {
|
|
// Check that the frame shared pointers match, or both are valid and their
|
|
// stack IDs match since sometimes we get new objects that represent the same
|
|
// frame within a thread.
|
|
if ((m_frame_sp == rhs.m_frame_sp) ||
|
|
(m_frame_sp && rhs.m_frame_sp &&
|
|
m_frame_sp->GetStackID() == rhs.m_frame_sp->GetStackID())) {
|
|
// Check that the thread shared pointers match, or both are valid and their
|
|
// thread IDs match since sometimes we get new objects that represent the
|
|
// same thread within a process.
|
|
if ((m_thread_sp == rhs.m_thread_sp) ||
|
|
(m_thread_sp && rhs.m_thread_sp &&
|
|
m_thread_sp->GetID() == rhs.m_thread_sp->GetID())) {
|
|
// Processes and targets don't change much
|
|
return m_process_sp == rhs.m_process_sp && m_target_sp == rhs.m_target_sp;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ExecutionContext::operator!=(const ExecutionContext &rhs) const {
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
bool ExecutionContext::HasTargetScope() const {
|
|
return ((bool)m_target_sp && m_target_sp->IsValid());
|
|
}
|
|
|
|
bool ExecutionContext::HasProcessScope() const {
|
|
return (HasTargetScope() && ((bool)m_process_sp && m_process_sp->IsValid()));
|
|
}
|
|
|
|
bool ExecutionContext::HasThreadScope() const {
|
|
return (HasProcessScope() && ((bool)m_thread_sp && m_thread_sp->IsValid()));
|
|
}
|
|
|
|
bool ExecutionContext::HasFrameScope() const {
|
|
return HasThreadScope() && m_frame_sp;
|
|
}
|
|
|
|
ExecutionContextRef::ExecutionContextRef()
|
|
: m_target_wp(), m_process_wp(), m_thread_wp(), m_stack_id() {}
|
|
|
|
ExecutionContextRef::ExecutionContextRef(const ExecutionContext *exe_ctx)
|
|
: m_target_wp(), m_process_wp(), m_thread_wp(), m_stack_id() {
|
|
if (exe_ctx)
|
|
*this = *exe_ctx;
|
|
}
|
|
|
|
ExecutionContextRef::ExecutionContextRef(const ExecutionContext &exe_ctx)
|
|
: m_target_wp(), m_process_wp(), m_thread_wp(), m_stack_id() {
|
|
*this = exe_ctx;
|
|
}
|
|
|
|
ExecutionContextRef::ExecutionContextRef(Target *target, bool adopt_selected)
|
|
: m_target_wp(), m_process_wp(), m_thread_wp(), m_stack_id() {
|
|
SetTargetPtr(target, adopt_selected);
|
|
}
|
|
|
|
ExecutionContextRef::ExecutionContextRef(Process *process, bool adopt_selected)
|
|
: m_target_wp(), m_process_wp(), m_thread_wp(), m_stack_id() {
|
|
SetProcessPtr(process, adopt_selected);
|
|
}
|
|
|
|
ExecutionContextRef::ExecutionContextRef(Thread *thread, bool adopt_selected)
|
|
: m_target_wp(), m_process_wp(), m_thread_wp(), m_stack_id() {
|
|
SetThreadPtr(thread, adopt_selected);
|
|
}
|
|
|
|
ExecutionContextRef::ExecutionContextRef(const ExecutionContextRef &rhs)
|
|
|
|
= default;
|
|
|
|
ExecutionContextRef &ExecutionContextRef::
|
|
operator=(const ExecutionContextRef &rhs) {
|
|
if (this != &rhs) {
|
|
m_target_wp = rhs.m_target_wp;
|
|
m_process_wp = rhs.m_process_wp;
|
|
m_thread_wp = rhs.m_thread_wp;
|
|
m_tid = rhs.m_tid;
|
|
m_stack_id = rhs.m_stack_id;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
ExecutionContextRef &ExecutionContextRef::
|
|
operator=(const ExecutionContext &exe_ctx) {
|
|
m_target_wp = exe_ctx.GetTargetSP();
|
|
m_process_wp = exe_ctx.GetProcessSP();
|
|
lldb::ThreadSP thread_sp(exe_ctx.GetThreadSP());
|
|
m_thread_wp = thread_sp;
|
|
if (thread_sp)
|
|
m_tid = thread_sp->GetID();
|
|
else
|
|
m_tid = LLDB_INVALID_THREAD_ID;
|
|
lldb::StackFrameSP frame_sp(exe_ctx.GetFrameSP());
|
|
|
|
if (frame_sp && thread_sp) {
|
|
lldb::frame_list_id_t frame_list_id =
|
|
frame_sp->GetContainingStackFrameListIdentifier();
|
|
auto frame_list_descriptor_or_err =
|
|
thread_sp->GetScriptedFrameProviderDescriptorForID(frame_list_id);
|
|
if (frame_list_descriptor_or_err) {
|
|
m_stack_id = frame_sp->GetStackID();
|
|
m_frame_list_id = {*frame_list_descriptor_or_err, frame_list_id};
|
|
} else {
|
|
LLDB_LOG_ERROR(GetLog(LLDBLog::Process),
|
|
frame_list_descriptor_or_err.takeError(),
|
|
"Failed to fetch scripted frame provider descriptor: {0}");
|
|
m_stack_id.Clear();
|
|
m_frame_list_id.reset();
|
|
}
|
|
} else {
|
|
m_stack_id.Clear();
|
|
m_frame_list_id.reset();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void ExecutionContextRef::Clear() {
|
|
m_target_wp.reset();
|
|
m_process_wp.reset();
|
|
ClearThread();
|
|
ClearFrame();
|
|
}
|
|
|
|
ExecutionContextRef::~ExecutionContextRef() = default;
|
|
|
|
void ExecutionContextRef::SetTargetSP(const lldb::TargetSP &target_sp) {
|
|
m_target_wp = target_sp;
|
|
}
|
|
|
|
void ExecutionContextRef::SetProcessSP(const lldb::ProcessSP &process_sp) {
|
|
if (process_sp) {
|
|
m_process_wp = process_sp;
|
|
SetTargetSP(process_sp->GetTarget().shared_from_this());
|
|
} else {
|
|
m_process_wp.reset();
|
|
m_target_wp.reset();
|
|
}
|
|
}
|
|
|
|
void ExecutionContextRef::SetThreadSP(const lldb::ThreadSP &thread_sp) {
|
|
if (thread_sp) {
|
|
m_thread_wp = thread_sp;
|
|
m_tid = thread_sp->GetID();
|
|
SetProcessSP(thread_sp->GetProcess());
|
|
} else {
|
|
ClearThread();
|
|
m_process_wp.reset();
|
|
m_target_wp.reset();
|
|
}
|
|
}
|
|
|
|
void ExecutionContextRef::SetFrameSP(const lldb::StackFrameSP &frame_sp) {
|
|
if (!frame_sp) {
|
|
Clear();
|
|
return;
|
|
}
|
|
|
|
lldb::ThreadSP thread_sp = frame_sp->GetThread();
|
|
lldb::frame_list_id_t frame_list_id =
|
|
frame_sp->GetContainingStackFrameListIdentifier();
|
|
auto frame_list_descriptor_or_err =
|
|
thread_sp->GetScriptedFrameProviderDescriptorForID(frame_list_id);
|
|
|
|
if (frame_list_descriptor_or_err) {
|
|
m_stack_id = frame_sp->GetStackID();
|
|
m_frame_list_id = {*frame_list_descriptor_or_err, frame_list_id};
|
|
SetThreadSP(thread_sp);
|
|
} else {
|
|
LLDB_LOG_ERROR(GetLog(LLDBLog::Process),
|
|
frame_list_descriptor_or_err.takeError(),
|
|
"Failed to fetch scripted frame provider descriptor: {0}");
|
|
ClearFrame();
|
|
ClearThread();
|
|
m_process_wp.reset();
|
|
m_target_wp.reset();
|
|
}
|
|
}
|
|
|
|
void ExecutionContextRef::SetTargetPtr(Target *target, bool adopt_selected) {
|
|
Clear();
|
|
if (target) {
|
|
lldb::TargetSP target_sp = target->shared_from_this();
|
|
SetTargetSP(target_sp);
|
|
if (adopt_selected) {
|
|
if (lldb::ProcessSP process_sp = target_sp->GetProcessSP())
|
|
SetProcessPtr(process_sp.get(), adopt_selected);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExecutionContextRef::SetProcessPtr(Process *process, bool adopt_selected) {
|
|
if (process) {
|
|
lldb::ProcessSP process_sp = process->shared_from_this();
|
|
SetProcessSP(process_sp);
|
|
if (adopt_selected) {
|
|
// Only fill in the thread if our process is stopped.
|
|
// Don't just check the state, since we might be in the middle of
|
|
// resuming.
|
|
Process::StopLocker stop_locker;
|
|
if (stop_locker.TryLock(&process_sp->GetRunLock()) &&
|
|
StateIsStoppedState(process_sp->GetState(), true)) {
|
|
lldb::ThreadSP thread_sp(
|
|
process_sp->GetThreadList().GetSelectedThread());
|
|
if (!thread_sp)
|
|
thread_sp = process_sp->GetThreadList().GetThreadAtIndex(0);
|
|
if (thread_sp) {
|
|
SetThreadSP(thread_sp);
|
|
lldb::StackFrameSP frame_sp =
|
|
thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame);
|
|
if (!frame_sp)
|
|
frame_sp = thread_sp->GetStackFrameAtIndex(0);
|
|
if (frame_sp)
|
|
SetFrameSP(frame_sp);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
m_process_wp.reset();
|
|
m_target_wp.reset();
|
|
}
|
|
}
|
|
|
|
void ExecutionContextRef::SetThreadPtr(Thread *thread, bool adopt_selected) {
|
|
if (thread) {
|
|
lldb::ThreadSP thread_sp = thread->shared_from_this();
|
|
SetThreadSP(thread_sp);
|
|
if (adopt_selected) {
|
|
// Only fill in the frame if our process is stopped.
|
|
// Don't just check the state, since we might be in the middle of
|
|
// resuming.
|
|
Process::StopLocker stop_locker;
|
|
if (stop_locker.TryLock(&thread->GetProcess()->GetRunLock()) &&
|
|
StateIsStoppedState(thread->GetProcess()->GetState(), true)) {
|
|
lldb::StackFrameSP frame_sp =
|
|
thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame);
|
|
if (!frame_sp)
|
|
frame_sp = thread_sp->GetStackFrameAtIndex(0);
|
|
if (frame_sp)
|
|
SetFrameSP(frame_sp);
|
|
}
|
|
}
|
|
} else {
|
|
ClearThread();
|
|
m_process_wp.reset();
|
|
m_target_wp.reset();
|
|
}
|
|
}
|
|
|
|
void ExecutionContextRef::SetFramePtr(StackFrame *frame) {
|
|
if (frame)
|
|
SetFrameSP(frame->shared_from_this());
|
|
else
|
|
Clear();
|
|
}
|
|
|
|
lldb::TargetSP ExecutionContextRef::GetTargetSP() const {
|
|
lldb::TargetSP target_sp(m_target_wp.lock());
|
|
if (target_sp && !target_sp->IsValid())
|
|
target_sp.reset();
|
|
return target_sp;
|
|
}
|
|
|
|
lldb::ProcessSP ExecutionContextRef::GetProcessSP() const {
|
|
lldb::ProcessSP process_sp(m_process_wp.lock());
|
|
if (process_sp && !process_sp->IsValid())
|
|
process_sp.reset();
|
|
return process_sp;
|
|
}
|
|
|
|
lldb::ThreadSP ExecutionContextRef::GetThreadSP() const {
|
|
lldb::ThreadSP thread_sp(m_thread_wp.lock());
|
|
|
|
if (m_tid != LLDB_INVALID_THREAD_ID) {
|
|
// We check if the thread has been destroyed in cases where clients might
|
|
// still have shared pointer to a thread, but the thread is not valid
|
|
// anymore (not part of the process)
|
|
if (!thread_sp || !thread_sp->IsValid()) {
|
|
lldb::ProcessSP process_sp(GetProcessSP());
|
|
if (process_sp && process_sp->IsValid()) {
|
|
thread_sp = process_sp->GetThreadList().FindThreadByID(m_tid);
|
|
m_thread_wp = thread_sp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check that we aren't about to return an invalid thread sp. We might
|
|
// return a nullptr thread_sp, but don't return an invalid one.
|
|
|
|
if (thread_sp && !thread_sp->IsValid())
|
|
thread_sp.reset();
|
|
|
|
return thread_sp;
|
|
}
|
|
|
|
lldb::StackFrameSP ExecutionContextRef::GetFrameSP() const {
|
|
lldb::ThreadSP thread_sp(GetThreadSP());
|
|
if (!thread_sp || !m_stack_id.IsValid())
|
|
return lldb::StackFrameSP();
|
|
|
|
// Try the remembered frame list first to avoid circular dependencies
|
|
// during frame provider initialization.
|
|
if (m_frame_list_id) {
|
|
if (auto frame_list_sp =
|
|
thread_sp->GetFrameListByIdentifier(m_frame_list_id->second)) {
|
|
if (auto frame_sp = frame_list_sp->GetFrameWithStackID(m_stack_id))
|
|
return frame_sp;
|
|
}
|
|
}
|
|
|
|
// Fallback: ask the thread, which might re-trigger the frame provider
|
|
// initialization.
|
|
return thread_sp->GetFrameWithStackID(m_stack_id);
|
|
}
|
|
|
|
ExecutionContext
|
|
ExecutionContextRef::Lock(bool thread_and_frame_only_if_stopped) const {
|
|
return ExecutionContext(this, thread_and_frame_only_if_stopped);
|
|
}
|