Skip to content

Commit 7df3d99

Browse files
committed
Reduce the use of m_list_mutex, try to separate reader and writer
lock uses.
1 parent 3236978 commit 7df3d99

File tree

3 files changed

+98
-112
lines changed

3 files changed

+98
-112
lines changed

lldb/include/lldb/Target/StackFrameList.h

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ class StackFrameList {
9494
bool show_frame_info, uint32_t num_frames_with_source,
9595
bool show_unique = false, bool show_hidden = false,
9696
const char *frame_marker = nullptr);
97+
98+
/// Returns whether we have currently fetched all the frames of a stack.
99+
bool WereAllFramesFetched() const;
97100

98101
protected:
99102
friend class Thread;
@@ -108,14 +111,20 @@ class StackFrameList {
108111

109112
/// Ensures that frames up to (and including) `end_idx` are realized in the
110113
/// StackFrameList. `end_idx` can be larger than the actual number of frames,
111-
/// in which case all the frames will be fetched.
114+
/// in which case all the frames will be fetched. Acquires the writer end of
115+
/// the list mutex.
112116
/// Returns true if the function was interrupted, false otherwise.
113-
/// Must be called with a shared mutex locked in `guard`.
114-
bool GetFramesUpTo(uint32_t end_idx, InterruptionControl allow_interrupt,
115-
std::shared_lock<std::shared_mutex> &guard);
116-
117-
bool GetAllFramesFetched() { return m_concrete_frames_fetched == UINT32_MAX; }
117+
/// Callers should first check (under the shared mutex) whether we need to
118+
/// fetch frames or not.
119+
bool GetFramesUpTo(uint32_t end_idx, InterruptionControl allow_interrupt);
120+
121+
// This should be called with either the reader or writer end of the list
122+
// mutex held:
123+
bool GetAllFramesFetched() const {
124+
return m_concrete_frames_fetched == UINT32_MAX;
125+
}
118126

127+
// This should be called with the writer end of the list mutex held.
119128
void SetAllFramesFetched() { m_concrete_frames_fetched = UINT32_MAX; }
120129

121130
bool DecrementCurrentInlinedDepth();
@@ -188,21 +197,14 @@ class StackFrameList {
188197
GetFrameAtIndexNoLock(uint32_t idx,
189198
std::shared_lock<std::shared_mutex> &guard);
190199

191-
/// These two Fetch frames APIs are called in GetFramesUpTo, they are the ones
192-
/// that actually add frames. They must be called with the stack list shared
193-
/// lock acquired. They will acquire the writer end of the stack list mutex
194-
/// to add frames, but they will always exit with the shared side reacquired.
195-
/// Returns true if fetching frames was interrupted, false otherwise.
196-
bool FetchFramesUpTo(uint32_t end_idx, InterruptionControl allow_interrupt,
197-
std::shared_lock<std::shared_mutex> &guard);
200+
/// These two Fetch frames APIs and SynthesizeTailCallFrames are called in
201+
/// GetFramesUpTo, they are the ones that actually add frames. They must be
202+
/// called with the writer end of the list mutex held.
198203

199-
/// This is the same as FetchFramesUpTo, but only fetches concrete frames.
200-
/// It is not currently interruptible - so it returns nothing.
201-
void FetchOnlyConcreteFramesUpTo(uint32_t end_idx,
202-
std::shared_lock<std::shared_mutex> &guard);
203-
204-
// This is a utility function, called by FetchFramesUpTo. It must be called
205-
// with the writer end of the stack list mutex held.
204+
/// Returns true if fetching frames was interrupted, false otherwise.
205+
bool FetchFramesUpTo(uint32_t end_idx, InterruptionControl allow_interrupt);
206+
/// Not currently interruptible so returns void.
207+
void FetchOnlyConcreteFramesUpTo(uint32_t end_idx);
206208
void SynthesizeTailCallFrames(StackFrame &next_frame);
207209

208210
StackFrameList(const StackFrameList &) = delete;

lldb/source/Target/StackFrameList.cpp

Lines changed: 75 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ void StackFrameList::SetCurrentInlinedDepth(uint32_t new_depth) {
138138
m_current_inlined_pc = m_thread.GetRegisterContext()->GetPC();
139139
}
140140

141+
bool StackFrameList::WereAllFramesFetched() const {
142+
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
143+
return GetAllFramesFetched();
144+
}
145+
141146
/// A sequence of calls that comprise some portion of a backtrace. Each frame
142147
/// is represented as a pair of a callee (Function *) and an address within the
143148
/// callee.
@@ -335,56 +340,47 @@ void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) {
335340
}
336341

337342
bool StackFrameList::GetFramesUpTo(uint32_t end_idx,
338-
InterruptionControl allow_interrupt,
339-
std::shared_lock<std::shared_mutex> &guard) {
340-
// Do not fetch frames for an invalid thread.
341-
bool was_interrupted = false;
342-
if (!m_thread.IsValid())
343+
InterruptionControl allow_interrupt) {
344+
// GetFramesUpTo is always called with the intent to add frames, so get the
345+
// writer lock:
346+
std::unique_lock<std::shared_mutex> guard(m_list_mutex);
347+
// Now that we have the lock, check to make sure someone didn't get there
348+
// ahead of us:
349+
if (m_frames.size() > end_idx || GetAllFramesFetched())
343350
return false;
344351

345-
// We've already gotten more frames than asked for, or we've already finished
346-
// unwinding, return.
347-
if (m_frames.size() > end_idx || GetAllFramesFetched())
352+
// Do not fetch frames for an invalid thread.
353+
bool was_interrupted = false;
354+
if (!m_thread.IsValid())
348355
return false;
349356

357+
// lock the writer side of m_list_mutex as we're going to add frames here:
350358
if (!m_show_inlined_frames) {
351359
if (end_idx < m_concrete_frames_fetched)
352360
return false;
353361
// We're adding concrete frames now:
354362
// FIXME: This should also be interruptible:
355-
FetchOnlyConcreteFramesUpTo(end_idx, guard);
363+
FetchOnlyConcreteFramesUpTo(end_idx);
356364
return false;
357365
}
358366

359367
// We're adding concrete and inlined frames now:
360-
was_interrupted = FetchFramesUpTo(end_idx, allow_interrupt, guard);
368+
was_interrupted = FetchFramesUpTo(end_idx, allow_interrupt);
361369

362370
#if defined(DEBUG_STACK_FRAMES)
363371
s.PutCString("\n\nNew frames:\n");
364372
Dump(&s);
365373
s.EOL();
366374
#endif
367-
// Don't report interrupted if we happen to have gotten all the frames:
368-
if (!GetAllFramesFetched())
369-
return was_interrupted;
370-
return false;
371-
375+
return was_interrupted;
372376
}
373377

374-
void StackFrameList::FetchOnlyConcreteFramesUpTo(
375-
uint32_t end_idx, std::shared_lock<std::shared_mutex> &guard) {
378+
void StackFrameList::FetchOnlyConcreteFramesUpTo(uint32_t end_idx) {
376379
assert(m_thread.IsValid() && "Expected valid thread");
377380
assert(m_frames.size() <= end_idx && "Expected there to be frames to fill");
378-
assert(guard.owns_lock() && "Must be called with the shared lock acquired");
379381

380382
Unwind &unwinder = m_thread.GetUnwinder();
381383

382-
guard.unlock();
383-
m_list_mutex.lock();
384-
auto on_exit = llvm::make_scope_exit([&]() {
385-
m_list_mutex.unlock();
386-
guard.lock();
387-
});
388384
if (end_idx < m_concrete_frames_fetched)
389385
return;
390386

@@ -401,20 +397,10 @@ void StackFrameList::FetchOnlyConcreteFramesUpTo(
401397

402398

403399
bool StackFrameList::FetchFramesUpTo(uint32_t end_idx,
404-
InterruptionControl allow_interrupt,
405-
std::shared_lock<std::shared_mutex> &guard) {
406-
assert(guard.owns_lock() && "Must be called with the shared lock acquired");
407-
400+
InterruptionControl allow_interrupt) {
408401
Unwind &unwinder = m_thread.GetUnwinder();
409402
bool was_interrupted = false;
410403

411-
guard.unlock();
412-
m_list_mutex.lock();
413-
auto on_exit = llvm::make_scope_exit([&]() {
414-
m_list_mutex.unlock();
415-
guard.lock();
416-
});
417-
418404
#if defined(DEBUG_STACK_FRAMES)
419405
StreamFile s(stdout, false);
420406
#endif
@@ -583,16 +569,19 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx,
583569
// We are done with the old stack frame list, we can release it now.
584570
m_prev_frames_sp.reset();
585571
}
586-
return was_interrupted;
572+
// Don't report interrupted if we happen to have gotten all the frames:
573+
if (!GetAllFramesFetched())
574+
return was_interrupted;
575+
return false;
587576
}
588577

589578
uint32_t StackFrameList::GetNumFrames(bool can_create) {
590-
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
591-
592-
if (can_create) {
579+
if (!GetAllFramesFetched() && can_create) {
593580
// Don't allow interrupt or we might not return the correct count
594-
GetFramesUpTo(UINT32_MAX, DoNotAllowInterruption, guard);
581+
GetFramesUpTo(UINT32_MAX, DoNotAllowInterruption);
595582
}
583+
// Formally we should acquire the shared lock here, but since we've forced
584+
// fetching all the frames, it can't change anymore so that's not required.
596585
return GetVisibleStackFrameIndex(m_frames.size());
597586
}
598587

@@ -617,29 +606,30 @@ void StackFrameList::Dump(Stream *s) {
617606
}
618607

619608
StackFrameSP StackFrameList::GetFrameAtIndex(uint32_t idx) {
620-
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
621-
return GetFrameAtIndexNoLock(idx, guard);
622-
}
623-
624-
StackFrameSP StackFrameList::GetFrameAtIndexNoLock(
625-
uint32_t idx, std::shared_lock<std::shared_mutex> &guard) {
626609
StackFrameSP frame_sp;
627610
uint32_t original_idx = idx;
628611

629-
uint32_t inlined_depth = GetCurrentInlinedDepth();
630-
if (inlined_depth != UINT32_MAX)
631-
idx += inlined_depth;
612+
// We're going to consult the m_frames.size, but if there are already
613+
// enough frames for our request we don't want to block other readers, so
614+
// first acquire the shared lock:
615+
{ // Scope for shared lock:
616+
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
617+
618+
uint32_t inlined_depth = GetCurrentInlinedDepth();
619+
if (inlined_depth != UINT32_MAX)
620+
idx += inlined_depth;
632621

633-
if (idx < m_frames.size())
634-
frame_sp = m_frames[idx];
622+
if (idx < m_frames.size())
623+
frame_sp = m_frames[idx];
635624

636-
if (frame_sp)
637-
return frame_sp;
625+
if (frame_sp)
626+
return frame_sp;
627+
}
638628

639629
// GetFramesUpTo will fill m_frames with as many frames as you asked for, if
640630
// there are that many. If there weren't then you asked for too many frames.
641631
// GetFramesUpTo returns true if interrupted:
642-
if (GetFramesUpTo(idx, AllowInterruption, guard)) {
632+
if (GetFramesUpTo(idx, AllowInterruption)) {
643633
Log *log = GetLog(LLDBLog::Thread);
644634
LLDB_LOG(log, "GetFrameAtIndex was interrupted");
645635
return {};
@@ -674,12 +664,11 @@ StackFrameList::GetFrameWithConcreteFrameIndex(uint32_t unwind_idx) {
674664
// after we make all the inlined frames. Most of the time the unwind frame
675665
// index (or the concrete frame index) is the same as the frame index.
676666
uint32_t frame_idx = unwind_idx;
677-
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
678-
StackFrameSP frame_sp(GetFrameAtIndexNoLock(frame_idx, guard));
667+
StackFrameSP frame_sp(GetFrameAtIndex(frame_idx));
679668
while (frame_sp) {
680669
if (frame_sp->GetFrameIndex() == unwind_idx)
681670
break;
682-
frame_sp = GetFrameAtIndexNoLock(++frame_idx, guard);
671+
frame_sp = GetFrameAtIndex(++frame_idx);
683672
}
684673
return frame_sp;
685674
}
@@ -693,21 +682,26 @@ StackFrameSP StackFrameList::GetFrameWithStackID(const StackID &stack_id) {
693682
StackFrameSP frame_sp;
694683

695684
if (stack_id.IsValid()) {
696-
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
697685
uint32_t frame_idx = 0;
698-
// Do a binary search in case the stack frame is already in our cache
699-
collection::const_iterator begin = m_frames.begin();
700-
collection::const_iterator end = m_frames.end();
701-
if (begin != end) {
702-
collection::const_iterator pos =
703-
std::lower_bound(begin, end, stack_id, CompareStackID);
704-
if (pos != end) {
705-
if ((*pos)->GetStackID() == stack_id)
706-
return *pos;
686+
{
687+
// First see if the frame is already realized. This is the scope for
688+
// the shared mutex:
689+
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
690+
// Do a binary search in case the stack frame is already in our cache
691+
collection::const_iterator begin = m_frames.begin();
692+
collection::const_iterator end = m_frames.end();
693+
if (begin != end) {
694+
collection::const_iterator pos =
695+
std::lower_bound(begin, end, stack_id, CompareStackID);
696+
if (pos != end) {
697+
if ((*pos)->GetStackID() == stack_id)
698+
return *pos;
699+
}
707700
}
708701
}
702+
// If we needed to add more frames, we would get to here.
709703
do {
710-
frame_sp = GetFrameAtIndexNoLock(frame_idx, guard);
704+
frame_sp = GetFrameAtIndex(frame_idx);
711705
if (frame_sp && frame_sp->GetStackID() == stack_id)
712706
break;
713707
frame_idx++;
@@ -794,34 +788,23 @@ uint32_t
794788
StackFrameList::GetSelectedFrameIndex(SelectMostRelevant select_most_relevant) {
795789
if (!m_selected_frame_idx && select_most_relevant)
796790
SelectMostRelevantFrame();
797-
{ // Scope for lock guard
798-
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
799-
if (!m_selected_frame_idx) {
800-
// If we aren't selecting the most relevant frame, and the selected frame
801-
// isn't set, then don't force a selection here, just return 0.
802-
if (!select_most_relevant)
803-
return 0;
804-
// If the inlined stack frame is set, then use that:
805-
m_selected_frame_idx = 0;
806-
}
807-
return *m_selected_frame_idx;
808-
}
809-
}
810-
811-
uint32_t StackFrameList::SetSelectedFrame(lldb_private::StackFrame *frame) {
812-
uint32_t result = 0;
813-
{
814-
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
815-
result = SetSelectedFrameNoLock(frame);
816-
}
817-
SetDefaultFileAndLineToSelectedFrame();
818-
return result;
791+
if (!m_selected_frame_idx) {
792+
// If we aren't selecting the most relevant frame, and the selected frame
793+
// isn't set, then don't force a selection here, just return 0.
794+
if (!select_most_relevant)
795+
return 0;
796+
// If the inlined stack frame is set, then use that:
797+
m_selected_frame_idx = 0;
798+
}
799+
return *m_selected_frame_idx;
819800
}
820801

821802
uint32_t
822-
StackFrameList::SetSelectedFrameNoLock(lldb_private::StackFrame *frame) {
803+
StackFrameList::SetSelectedFrame(lldb_private::StackFrame *frame) {
823804
uint32_t result = 0;
824805

806+
std::shared_lock<std::shared_mutex> guard(m_list_mutex);
807+
825808
const_iterator pos;
826809
const_iterator begin = m_frames.begin();
827810
const_iterator end = m_frames.end();
@@ -836,6 +819,7 @@ StackFrameList::SetSelectedFrameNoLock(lldb_private::StackFrame *frame) {
836819
break;
837820
}
838821
}
822+
SetDefaultFileAndLineToSelectedFrame();
839823
result = *m_selected_frame_idx;
840824
return result;
841825
}

lldb/source/Target/Thread.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1449,7 +1449,7 @@ void Thread::ClearStackFrames() {
14491449
// frames:
14501450
// FIXME: At some point we can try to splice in the frames we have fetched
14511451
// into the new frame as we make it, but let's not try that now.
1452-
if (m_curr_frames_sp && m_curr_frames_sp->GetAllFramesFetched())
1452+
if (m_curr_frames_sp && m_curr_frames_sp->WereAllFramesFetched())
14531453
m_prev_frames_sp.swap(m_curr_frames_sp);
14541454
m_curr_frames_sp.reset();
14551455

0 commit comments

Comments
 (0)