diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h index 5af12ff6737e..7d3d208a2b62 100644 --- a/lldb/include/lldb/API/SBFrame.h +++ b/lldb/include/lldb/API/SBFrame.h @@ -103,6 +103,9 @@ public: lldb::SBValue EvaluateExpression (const char *expr, lldb::DynamicValueType use_dynamic); + lldb::SBValue + EvaluateExpression (const char *expr, lldb::DynamicValueType use_dynamic, bool unwind_on_error); + /// Gets the lexical block that defines the stack frame. Another way to think /// of this is it will return the block that contains all of the variables /// for a stack frame. Inlined functions are represented as SBBlock objects diff --git a/lldb/scripts/Python/interface/SBFrame.i b/lldb/scripts/Python/interface/SBFrame.i index 4bedbff8cce8..20093a026aa3 100644 --- a/lldb/scripts/Python/interface/SBFrame.i +++ b/lldb/scripts/Python/interface/SBFrame.i @@ -137,6 +137,9 @@ public: lldb::SBValue EvaluateExpression (const char *expr, lldb::DynamicValueType use_dynamic); + lldb::SBValue + EvaluateExpression (const char *expr, lldb::DynamicValueType use_dynamic, bool unwind_on_error); + %feature("docstring", " /// Gets the lexical block that defines the stack frame. Another way to think /// of this is it will return the block that contains all of the variables diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp index 27c07ef15d58..b38fa3dceb41 100644 --- a/lldb/source/API/SBFrame.cpp +++ b/lldb/source/API/SBFrame.cpp @@ -1027,6 +1027,12 @@ SBFrame::EvaluateExpression (const char *expr) SBValue SBFrame::EvaluateExpression (const char *expr, lldb::DynamicValueType fetch_dynamic_value) +{ + return EvaluateExpression (expr, fetch_dynamic_value, true); +} + +SBValue +SBFrame::EvaluateExpression (const char *expr, lldb::DynamicValueType fetch_dynamic_value, bool unwind_on_error) { LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); @@ -1056,7 +1062,6 @@ SBFrame::EvaluateExpression (const char *expr, lldb::DynamicValueType fetch_dyna expr, fetch_dynamic_value, frame_description.GetString().c_str()); const bool coerce_to_id = false; - const bool unwind_on_error = true; const bool keep_in_memory = false; exe_results = target->EvaluateExpression (expr, diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index 7d8398daec79..b831a4fcb89f 100644 --- a/lldb/source/API/SBThread.cpp +++ b/lldb/source/API/SBThread.cpp @@ -531,7 +531,7 @@ SBThread::StepOver (lldb::RunMode stop_other_threads) { Mutex::Locker api_locker (exe_ctx.GetTargetPtr()->GetAPIMutex()); Thread *thread = exe_ctx.GetThreadPtr(); - bool abort_other_plans = true; + bool abort_other_plans = false; StackFrameSP frame_sp(thread->GetStackFrameAtIndex (0)); ThreadPlan *new_plan = NULL; @@ -574,7 +574,7 @@ SBThread::StepInto (lldb::RunMode stop_other_threads) if (exe_ctx.HasThreadScope()) { Mutex::Locker api_locker (exe_ctx.GetTargetPtr()->GetAPIMutex()); - bool abort_other_plans = true; + bool abort_other_plans = false; Thread *thread = exe_ctx.GetThreadPtr(); StackFrameSP frame_sp(thread->GetStackFrameAtIndex (0)); @@ -616,7 +616,7 @@ SBThread::StepOut () if (exe_ctx.HasThreadScope()) { Mutex::Locker api_locker (exe_ctx.GetTargetPtr()->GetAPIMutex()); - bool abort_other_plans = true; + bool abort_other_plans = false; bool stop_other_threads = true; Thread *thread = exe_ctx.GetThreadPtr(); @@ -651,7 +651,7 @@ SBThread::StepOutOfFrame (lldb::SBFrame &sb_frame) if (exe_ctx.HasThreadScope()) { Mutex::Locker api_locker (exe_ctx.GetTargetPtr()->GetAPIMutex()); - bool abort_other_plans = true; + bool abort_other_plans = false; bool stop_other_threads = true; Thread *thread = exe_ctx.GetThreadPtr(); @@ -703,7 +703,7 @@ SBThread::RunToAddress (lldb::addr_t addr) if (exe_ctx.HasThreadScope()) { Mutex::Locker api_locker (exe_ctx.GetTargetPtr()->GetAPIMutex()); - bool abort_other_plans = true; + bool abort_other_plans = false; bool stop_other_threads = true; Address target_addr (addr); @@ -806,7 +806,7 @@ SBThread::StepOverUntil (lldb::SBFrame &sb_frame, AddressRange fun_range = frame_sc.function->GetAddressRange(); std::vector step_over_until_addrs; - const bool abort_other_plans = true; + const bool abort_other_plans = false; const bool stop_other_threads = true; const bool check_inlines = true; const bool exact = false; diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 73cc9a2eb1ca..040278bdfb43 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -946,7 +946,7 @@ public: return false; } - const bool abort_other_plans = true; + const bool abort_other_plans = false; StackFrame *frame = thread->GetStackFrameAtIndex(m_options.m_frame_idx).get(); if (frame == NULL) diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index 822015af75fd..1792aac61138 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -375,8 +375,8 @@ Thread::ShouldStop (Event* event_ptr) if (plan_ptr->MischiefManaged()) { - // We're going to pop the plans up to AND INCLUDING the plan that explains the stop. - plan_ptr = GetPreviousPlan(plan_ptr); + // We're going to pop the plans up to the plan that explains the stop. + ThreadPlan *prev_plan_ptr = GetPreviousPlan (plan_ptr); do { @@ -384,8 +384,13 @@ Thread::ShouldStop (Event* event_ptr) current_plan->WillStop(); PopPlan(); } - while ((current_plan = GetCurrentPlan()) != plan_ptr); - done_processing_current_plan = false; + while ((current_plan = GetCurrentPlan()) != prev_plan_ptr); + // Now, if the responsible plan was not "Okay to discard" then we're done, + // otherwise we forward this to the next plan in the stack below. + if (plan_ptr->IsMasterPlan() && !plan_ptr->OkayToDiscard()) + done_processing_current_plan = true; + else + done_processing_current_plan = false; } else done_processing_current_plan = true; diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp index 8e91e8b35d63..3ab2a56f2868 100644 --- a/lldb/source/Target/ThreadPlanCallFunction.cpp +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -416,7 +416,7 @@ ThreadPlanCallFunction::PlanExplainsStop () bool ThreadPlanCallFunction::ShouldStop (Event *event_ptr) { - if (PlanExplainsStop()) + if (IsPlanComplete() || PlanExplainsStop()) { ReportRegisterState ("Function completed. Register state was:"); diff --git a/lldb/test/lang/c/stepping/TestStepAndBreakpoints.py b/lldb/test/lang/c/stepping/TestStepAndBreakpoints.py new file mode 100644 index 000000000000..d827c0c26431 --- /dev/null +++ b/lldb/test/lang/c/stepping/TestStepAndBreakpoints.py @@ -0,0 +1,167 @@ +"""Test stepping over vrs. hitting breakpoints & subsequent stepping in various forms.""" + +import os, time +import unittest2 +import lldb +import lldbutil +from lldbtest import * + +class TestObjCStepping(TestBase): + + mydir = os.path.join("lang", "c", "stepping") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @python_api_test + @dsym_test + def test_with_dsym_and_python_api(self): + """Test stepping over vrs. hitting breakpoints & subsequent stepping in various forms.""" + self.buildDsym() + self.step_over_stepping() + + @python_api_test + @dwarf_test + def test_with_dwarf_and_python_api(self): + """Test stepping over vrs. hitting breakpoints & subsequent stepping in various forms.""" + self.buildDwarf() + self.step_over_stepping() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers that we will step to in main: + self.main_source = "main.c" + + def step_over_stepping(self): + """Use Python APIs to test stepping over and hitting breakpoints.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + self.main_source_spec = lldb.SBFileSpec (self.main_source) + + breakpoints_to_disable = [] + + break_1_in_main = target.BreakpointCreateBySourceRegex ('// frame select 2, thread step-out while stopped at .c.1..', self.main_source_spec) + self.assertTrue(break_1_in_main, VALID_BREAKPOINT) + breakpoints_to_disable.append (break_1_in_main) + + break_in_a = target.BreakpointCreateBySourceRegex ('// break here to stop in a before calling b', self.main_source_spec) + self.assertTrue(break_in_a, VALID_BREAKPOINT) + breakpoints_to_disable.append (break_in_a) + + break_in_b = target.BreakpointCreateBySourceRegex ('// thread step-out while stopped at .c.2..', self.main_source_spec) + self.assertTrue(break_in_b, VALID_BREAKPOINT) + breakpoints_to_disable.append (break_in_b) + + break_in_c = target.BreakpointCreateBySourceRegex ('// Find the line number of function .c. here.', self.main_source_spec) + self.assertTrue(break_in_c, VALID_BREAKPOINT) + breakpoints_to_disable.append (break_in_c) + + # Now launch the process, and do not stop at entry point. + process = target.LaunchSimple (None, None, os.getcwd()) + + self.assertTrue(process, PROCESS_IS_VALID) + + # The stop reason of the thread should be breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_1_in_main) + + if len(threads) != 1: + self.fail ("Failed to stop at first breakpoint in main.") + + thread = threads[0] + + # Now step over, which should cause us to hit the breakpoint in "a" + thread.StepOver() + + # The stop reason of the thread should be breakpoint. + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_in_a) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint in a.") + + thread = threads[0] + + # Step over, and we should hit the breakpoint in b: + thread.StepOver() + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_in_b) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint in b.") + thread = threads[0] + + # Now try running some function, and make sure that we still end up in the same place + # and with the same stop reason. + frame = thread.GetFrameAtIndex(0) + current_line = frame.GetLineEntry().GetLine() + current_file = frame.GetLineEntry().GetFileSpec() + current_bp = [] + current_bp.append(thread.GetStopReasonDataAtIndex(0)) + current_bp.append(thread.GetStopReasonDataAtIndex(1)) + + frame.EvaluateExpression ('(int) printf ("aaaaaaaaaa\n")') + + frame = thread.GetFrameAtIndex(0) + self.assertTrue (current_line == frame.GetLineEntry().GetLine(), "The line stayed the same after expression.") + self.assertTrue (current_file == frame.GetLineEntry().GetFileSpec(), "The file stayed the same after expression.") + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonBreakpoint, "We still say we stopped for a breakpoint.") + self.assertTrue (thread.GetStopReasonDataAtIndex(0) == current_bp[0] and thread.GetStopReasonDataAtIndex(1) == current_bp[1], "And it is the same breakpoint.") + + # Do the same thing with an expression that's going to crash, and make sure we are still unchanged. + + frame.EvaluateExpression ("((char *) 0)[0] = 'a'") + + frame = thread.GetFrameAtIndex(0) + self.assertTrue (current_line == frame.GetLineEntry().GetLine(), "The line stayed the same after expression.") + self.assertTrue (current_file == frame.GetLineEntry().GetFileSpec(), "The file stayed the same after expression.") + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonBreakpoint, "We still say we stopped for a breakpoint.") + self.assertTrue (thread.GetStopReasonDataAtIndex(0) == current_bp[0] and thread.GetStopReasonDataAtIndex(1) == current_bp[1], "And it is the same breakpoint.") + + # Now continue and make sure we just complete the step: + # Disable all our breakpoints first - sometimes the compiler puts two line table entries in for the + # breakpoint a "b" and we don't want to hit that. + for bkpt in breakpoints_to_disable: + bkpt.SetEnabled(False) + + process.Continue() + + self.assertTrue (thread.GetFrameAtIndex(0).GetFunctionName() == "a") + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonPlanComplete) + + # And one more time should get us back to main: + process.Continue() + + self.assertTrue (thread.GetFrameAtIndex(0).GetFunctionName() == "main") + self.assertTrue (thread.GetStopReason() == lldb.eStopReasonPlanComplete) + + # Now make sure we can call a function, break in the called function, then have "continue" get us back out again: + frame = thread.GetFrameAtIndex(0) + frame = thread.GetFrameAtIndex(0) + current_line = frame.GetLineEntry().GetLine() + current_file = frame.GetLineEntry().GetFileSpec() + + break_in_b.SetEnabled(True) + frame.EvaluateExpression ("b (4)", lldb.eNoDynamicValues, False) + + threads = lldbutil.get_threads_stopped_at_breakpoint (process, break_in_b) + if len(threads) != 1: + self.fail ("Failed to stop at breakpoint in b when calling b.") + thread = threads[0] + + # So do a step over here to make sure we can still do that: + + thread.StepOver() + + # See that we are still in b: + self.assertTrue (thread.GetFrameAtIndex(0).GetFunctionName() == "b") + + # Okay, now if we continue, we will finish off our function call and we should end up back in "a" as if nothing had happened: + process.Continue () + + self.assertTrue (thread.GetFrameAtIndex(0).GetLineEntry().GetLine() == current_line) + self.assertTrue (thread.GetFrameAtIndex(0).GetLineEntry().GetFileSpec() == current_file) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/lang/c/stepping/main.c b/lldb/test/lang/c/stepping/main.c index ce3baa5fd057..624dcddbcfa3 100644 --- a/lldb/test/lang/c/stepping/main.c +++ b/lldb/test/lang/c/stepping/main.c @@ -14,12 +14,18 @@ int c(int); int a(int val) { - if (val <= 1) - return b(val); - else if (val >= 3) - return c(val); + int return_value = val; - return val; + if (val <= 1) + { + return_value = b(val); // break here to stop in a before calling b + } + else if (val >= 3) + { + return_value = c(val); + } + + return return_value; } int b(int val)