diff --git a/lldb/include/lldb/Target/StackFrameList.h b/lldb/include/lldb/Target/StackFrameList.h --- a/lldb/include/lldb/Target/StackFrameList.h +++ b/lldb/include/lldb/Target/StackFrameList.h @@ -46,7 +46,7 @@ uint32_t SetSelectedFrame(lldb_private::StackFrame *frame); /// Get the currently selected frame index. - uint32_t GetSelectedFrameIndex() const; + uint32_t GetSelectedFrameIndex(); /// Mark a stack frame as the currently selected frame using the frame index /// \p idx. Like \ref GetFrameAtIndex, invisible frames cannot be selected. @@ -109,6 +109,8 @@ uint32_t GetCurrentInlinedDepth(); void SetCurrentInlinedDepth(uint32_t new_depth); + + void SelectMostRelevantFrame(); typedef std::vector collection; typedef collection::iterator iterator; @@ -136,6 +138,10 @@ /// The currently selected frame. uint32_t m_selected_frame_idx; + // Records whether anyone has set the selected frame on this stack yet. + // We only let recognizers change the frame if this is the first time + // GetSelectedFrame is called. + bool m_selected_frame_set = false; /// The number of concrete frames fetched while filling the frame list. This /// is only used when synthetic frames are enabled. diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -217,8 +217,6 @@ virtual void RefreshStateAfterStop() = 0; - void SelectMostRelevantFrame(); - std::string GetStopDescription(); std::string GetStopDescriptionRaw(); diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp --- a/lldb/source/Target/StackFrameList.cpp +++ b/lldb/source/Target/StackFrameList.cpp @@ -18,6 +18,7 @@ #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" +#include "lldb/Target/StackFrameRecognizer.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" @@ -781,8 +782,46 @@ return false; // resize failed, out of memory? } -uint32_t StackFrameList::GetSelectedFrameIndex() const { +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. + if (m_thread.GetProcess()->CurrentThreadIsPrivateStateThread()) + 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(); + + if (!recognized_frame_sp) { + LLDB_LOG(log, "Frame #0 not recognized"); + return; + } + + 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()); + } else { + LLDB_LOG(log, "No relevant frame!"); + } +} + +uint32_t StackFrameList::GetSelectedFrameIndex() { std::lock_guard guard(m_mutex); + if (m_selected_frame_set == false) { + SelectMostRelevantFrame(); + m_selected_frame_set = true; + } return m_selected_frame_idx; } @@ -792,6 +831,8 @@ const_iterator begin = m_frames.begin(); const_iterator end = m_frames.end(); m_selected_frame_idx = 0; + m_selected_frame_set = true; + for (pos = begin; pos != end; ++pos) { if (pos->get() == frame) { m_selected_frame_idx = std::distance(begin, pos); @@ -830,10 +871,16 @@ // The thread has been run, reset the number stack frames to zero so we can // determine how many frames we have lazily. +// 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. void StackFrameList::Clear() { std::lock_guard guard(m_mutex); m_frames.clear(); m_concrete_frames_fetched = 0; + m_selected_frame_set = false; } lldb::StackFrameSP diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -588,36 +588,9 @@ return raw_stop_description; } -void Thread::SelectMostRelevantFrame() { - Log *log = GetLog(LLDBLog::Thread); - - auto frames_list_sp = GetStackFrameList(); - - // Only the top frame should be recognized. - auto frame_sp = frames_list_sp->GetFrameAtIndex(0); - - auto recognized_frame_sp = frame_sp->GetRecognizedFrame(); - - if (!recognized_frame_sp) { - LLDB_LOG(log, "Frame #0 not recognized"); - return; - } - - 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()); - } else { - LLDB_LOG(log, "No relevant frame!"); - } -} - void Thread::WillStop() { ThreadPlan *current_plan = GetCurrentPlan(); - SelectMostRelevantFrame(); - // FIXME: I may decide to disallow threads with no plans. In which // case this should go to an assert.