Files
llvm-project/lldb/source/Target/ExecutionContext.cpp
Med Ismail Bennani c373d7632a [lldb] Fix variable access in old SBFrames after inferior function calls (#178823)
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>
2026-02-03 03:12:35 +00:00

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);
}