summaryrefslogtreecommitdiff
path: root/lldb/source/Target/StackFrameList.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Target/StackFrameList.cpp')
-rw-r--r--lldb/source/Target/StackFrameList.cpp237
1 files changed, 123 insertions, 114 deletions
diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp
index 94a381edd5e2..9c6208e9e0a6 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -38,7 +38,7 @@ using namespace lldb_private;
StackFrameList::StackFrameList(Thread &thread,
const lldb::StackFrameListSP &prev_frames_sp,
bool show_inline_frames)
- : m_thread(thread), m_prev_frames_sp(prev_frames_sp), m_mutex(), m_frames(),
+ : m_thread(thread), m_prev_frames_sp(prev_frames_sp), m_frames(),
m_selected_frame_idx(), m_concrete_frames_fetched(0),
m_current_inlined_depth(UINT32_MAX),
m_current_inlined_pc(LLDB_INVALID_ADDRESS),
@@ -63,6 +63,7 @@ void StackFrameList::CalculateCurrentInlinedDepth() {
}
uint32_t StackFrameList::GetCurrentInlinedDepth() {
+ std::lock_guard<std::mutex> guard(m_inlined_depth_mutex);
if (m_show_inlined_frames && m_current_inlined_pc != LLDB_INVALID_ADDRESS) {
lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC();
if (cur_pc != m_current_inlined_pc) {
@@ -84,11 +85,6 @@ void StackFrameList::ResetCurrentInlinedDepth() {
if (!m_show_inlined_frames)
return;
- std::lock_guard<std::recursive_mutex> guard(m_mutex);
-
- m_current_inlined_pc = LLDB_INVALID_ADDRESS;
- m_current_inlined_depth = UINT32_MAX;
-
StopInfoSP stop_info_sp = m_thread.GetStopInfo();
if (!stop_info_sp)
return;
@@ -98,6 +94,7 @@ void StackFrameList::ResetCurrentInlinedDepth() {
// We're only adjusting the inlined stack here.
Log *log = GetLog(LLDBLog::Step);
if (inline_depth) {
+ std::lock_guard<std::mutex> guard(m_inlined_depth_mutex);
m_current_inlined_depth = *inline_depth;
m_current_inlined_pc = m_thread.GetRegisterContext()->GetPC();
@@ -107,6 +104,9 @@ void StackFrameList::ResetCurrentInlinedDepth() {
"depth: %d 0x%" PRIx64 ".\n",
m_current_inlined_depth, m_current_inlined_pc);
} else {
+ std::lock_guard<std::mutex> guard(m_inlined_depth_mutex);
+ m_current_inlined_pc = LLDB_INVALID_ADDRESS;
+ m_current_inlined_depth = UINT32_MAX;
if (log && log->GetVerbose())
LLDB_LOGF(
log,
@@ -119,6 +119,7 @@ bool StackFrameList::DecrementCurrentInlinedDepth() {
uint32_t current_inlined_depth = GetCurrentInlinedDepth();
if (current_inlined_depth != UINT32_MAX) {
if (current_inlined_depth > 0) {
+ std::lock_guard<std::mutex> guard(m_inlined_depth_mutex);
m_current_inlined_depth--;
return true;
}
@@ -128,6 +129,7 @@ bool StackFrameList::DecrementCurrentInlinedDepth() {
}
void StackFrameList::SetCurrentInlinedDepth(uint32_t new_depth) {
+ std::lock_guard<std::mutex> guard(m_inlined_depth_mutex);
m_current_inlined_depth = new_depth;
if (new_depth == UINT32_MAX)
m_current_inlined_pc = LLDB_INVALID_ADDRESS;
@@ -135,23 +137,9 @@ void StackFrameList::SetCurrentInlinedDepth(uint32_t new_depth) {
m_current_inlined_pc = m_thread.GetRegisterContext()->GetPC();
}
-void StackFrameList::GetOnlyConcreteFramesUpTo(uint32_t end_idx,
- Unwind &unwinder) {
- assert(m_thread.IsValid() && "Expected valid thread");
- assert(m_frames.size() <= end_idx && "Expected there to be frames to fill");
-
- 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;
- }
-
- // 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::WereAllFramesFetched() const {
+ std::shared_lock<std::shared_mutex> guard(m_list_mutex);
+ return GetAllFramesFetched();
}
/// A sequence of calls that comprise some portion of a backtrace. Each frame
@@ -167,6 +155,8 @@ using CallSequence = std::vector<CallDescriptor>;
/// Find the unique path through the call graph from \p begin (with return PC
/// \p return_pc) to \p end. On success this path is stored into \p path, and
/// on failure \p path is unchanged.
+/// This function doesn't currently access StackFrameLists at all, it only looks
+/// at the frame set in the ExecutionContext it passes around.
static void FindInterveningFrames(Function &begin, Function &end,
ExecutionContext &exe_ctx, Target &target,
addr_t return_pc, CallSequence &path,
@@ -350,23 +340,65 @@ void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) {
bool StackFrameList::GetFramesUpTo(uint32_t end_idx,
InterruptionControl allow_interrupt) {
+ // 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;
+
// Do not fetch frames for an invalid thread.
bool was_interrupted = false;
if (!m_thread.IsValid())
return false;
- // We've already gotten more frames than asked for, or we've already finished
- // unwinding, return.
- if (m_frames.size() > end_idx || GetAllFramesFetched())
+ // 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);
return false;
+ }
+
+ // 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");
Unwind &unwinder = m_thread.GetUnwinder();
- if (!m_show_inlined_frames) {
- GetOnlyConcreteFramesUpTo(end_idx, unwinder);
- return false;
+ 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;
}
+ // 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;
+
#if defined(DEBUG_STACK_FRAMES)
StreamFile s(stdout, false);
#endif
@@ -421,11 +453,11 @@ bool StackFrameList::GetFramesUpTo(uint32_t end_idx,
} else {
// Check for interruption when building the frames.
// Do the check in idx > 0 so that we'll always create a 0th frame.
- if (allow_interrupt
- && INTERRUPT_REQUESTED(dbg, "Interrupted having fetched {0} frames",
- m_frames.size())) {
- was_interrupted = true;
- break;
+ if (allow_interrupt &&
+ INTERRUPT_REQUESTED(dbg, "Interrupted having fetched {0} frames",
+ m_frames.size())) {
+ was_interrupted = true;
+ break;
}
const bool success =
@@ -534,12 +566,6 @@ bool StackFrameList::GetFramesUpTo(uint32_t end_idx,
// We are done with the old stack frame list, we can release it now.
m_prev_frames_sp.reset();
}
-
-#if defined(DEBUG_STACK_FRAMES)
- s.PutCString("\n\nNew frames:\n");
- Dump(&s);
- s.EOL();
-#endif
// Don't report interrupted if we happen to have gotten all the frames:
if (!GetAllFramesFetched())
return was_interrupted;
@@ -547,20 +573,23 @@ bool StackFrameList::GetFramesUpTo(uint32_t end_idx,
}
uint32_t StackFrameList::GetNumFrames(bool can_create) {
- std::lock_guard<std::recursive_mutex> guard(m_mutex);
-
- if (can_create) {
+ if (!WereAllFramesFetched() && can_create) {
// Don't allow interrupt or we might not return the correct count
- GetFramesUpTo(UINT32_MAX, DoNotAllowInterruption);
+ GetFramesUpTo(UINT32_MAX, DoNotAllowInterruption);
+ }
+ uint32_t frame_idx;
+ {
+ std::shared_lock<std::shared_mutex> guard(m_list_mutex);
+ frame_idx = GetVisibleStackFrameIndex(m_frames.size());
}
- return GetVisibleStackFrameIndex(m_frames.size());
+ return frame_idx;
}
void StackFrameList::Dump(Stream *s) {
if (s == nullptr)
return;
- std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ std::shared_lock<std::shared_mutex> guard(m_list_mutex);
const_iterator pos, begin = m_frames.begin(), end = m_frames.end();
for (pos = begin; pos != end; ++pos) {
@@ -578,72 +607,53 @@ void StackFrameList::Dump(Stream *s) {
StackFrameSP StackFrameList::GetFrameAtIndex(uint32_t idx) {
StackFrameSP frame_sp;
- std::lock_guard<std::recursive_mutex> guard(m_mutex);
uint32_t original_idx = idx;
- uint32_t inlined_depth = GetCurrentInlinedDepth();
- if (inlined_depth != UINT32_MAX)
- idx += inlined_depth;
+ // 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);
- if (idx < m_frames.size())
- frame_sp = m_frames[idx];
+ uint32_t inlined_depth = GetCurrentInlinedDepth();
+ if (inlined_depth != UINT32_MAX)
+ idx += inlined_depth;
- if (frame_sp)
- return frame_sp;
+ if (idx < m_frames.size())
+ frame_sp = m_frames[idx];
+
+ if (frame_sp)
+ return frame_sp;
+ } // End of reader lock scope
// 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.
// GetFramesUpTo returns true if interrupted:
- if (GetFramesUpTo(idx)) {
+ if (GetFramesUpTo(idx, AllowInterruption)) {
Log *log = GetLog(LLDBLog::Thread);
LLDB_LOG(log, "GetFrameAtIndex was interrupted");
return {};
}
- if (idx < m_frames.size()) {
- if (m_show_inlined_frames) {
- // When inline frames are enabled we actually create all the frames in
- // GetFramesUpTo.
+ { // 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()) {
frame_sp = m_frames[idx];
- } else {
- addr_t pc, cfa;
- bool behaves_like_zeroth_frame = (idx == 0);
- if (m_thread.GetUnwinder().GetFrameInfoAtIndex(
- idx, cfa, pc, behaves_like_zeroth_frame)) {
- const bool cfa_is_valid = true;
- frame_sp = std::make_shared<StackFrame>(
- m_thread.shared_from_this(), idx, idx, cfa, cfa_is_valid, pc,
- StackFrame::Kind::Regular, behaves_like_zeroth_frame, nullptr);
-
- Function *function =
- frame_sp->GetSymbolContext(eSymbolContextFunction).function;
- if (function) {
- // When we aren't showing inline functions we always use the top
- // most function block as the scope.
- frame_sp->SetSymbolContextScope(&function->GetBlock(false));
- } else {
- // Set the symbol scope from the symbol regardless if it is nullptr
- // or valid.
- frame_sp->SetSymbolContextScope(
- frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol);
- }
- SetFrameAtIndex(idx, frame_sp);
+ } 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];
}
}
- } 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];
- }
- }
+ } // End of reader lock scope
return frame_sp;
}
@@ -675,19 +685,18 @@ StackFrameSP StackFrameList::GetFrameWithStackID(const StackID &stack_id) {
StackFrameSP frame_sp;
if (stack_id.IsValid()) {
- std::lock_guard<std::recursive_mutex> guard(m_mutex);
uint32_t frame_idx = 0;
- // Do a binary search in case the stack frame is already in our cache
- collection::const_iterator begin = m_frames.begin();
- collection::const_iterator end = m_frames.end();
- if (begin != end) {
+ {
+ // 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
collection::const_iterator pos =
- std::lower_bound(begin, end, stack_id, CompareStackID);
- if (pos != end) {
- if ((*pos)->GetStackID() == stack_id)
- return *pos;
- }
+ llvm::lower_bound(m_frames, stack_id, CompareStackID);
+ if (pos != m_frames.end() && (*pos)->GetStackID() == stack_id)
+ return *pos;
}
+ // If we needed to add more frames, we would get to here.
do {
frame_sp = GetFrameAtIndex(frame_idx);
if (frame_sp && frame_sp->GetStackID() == stack_id)
@@ -699,6 +708,7 @@ StackFrameSP StackFrameList::GetFrameWithStackID(const StackID &stack_id) {
}
bool StackFrameList::SetFrameAtIndex(uint32_t idx, StackFrameSP &frame_sp) {
+ std::unique_lock<std::shared_mutex> guard(m_list_mutex);
if (idx >= m_frames.size())
m_frames.resize(idx + 1);
// Make sure allocation succeeded by checking bounds again
@@ -738,7 +748,7 @@ void StackFrameList::SelectMostRelevantFrame() {
}
LLDB_LOG(log, "Frame #0 not recognized");
- // If this thread has a non-trivial StopInof, then let it suggest
+ // If this thread has a non-trivial StopInfo, then let it suggest
// a most relevant frame:
StopInfoSP stop_info_sp = m_thread.GetStopInfo();
uint32_t stack_idx = 0;
@@ -771,9 +781,8 @@ void StackFrameList::SelectMostRelevantFrame() {
LLDB_LOG(log, "No relevant frame!");
}
-uint32_t StackFrameList::GetSelectedFrameIndex(
- SelectMostRelevant select_most_relevant) {
- std::lock_guard<std::recursive_mutex> guard(m_mutex);
+uint32_t
+StackFrameList::GetSelectedFrameIndex(SelectMostRelevant select_most_relevant) {
if (!m_selected_frame_idx && select_most_relevant)
SelectMostRelevantFrame();
if (!m_selected_frame_idx) {
@@ -788,7 +797,8 @@ uint32_t StackFrameList::GetSelectedFrameIndex(
}
uint32_t StackFrameList::SetSelectedFrame(lldb_private::StackFrame *frame) {
- std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ std::shared_lock<std::shared_mutex> guard(m_list_mutex);
+
const_iterator pos;
const_iterator begin = m_frames.begin();
const_iterator end = m_frames.end();
@@ -803,13 +813,11 @@ uint32_t StackFrameList::SetSelectedFrame(lldb_private::StackFrame *frame) {
break;
}
}
-
SetDefaultFileAndLineToSelectedFrame();
return *m_selected_frame_idx;
}
bool StackFrameList::SetSelectedFrameByIndex(uint32_t idx) {
- std::lock_guard<std::recursive_mutex> guard(m_mutex);
StackFrameSP frame_sp(GetFrameAtIndex(idx));
if (frame_sp) {
SetSelectedFrame(frame_sp.get());
@@ -840,7 +848,7 @@ void StackFrameList::SetDefaultFileAndLineToSelectedFrame() {
// does not describe how StackFrameLists are currently used.
// Clear is currently only used to clear the list in the destructor.
void StackFrameList::Clear() {
- std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ std::unique_lock<std::shared_mutex> guard(m_list_mutex);
m_frames.clear();
m_concrete_frames_fetched = 0;
m_selected_frame_idx.reset();
@@ -848,6 +856,7 @@ void StackFrameList::Clear() {
lldb::StackFrameSP
StackFrameList::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) {
+ std::shared_lock<std::shared_mutex> guard(m_list_mutex);
const_iterator pos;
const_iterator begin = m_frames.begin();
const_iterator end = m_frames.end();