diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp index 3c825058b8c37..b64ce6f0dc9c2 100644 --- a/lldb/source/Target/ThreadPlanStepRange.cpp +++ b/lldb/source/Target/ThreadPlanStepRange.cpp @@ -379,10 +379,10 @@ bool ThreadPlanStepRange::SetNextBranchBreakpoint() { !m_next_branch_bp_sp->HasResolvedLocations()) m_could_not_resolve_hw_bp = true; + BreakpointLocationSP bp_loc = + m_next_branch_bp_sp->GetLocationAtIndex(0); if (log) { lldb::break_id_t bp_site_id = LLDB_INVALID_BREAK_ID; - BreakpointLocationSP bp_loc = - m_next_branch_bp_sp->GetLocationAtIndex(0); if (bp_loc) { BreakpointSiteSP bp_site = bp_loc->GetBreakpointSite(); if (bp_site) { @@ -395,7 +395,51 @@ bool ThreadPlanStepRange::SetNextBranchBreakpoint() { m_next_branch_bp_sp->GetID(), bp_site_id, run_to_address.GetLoadAddress(&m_process.GetTarget())); } - + // The "next branch breakpoint might land on a virtual inlined call + // stack. If that's true, we should always stop at the top of the + // inlined call stack. Only virtual steps should walk deeper into the + // inlined call stack. + Block *block = run_to_address.CalculateSymbolContextBlock(); + if (bp_loc && block) { + LineEntry top_most_line_entry; + lldb::addr_t run_to_addr = run_to_address.GetFileAddress(); + for (Block *inlined_parent = block->GetContainingInlinedBlock(); + inlined_parent; + inlined_parent = inlined_parent->GetInlinedParent()) { + AddressRange range; + if (!inlined_parent->GetRangeContainingAddress(run_to_address, + range)) + break; + Address range_start_address = range.GetBaseAddress(); + // Only compare addresses here, we may have different symbol + // contexts (for virtual inlined stacks), but we just want to know + // that they are all at the same address. + if (range_start_address.GetFileAddress() != run_to_addr) + break; + const InlineFunctionInfo *inline_info = + inlined_parent->GetInlinedFunctionInfo(); + if (!inline_info) + break; + const Declaration &call_site = inline_info->GetCallSite(); + top_most_line_entry.line = call_site.GetLine(); + top_most_line_entry.column = call_site.GetColumn(); + FileSpec call_site_file_spec = call_site.GetFile(); + top_most_line_entry.original_file_sp.reset( + new SupportFile(call_site_file_spec)); + top_most_line_entry.range = range; + top_most_line_entry.file_sp.reset(); + top_most_line_entry.ApplyFileMappings( + GetThread().CalculateTarget()); + if (!top_most_line_entry.file_sp) + top_most_line_entry.file_sp = + top_most_line_entry.original_file_sp; + } + if (top_most_line_entry.IsValid()) { + LLDB_LOG(log, "Setting preferred line entry: {0}:{1}", + top_most_line_entry.GetFile(), top_most_line_entry.line); + bp_loc->SetPreferredLineEntry(top_most_line_entry); + } + } m_next_branch_bp_sp->SetThreadID(m_tid); m_next_branch_bp_sp->SetBreakpointKind("next-branch-location"); diff --git a/lldb/test/API/functionalities/inline-stepping/TestInlineStepping.py b/lldb/test/API/functionalities/inline-stepping/TestInlineStepping.py index 4e2d908e63b81..bb5606260e15f 100644 --- a/lldb/test/API/functionalities/inline-stepping/TestInlineStepping.py +++ b/lldb/test/API/functionalities/inline-stepping/TestInlineStepping.py @@ -13,7 +13,6 @@ class TestInlineStepping(TestBase): compiler="icc", bugnumber="# Not really a bug. ICC combines two inlined functions.", ) - @skipIf(oslist=["linux"], archs=["arm"]) # Fails for 32 bit arm def test_with_python_api(self): """Test stepping over and into inlined functions.""" self.build() @@ -291,7 +290,7 @@ def inline_stepping_step_over(self): break_1_in_main = target.BreakpointCreateBySourceRegex( "// At second call of caller_ref_1 in main.", self.main_source_spec ) - self.assertTrue(break_1_in_main, VALID_BREAKPOINT) + self.assertGreater(break_1_in_main.GetNumLocations(), 0, VALID_BREAKPOINT) # Now launch the process, and do not stop at entry point. self.process = target.LaunchSimple( @@ -317,6 +316,24 @@ def inline_stepping_step_over(self): ] self.run_step_sequence(step_sequence) + # Now make sure that next to a virtual inlined call stack + # gets the call stack depth correct. + break_2_in_main = target.BreakpointCreateBySourceRegex( + "// Call max_value specialized", self.main_source_spec + ) + self.assertGreater(break_2_in_main.GetNumLocations(), 0, VALID_BREAKPOINT) + threads = lldbutil.continue_to_breakpoint(self.process, break_2_in_main) + self.assertEqual(len(threads), 1, "Hit our second breakpoint") + self.assertEqual(threads[0].id, self.thread.id, "Stopped at right thread") + self.thread.StepOver() + frame_0 = self.thread.frames[0] + line_entry = frame_0.line_entry + self.assertEqual( + line_entry.file.basename, self.main_source_spec.basename, "File matches" + ) + target_line = line_number("calling.cpp", "// At caller_trivial_inline_1") + self.assertEqual(line_entry.line, target_line, "Lines match as well.") + def step_in_template(self): """Use Python APIs to test stepping in to templated functions.""" exe = self.getBuildArtifact("a.out")