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>
88 lines
1.9 KiB
C++
88 lines
1.9 KiB
C++
// Multi-threaded test program for testing frame providers.
|
|
|
|
#include <condition_variable>
|
|
#include <iostream>
|
|
#include <mutex>
|
|
#include <thread>
|
|
|
|
std::mutex mtx;
|
|
std::condition_variable cv;
|
|
int ready_count = 0;
|
|
constexpr int NUM_THREADS = 2;
|
|
|
|
int foo(int x) {
|
|
int foo_local = x * 2;
|
|
int foo_result = foo_local + 1;
|
|
return foo_result; // Break in foo.
|
|
}
|
|
|
|
int bar(int x) {
|
|
int bar_local = x * x;
|
|
int bar_result = bar_local - 3;
|
|
return bar_result; // Break in bar.
|
|
}
|
|
|
|
int baz(int x) {
|
|
int baz_local = x + 7;
|
|
int baz_result = baz_local / 2;
|
|
return baz_result; // Break in baz.
|
|
}
|
|
|
|
void thread_func(int thread_num) {
|
|
std::cout << "Thread " << thread_num << " started\n";
|
|
|
|
{
|
|
std::unique_lock<std::mutex> lock(mtx);
|
|
ready_count++;
|
|
if (ready_count == NUM_THREADS + 1) {
|
|
cv.notify_all();
|
|
} else {
|
|
cv.wait(lock, [] { return ready_count == NUM_THREADS + 1; });
|
|
}
|
|
}
|
|
|
|
std::cout << "Thread " << thread_num << " at breakpoint\n"; // Break here.
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
std::thread threads[NUM_THREADS];
|
|
|
|
// Used as an existing C++ variable we can anchor on.
|
|
int variable_in_main = 123;
|
|
(void)variable_in_main; // Breakpoint for variable tests.
|
|
|
|
// Call foo for first breakpoint.
|
|
int result_foo = foo(10);
|
|
(void)result_foo;
|
|
|
|
for (int i = 0; i < NUM_THREADS; i++) {
|
|
threads[i] = std::thread(thread_func, i);
|
|
}
|
|
|
|
{
|
|
std::unique_lock<std::mutex> lock(mtx);
|
|
ready_count++;
|
|
if (ready_count == NUM_THREADS + 1) {
|
|
cv.notify_all();
|
|
} else {
|
|
cv.wait(lock, [] { return ready_count == NUM_THREADS + 1; });
|
|
}
|
|
}
|
|
|
|
std::cout << "Main thread at barrier\n";
|
|
|
|
// Call bar for second breakpoint.
|
|
int result_bar = bar(5);
|
|
(void)result_bar;
|
|
|
|
// Call baz for third breakpoint.
|
|
int result_baz = baz(11);
|
|
(void)result_baz;
|
|
|
|
for (int i = 0; i < NUM_THREADS; i++)
|
|
threads[i].join();
|
|
|
|
std::cout << "All threads completed\n";
|
|
return 0;
|
|
}
|