-
Notifications
You must be signed in to change notification settings - Fork 15.2k
Add the ability to break on call-site locations, improve inline stepping #112939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
9c6705b
7a60871
a19b3dc
b35e42b
ecda9b3
8662acf
38f883f
03764f2
594fb63
e3c26b0
9861c7d
a2aeab6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -508,8 +508,20 @@ void BreakpointLocation::GetDescription(Stream *s, | |
| s->PutCString("re-exported target = "); | ||
| else | ||
| s->PutCString("where = "); | ||
|
|
||
| // If there's a preferred line entry for printing, use that. | ||
| bool show_function_info = true; | ||
| if (GetPreferredLineEntry()) { | ||
| sc.line_entry = *GetPreferredLineEntry(); | ||
| // FIXME: We're going to get the function name wrong when the preferred | ||
| // line entry is not the lowest one. For now, just leave the function | ||
| // out in this case, but we really should also figure out how to easily | ||
| // fake the function name here. | ||
|
Comment on lines
+516
to
+519
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would a solution that changed That would calll
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What would it mean to replace the Module, the CompileUnit or the Function? This is for a breakpoint location, so whatever we do has to result in this being the same address. I was hesitant to introduce a more general "breakpoint gets to modify its symbol context" if I didn't know what it meant or how to ensure we didn't end up with a SymbolContext that wasn't at the breakpoint location's address. I was originally planning to JUST store the "inlined call stack depth" to make it clear this couldn't change the PC value, but that ended up being more annoying than helpful.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added one more change here, since the intention is that you cannot change the PC of the breakpoint Location by setting a PreferredLineEntry, I changed the code to actually check that when you call SetPreferredLineEntry. I put in an assert in the setter, so we can catch any violations of this in the testsuite, and then if this happens IRL, I made the BreakpointLocation log the event and ignore the setting. |
||
| show_function_info = false; | ||
| } | ||
| sc.DumpStopContext(s, m_owner.GetTarget().GetProcessSP().get(), m_address, | ||
| false, true, false, true, true, true); | ||
| false, true, false, show_function_info, | ||
| show_function_info, show_function_info); | ||
| } else { | ||
| if (sc.module_sp) { | ||
| s->EOL(); | ||
|
|
@@ -537,7 +549,10 @@ void BreakpointLocation::GetDescription(Stream *s, | |
| if (sc.line_entry.line > 0) { | ||
| s->EOL(); | ||
| s->Indent("location = "); | ||
| sc.line_entry.DumpStopContext(s, true); | ||
| if (GetPreferredLineEntry()) | ||
| GetPreferredLineEntry()->DumpStopContext(s, true); | ||
jimingham marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| else | ||
| sc.line_entry.DumpStopContext(s, true); | ||
| } | ||
|
|
||
| } else { | ||
|
|
@@ -656,6 +671,49 @@ void BreakpointLocation::SendBreakpointLocationChangedEvent( | |
| } | ||
| } | ||
|
|
||
| std::optional<uint32_t> BreakpointLocation::GetSuggestedStackFrameIndex() { | ||
| if (!GetPreferredLineEntry()) | ||
| return {}; | ||
| LineEntry preferred = *GetPreferredLineEntry(); | ||
jimingham marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| SymbolContext sc; | ||
| if (!m_address.CalculateSymbolContext(&sc)) | ||
| return {}; | ||
| // Don't return anything special if frame 0 is the preferred line entry. | ||
| // We not really telling the stack frame list to do anything special in that | ||
| // case. | ||
| if (!LineEntry::Compare(sc.line_entry, preferred)) | ||
| return {}; | ||
|
|
||
| if (!sc.block) | ||
| return {}; | ||
|
|
||
| // Blocks have their line info in Declaration form, so make one here: | ||
| Declaration preferred_decl(preferred.GetFile(), preferred.line, | ||
| preferred.column); | ||
|
|
||
| uint32_t depth = 0; | ||
| Block *inlined_block = sc.block->GetContainingInlinedBlock(); | ||
| while (inlined_block) { | ||
| // If we've moved to a block that this isn't the start of, that's not | ||
| // our inlining info or call site, so we can stop here. | ||
| Address start_address; | ||
| if (!inlined_block->GetStartAddress(start_address) || | ||
| start_address != m_address) | ||
| return {}; | ||
|
|
||
| const InlineFunctionInfo *info = inlined_block->GetInlinedFunctionInfo(); | ||
| if (info) { | ||
| if (preferred_decl == info->GetDeclaration()) | ||
| return depth; | ||
| if (preferred_decl == info->GetCallSite()) | ||
| return depth + 1; | ||
| } | ||
| inlined_block = inlined_block->GetInlinedParent(); | ||
| depth++; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should there be a
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would mean there were cycles in the blocks? I don't think we guard against that anywhere else that we're traversing the block structure. |
||
| } | ||
| return {}; | ||
| } | ||
|
|
||
| void BreakpointLocation::SwapLocation(BreakpointLocationSP swap_from) { | ||
| m_address = swap_from->m_address; | ||
| m_should_resolve_indirect_functions = | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -251,7 +251,10 @@ void CompileUnit::ResolveSymbolContext( | |||||||||||||
| SymbolContextItem resolve_scope, SymbolContextList &sc_list, | ||||||||||||||
| RealpathPrefixes *realpath_prefixes) { | ||||||||||||||
| const FileSpec file_spec = src_location_spec.GetFileSpec(); | ||||||||||||||
| const uint32_t line = src_location_spec.GetLine().value_or(0); | ||||||||||||||
| const uint32_t line = | ||||||||||||||
| src_location_spec.GetLine().value_or(LLDB_INVALID_LINE_NUMBER); | ||||||||||||||
| const uint32_t column_num = | ||||||||||||||
| src_location_spec.GetColumn().value_or(LLDB_INVALID_COLUMN_NUMBER); | ||||||||||||||
| const bool check_inlines = src_location_spec.GetCheckInlines(); | ||||||||||||||
|
|
||||||||||||||
| // First find all of the file indexes that match our "file_spec". If | ||||||||||||||
|
|
@@ -312,6 +315,112 @@ void CompileUnit::ResolveSymbolContext( | |||||||||||||
| 0, file_indexes, src_location_spec, &line_entry); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // If we didn't manage to find a breakpoint that matched the line number | ||||||||||||||
| // requested, that might be because it is only an inline call site, and | ||||||||||||||
| // doesn't have a line entry in the line table. Scan for that here. | ||||||||||||||
| // | ||||||||||||||
| // We are making the assumption that if there was an inlined function it will | ||||||||||||||
| // contribute at least 1 non-call-site entry to the line table. That's handy | ||||||||||||||
| // because we don't move line breakpoints over function boundaries, so if we | ||||||||||||||
| // found a hit, and there were also a call site entry, it would have to be in | ||||||||||||||
| // the function containing the PC of the line table match. That way we can | ||||||||||||||
| // limit the call site search to that function. | ||||||||||||||
| // We will miss functions that ONLY exist as a call site entry. | ||||||||||||||
|
|
||||||||||||||
| if (line_entry.IsValid() && | ||||||||||||||
| (line_entry.line != line || line_entry.column != column_num) && | ||||||||||||||
| resolve_scope & eSymbolContextLineEntry && check_inlines) { | ||||||||||||||
| // We don't move lines over function boundaries, so the address in the | ||||||||||||||
| // line entry will be the in function that contained the line that might | ||||||||||||||
| // be a CallSite, and we can just iterate over that function to find any | ||||||||||||||
| // inline records, and dig up their call sites. | ||||||||||||||
| Address start_addr = line_entry.range.GetBaseAddress(); | ||||||||||||||
| Function *function = start_addr.CalculateSymbolContextFunction(); | ||||||||||||||
|
|
||||||||||||||
| Declaration sought_decl(file_spec, line, column_num); | ||||||||||||||
| // We use this recursive function to descend the block structure looking | ||||||||||||||
| // for a block that has this Declaration as in it's CallSite info. | ||||||||||||||
| // This function recursively scans the sibling blocks of the incoming | ||||||||||||||
| // block parameter. | ||||||||||||||
| std::function<void(Block &)> examine_block = | ||||||||||||||
| [&sought_decl, &sc_list, &src_location_spec, resolve_scope, | ||||||||||||||
| &examine_block](Block &block) -> void { | ||||||||||||||
| // Iterate over the sibling child blocks of the incoming block. | ||||||||||||||
| Block *sibling_block = block.GetFirstChild(); | ||||||||||||||
| while (sibling_block) { | ||||||||||||||
| // We only have to descend through the regular blocks, looking for | ||||||||||||||
| // immediate inlines, since those are the only ones that will have this | ||||||||||||||
| // callsite. | ||||||||||||||
| const InlineFunctionInfo *inline_info = | ||||||||||||||
| sibling_block->GetInlinedFunctionInfo(); | ||||||||||||||
| if (inline_info) { | ||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that's right. We still need to do the bit after the if block to recurse the children and set the next sibling block. I'd have to have a "goto", you can't just continue here. |
||||||||||||||
| // If this is the call-site we are looking for, record that: | ||||||||||||||
| // We need to be careful because the call site from the debug info | ||||||||||||||
| // will generally have a column, but the user might not have specified | ||||||||||||||
| // it. | ||||||||||||||
| Declaration found_decl = inline_info->GetCallSite(); | ||||||||||||||
| uint32_t sought_column = sought_decl.GetColumn(); | ||||||||||||||
| if (found_decl.FileAndLineEqual(sought_decl) && | ||||||||||||||
|
||||||||||||||
| if (found_decl.FileAndLineEqual(sought_decl) && | |
| if (!found_decl.FileAndLineEqual(sought_decl)) | |
| continue; | |
| if (sought_column != LLDB_INVALID_COLUMN_NUMBER && | |
| sought_column != found_decl.GetColumn()) | |
| continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These can't continue either, they'd need a goto to get the sibling iteration & child recursion at the end of the while.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
etc ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: avoid 2 function calls