[lldb][NFC] Fix all formatting errors in .cpp file headers
Summary:
A *.cpp file header in LLDB (and in LLDB) should like this:
```
//===-- TestUtilities.cpp -------------------------------------------------===//
```
However in LLDB most of our source files have arbitrary changes to this format and
these changes are spreading through LLDB as folks usually just use the existing
source files as templates for their new files (most notably the unnecessary
editor language indicator `-*- C++ -*-` is spreading and in every review
someone is pointing out that this is wrong, resulting in people pointing out that this
is done in the same way in other files).
This patch removes most of these inconsistencies including the editor language indicators,
all the different missing/additional '-' characters, files that center the file name, missing
trailing `===//` (mostly caused by clang-format breaking the line).
Reviewers: aprantl, espindola, jfb, shafik, JDevlieghere
Reviewed By: JDevlieghere
Subscribers: dexonsmith, wuzish, emaste, sdardis, nemanjai, kbarton, MaskRay, atanasyan, arphaman, jfb, abidh, jsji, JDevlieghere, usaxena95, lldb-commits
Tags: #lldb
Differential Revision: https://reviews.llvm.org/D73258
2020-01-24 08:23:27 +01:00
|
|
|
//===-- StackFrameList.cpp ------------------------------------------------===//
|
2010-06-08 16:52:24 +00:00
|
|
|
//
|
2019-01-19 08:50:56 +00:00
|
|
|
// 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
|
2010-06-08 16:52:24 +00:00
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
2016-02-18 18:52:47 +00:00
|
|
|
#include "lldb/Target/StackFrameList.h"
|
2012-09-08 00:26:49 +00:00
|
|
|
#include "lldb/Breakpoint/Breakpoint.h"
|
|
|
|
|
#include "lldb/Breakpoint/BreakpointLocation.h"
|
2023-03-15 11:20:20 -07:00
|
|
|
#include "lldb/Core/Debugger.h"
|
2011-09-08 22:13:49 +00:00
|
|
|
#include "lldb/Core/SourceManager.h"
|
2023-08-08 17:18:47 -07:00
|
|
|
#include "lldb/Host/StreamFile.h"
|
2010-08-25 00:35:26 +00:00
|
|
|
#include "lldb/Symbol/Block.h"
|
|
|
|
|
#include "lldb/Symbol/Function.h"
|
2010-08-30 18:11:35 +00:00
|
|
|
#include "lldb/Symbol/Symbol.h"
|
2011-09-08 22:13:49 +00:00
|
|
|
#include "lldb/Target/Process.h"
|
2010-08-25 00:35:26 +00:00
|
|
|
#include "lldb/Target/RegisterContext.h"
|
2010-06-08 16:52:24 +00:00
|
|
|
#include "lldb/Target/StackFrame.h"
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
#include "lldb/Target/StackFrameRecognizer.h"
|
2012-09-01 01:02:41 +00:00
|
|
|
#include "lldb/Target/StopInfo.h"
|
2011-09-08 22:13:49 +00:00
|
|
|
#include "lldb/Target/Target.h"
|
2010-08-25 00:35:26 +00:00
|
|
|
#include "lldb/Target/Thread.h"
|
|
|
|
|
#include "lldb/Target/Unwind.h"
|
2022-02-03 13:26:10 +01:00
|
|
|
#include "lldb/Utility/LLDBLog.h"
|
2017-03-03 20:56:28 +00:00
|
|
|
#include "lldb/Utility/Log.h"
|
2018-10-05 23:23:15 +00:00
|
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
2010-06-08 16:52:24 +00:00
|
|
|
|
2019-02-11 23:13:08 +00:00
|
|
|
#include <memory>
|
|
|
|
|
|
2010-08-27 18:24:16 +00:00
|
|
|
//#define DEBUG_STACK_FRAMES 1
|
|
|
|
|
|
2010-06-08 16:52:24 +00:00
|
|
|
using namespace lldb;
|
|
|
|
|
using namespace lldb_private;
|
|
|
|
|
|
|
|
|
|
// StackFrameList constructor
|
2016-05-19 05:13:57 +00:00
|
|
|
StackFrameList::StackFrameList(Thread &thread,
|
|
|
|
|
const lldb::StackFrameListSP &prev_frames_sp,
|
|
|
|
|
bool show_inline_frames)
|
2024-12-12 12:48:41 -08:00
|
|
|
: m_thread(thread), m_prev_frames_sp(prev_frames_sp), m_frames(),
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
m_selected_frame_idx(), m_concrete_frames_fetched(0),
|
2016-05-19 05:13:57 +00:00
|
|
|
m_current_inlined_depth(UINT32_MAX),
|
|
|
|
|
m_current_inlined_pc(LLDB_INVALID_ADDRESS),
|
|
|
|
|
m_show_inlined_frames(show_inline_frames) {
|
2012-09-01 01:02:41 +00:00
|
|
|
if (prev_frames_sp) {
|
|
|
|
|
m_current_inlined_depth = prev_frames_sp->m_current_inlined_depth;
|
2016-05-19 05:13:57 +00:00
|
|
|
m_current_inlined_pc = prev_frames_sp->m_current_inlined_pc;
|
2012-09-01 01:02:41 +00:00
|
|
|
}
|
2010-06-08 16:52:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StackFrameList::~StackFrameList() {
|
2018-04-30 16:49:04 +00:00
|
|
|
// Call clear since this takes a lock and clears the stack frame list in case
|
|
|
|
|
// another thread is currently using this stack frame list
|
2013-03-28 18:41:44 +00:00
|
|
|
Clear();
|
2010-06-08 16:52:24 +00:00
|
|
|
}
|
|
|
|
|
|
2012-09-01 01:02:41 +00:00
|
|
|
void StackFrameList::CalculateCurrentInlinedDepth() {
|
|
|
|
|
uint32_t cur_inlined_depth = GetCurrentInlinedDepth();
|
|
|
|
|
if (cur_inlined_depth == UINT32_MAX) {
|
|
|
|
|
ResetCurrentInlinedDepth();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t StackFrameList::GetCurrentInlinedDepth() {
|
2024-12-12 12:48:41 -08:00
|
|
|
std::lock_guard<std::mutex> guard(m_inlined_depth_mutex);
|
2012-09-07 23:35:54 +00:00
|
|
|
if (m_show_inlined_frames && m_current_inlined_pc != LLDB_INVALID_ADDRESS) {
|
2012-09-01 01:02:41 +00:00
|
|
|
lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC();
|
|
|
|
|
if (cur_pc != m_current_inlined_pc) {
|
|
|
|
|
m_current_inlined_pc = LLDB_INVALID_ADDRESS;
|
|
|
|
|
m_current_inlined_depth = UINT32_MAX;
|
2022-01-31 15:57:48 +01:00
|
|
|
Log *log = GetLog(LLDBLog::Step);
|
2012-09-07 23:35:54 +00:00
|
|
|
if (log && log->GetVerbose())
|
2019-07-24 17:56:10 +00:00
|
|
|
LLDB_LOGF(
|
|
|
|
|
log,
|
2012-09-07 23:35:54 +00:00
|
|
|
"GetCurrentInlinedDepth: invalidating current inlined depth.\n");
|
2012-09-01 01:02:41 +00:00
|
|
|
}
|
|
|
|
|
return m_current_inlined_depth;
|
2016-09-06 20:57:50 +00:00
|
|
|
} else {
|
2012-09-01 01:02:41 +00:00
|
|
|
return UINT32_MAX;
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2012-09-01 01:02:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StackFrameList::ResetCurrentInlinedDepth() {
|
2018-08-01 17:07:56 +00:00
|
|
|
if (!m_show_inlined_frames)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
StopInfoSP stop_info_sp = m_thread.GetStopInfo();
|
|
|
|
|
if (!stop_info_sp)
|
|
|
|
|
return;
|
|
|
|
|
|
2024-10-30 09:28:38 -07:00
|
|
|
bool inlined = true;
|
|
|
|
|
auto inline_depth = stop_info_sp->GetSuggestedStackFrameIndex(inlined);
|
|
|
|
|
// We're only adjusting the inlined stack here.
|
|
|
|
|
Log *log = GetLog(LLDBLog::Step);
|
|
|
|
|
if (inline_depth) {
|
2024-12-12 12:48:41 -08:00
|
|
|
std::lock_guard<std::mutex> guard(m_inlined_depth_mutex);
|
2024-10-30 09:28:38 -07:00
|
|
|
m_current_inlined_depth = *inline_depth;
|
|
|
|
|
m_current_inlined_pc = m_thread.GetRegisterContext()->GetPC();
|
|
|
|
|
|
2018-08-01 17:07:56 +00:00
|
|
|
if (log && log->GetVerbose())
|
2019-07-24 17:56:10 +00:00
|
|
|
LLDB_LOGF(log,
|
|
|
|
|
"ResetCurrentInlinedDepth: setting inlined "
|
|
|
|
|
"depth: %d 0x%" PRIx64 ".\n",
|
2024-10-30 09:28:38 -07:00
|
|
|
m_current_inlined_depth, m_current_inlined_pc);
|
|
|
|
|
} else {
|
2024-12-12 12:48:41 -08:00
|
|
|
std::lock_guard<std::mutex> guard(m_inlined_depth_mutex);
|
|
|
|
|
m_current_inlined_pc = LLDB_INVALID_ADDRESS;
|
|
|
|
|
m_current_inlined_depth = UINT32_MAX;
|
2024-10-30 09:28:38 -07:00
|
|
|
if (log && log->GetVerbose())
|
|
|
|
|
LLDB_LOGF(
|
|
|
|
|
log,
|
|
|
|
|
"ResetCurrentInlinedDepth: Invalidating current inlined depth.\n");
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2012-09-01 01:02:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool StackFrameList::DecrementCurrentInlinedDepth() {
|
|
|
|
|
if (m_show_inlined_frames) {
|
|
|
|
|
uint32_t current_inlined_depth = GetCurrentInlinedDepth();
|
|
|
|
|
if (current_inlined_depth != UINT32_MAX) {
|
|
|
|
|
if (current_inlined_depth > 0) {
|
2024-12-12 12:48:41 -08:00
|
|
|
std::lock_guard<std::mutex> guard(m_inlined_depth_mutex);
|
2012-09-01 01:02:41 +00:00
|
|
|
m_current_inlined_depth--;
|
2012-09-06 19:24:57 +00:00
|
|
|
return true;
|
2012-09-01 01:02:41 +00:00
|
|
|
}
|
|
|
|
|
}
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2012-09-01 01:02:41 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-07 23:35:54 +00:00
|
|
|
void StackFrameList::SetCurrentInlinedDepth(uint32_t new_depth) {
|
2024-12-12 12:48:41 -08:00
|
|
|
std::lock_guard<std::mutex> guard(m_inlined_depth_mutex);
|
2012-09-07 23:35:54 +00:00
|
|
|
m_current_inlined_depth = new_depth;
|
|
|
|
|
if (new_depth == UINT32_MAX)
|
|
|
|
|
m_current_inlined_pc = LLDB_INVALID_ADDRESS;
|
|
|
|
|
else
|
|
|
|
|
m_current_inlined_pc = m_thread.GetRegisterContext()->GetPC();
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 12:48:41 -08:00
|
|
|
bool StackFrameList::WereAllFramesFetched() const {
|
|
|
|
|
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
|
|
|
|
|
return GetAllFramesFetched();
|
2018-08-01 17:07:40 +00:00
|
|
|
}
|
|
|
|
|
|
2020-03-17 17:59:08 -07:00
|
|
|
/// A sequence of calls that comprise some portion of a backtrace. Each frame
|
|
|
|
|
/// is represented as a pair of a callee (Function *) and an address within the
|
|
|
|
|
/// callee.
|
2020-06-02 16:01:11 +02:00
|
|
|
struct CallDescriptor {
|
|
|
|
|
Function *func;
|
2020-06-09 11:53:59 +02:00
|
|
|
CallEdge::AddrType address_type = CallEdge::AddrType::Call;
|
|
|
|
|
addr_t address = LLDB_INVALID_ADDRESS;
|
2020-06-02 16:01:11 +02:00
|
|
|
};
|
|
|
|
|
using CallSequence = std::vector<CallDescriptor>;
|
2020-03-17 17:59:08 -07:00
|
|
|
|
2018-10-05 23:23:15 +00:00
|
|
|
/// Find the unique path through the call graph from \p begin (with return PC
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
/// \p return_pc) to \p end. On success this path is stored into \p path, and
|
2018-10-05 23:23:15 +00:00
|
|
|
/// on failure \p path is unchanged.
|
2024-12-12 12:48:41 -08:00
|
|
|
/// This function doesn't currently access StackFrameLists at all, it only looks
|
|
|
|
|
/// at the frame set in the ExecutionContext it passes around.
|
2018-10-05 23:23:15 +00:00
|
|
|
static void FindInterveningFrames(Function &begin, Function &end,
|
2019-09-14 19:43:16 -07:00
|
|
|
ExecutionContext &exe_ctx, Target &target,
|
2020-03-17 17:59:08 -07:00
|
|
|
addr_t return_pc, CallSequence &path,
|
2018-10-05 23:23:15 +00:00
|
|
|
ModuleList &images, Log *log) {
|
|
|
|
|
LLDB_LOG(log, "Finding frames between {0} and {1}, retn-pc={2:x}",
|
|
|
|
|
begin.GetDisplayName(), end.GetDisplayName(), return_pc);
|
|
|
|
|
|
|
|
|
|
// Find a non-tail calling edge with the correct return PC.
|
|
|
|
|
if (log)
|
2019-09-14 19:43:16 -07:00
|
|
|
for (const auto &edge : begin.GetCallEdges())
|
2018-10-05 23:23:15 +00:00
|
|
|
LLDB_LOG(log, "FindInterveningFrames: found call with retn-PC = {0:x}",
|
2019-09-14 19:43:16 -07:00
|
|
|
edge->GetReturnPCAddress(begin, target));
|
2019-09-10 18:36:50 +00:00
|
|
|
CallEdge *first_edge = begin.GetCallEdgeForReturnAddress(return_pc, target);
|
|
|
|
|
if (!first_edge) {
|
2018-10-05 23:23:15 +00:00
|
|
|
LLDB_LOG(log, "No call edge outgoing from {0} with retn-PC == {1:x}",
|
|
|
|
|
begin.GetDisplayName(), return_pc);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The first callee may not be resolved, or there may be nothing to fill in.
|
2019-09-14 19:43:16 -07:00
|
|
|
Function *first_callee = first_edge->GetCallee(images, exe_ctx);
|
2018-10-05 23:23:15 +00:00
|
|
|
if (!first_callee) {
|
|
|
|
|
LLDB_LOG(log, "Could not resolve callee");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (first_callee == &end) {
|
|
|
|
|
LLDB_LOG(log, "Not searching further, first callee is {0} (retn-PC: {1:x})",
|
|
|
|
|
end.GetDisplayName(), return_pc);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run DFS on the tail-calling edges out of the first callee to find \p end.
|
|
|
|
|
// Fully explore the set of functions reachable from the first edge via tail
|
|
|
|
|
// calls in order to detect ambiguous executions.
|
|
|
|
|
struct DFS {
|
2020-03-17 17:59:08 -07:00
|
|
|
CallSequence active_path = {};
|
|
|
|
|
CallSequence solution_path = {};
|
2018-10-05 23:23:15 +00:00
|
|
|
llvm::SmallPtrSet<Function *, 2> visited_nodes = {};
|
|
|
|
|
bool ambiguous = false;
|
|
|
|
|
Function *end;
|
|
|
|
|
ModuleList &images;
|
2020-03-17 17:59:08 -07:00
|
|
|
Target ⌖
|
2019-09-14 19:43:16 -07:00
|
|
|
ExecutionContext &context;
|
2018-10-05 23:23:15 +00:00
|
|
|
|
2020-03-17 17:59:08 -07:00
|
|
|
DFS(Function *end, ModuleList &images, Target &target,
|
|
|
|
|
ExecutionContext &context)
|
|
|
|
|
: end(end), images(images), target(target), context(context) {}
|
2018-10-05 23:23:15 +00:00
|
|
|
|
2020-03-17 17:59:08 -07:00
|
|
|
void search(CallEdge &first_edge, Function &first_callee,
|
|
|
|
|
CallSequence &path) {
|
|
|
|
|
dfs(first_edge, first_callee);
|
2018-10-05 23:23:15 +00:00
|
|
|
if (!ambiguous)
|
|
|
|
|
path = std::move(solution_path);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-17 17:59:08 -07:00
|
|
|
void dfs(CallEdge ¤t_edge, Function &callee) {
|
2018-10-05 23:23:15 +00:00
|
|
|
// Found a path to the target function.
|
2019-09-30 21:20:14 +00:00
|
|
|
if (&callee == end) {
|
2018-10-05 23:23:15 +00:00
|
|
|
if (solution_path.empty())
|
|
|
|
|
solution_path = active_path;
|
|
|
|
|
else
|
|
|
|
|
ambiguous = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Terminate the search if tail recursion is found, or more generally if
|
|
|
|
|
// there's more than one way to reach a target. This errs on the side of
|
|
|
|
|
// caution: it conservatively stops searching when some solutions are
|
|
|
|
|
// still possible to save time in the average case.
|
2019-09-30 21:20:14 +00:00
|
|
|
if (!visited_nodes.insert(&callee).second) {
|
2018-10-05 23:23:15 +00:00
|
|
|
ambiguous = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Search the calls made from this callee.
|
2020-06-02 16:01:11 +02:00
|
|
|
active_path.push_back(CallDescriptor{&callee});
|
2019-09-14 19:43:16 -07:00
|
|
|
for (const auto &edge : callee.GetTailCallingEdges()) {
|
|
|
|
|
Function *next_callee = edge->GetCallee(images, context);
|
2018-10-05 23:23:15 +00:00
|
|
|
if (!next_callee)
|
|
|
|
|
continue;
|
|
|
|
|
|
2020-06-02 16:01:11 +02:00
|
|
|
std::tie(active_path.back().address_type, active_path.back().address) =
|
|
|
|
|
edge->GetCallerAddress(callee, target);
|
2020-03-17 17:59:08 -07:00
|
|
|
|
|
|
|
|
dfs(*edge, *next_callee);
|
2018-10-05 23:23:15 +00:00
|
|
|
if (ambiguous)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
active_path.pop_back();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-03-17 17:59:08 -07:00
|
|
|
DFS(&end, images, target, exe_ctx).search(*first_edge, *first_callee, path);
|
2018-10-05 23:23:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Given that \p next_frame will be appended to the frame list, synthesize
|
|
|
|
|
/// tail call frames between the current end of the list and \p next_frame.
|
|
|
|
|
/// If any frames are added, adjust the frame index of \p next_frame.
|
|
|
|
|
///
|
|
|
|
|
/// --------------
|
|
|
|
|
/// | ... | <- Completed frames.
|
|
|
|
|
/// --------------
|
|
|
|
|
/// | prev_frame |
|
|
|
|
|
/// --------------
|
|
|
|
|
/// | ... | <- Artificial frames inserted here.
|
|
|
|
|
/// --------------
|
|
|
|
|
/// | next_frame |
|
|
|
|
|
/// --------------
|
|
|
|
|
/// | ... | <- Not-yet-visited frames.
|
|
|
|
|
/// --------------
|
|
|
|
|
void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) {
|
2020-02-04 10:12:47 -08:00
|
|
|
// Cannot synthesize tail call frames when the stack is empty (there is no
|
|
|
|
|
// "previous" frame).
|
|
|
|
|
if (m_frames.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
2018-10-05 23:23:15 +00:00
|
|
|
TargetSP target_sp = next_frame.CalculateTarget();
|
|
|
|
|
if (!target_sp)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
lldb::RegisterContextSP next_reg_ctx_sp = next_frame.GetRegisterContext();
|
|
|
|
|
if (!next_reg_ctx_sp)
|
|
|
|
|
return;
|
|
|
|
|
|
2022-01-31 15:57:48 +01:00
|
|
|
Log *log = GetLog(LLDBLog::Step);
|
2018-10-05 23:23:15 +00:00
|
|
|
|
|
|
|
|
StackFrame &prev_frame = *m_frames.back().get();
|
|
|
|
|
|
|
|
|
|
// Find the functions prev_frame and next_frame are stopped in. The function
|
|
|
|
|
// objects are needed to search the lazy call graph for intervening frames.
|
|
|
|
|
Function *prev_func =
|
|
|
|
|
prev_frame.GetSymbolContext(eSymbolContextFunction).function;
|
|
|
|
|
if (!prev_func) {
|
|
|
|
|
LLDB_LOG(log, "SynthesizeTailCallFrames: can't find previous function");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Function *next_func =
|
|
|
|
|
next_frame.GetSymbolContext(eSymbolContextFunction).function;
|
|
|
|
|
if (!next_func) {
|
|
|
|
|
LLDB_LOG(log, "SynthesizeTailCallFrames: can't find next function");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try to find the unique sequence of (tail) calls which led from next_frame
|
|
|
|
|
// to prev_frame.
|
2020-03-17 17:59:08 -07:00
|
|
|
CallSequence path;
|
2018-10-05 23:23:15 +00:00
|
|
|
addr_t return_pc = next_reg_ctx_sp->GetPC();
|
|
|
|
|
Target &target = *target_sp.get();
|
|
|
|
|
ModuleList &images = next_frame.CalculateTarget()->GetImages();
|
2019-09-14 19:43:16 -07:00
|
|
|
ExecutionContext exe_ctx(target_sp, /*get_process=*/true);
|
|
|
|
|
exe_ctx.SetFramePtr(&next_frame);
|
|
|
|
|
FindInterveningFrames(*next_func, *prev_func, exe_ctx, target, return_pc,
|
|
|
|
|
path, images, log);
|
2018-10-05 23:23:15 +00:00
|
|
|
|
|
|
|
|
// Push synthetic tail call frames.
|
2020-03-17 17:59:08 -07:00
|
|
|
for (auto calleeInfo : llvm::reverse(path)) {
|
2020-06-02 16:01:11 +02:00
|
|
|
Function *callee = calleeInfo.func;
|
2018-10-05 23:23:15 +00:00
|
|
|
uint32_t frame_idx = m_frames.size();
|
|
|
|
|
uint32_t concrete_frame_idx = next_frame.GetConcreteFrameIndex();
|
|
|
|
|
addr_t cfa = LLDB_INVALID_ADDRESS;
|
|
|
|
|
bool cfa_is_valid = false;
|
2020-06-02 16:01:11 +02:00
|
|
|
addr_t pc = calleeInfo.address;
|
|
|
|
|
// If the callee address refers to the call instruction, we do not want to
|
|
|
|
|
// subtract 1 from this value.
|
2025-09-03 15:58:14 -07:00
|
|
|
const bool artificial = true;
|
2020-06-02 16:01:11 +02:00
|
|
|
const bool behaves_like_zeroth_frame =
|
|
|
|
|
calleeInfo.address_type == CallEdge::AddrType::Call;
|
2018-10-05 23:23:15 +00:00
|
|
|
SymbolContext sc;
|
|
|
|
|
callee->CalculateSymbolContext(&sc);
|
|
|
|
|
auto synth_frame = std::make_shared<StackFrame>(
|
|
|
|
|
m_thread.shared_from_this(), frame_idx, concrete_frame_idx, cfa,
|
2025-09-03 15:58:14 -07:00
|
|
|
cfa_is_valid, pc, StackFrame::Kind::Regular, artificial,
|
2019-08-02 16:53:42 +00:00
|
|
|
behaves_like_zeroth_frame, &sc);
|
2018-10-05 23:23:15 +00:00
|
|
|
m_frames.push_back(synth_frame);
|
2020-03-17 17:59:08 -07:00
|
|
|
LLDB_LOG(log, "Pushed frame {0} at {1:x}", callee->GetDisplayName(), pc);
|
2018-10-05 23:23:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If any frames were created, adjust next_frame's index.
|
|
|
|
|
if (!path.empty())
|
|
|
|
|
next_frame.SetFrameIndex(m_frames.size());
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-10 17:48:48 -07:00
|
|
|
bool StackFrameList::GetFramesUpTo(uint32_t end_idx,
|
|
|
|
|
InterruptionControl allow_interrupt) {
|
2024-12-12 12:48:41 -08:00
|
|
|
// GetFramesUpTo is always called with the intent to add frames, so get the
|
|
|
|
|
// writer lock:
|
|
|
|
|
std::unique_lock<std::shared_mutex> guard(m_list_mutex);
|
|
|
|
|
// Now that we have the lock, check to make sure someone didn't get there
|
|
|
|
|
// ahead of us:
|
|
|
|
|
if (m_frames.size() > end_idx || GetAllFramesFetched())
|
|
|
|
|
return false;
|
|
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
// Do not fetch frames for an invalid thread.
|
2023-05-10 17:48:48 -07:00
|
|
|
bool was_interrupted = false;
|
2016-02-18 18:52:47 +00:00
|
|
|
if (!m_thread.IsValid())
|
2023-05-10 17:48:48 -07:00
|
|
|
return false;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2024-12-12 12:48:41 -08:00
|
|
|
// lock the writer side of m_list_mutex as we're going to add frames here:
|
|
|
|
|
if (!m_show_inlined_frames) {
|
|
|
|
|
if (end_idx < m_concrete_frames_fetched)
|
|
|
|
|
return false;
|
|
|
|
|
// We're adding concrete frames now:
|
|
|
|
|
// FIXME: This should also be interruptible:
|
|
|
|
|
FetchOnlyConcreteFramesUpTo(end_idx);
|
2023-05-10 17:48:48 -07:00
|
|
|
return false;
|
2024-12-12 12:48:41 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We're adding concrete and inlined frames now:
|
|
|
|
|
was_interrupted = FetchFramesUpTo(end_idx, allow_interrupt);
|
|
|
|
|
|
|
|
|
|
#if defined(DEBUG_STACK_FRAMES)
|
|
|
|
|
s.PutCString("\n\nNew frames:\n");
|
|
|
|
|
Dump(&s);
|
|
|
|
|
s.EOL();
|
|
|
|
|
#endif
|
|
|
|
|
return was_interrupted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StackFrameList::FetchOnlyConcreteFramesUpTo(uint32_t end_idx) {
|
|
|
|
|
assert(m_thread.IsValid() && "Expected valid thread");
|
|
|
|
|
assert(m_frames.size() <= end_idx && "Expected there to be frames to fill");
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2020-03-09 14:10:41 +01:00
|
|
|
Unwind &unwinder = m_thread.GetUnwinder();
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2024-12-12 12:48:41 -08:00
|
|
|
if (end_idx < m_concrete_frames_fetched)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
uint32_t num_frames = unwinder.GetFramesUpTo(end_idx);
|
|
|
|
|
if (num_frames <= end_idx + 1) {
|
|
|
|
|
// Done unwinding.
|
|
|
|
|
m_concrete_frames_fetched = UINT32_MAX;
|
2018-08-01 17:07:40 +00:00
|
|
|
}
|
|
|
|
|
|
2024-12-12 12:48:41 -08:00
|
|
|
// Don't create the frames eagerly. Defer this work to GetFrameAtIndex,
|
|
|
|
|
// which can lazily query the unwinder to create frames.
|
|
|
|
|
m_frames.resize(num_frames);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool StackFrameList::FetchFramesUpTo(uint32_t end_idx,
|
|
|
|
|
InterruptionControl allow_interrupt) {
|
|
|
|
|
Unwind &unwinder = m_thread.GetUnwinder();
|
|
|
|
|
bool was_interrupted = false;
|
|
|
|
|
|
2010-08-27 18:24:16 +00:00
|
|
|
#if defined(DEBUG_STACK_FRAMES)
|
2018-08-01 17:07:40 +00:00
|
|
|
StreamFile s(stdout, false);
|
2010-08-27 18:24:16 +00:00
|
|
|
#endif
|
2018-08-01 17:07:40 +00:00
|
|
|
// If we are hiding some frames from the outside world, we need to add
|
|
|
|
|
// those onto the total count of frames to fetch. However, we don't need
|
|
|
|
|
// to do that if end_idx is 0 since in that case we always get the first
|
|
|
|
|
// concrete frame and all the inlined frames below it... And of course, if
|
|
|
|
|
// end_idx is UINT32_MAX that means get all, so just do that...
|
|
|
|
|
|
|
|
|
|
uint32_t inlined_depth = 0;
|
|
|
|
|
if (end_idx > 0 && end_idx != UINT32_MAX) {
|
|
|
|
|
inlined_depth = GetCurrentInlinedDepth();
|
|
|
|
|
if (inlined_depth != UINT32_MAX) {
|
|
|
|
|
if (end_idx > 0)
|
|
|
|
|
end_idx += inlined_depth;
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2018-08-01 17:07:40 +00:00
|
|
|
}
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
StackFrameSP unwind_frame_sp;
|
2023-03-15 11:20:20 -07:00
|
|
|
Debugger &dbg = m_thread.GetProcess()->GetTarget().GetDebugger();
|
2018-08-01 17:07:40 +00:00
|
|
|
do {
|
|
|
|
|
uint32_t idx = m_concrete_frames_fetched++;
|
|
|
|
|
lldb::addr_t pc = LLDB_INVALID_ADDRESS;
|
|
|
|
|
lldb::addr_t cfa = LLDB_INVALID_ADDRESS;
|
2019-08-02 16:53:42 +00:00
|
|
|
bool behaves_like_zeroth_frame = (idx == 0);
|
2018-08-01 17:07:40 +00:00
|
|
|
if (idx == 0) {
|
|
|
|
|
// We might have already created frame zero, only create it if we need
|
|
|
|
|
// to.
|
|
|
|
|
if (m_frames.empty()) {
|
|
|
|
|
RegisterContextSP reg_ctx_sp(m_thread.GetRegisterContext());
|
|
|
|
|
|
|
|
|
|
if (reg_ctx_sp) {
|
2020-03-09 14:10:41 +01:00
|
|
|
const bool success = unwinder.GetFrameInfoAtIndex(
|
|
|
|
|
idx, cfa, pc, behaves_like_zeroth_frame);
|
2018-08-01 17:07:40 +00:00
|
|
|
// There shouldn't be any way not to get the frame info for frame
|
|
|
|
|
// 0. But if the unwinder can't make one, lets make one by hand
|
|
|
|
|
// with the SP as the CFA and see if that gets any further.
|
|
|
|
|
if (!success) {
|
|
|
|
|
cfa = reg_ctx_sp->GetSP();
|
|
|
|
|
pc = reg_ctx_sp->GetPC();
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2018-08-01 17:07:40 +00:00
|
|
|
|
2019-02-11 23:13:08 +00:00
|
|
|
unwind_frame_sp = std::make_shared<StackFrame>(
|
|
|
|
|
m_thread.shared_from_this(), m_frames.size(), idx, reg_ctx_sp,
|
2019-08-02 16:53:42 +00:00
|
|
|
cfa, pc, behaves_like_zeroth_frame, nullptr);
|
2018-08-01 17:07:40 +00:00
|
|
|
m_frames.push_back(unwind_frame_sp);
|
2012-09-01 01:02:41 +00:00
|
|
|
}
|
2016-09-06 20:57:50 +00:00
|
|
|
} else {
|
2018-08-01 17:07:40 +00:00
|
|
|
unwind_frame_sp = m_frames.front();
|
[lldb] Track CFA pointer metadata in StackID (#157498)
[lldb] Track CFA pointer metadata in StackID
In this commit:
9c8e71644227 [lldb] Make StackID call Fix{Code,Data} pointers (#152796)
We made StackID keep track of the CFA without any pointer metadata in
it. This is necessary when comparing two StackIDs to determine which one
is "younger".
However, the CFA inside StackIDs is also used in other contexts through
the method StackID::GetCallFrameAddress. One notable case is
DWARFExpression: the computation of `DW_OP_call_frame_address` is done
using StackID. This feeds into many other places, e.g. expression
evaluation may require the address of a variable that is computed from
the CFA; to access the variable without faulting, we may need to
preserve the pointer metadata. As such, StackID must be able to provide
both versions of the CFA.
In the spirit of allowing consumers of pointers to decide what to do
with pointer metadata, this patch changes StackID to store both versions
of the cfa pointer. Two getter methods are provided, and all call sites
except DWARFExpression preserve their existing behavior (stripped
pointer). Other alternatives were considered:
* Just store the raw pointer. This would require changing the
comparisong operator `<` to also receive a Process, as the comparison
requires stripped pointers. It wasn't clear if all call-sites had a
non-null process, whereas we know we have a process when creating a
StackID.
* Store a weak pointer to the process inside the class, and then strip
metadata as needed. This would require a `weak_ptr::lock` in many
operations of LLDB, and it felt wasteful. It also prevents stripping
of the pointer if the process has gone away.
This patch also changes RegisterContextUnwind::ReadFrameAddress, which
is the method computing the CFA fed into StackID, to also preserve the
signature pointers.
2025-09-12 09:17:48 -07:00
|
|
|
cfa = unwind_frame_sp->m_id.GetCallFrameAddressWithoutMetadata();
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2018-08-01 17:07:40 +00:00
|
|
|
} else {
|
2023-05-10 17:48:48 -07:00
|
|
|
// Check for interruption when building the frames.
|
|
|
|
|
// Do the check in idx > 0 so that we'll always create a 0th frame.
|
2024-12-12 12:48:41 -08:00
|
|
|
if (allow_interrupt &&
|
|
|
|
|
INTERRUPT_REQUESTED(dbg, "Interrupted having fetched {0} frames",
|
|
|
|
|
m_frames.size())) {
|
|
|
|
|
was_interrupted = true;
|
|
|
|
|
break;
|
2023-05-10 17:48:48 -07:00
|
|
|
}
|
|
|
|
|
|
2020-03-09 14:10:41 +01:00
|
|
|
const bool success =
|
|
|
|
|
unwinder.GetFrameInfoAtIndex(idx, cfa, pc, behaves_like_zeroth_frame);
|
2018-08-01 17:07:40 +00:00
|
|
|
if (!success) {
|
|
|
|
|
// We've gotten to the end of the stack.
|
|
|
|
|
SetAllFramesFetched();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
const bool cfa_is_valid = true;
|
2019-02-11 23:13:08 +00:00
|
|
|
unwind_frame_sp = std::make_shared<StackFrame>(
|
|
|
|
|
m_thread.shared_from_this(), m_frames.size(), idx, cfa, cfa_is_valid,
|
2025-09-03 15:58:14 -07:00
|
|
|
pc, StackFrame::Kind::Regular, false, behaves_like_zeroth_frame,
|
|
|
|
|
nullptr);
|
2018-10-05 23:23:15 +00:00
|
|
|
|
|
|
|
|
// Create synthetic tail call frames between the previous frame and the
|
|
|
|
|
// newly-found frame. The new frame's index may change after this call,
|
|
|
|
|
// although its concrete index will stay the same.
|
|
|
|
|
SynthesizeTailCallFrames(*unwind_frame_sp.get());
|
|
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
m_frames.push_back(unwind_frame_sp);
|
|
|
|
|
}
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
assert(unwind_frame_sp);
|
|
|
|
|
SymbolContext unwind_sc = unwind_frame_sp->GetSymbolContext(
|
|
|
|
|
eSymbolContextBlock | eSymbolContextFunction);
|
|
|
|
|
Block *unwind_block = unwind_sc.block;
|
2021-03-03 19:25:30 -08:00
|
|
|
TargetSP target_sp = m_thread.CalculateTarget();
|
2018-08-01 17:07:40 +00:00
|
|
|
if (unwind_block) {
|
2021-03-03 19:25:30 -08:00
|
|
|
Address curr_frame_address(
|
|
|
|
|
unwind_frame_sp->GetFrameCodeAddressForSymbolication());
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
SymbolContext next_frame_sc;
|
|
|
|
|
Address next_frame_address;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
while (unwind_sc.GetParentOfInlinedScope(
|
|
|
|
|
curr_frame_address, next_frame_sc, next_frame_address)) {
|
|
|
|
|
next_frame_sc.line_entry.ApplyFileMappings(target_sp);
|
2019-08-02 16:53:42 +00:00
|
|
|
behaves_like_zeroth_frame = false;
|
|
|
|
|
StackFrameSP frame_sp(new StackFrame(
|
|
|
|
|
m_thread.shared_from_this(), m_frames.size(), idx,
|
|
|
|
|
unwind_frame_sp->GetRegisterContextSP(), cfa, next_frame_address,
|
|
|
|
|
behaves_like_zeroth_frame, &next_frame_sc));
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
m_frames.push_back(frame_sp);
|
|
|
|
|
unwind_sc = next_frame_sc;
|
|
|
|
|
curr_frame_address = next_frame_address;
|
2010-08-27 18:24:16 +00:00
|
|
|
}
|
2018-08-01 17:07:40 +00:00
|
|
|
}
|
|
|
|
|
} while (m_frames.size() - 1 < end_idx);
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
// Don't try to merge till you've calculated all the frames in this stack.
|
|
|
|
|
if (GetAllFramesFetched() && m_prev_frames_sp) {
|
|
|
|
|
StackFrameList *prev_frames = m_prev_frames_sp.get();
|
|
|
|
|
StackFrameList *curr_frames = this;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2010-08-27 18:24:16 +00:00
|
|
|
#if defined(DEBUG_STACK_FRAMES)
|
2018-08-01 17:07:40 +00:00
|
|
|
s.PutCString("\nprev_frames:\n");
|
|
|
|
|
prev_frames->Dump(&s);
|
|
|
|
|
s.PutCString("\ncurr_frames:\n");
|
|
|
|
|
curr_frames->Dump(&s);
|
|
|
|
|
s.EOL();
|
2010-08-27 18:24:16 +00:00
|
|
|
#endif
|
2018-08-01 17:07:40 +00:00
|
|
|
size_t curr_frame_num, prev_frame_num;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
for (curr_frame_num = curr_frames->m_frames.size(),
|
|
|
|
|
prev_frame_num = prev_frames->m_frames.size();
|
|
|
|
|
curr_frame_num > 0 && prev_frame_num > 0;
|
|
|
|
|
--curr_frame_num, --prev_frame_num) {
|
|
|
|
|
const size_t curr_frame_idx = curr_frame_num - 1;
|
|
|
|
|
const size_t prev_frame_idx = prev_frame_num - 1;
|
|
|
|
|
StackFrameSP curr_frame_sp(curr_frames->m_frames[curr_frame_idx]);
|
|
|
|
|
StackFrameSP prev_frame_sp(prev_frames->m_frames[prev_frame_idx]);
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2010-08-27 18:24:16 +00:00
|
|
|
#if defined(DEBUG_STACK_FRAMES)
|
2018-08-01 17:07:40 +00:00
|
|
|
s.Printf("\n\nCurr frame #%u ", curr_frame_idx);
|
|
|
|
|
if (curr_frame_sp)
|
|
|
|
|
curr_frame_sp->Dump(&s, true, false);
|
|
|
|
|
else
|
|
|
|
|
s.PutCString("NULL");
|
|
|
|
|
s.Printf("\nPrev frame #%u ", prev_frame_idx);
|
|
|
|
|
if (prev_frame_sp)
|
|
|
|
|
prev_frame_sp->Dump(&s, true, false);
|
|
|
|
|
else
|
|
|
|
|
s.PutCString("NULL");
|
2010-08-27 18:24:16 +00:00
|
|
|
#endif
|
|
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
StackFrame *curr_frame = curr_frame_sp.get();
|
|
|
|
|
StackFrame *prev_frame = prev_frame_sp.get();
|
2012-02-29 03:40:22 +00:00
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
if (curr_frame == nullptr || prev_frame == nullptr)
|
|
|
|
|
break;
|
2012-02-29 03:40:22 +00:00
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
// Check the stack ID to make sure they are equal.
|
|
|
|
|
if (curr_frame->GetStackID() != prev_frame->GetStackID())
|
|
|
|
|
break;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2018-08-01 17:07:40 +00:00
|
|
|
prev_frame->UpdatePreviousFrameFromCurrentFrame(*curr_frame);
|
|
|
|
|
// Now copy the fixed up previous frame into the current frames so the
|
|
|
|
|
// pointer doesn't change.
|
|
|
|
|
m_frames[curr_frame_idx] = prev_frame_sp;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2010-08-27 18:24:16 +00:00
|
|
|
#if defined(DEBUG_STACK_FRAMES)
|
2018-08-01 17:07:40 +00:00
|
|
|
s.Printf("\n Copying previous frame to current frame");
|
2010-08-27 21:47:54 +00:00
|
|
|
#endif
|
2012-02-29 03:40:22 +00:00
|
|
|
}
|
2018-08-01 17:07:40 +00:00
|
|
|
// We are done with the old stack frame list, we can release it now.
|
|
|
|
|
m_prev_frames_sp.reset();
|
|
|
|
|
}
|
2023-05-10 17:48:48 -07:00
|
|
|
// Don't report interrupted if we happen to have gotten all the frames:
|
|
|
|
|
if (!GetAllFramesFetched())
|
|
|
|
|
return was_interrupted;
|
|
|
|
|
return false;
|
2012-02-29 03:40:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t StackFrameList::GetNumFrames(bool can_create) {
|
2024-12-12 12:48:41 -08:00
|
|
|
if (!WereAllFramesFetched() && can_create) {
|
2023-05-10 17:48:48 -07:00
|
|
|
// Don't allow interrupt or we might not return the correct count
|
2024-12-12 12:48:41 -08:00
|
|
|
GetFramesUpTo(UINT32_MAX, DoNotAllowInterruption);
|
|
|
|
|
}
|
|
|
|
|
uint32_t frame_idx;
|
|
|
|
|
{
|
|
|
|
|
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
|
|
|
|
|
frame_idx = GetVisibleStackFrameIndex(m_frames.size());
|
2023-05-10 17:48:48 -07:00
|
|
|
}
|
2024-12-12 12:48:41 -08:00
|
|
|
return frame_idx;
|
2010-06-08 16:52:24 +00:00
|
|
|
}
|
|
|
|
|
|
2010-08-27 18:24:16 +00:00
|
|
|
void StackFrameList::Dump(Stream *s) {
|
2016-02-18 18:52:47 +00:00
|
|
|
if (s == nullptr)
|
2010-08-27 18:24:16 +00:00
|
|
|
return;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2024-12-12 12:48:41 -08:00
|
|
|
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2010-08-27 18:24:16 +00:00
|
|
|
const_iterator pos, begin = m_frames.begin(), end = m_frames.end();
|
|
|
|
|
for (pos = begin; pos != end; ++pos) {
|
2013-11-04 09:33:30 +00:00
|
|
|
StackFrame *frame = (*pos).get();
|
2014-04-04 04:06:10 +00:00
|
|
|
s->Printf("%p: ", static_cast<void *>(frame));
|
2010-08-27 18:24:16 +00:00
|
|
|
if (frame) {
|
2010-08-30 18:11:35 +00:00
|
|
|
frame->GetStackID().Dump(s);
|
2010-10-04 01:05:56 +00:00
|
|
|
frame->DumpUsingSettingsFormat(s);
|
2010-08-27 18:24:16 +00:00
|
|
|
} else
|
2011-11-04 03:34:56 +00:00
|
|
|
s->Printf("frame #%u", (uint32_t)std::distance(begin, pos));
|
2010-08-27 18:24:16 +00:00
|
|
|
s->EOL();
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2010-08-27 18:24:16 +00:00
|
|
|
s->EOL();
|
2010-06-08 16:52:24 +00:00
|
|
|
}
|
|
|
|
|
|
2010-08-25 00:35:26 +00:00
|
|
|
StackFrameSP StackFrameList::GetFrameAtIndex(uint32_t idx) {
|
2013-11-04 09:33:30 +00:00
|
|
|
StackFrameSP frame_sp;
|
2013-03-28 00:13:30 +00:00
|
|
|
uint32_t original_idx = idx;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2024-12-12 12:48:41 -08:00
|
|
|
// We're going to consult the m_frames.size, but if there are already
|
|
|
|
|
// enough frames for our request we don't want to block other readers, so
|
|
|
|
|
// first acquire the shared lock:
|
|
|
|
|
{ // Scope for shared lock:
|
|
|
|
|
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2024-12-12 12:48:41 -08:00
|
|
|
uint32_t inlined_depth = GetCurrentInlinedDepth();
|
|
|
|
|
if (inlined_depth != UINT32_MAX)
|
|
|
|
|
idx += inlined_depth;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2024-12-12 12:48:41 -08:00
|
|
|
if (idx < m_frames.size())
|
|
|
|
|
frame_sp = m_frames[idx];
|
|
|
|
|
|
|
|
|
|
if (frame_sp)
|
|
|
|
|
return frame_sp;
|
|
|
|
|
} // End of reader lock scope
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2018-04-30 16:49:04 +00:00
|
|
|
// GetFramesUpTo will fill m_frames with as many frames as you asked for, if
|
|
|
|
|
// there are that many. If there weren't then you asked for too many frames.
|
2023-05-10 17:48:48 -07:00
|
|
|
// GetFramesUpTo returns true if interrupted:
|
2024-12-12 12:48:41 -08:00
|
|
|
if (GetFramesUpTo(idx, AllowInterruption)) {
|
2023-05-10 17:48:48 -07:00
|
|
|
Log *log = GetLog(LLDBLog::Thread);
|
|
|
|
|
LLDB_LOG(log, "GetFrameAtIndex was interrupted");
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-12 12:48:41 -08:00
|
|
|
{ // Now we're accessing m_frames as a reader, so acquire the reader lock.
|
|
|
|
|
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
|
|
|
|
|
if (idx < m_frames.size()) {
|
2013-03-28 00:13:30 +00:00
|
|
|
frame_sp = m_frames[idx];
|
2024-12-12 12:48:41 -08:00
|
|
|
} else if (original_idx == 0) {
|
|
|
|
|
// There should ALWAYS be a frame at index 0. If something went wrong
|
|
|
|
|
// with the CurrentInlinedDepth such that there weren't as many frames as
|
|
|
|
|
// we thought taking that into account, then reset the current inlined
|
|
|
|
|
// depth and return the real zeroth frame.
|
|
|
|
|
if (m_frames.empty()) {
|
|
|
|
|
// Why do we have a thread with zero frames, that should not ever
|
|
|
|
|
// happen...
|
|
|
|
|
assert(!m_thread.IsValid() && "A valid thread has no frames.");
|
|
|
|
|
} else {
|
|
|
|
|
ResetCurrentInlinedDepth();
|
|
|
|
|
frame_sp = m_frames[original_idx];
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2013-03-28 00:13:30 +00:00
|
|
|
}
|
2024-12-12 12:48:41 -08:00
|
|
|
} // End of reader lock scope
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2010-06-08 16:52:24 +00:00
|
|
|
return frame_sp;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-04 09:33:30 +00:00
|
|
|
StackFrameSP
|
2011-01-06 22:15:06 +00:00
|
|
|
StackFrameList::GetFrameWithConcreteFrameIndex(uint32_t unwind_idx) {
|
|
|
|
|
// First try assuming the unwind index is the same as the frame index. The
|
2018-04-30 16:49:04 +00:00
|
|
|
// unwind index is always greater than or equal to the frame index, so it is
|
|
|
|
|
// a good place to start. If we have inlined frames we might have 5 concrete
|
|
|
|
|
// frames (frame unwind indexes go from 0-4), but we might have 15 frames
|
|
|
|
|
// after we make all the inlined frames. Most of the time the unwind frame
|
|
|
|
|
// index (or the concrete frame index) is the same as the frame index.
|
2011-01-06 22:15:06 +00:00
|
|
|
uint32_t frame_idx = unwind_idx;
|
2013-11-04 09:33:30 +00:00
|
|
|
StackFrameSP frame_sp(GetFrameAtIndex(frame_idx));
|
2011-01-06 22:15:06 +00:00
|
|
|
while (frame_sp) {
|
|
|
|
|
if (frame_sp->GetFrameIndex() == unwind_idx)
|
|
|
|
|
break;
|
|
|
|
|
frame_sp = GetFrameAtIndex(++frame_idx);
|
|
|
|
|
}
|
|
|
|
|
return frame_sp;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-04 09:33:30 +00:00
|
|
|
static bool CompareStackID(const StackFrameSP &stack_sp,
|
|
|
|
|
const StackID &stack_id) {
|
2013-05-24 00:58:29 +00:00
|
|
|
return stack_sp->GetStackID() < stack_id;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-17 07:49:44 +00:00
|
|
|
StackFrameSP StackFrameList::GetFrameWithStackID(const StackID &stack_id) {
|
2013-11-04 09:33:30 +00:00
|
|
|
StackFrameSP frame_sp;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2013-05-24 00:58:29 +00:00
|
|
|
if (stack_id.IsValid()) {
|
|
|
|
|
uint32_t frame_idx = 0;
|
2024-12-12 12:48:41 -08:00
|
|
|
{
|
|
|
|
|
// First see if the frame is already realized. This is the scope for
|
|
|
|
|
// the shared mutex:
|
|
|
|
|
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
|
|
|
|
|
// Do a binary search in case the stack frame is already in our cache
|
2013-05-24 00:58:29 +00:00
|
|
|
collection::const_iterator pos =
|
2024-12-12 12:48:41 -08:00
|
|
|
llvm::lower_bound(m_frames, stack_id, CompareStackID);
|
|
|
|
|
if (pos != m_frames.end() && (*pos)->GetStackID() == stack_id)
|
|
|
|
|
return *pos;
|
2011-03-31 00:15:49 +00:00
|
|
|
}
|
2024-12-12 12:48:41 -08:00
|
|
|
// If we needed to add more frames, we would get to here.
|
2016-09-06 20:57:50 +00:00
|
|
|
do {
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 08:33:37 +00:00
|
|
|
frame_sp = GetFrameAtIndex(frame_idx);
|
2013-05-24 00:58:29 +00:00
|
|
|
if (frame_sp && frame_sp->GetStackID() == stack_id)
|
2016-09-06 20:57:50 +00:00
|
|
|
break;
|
2013-05-24 00:58:29 +00:00
|
|
|
frame_idx++;
|
|
|
|
|
} while (frame_sp);
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2011-03-31 00:15:49 +00:00
|
|
|
return frame_sp;
|
|
|
|
|
}
|
2011-01-06 22:15:06 +00:00
|
|
|
|
2013-11-04 09:33:30 +00:00
|
|
|
bool StackFrameList::SetFrameAtIndex(uint32_t idx, StackFrameSP &frame_sp) {
|
2024-12-12 12:48:41 -08:00
|
|
|
std::unique_lock<std::shared_mutex> guard(m_list_mutex);
|
2010-08-27 18:24:16 +00:00
|
|
|
if (idx >= m_frames.size())
|
|
|
|
|
m_frames.resize(idx + 1);
|
2010-08-25 00:35:26 +00:00
|
|
|
// Make sure allocation succeeded by checking bounds again
|
2010-08-27 18:24:16 +00:00
|
|
|
if (idx < m_frames.size()) {
|
|
|
|
|
m_frames[idx] = frame_sp;
|
2010-06-08 16:52:24 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false; // resize failed, out of memory?
|
|
|
|
|
}
|
|
|
|
|
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
void StackFrameList::SelectMostRelevantFrame() {
|
|
|
|
|
// Don't call into the frame recognizers on the private state thread as
|
|
|
|
|
// they can cause code to run in the target, and that can cause deadlocks
|
|
|
|
|
// when fetching stop events for the expression.
|
2025-07-11 10:02:07 -07:00
|
|
|
if (m_thread.GetProcess()->CurrentThreadPosesAsPrivateStateThread())
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Log *log = GetLog(LLDBLog::Thread);
|
|
|
|
|
|
|
|
|
|
// Only the top frame should be recognized.
|
|
|
|
|
StackFrameSP frame_sp = GetFrameAtIndex(0);
|
|
|
|
|
if (!frame_sp) {
|
|
|
|
|
LLDB_LOG(log, "Failed to construct Frame #0");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RecognizedStackFrameSP recognized_frame_sp = frame_sp->GetRecognizedFrame();
|
|
|
|
|
|
2024-10-30 09:28:38 -07:00
|
|
|
if (recognized_frame_sp) {
|
|
|
|
|
if (StackFrameSP most_relevant_frame_sp =
|
|
|
|
|
recognized_frame_sp->GetMostRelevantFrame()) {
|
|
|
|
|
LLDB_LOG(log, "Found most relevant frame at index {0}",
|
|
|
|
|
most_relevant_frame_sp->GetFrameIndex());
|
|
|
|
|
SetSelectedFrame(most_relevant_frame_sp.get());
|
|
|
|
|
return;
|
|
|
|
|
}
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
}
|
2024-10-30 09:28:38 -07:00
|
|
|
LLDB_LOG(log, "Frame #0 not recognized");
|
2024-10-28 10:01:57 -07:00
|
|
|
|
2024-12-12 12:48:41 -08:00
|
|
|
// If this thread has a non-trivial StopInfo, then let it suggest
|
2024-10-30 09:28:38 -07:00
|
|
|
// a most relevant frame:
|
|
|
|
|
StopInfoSP stop_info_sp = m_thread.GetStopInfo();
|
|
|
|
|
uint32_t stack_idx = 0;
|
|
|
|
|
bool found_relevant = false;
|
|
|
|
|
if (stop_info_sp) {
|
|
|
|
|
// Here we're only asking the stop info if it wants to adjust the real stack
|
|
|
|
|
// index. We have to ask about the m_inlined_stack_depth in
|
|
|
|
|
// Thread::ShouldStop since the plans need to reason with that info.
|
|
|
|
|
bool inlined = false;
|
|
|
|
|
std::optional<uint32_t> stack_opt =
|
|
|
|
|
stop_info_sp->GetSuggestedStackFrameIndex(inlined);
|
|
|
|
|
if (stack_opt) {
|
|
|
|
|
stack_idx = *stack_opt;
|
|
|
|
|
found_relevant = true;
|
|
|
|
|
}
|
2024-10-28 11:52:32 -07:00
|
|
|
}
|
2024-10-30 09:28:38 -07:00
|
|
|
|
|
|
|
|
frame_sp = GetFrameAtIndex(stack_idx);
|
|
|
|
|
if (!frame_sp)
|
|
|
|
|
LLDB_LOG(log, "Stop info suggested relevant frame {0} but it didn't exist",
|
|
|
|
|
stack_idx);
|
|
|
|
|
else if (found_relevant)
|
|
|
|
|
LLDB_LOG(log, "Setting selected frame from stop info to {0}", stack_idx);
|
|
|
|
|
// Note, we don't have to worry about "inlined" frames here, because we've
|
|
|
|
|
// already calculated the inlined frame in Thread::ShouldStop, and
|
|
|
|
|
// SetSelectedFrame will take care of that adjustment for us.
|
|
|
|
|
SetSelectedFrame(frame_sp.get());
|
|
|
|
|
|
|
|
|
|
if (!found_relevant)
|
|
|
|
|
LLDB_LOG(log, "No relevant frame!");
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
}
|
|
|
|
|
|
2024-12-12 12:48:41 -08:00
|
|
|
uint32_t
|
|
|
|
|
StackFrameList::GetSelectedFrameIndex(SelectMostRelevant select_most_relevant) {
|
2025-07-25 18:03:09 -07:00
|
|
|
std::lock_guard<std::recursive_mutex> guard(m_selected_frame_mutex);
|
|
|
|
|
|
2023-04-21 13:49:01 -07:00
|
|
|
if (!m_selected_frame_idx && select_most_relevant)
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
SelectMostRelevantFrame();
|
2023-04-21 13:49:01 -07:00
|
|
|
if (!m_selected_frame_idx) {
|
|
|
|
|
// If we aren't selecting the most relevant frame, and the selected frame
|
|
|
|
|
// isn't set, then don't force a selection here, just return 0.
|
|
|
|
|
if (!select_most_relevant)
|
|
|
|
|
return 0;
|
2024-10-30 09:28:38 -07:00
|
|
|
// If the inlined stack frame is set, then use that:
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
m_selected_frame_idx = 0;
|
2023-04-21 13:49:01 -07:00
|
|
|
}
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
return *m_selected_frame_idx;
|
2010-06-08 16:52:24 +00:00
|
|
|
}
|
|
|
|
|
|
2013-11-04 09:33:30 +00:00
|
|
|
uint32_t StackFrameList::SetSelectedFrame(lldb_private::StackFrame *frame) {
|
2024-12-12 12:48:41 -08:00
|
|
|
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
|
2025-07-25 18:03:09 -07:00
|
|
|
std::lock_guard<std::recursive_mutex> selected_frame_guard(
|
|
|
|
|
m_selected_frame_mutex);
|
2024-12-12 12:48:41 -08:00
|
|
|
|
2010-08-25 00:35:26 +00:00
|
|
|
const_iterator pos;
|
2010-08-27 18:24:16 +00:00
|
|
|
const_iterator begin = m_frames.begin();
|
|
|
|
|
const_iterator end = m_frames.end();
|
2011-09-08 22:13:49 +00:00
|
|
|
m_selected_frame_idx = 0;
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
|
2010-06-08 16:52:24 +00:00
|
|
|
for (pos = begin; pos != end; ++pos) {
|
|
|
|
|
if (pos->get() == frame) {
|
2010-08-26 21:32:51 +00:00
|
|
|
m_selected_frame_idx = std::distance(begin, pos);
|
2012-09-01 01:02:41 +00:00
|
|
|
uint32_t inlined_depth = GetCurrentInlinedDepth();
|
|
|
|
|
if (inlined_depth != UINT32_MAX)
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
m_selected_frame_idx = *m_selected_frame_idx - inlined_depth;
|
2011-09-08 22:13:49 +00:00
|
|
|
break;
|
2010-06-08 16:52:24 +00:00
|
|
|
}
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2011-09-08 22:13:49 +00:00
|
|
|
SetDefaultFileAndLineToSelectedFrame();
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
return *m_selected_frame_idx;
|
2010-06-08 16:52:24 +00:00
|
|
|
}
|
|
|
|
|
|
2010-08-26 21:32:51 +00:00
|
|
|
bool StackFrameList::SetSelectedFrameByIndex(uint32_t idx) {
|
2013-11-04 09:33:30 +00:00
|
|
|
StackFrameSP frame_sp(GetFrameAtIndex(idx));
|
2012-02-29 03:40:22 +00:00
|
|
|
if (frame_sp) {
|
|
|
|
|
SetSelectedFrame(frame_sp.get());
|
|
|
|
|
return true;
|
|
|
|
|
} else
|
|
|
|
|
return false;
|
2011-09-08 22:13:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StackFrameList::SetDefaultFileAndLineToSelectedFrame() {
|
2012-02-21 00:09:25 +00:00
|
|
|
if (m_thread.GetID() ==
|
|
|
|
|
m_thread.GetProcess()->GetThreadList().GetSelectedThread()->GetID()) {
|
2023-04-21 13:49:01 -07:00
|
|
|
StackFrameSP frame_sp(
|
|
|
|
|
GetFrameAtIndex(GetSelectedFrameIndex(DoNoSelectMostRelevantFrame)));
|
2011-09-08 22:13:49 +00:00
|
|
|
if (frame_sp) {
|
2011-10-05 03:14:31 +00:00
|
|
|
SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextLineEntry);
|
2024-03-21 08:40:08 -07:00
|
|
|
if (sc.line_entry.GetFile())
|
2012-02-21 00:09:25 +00:00
|
|
|
m_thread.CalculateTarget()->GetSourceManager().SetDefaultFileAndLine(
|
2024-08-30 10:58:32 -07:00
|
|
|
sc.line_entry.file_sp, sc.line_entry.line);
|
2011-09-08 22:13:49 +00:00
|
|
|
}
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2010-06-08 16:52:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The thread has been run, reset the number stack frames to zero so we can
|
|
|
|
|
// determine how many frames we have lazily.
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
// Note, we don't actually re-use StackFrameLists, we always make a new
|
|
|
|
|
// StackFrameList every time we stop, and then copy frame information frame
|
|
|
|
|
// by frame from the old to the new StackFrameList. So the comment above,
|
|
|
|
|
// does not describe how StackFrameLists are currently used.
|
|
|
|
|
// Clear is currently only used to clear the list in the destructor.
|
2010-06-08 16:52:24 +00:00
|
|
|
void StackFrameList::Clear() {
|
2024-12-12 12:48:41 -08:00
|
|
|
std::unique_lock<std::shared_mutex> guard(m_list_mutex);
|
2010-08-27 18:24:16 +00:00
|
|
|
m_frames.clear();
|
2012-02-29 03:40:22 +00:00
|
|
|
m_concrete_frames_fetched = 0;
|
2025-07-25 18:03:09 -07:00
|
|
|
std::lock_guard<std::recursive_mutex> selected_frame_guard(
|
|
|
|
|
m_selected_frame_mutex);
|
[lldb] Move "SelectMostRelevantFrame" from Thread::WillStop
SelectMostRelevantFrame triggers the StackFrameRecognizer construction,
which can run arbitrary Python code, call expressions etc. WillStop gets
called on every private stop while the recognizers are a user-facing
feature, so first off doing this work on every stop is inefficient. But
more importantly, you can get in to situations where the recognizer
causes an expression to get run, then when we fetch the stop event at
the end of the expression evaluation, we call WillStop again on the
expression handling thread, which will do the same StackFrameRecognizer
work again. If anyone is locking along that path, you will end up with a
deadlock between the two threads.
The example that brought this to my attention was the
objc_exception_throw recognizer which can cause the objc runtime
introspection functions to get run, and those take a lock in
AppleObjCRuntimeV2::DynamicClassInfoExtractor::UpdateISAToDescriptorMap
along this path, so the second thread servicing the expression deadlocks
against the first thread waiting for the expression to complete.
It makes more sense to have the frame recognizers run on demand, either
when someone asks for the variables for the frame, or when someone does
GetSelectedFrame. The former already worked that way, the only reason
this was being done in WillStop was because the StackFrameRecognizers
can change the SelectedFrame, so you needed to run them before the
anyone requested the SelectedFrame.
This patch moves SelectMostRelevantFrame to StackFrameList, and runs it
when GetSelectedFrame is called for the first time on a given stop. If
you call SetSelectedFrame before GetSelectedFrame, then you should NOT
run the recognizer & change the frame out from under you. This patch
also makes that work. There were already tests for this behavior, and
for the feature that caused the hang, but the hang is racy, and it
doesn't trigger all the time, so I don't have a way to test that
explicitly.
One more detail: it's actually pretty easy to end up calling
GetSelectedFrame, for instance if you ask for the best ExecutionContext
from an ExecutionContextRef it will fill the StackFrame with the result
of GetSelectedFrame and that would still have the same problems if this
happens on the Private State Thread. So this patch also short-circuits
SelectMostRelevantFrame if run on the that thread. I can't think of any
reason the computations that go on on the Private State Thread would
actually want the SelectedFrame - that's a user-facing concept, so
avoiding that complication is the best way to go.
rdar://107643231
Differential revision: https://reviews.llvm.org/D147753
2023-04-07 12:17:57 -07:00
|
|
|
m_selected_frame_idx.reset();
|
2010-06-08 16:52:24 +00:00
|
|
|
}
|
|
|
|
|
|
2013-11-04 09:33:30 +00:00
|
|
|
lldb::StackFrameSP
|
|
|
|
|
StackFrameList::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) {
|
2024-12-12 12:48:41 -08:00
|
|
|
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
|
2010-09-23 17:40:12 +00:00
|
|
|
const_iterator pos;
|
|
|
|
|
const_iterator begin = m_frames.begin();
|
|
|
|
|
const_iterator end = m_frames.end();
|
2013-11-04 09:33:30 +00:00
|
|
|
lldb::StackFrameSP ret_sp;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2010-09-23 17:40:12 +00:00
|
|
|
for (pos = begin; pos != end; ++pos) {
|
|
|
|
|
if (pos->get() == stack_frame_ptr) {
|
|
|
|
|
ret_sp = (*pos);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2010-09-23 17:40:12 +00:00
|
|
|
return ret_sp;
|
|
|
|
|
}
|
|
|
|
|
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 08:33:37 +00:00
|
|
|
size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
|
|
|
|
|
uint32_t num_frames, bool show_frame_info,
|
2013-10-18 17:38:31 +00:00
|
|
|
uint32_t num_frames_with_source,
|
2024-08-23 09:55:47 -07:00
|
|
|
bool show_unique, bool show_hidden,
|
2013-10-18 17:38:31 +00:00
|
|
|
const char *selected_frame_marker) {
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 08:33:37 +00:00
|
|
|
size_t num_frames_displayed = 0;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 08:33:37 +00:00
|
|
|
if (num_frames == 0)
|
|
|
|
|
return 0;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2013-11-04 09:33:30 +00:00
|
|
|
StackFrameSP frame_sp;
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 08:33:37 +00:00
|
|
|
uint32_t frame_idx = 0;
|
|
|
|
|
uint32_t last_frame;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 08:33:37 +00:00
|
|
|
// Don't let the last frame wrap around...
|
|
|
|
|
if (num_frames == UINT32_MAX)
|
|
|
|
|
last_frame = UINT32_MAX;
|
|
|
|
|
else
|
|
|
|
|
last_frame = first_frame + num_frames;
|
2016-09-06 20:57:50 +00:00
|
|
|
|
2023-04-21 13:49:01 -07:00
|
|
|
StackFrameSP selected_frame_sp =
|
|
|
|
|
m_thread.GetSelectedFrame(DoNoSelectMostRelevantFrame);
|
2016-02-18 18:52:47 +00:00
|
|
|
const char *unselected_marker = nullptr;
|
2013-10-18 17:38:31 +00:00
|
|
|
std::string buffer;
|
|
|
|
|
if (selected_frame_marker) {
|
|
|
|
|
size_t len = strlen(selected_frame_marker);
|
|
|
|
|
buffer.insert(buffer.begin(), len, ' ');
|
|
|
|
|
unselected_marker = buffer.c_str();
|
2016-09-06 20:57:50 +00:00
|
|
|
}
|
2016-02-18 18:52:47 +00:00
|
|
|
const char *marker = nullptr;
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 08:33:37 +00:00
|
|
|
for (frame_idx = first_frame; frame_idx < last_frame; ++frame_idx) {
|
|
|
|
|
frame_sp = GetFrameAtIndex(frame_idx);
|
2016-02-18 18:52:47 +00:00
|
|
|
if (!frame_sp)
|
2016-09-06 20:57:50 +00:00
|
|
|
break;
|
|
|
|
|
|
2016-02-18 18:52:47 +00:00
|
|
|
if (selected_frame_marker != nullptr) {
|
2013-10-18 17:38:31 +00:00
|
|
|
if (frame_sp == selected_frame_sp)
|
|
|
|
|
marker = selected_frame_marker;
|
2016-09-06 20:57:50 +00:00
|
|
|
else
|
2013-10-18 17:38:31 +00:00
|
|
|
marker = unselected_marker;
|
|
|
|
|
}
|
2024-08-23 09:55:47 -07:00
|
|
|
|
|
|
|
|
// Hide uninteresting frames unless it's the selected frame.
|
|
|
|
|
if (!show_hidden && frame_sp != selected_frame_sp && frame_sp->IsHidden())
|
|
|
|
|
continue;
|
|
|
|
|
|
2023-05-10 17:48:48 -07:00
|
|
|
// Check for interruption here. If we're fetching arguments, this loop
|
|
|
|
|
// can go slowly:
|
|
|
|
|
Debugger &dbg = m_thread.GetProcess()->GetTarget().GetDebugger();
|
2024-07-09 01:34:06 -07:00
|
|
|
if (INTERRUPT_REQUESTED(
|
|
|
|
|
dbg, "Interrupted dumping stack for thread {0:x} with {1} shown.",
|
|
|
|
|
m_thread.GetID(), num_frames_displayed))
|
2023-05-10 17:48:48 -07:00
|
|
|
break;
|
2023-05-23 11:13:36 -07:00
|
|
|
|
2016-09-06 20:57:50 +00:00
|
|
|
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 08:33:37 +00:00
|
|
|
if (!frame_sp->GetStatus(strm, show_frame_info,
|
|
|
|
|
num_frames_with_source > (first_frame - frame_idx),
|
2017-06-12 16:25:24 +00:00
|
|
|
show_unique, marker))
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 08:33:37 +00:00
|
|
|
break;
|
|
|
|
|
++num_frames_displayed;
|
|
|
|
|
}
|
2016-09-06 20:57:50 +00:00
|
|
|
|
Centralized a lot of the status information for processes,
threads, and stack frame down in the lldb_private::Process,
lldb_private::Thread, lldb_private::StackFrameList and the
lldb_private::StackFrame classes. We had some command line
commands that had duplicate versions of the process status
output ("thread list" and "process status" for example).
Removed the "file" command and placed it where it should
have been: "target create". Made an alias for "file" to
"target create" so we stay compatible with GDB commands.
We can now have multple usable targets in lldb at the
same time. This is nice for comparing two runs of a program
or debugging more than one binary at the same time. The
new command is "target select <target-idx>" and also to see
a list of the current targets you can use the new "target list"
command. The flow in a debug session can be:
(lldb) target create /path/to/exe/a.out
(lldb) breakpoint set --name main
(lldb) run
... hit breakpoint
(lldb) target create /bin/ls
(lldb) run /tmp
Process 36001 exited with status = 0 (0x00000000)
(lldb) target list
Current targets:
target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
* target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) target select 0
Current targets:
* target #0: /tmp/args/a.out ( arch=x86_64-apple-darwin, platform=localhost, pid=35999, state=stopped )
target #1: /bin/ls ( arch=x86_64-apple-darwin, platform=localhost, pid=36001, state=exited )
(lldb) bt
* thread #1: tid = 0x2d03, 0x0000000100000b9a a.out`main + 42 at main.c:16, stop reason = breakpoint 1.1
frame #0: 0x0000000100000b9a a.out`main + 42 at main.c:16
frame #1: 0x0000000100000b64 a.out`start + 52
Above we created a target for "a.out" and ran and hit a
breakpoint at "main". Then we created a new target for /bin/ls
and ran it. Then we listed the targest and selected our original
"a.out" program, so we showed two concurent debug sessions
going on at the same time.
llvm-svn: 129695
2011-04-18 08:33:37 +00:00
|
|
|
strm.IndentLess();
|
|
|
|
|
return num_frames_displayed;
|
|
|
|
|
}
|
2025-09-08 17:18:25 +01:00
|
|
|
|
|
|
|
|
void StackFrameList::ClearSelectedFrameIndex() { m_selected_frame_idx.reset(); }
|