Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6fc6d00

Browse files
medismailbenc-rhodes
authored andcommitted
[lldb] Fix variable access in old SBFrames after inferior function calls (llvm#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 <[email protected]> (cherry picked from commit c373d76)
1 parent 6b5252e commit 6fc6d00

14 files changed

Lines changed: 811 additions & 87 deletions

File tree

lldb/include/lldb/Target/ExecutionContext.h

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313

1414
#include "lldb/Host/ProcessRunLock.h"
1515
#include "lldb/Target/StackID.h"
16+
#include "lldb/Target/SyntheticFrameProvider.h"
1617
#include "lldb/lldb-private.h"
1718

1819
namespace lldb_private {
1920

21+
struct StoppedExecutionContext;
22+
2023
//===----------------------------------------------------------------------===//
2124
/// Execution context objects refer to objects in the execution of the program
2225
/// that is being debugged. The consist of one or more of the following
@@ -270,9 +273,12 @@ class ExecutionContextRef {
270273

271274
void ClearFrame() {
272275
m_stack_id.Clear();
273-
m_frame_list_wp.reset();
276+
m_frame_list_id.reset();
274277
}
275278

279+
friend llvm::Expected<StoppedExecutionContext>
280+
GetStoppedExecutionContext(const ExecutionContextRef *exe_ctx_ref_ptr);
281+
276282
protected:
277283
// Member variables
278284
lldb::TargetWP m_target_wp; ///< A weak reference to a target
@@ -283,13 +289,10 @@ class ExecutionContextRef {
283289
/// backing object changes
284290
StackID m_stack_id; ///< The stack ID that this object refers to in case the
285291
///< backing object changes
286-
mutable lldb::StackFrameListWP
287-
m_frame_list_wp; ///< Weak reference to the
288-
///< frame list that contains
289-
///< this frame. If we can create a valid
290-
///< StackFrameListSP from it, we must use it to resolve
291-
///< the StackID, otherwise, we should ask the Thread's
292-
///< StackFrameList.
292+
/// A map of identifiers to scripted frame providers used in this thread.
293+
mutable std::optional<
294+
std::pair<ScriptedFrameProviderDescriptor, lldb::frame_list_id_t>>
295+
m_frame_list_id;
293296
};
294297

295298
/// \class ExecutionContext ExecutionContext.h

lldb/include/lldb/Target/StackFrame.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -542,17 +542,17 @@ class StackFrame : public ExecutionContextScope,
542542

543543
virtual lldb::RecognizedStackFrameSP GetRecognizedFrame();
544544

545-
/// Get the StackFrameList that contains this frame.
545+
/// Get the identifier of the StackFrameList that contains this frame.
546546
///
547-
/// Returns the StackFrameList that contains this frame, allowing
547+
/// Returns the StackFrameList identifier that contains this frame, allowing
548548
/// frames to resolve execution contexts without calling
549549
/// Thread::GetStackFrameList(), which can cause circular dependencies
550550
/// during frame provider initialization.
551551
///
552552
/// \return
553-
/// The StackFrameList that contains this frame, or nullptr if not set.
554-
virtual lldb::StackFrameListSP GetContainingStackFrameList() const {
555-
return m_frame_list_wp.lock();
553+
/// The identifier of the containing StackFrameList
554+
lldb::frame_list_id_t GetContainingStackFrameListIdentifier() const {
555+
return m_frame_list_id;
556556
}
557557

558558
protected:
@@ -598,8 +598,8 @@ class StackFrame : public ExecutionContextScope,
598598
/// be the first address of its function). True for actual frame zero as
599599
/// well as any other frame with the same trait.
600600
bool m_behaves_like_zeroth_frame;
601+
lldb::frame_list_id_t m_frame_list_id = 0;
601602
lldb::VariableListSP m_variable_list_sp;
602-
lldb::StackFrameListWP m_frame_list_wp;
603603
/// Value objects for each variable in m_variable_list_sp.
604604
ValueObjectList m_variable_list_value_objects;
605605
std::optional<lldb::RecognizedStackFrameSP> m_recognized_frame_sp;

lldb/include/lldb/Target/StackFrameList.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ class StackFrameList : public std::enable_shared_from_this<StackFrameList> {
2424
public:
2525
// Constructors and Destructors
2626
StackFrameList(Thread &thread, const lldb::StackFrameListSP &prev_frames_sp,
27-
bool show_inline_frames);
27+
bool show_inline_frames,
28+
lldb::frame_list_id_t provider_id = 0);
2829

2930
virtual ~StackFrameList();
3031

@@ -104,6 +105,9 @@ class StackFrameList : public std::enable_shared_from_this<StackFrameList> {
104105
/// Get the thread associated with this frame list.
105106
Thread &GetThread() const { return m_thread; }
106107

108+
/// Get the unique identifier for this frame list.
109+
lldb::frame_list_id_t GetIdentifier() const { return m_identifier; }
110+
107111
protected:
108112
friend class Thread;
109113
friend class ScriptedFrameProvider;
@@ -212,6 +216,9 @@ class StackFrameList : public std::enable_shared_from_this<StackFrameList> {
212216
/// Whether or not to show synthetic (inline) frames. Immutable.
213217
const bool m_show_inlined_frames;
214218

219+
/// Unique identifier for this frame list instance.
220+
lldb::frame_list_id_t m_identifier = 0;
221+
215222
/// Returns true if fetching frames was interrupted, false otherwise.
216223
virtual bool FetchFramesUpTo(uint32_t end_idx,
217224
InterruptionControl allow_interrupt);
@@ -244,7 +251,8 @@ class SyntheticStackFrameList : public StackFrameList {
244251
SyntheticStackFrameList(Thread &thread, lldb::StackFrameListSP input_frames,
245252
const lldb::StackFrameListSP &prev_frames_sp,
246253
bool show_inline_frames,
247-
lldb::SyntheticFrameProviderSP provider_sp);
254+
lldb::SyntheticFrameProviderSP provider_sp,
255+
uint64_t provider_id);
248256

249257
protected:
250258
/// Override FetchFramesUpTo to lazily return frames from the provider

lldb/include/lldb/Target/Thread.h

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919
#include "lldb/Target/ExecutionContextScope.h"
2020
#include "lldb/Target/RegisterCheckpoint.h"
2121
#include "lldb/Target/StackFrameList.h"
22+
#include "lldb/Target/SyntheticFrameProvider.h"
2223
#include "lldb/Utility/Broadcaster.h"
2324
#include "lldb/Utility/CompletionRequest.h"
2425
#include "lldb/Utility/Event.h"
2526
#include "lldb/Utility/StructuredData.h"
2627
#include "lldb/Utility/UnimplementedError.h"
2728
#include "lldb/Utility/UserID.h"
2829
#include "lldb/lldb-private.h"
30+
#include "llvm/ADT/DenseMap.h"
2931
#include "llvm/Support/MemoryBuffer.h"
3032

3133
#define LLDB_THREAD_MAX_STOP_EXC_DATA 8
@@ -1297,12 +1299,18 @@ class Thread : public std::enable_shared_from_this<Thread>,
12971299

12981300
lldb::StackFrameListSP GetStackFrameList();
12991301

1302+
/// Get a frame list by its unique identifier.
1303+
lldb::StackFrameListSP GetFrameListByIdentifier(lldb::frame_list_id_t id);
1304+
13001305
llvm::Error
13011306
LoadScriptedFrameProvider(const ScriptedFrameProviderDescriptor &descriptor);
13021307

1308+
llvm::Expected<ScriptedFrameProviderDescriptor>
1309+
GetScriptedFrameProviderDescriptorForID(lldb::frame_list_id_t id) const;
1310+
13031311
void ClearScriptedFrameProvider();
13041312

1305-
const llvm::SmallVector<lldb::SyntheticFrameProviderSP, 0> &
1313+
const llvm::DenseMap<lldb::frame_list_id_t, lldb::SyntheticFrameProviderSP> &
13061314
GetFrameProviders() const {
13071315
return m_frame_providers;
13081316
}
@@ -1384,6 +1392,8 @@ class Thread : public std::enable_shared_from_this<Thread>,
13841392
m_state_mutex; ///< Multithreaded protection for m_state.
13851393
mutable std::recursive_mutex
13861394
m_frame_mutex; ///< Multithreaded protection for m_state.
1395+
lldb::StackFrameListSP
1396+
m_unwinder_frames_sp; ///< The unwinder frame list (ID 0).
13871397
lldb::StackFrameListSP m_curr_frames_sp; ///< The stack frames that get lazily
13881398
///populated after a thread stops.
13891399
lldb::StackFrameListSP m_prev_frames_sp; ///< The previous stack frames from
@@ -1410,8 +1420,23 @@ class Thread : public std::enable_shared_from_this<Thread>,
14101420
/// The Thread backed by this thread, if any.
14111421
lldb::ThreadWP m_backed_thread;
14121422

1413-
/// The Scripted Frame Providers for this thread.
1414-
llvm::SmallVector<lldb::SyntheticFrameProviderSP, 0> m_frame_providers;
1423+
/// Map from frame list ID to its frame provider.
1424+
/// Cleared in ClearStackFrames(), repopulated in GetStackFrameList().
1425+
llvm::DenseMap<lldb::frame_list_id_t, lldb::SyntheticFrameProviderSP>
1426+
m_frame_providers;
1427+
1428+
/// Ordered chain of provider IDs.
1429+
/// Persists across ClearStackFrames() to maintain stable provider IDs.
1430+
std::vector<std::pair<ScriptedFrameProviderDescriptor, lldb::frame_list_id_t>>
1431+
m_provider_chain_ids;
1432+
1433+
/// Map from frame list identifier to frame list weak pointer.
1434+
mutable llvm::DenseMap<lldb::frame_list_id_t, lldb::StackFrameListWP>
1435+
m_frame_lists_by_id;
1436+
1437+
/// Counter for assigning unique provider IDs. Starts at 1 since 0 is
1438+
/// reserved for normal unwinder frames. Persists across ClearStackFrames.
1439+
lldb::frame_list_id_t m_next_provider_id = 1;
14151440

14161441
private:
14171442
bool m_extended_info_fetched; // Have we tried to retrieve the m_extended_info

lldb/include/lldb/lldb-defines.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
#define LLDB_INVALID_PROCESS_ID 0
9090
#define LLDB_INVALID_THREAD_ID 0
9191
#define LLDB_INVALID_FRAME_ID UINT32_MAX
92+
#define LLDB_UNWINDER_FRAME_LIST_ID 0
9293
#define LLDB_INVALID_SIGNAL_NUMBER INT32_MAX
9394
#define LLDB_INVALID_SYMBOL_ID UINT32_MAX
9495
#define LLDB_INVALID_OFFSET UINT64_MAX // Must match max of lldb::offset_t

lldb/include/lldb/lldb-types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ typedef uint64_t user_id_t;
8383
typedef uint64_t pid_t;
8484
typedef uint64_t tid_t;
8585
typedef uint64_t offset_t;
86+
typedef uint32_t frame_list_id_t;
8687
typedef int32_t break_id_t;
8788
typedef int32_t watch_id_t;
8889
typedef uint32_t wp_resource_id_t;

lldb/source/Target/ExecutionContext.cpp

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,15 @@ lldb_private::GetStoppedExecutionContext(
160160

161161
auto thread_sp = exe_ctx_ref_ptr->GetThreadSP();
162162
auto frame_sp = exe_ctx_ref_ptr->GetFrameSP();
163+
164+
if (!frame_sp && exe_ctx_ref_ptr->m_frame_list_id) {
165+
return llvm::createStringError(
166+
"attempted to create a StoppedExecutionContext but "
167+
"ScriptedFrameProvider (name = %s - id = %u) is no longer available",
168+
exe_ctx_ref_ptr->m_frame_list_id->first.GetName().str().c_str(),
169+
exe_ctx_ref_ptr->m_frame_list_id->second);
170+
}
171+
163172
return StoppedExecutionContext(target_sp, process_sp, thread_sp, frame_sp,
164173
std::move(api_lock), std::move(stop_locker));
165174
}
@@ -466,12 +475,25 @@ operator=(const ExecutionContext &exe_ctx) {
466475
else
467476
m_tid = LLDB_INVALID_THREAD_ID;
468477
lldb::StackFrameSP frame_sp(exe_ctx.GetFrameSP());
469-
if (frame_sp) {
470-
m_stack_id = frame_sp->GetStackID();
471-
m_frame_list_wp = frame_sp->GetContainingStackFrameList();
478+
479+
if (frame_sp && thread_sp) {
480+
lldb::frame_list_id_t frame_list_id =
481+
frame_sp->GetContainingStackFrameListIdentifier();
482+
auto frame_list_descriptor_or_err =
483+
thread_sp->GetScriptedFrameProviderDescriptorForID(frame_list_id);
484+
if (frame_list_descriptor_or_err) {
485+
m_stack_id = frame_sp->GetStackID();
486+
m_frame_list_id = {*frame_list_descriptor_or_err, frame_list_id};
487+
} else {
488+
LLDB_LOG_ERROR(GetLog(LLDBLog::Process),
489+
frame_list_descriptor_or_err.takeError(),
490+
"Failed to fetch scripted frame provider descriptor: {0}");
491+
m_stack_id.Clear();
492+
m_frame_list_id.reset();
493+
}
472494
} else {
473495
m_stack_id.Clear();
474-
m_frame_list_wp.reset();
496+
m_frame_list_id.reset();
475497
}
476498
return *this;
477499
}
@@ -512,11 +534,25 @@ void ExecutionContextRef::SetThreadSP(const lldb::ThreadSP &thread_sp) {
512534
}
513535

514536
void ExecutionContextRef::SetFrameSP(const lldb::StackFrameSP &frame_sp) {
515-
if (frame_sp) {
537+
if (!frame_sp) {
538+
Clear();
539+
return;
540+
}
541+
542+
lldb::ThreadSP thread_sp = frame_sp->GetThread();
543+
lldb::frame_list_id_t frame_list_id =
544+
frame_sp->GetContainingStackFrameListIdentifier();
545+
auto frame_list_descriptor_or_err =
546+
thread_sp->GetScriptedFrameProviderDescriptorForID(frame_list_id);
547+
548+
if (frame_list_descriptor_or_err) {
516549
m_stack_id = frame_sp->GetStackID();
517-
m_frame_list_wp = frame_sp->GetContainingStackFrameList();
518-
SetThreadSP(frame_sp->GetThread());
550+
m_frame_list_id = {*frame_list_descriptor_or_err, frame_list_id};
551+
SetThreadSP(thread_sp);
519552
} else {
553+
LLDB_LOG_ERROR(GetLog(LLDBLog::Process),
554+
frame_list_descriptor_or_err.takeError(),
555+
"Failed to fetch scripted frame provider descriptor: {0}");
520556
ClearFrame();
521557
ClearThread();
522558
m_process_wp.reset();
@@ -641,21 +677,23 @@ lldb::ThreadSP ExecutionContextRef::GetThreadSP() const {
641677
}
642678

643679
lldb::StackFrameSP ExecutionContextRef::GetFrameSP() const {
644-
if (m_stack_id.IsValid()) {
645-
// Try the remembered frame list first to avoid circular dependencies
646-
// during frame provider initialization.
647-
if (auto frame_list_sp = m_frame_list_wp.lock()) {
680+
lldb::ThreadSP thread_sp(GetThreadSP());
681+
if (!thread_sp || !m_stack_id.IsValid())
682+
return lldb::StackFrameSP();
683+
684+
// Try the remembered frame list first to avoid circular dependencies
685+
// during frame provider initialization.
686+
if (m_frame_list_id) {
687+
if (auto frame_list_sp =
688+
thread_sp->GetFrameListByIdentifier(m_frame_list_id->second)) {
648689
if (auto frame_sp = frame_list_sp->GetFrameWithStackID(m_stack_id))
649690
return frame_sp;
650691
}
651-
652-
// Fallback: ask the thread, which might re-trigger the frame provider
653-
// initialization.
654-
lldb::ThreadSP thread_sp(GetThreadSP());
655-
if (thread_sp)
656-
return thread_sp->GetFrameWithStackID(m_stack_id);
657692
}
658-
return lldb::StackFrameSP();
693+
694+
// Fallback: ask the thread, which might re-trigger the frame provider
695+
// initialization.
696+
return thread_sp->GetFrameWithStackID(m_stack_id);
659697
}
660698

661699
ExecutionContext

0 commit comments

Comments
 (0)