This is an archive of the discontinued LLVM Phabricator instance.

Move "SelectMostRelevantFrame" from Thread::WillStop
ClosedPublic

Authored by jingham on Apr 6 2023, 6:31 PM.

Details

Summary

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 lldb_private::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.

Diff Detail

Event Timeline

jingham created this revision.Apr 6 2023, 6:31 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 6 2023, 6:31 PM
jingham requested review of this revision.Apr 6 2023, 6:31 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 6 2023, 6:31 PM
JDevlieghere added inline comments.Apr 6 2023, 7:20 PM
lldb/include/lldb/Target/StackFrameList.h
138–143

The purpose of the boolean is to differentiate between m_selected_frame_idx == 0 meaning the zeroth frame is selected versus we haven't yet selected the current frame, right? If so I think we should make this an std::optional<bool>.

lldb/source/Target/StackFrameList.cpp
822

IIUC SetSelectedFrame always sets m_selected_frame_set to true, so is this redundant?

882

Should m_selected_frame_idx also be set to 0 here?

jingham added inline comments.Apr 6 2023, 11:07 PM
lldb/include/lldb/Target/StackFrameList.h
138–143

I don't follow. Did you mean make m_selected_frame_idx a std::optional<uint32_t>?

You could do it that way for sure. I didn't because I'm not as fond of gadgets. Most clients should be calling GetSelectedFrameIndex which will always set it to something (0 if it can't figure anything else out). So the optionality shouldn't be much visible.

lldb/source/Target/StackFrameList.cpp
822

Sure, but if you do that you have to change SelectMostRelevantFrame to select the 0th frame if there's no relevant frame. At present it leaves it unselected. Doing the selection always in SMRF is clearer, though, so it would be a good change.

882

Yes, though this function doesn't currently matter.

JDevlieghere added inline comments.Apr 6 2023, 11:15 PM
lldb/source/Target/StackFrameList.cpp
822

Nvm, I confused SelectMostRelevantFrame with SetSelectedFrame

mib accepted this revision.Apr 7 2023, 11:19 AM

This makes sense to me. LGTM!

This revision is now accepted and ready to land.Apr 7 2023, 11:19 AM
This revision was automatically updated to reflect the committed changes.

Jim asked me to land this on his behalf as he's OOO. I've addressed my own comments :-)