diff options
Diffstat (limited to 'lldb/source/Target/StackFrameList.cpp')
| -rw-r--r-- | lldb/source/Target/StackFrameList.cpp | 237 |
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(); |
