From f63f20b72fa750109376b099a14b4910876c745f Mon Sep 17 00:00:00 2001 From: Nikolai Kholiavin Date: Wed, 21 Dec 2022 13:12:48 +0000 Subject: [PATCH] fixed e5ebac6 from https://gitee.com/zhengweiwei2/third_party_llvm-project/pulls/122 [lldb] Consider all breakpoints in internal breakpoint detection Currently in some cases lldb reports stop reason as "step out" or "step over" (from thread plan completion) instead of "breakpoint", if the user breakpoint happens to be set on the same address. The part of https://github.com/llvm/llvm-project/commit/f08f5c99262ff9eaa08956334accbb2614b0f7a2 seems to overwrite internal breakpoint detection logic, so that only the last breakpoint for the current stop address is considered. Together with step-out plans not clearing its breakpoint until they are destrouyed, this creates a situation when there is a user breakpoint set for address, but internal breakpoint makes lldb report a plan completion stop reason instead of breakpoint. This patch reverts that internal breakpoint detection logic to consider all breakpoints. Change-Id: I4158d0ebde04ffb6a677557020671389abdb155b Signed-off-by: Nikolai Kholiavin --- lldb/source/Target/StopInfo.cpp | 9 +- .../thread_plan_user_breakpoint/Makefile | 8 ++ .../TestThreadPlanUserBreakpoint.py | 132 ++++++++++++++++++ .../thread_plan_user_breakpoint/main.cpp | 11 ++ 4 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/Makefile create mode 100644 lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/TestThreadPlanUserBreakpoint.py create mode 100644 lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/main.cpp diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp index 7e830c6e2bed..acb2f12a3def 100644 --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -410,8 +410,6 @@ protected: continue; } - internal_breakpoint = bp_loc_sp->GetBreakpoint().IsInternal(); - // First run the precondition, but since the precondition is per // breakpoint, only run it once per breakpoint. std::pair::iterator, bool> result = @@ -481,7 +479,7 @@ protected: loc_desc.GetData()); // We want this stop reported, so you will know we auto-continued // but only for external breakpoints: - if (!internal_breakpoint) + if (!bp_loc_sp->GetBreakpoint().IsInternal()) thread_sp->SetShouldReportStop(eVoteYes); auto_continue_says_stop = false; } @@ -504,7 +502,10 @@ protected: if (callback_says_stop && auto_continue_says_stop) m_should_stop = true; - + + if (m_should_stop && !bp_loc_sp->GetBreakpoint().IsInternal()) + internal_breakpoint = false; + // If we are going to stop for this breakpoint, then remove the // breakpoint. if (callback_says_stop && bp_loc_sp && diff --git a/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/Makefile b/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/Makefile new file mode 100644 index 000000000000..2c00681fa228 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/Makefile @@ -0,0 +1,8 @@ +CXX_SOURCES := main.cpp + +ifneq (,$(findstring icc,$(CC))) + CXXFLAGS_EXTRAS := -debug inline-debug-info +endif + + +include Makefile.rules diff --git a/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/TestThreadPlanUserBreakpoint.py b/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/TestThreadPlanUserBreakpoint.py new file mode 100644 index 000000000000..c85dca24a734 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/TestThreadPlanUserBreakpoint.py @@ -0,0 +1,132 @@ +""" +Test that breakpoints (reason = breakpoint) have more priority than +plan completion (reason = step in/out/over) when reporting stop reason after step, +in particular 'step out' and 'step over', and in addition 'step in'. +Check for correct StopReason when stepping to the line with breakpoint, +which should be eStopReasonBreakpoint in general, +and eStopReasonPlanComplete when breakpoint's condition fails or it is disabled. +""" + + +import unittest2 +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class ThreadPlanUserBreakpointsTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def setUp(self): + TestBase.setUp(self) + + self.build() + exe = self.getBuildArtifact("a.out") + src = lldb.SBFileSpec("main.cpp") + + # Create a target by the debugger. + self.target = self.dbg.CreateTarget(exe) + self.assertTrue(self.target, VALID_TARGET) + + # Setup three breakpoints + self.lines = [line_number('main.cpp', "breakpoint_%i" % i) for i in range(3)] + + self.breakpoints = [self.target.BreakpointCreateByLocation(src, line) for line in self.lines] + self.assertTrue( + self.breakpoints[0] and self.breakpoints[0].GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Start debugging + self.process = self.target.LaunchSimple( + None, None, self.get_process_working_directory()) + self.assertIsNotNone(self.process, PROCESS_IS_VALID) + self.thread = lldbutil.get_one_thread_stopped_at_breakpoint(self.process, self.breakpoints[0]) + self.assertIsNotNone(self.thread, "Didn't stop at breakpoint 0.") + + def check_correct_stop_reason(self, breakpoint_idx, condition): + self.assertEquals(self.process.GetState(), lldb.eStateStopped) + if condition: + # All breakpoints active, stop reason is breakpoint + thread1 = lldbutil.get_one_thread_stopped_at_breakpoint(self.process, self.breakpoints[breakpoint_idx]) + self.assertEquals(self.thread, thread1, "Didn't stop at breakpoint %i." % breakpoint_idx) + else: + # Breakpoints are inactive, stop reason is plan complete + self.assertEquals(self.thread.GetStopReason(), lldb.eStopReasonPlanComplete, + "Expected stop reason to be step into/over/out for inactive breakpoint %i line." % breakpoint_idx) + + def change_breakpoints(self, action): + for i in range(1, len(self.breakpoints)): + action(self.breakpoints[i]) + + def check_thread_plan_user_breakpoint(self, condition, set_up_breakpoint_func): + # Make breakpoints active/inactive in different ways + self.change_breakpoints(lambda bp: set_up_breakpoint_func(condition, bp)) + + self.thread.StepInto() + # We should be stopped at the breakpoint_1 line with the correct stop reason + self.check_correct_stop_reason(1, condition) + + # This step-over creates a step-out from `func_1` plan + self.thread.StepOver() + # We should be stopped at the breakpoint_2 line with the correct stop reason + self.check_correct_stop_reason(2, condition) + + # Check explicit step-out + # Make sure we install the breakpoint at the right address: + # on some architectures (e.g, aarch64), step-out stops before the next source line + return_addr = self.thread.GetFrameAtIndex(1).GetPC() + step_out_breakpoint = self.target.BreakpointCreateByAddress(return_addr) + set_up_breakpoint_func(condition, step_out_breakpoint) + self.breakpoints.append(step_out_breakpoint) + + self.thread.StepOut() + # We should be stopped somewhere in the main frame with the correct stop reason + self.check_correct_stop_reason(3, condition) + + # Run the process until termination + self.process.Continue() + self.assertEquals(self.process.GetState(), lldb.eStateExited) + + def set_up_breakpoints_condition(self, condition, bp): + # Set breakpoint condition to true/false + conditionStr = 'true' if condition else 'false' + bp.SetCondition(conditionStr) + + def set_up_breakpoints_enable(self, condition, bp): + # Enable/disable breakpoint + bp.SetEnabled(condition) + + def set_up_breakpoints_callback(self, condition, bp): + # Set breakpoint callback to return True/False + bp.SetScriptCallbackBody('return %s' % condition) + + def test_thread_plan_user_breakpoint_conditional_active(self): + # Test with breakpoints 1, 2, 3 having true condition + self.check_thread_plan_user_breakpoint(condition=True, + set_up_breakpoint_func=self.set_up_breakpoints_condition) + + def test_thread_plan_user_breakpoint_conditional_inactive(self): + # Test with breakpoints 1, 2, 3 having false condition + self.check_thread_plan_user_breakpoint(condition=False, + set_up_breakpoint_func=self.set_up_breakpoints_condition) + + def test_thread_plan_user_breakpoint_unconditional_active(self): + # Test with breakpoints 1, 2, 3 enabled unconditionally + self.check_thread_plan_user_breakpoint(condition=True, + set_up_breakpoint_func=self.set_up_breakpoints_enable) + + def test_thread_plan_user_breakpoint_unconditional_inactive(self): + # Test with breakpoints 1, 2, 3 disabled unconditionally + self.check_thread_plan_user_breakpoint(condition=False, + set_up_breakpoint_func=self.set_up_breakpoints_enable) + + def test_thread_plan_user_breakpoint_callback_active(self): + # Test with breakpoints 1, 2, 3 with callback that returns 'True' + self.check_thread_plan_user_breakpoint(condition=True, + set_up_breakpoint_func=self.set_up_breakpoints_callback) + + def test_thread_plan_user_breakpoint_callback_inactive(self): + # Test with breakpoints 1, 2, 3 with callback that returns 'False' + self.check_thread_plan_user_breakpoint(condition=False, + set_up_breakpoint_func=self.set_up_breakpoints_callback) diff --git a/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/main.cpp b/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/main.cpp new file mode 100644 index 000000000000..cfb96add7600 --- /dev/null +++ b/lldb/test/API/functionalities/breakpoint/thread_plan_user_breakpoint/main.cpp @@ -0,0 +1,11 @@ +int func_1() { return 1; } + +int func_2() { + func_1(); // breakpoint_1 + return 1 + func_1(); // breakpoint_2 +} + +int main(int argc, char const *argv[]) { + func_2(); // breakpoint_0 + return 0; +} -- Gitee