From 184e981111d74fe650b7ea4506adc9bc356273ee Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Tue, 15 Jan 2013 02:47:48 +0000 Subject: [PATCH] Separated the "expr --unwind-on-error" behavior into two parts, actual errors (i.e. crashes) which continue to be controlled by the --unwind-on-error flag, and --ignore-breakpoint which separately controls behavior when a called function hits a breakpoint. For breakpoints, we don't unwind, we either stop, or ignore the breakpoint, which makes more sense. Also make both these behaviors globally settable through "settings set". Also handle the case where a breakpoint command calls code that ends up re-hitting the breakpoint. We were recursing and crashing. Now we just stop without calling the second command. llvm-svn: 172503 --- lldb/include/lldb/API/SBExpressionOptions.h | 6 ++ lldb/include/lldb/Expression/ClangFunction.h | 24 +++-- lldb/include/lldb/Expression/ClangUserExpression.h | 19 +++- lldb/include/lldb/Target/Process.h | 15 ++- lldb/include/lldb/Target/Target.h | 15 +++ lldb/include/lldb/Target/Thread.h | 3 +- lldb/include/lldb/Target/ThreadPlanCallFunction.h | 9 +- .../lldb/Target/ThreadPlanCallUserExpression.h | 3 +- lldb/include/lldb/lldb-private-enumerations.h | 1 + .../scripts/Python/interface/SBExpressionOptions.i | 8 ++ lldb/source/API/SBExpressionOptions.cpp | 12 +++ lldb/source/Commands/CommandObjectExpression.cpp | 34 ++++++- lldb/source/Commands/CommandObjectExpression.h | 1 + lldb/source/Expression/ClangFunction.cpp | 32 +++--- lldb/source/Expression/ClangUserExpression.cpp | 34 ++++--- lldb/source/Interpreter/CommandInterpreter.cpp | 4 + .../ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp | 10 +- .../ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp | 10 +- .../AppleThreadPlanStepThroughObjCTrampoline.cpp | 9 +- .../Plugins/Process/Utility/InferiorCallPOSIX.cpp | 18 ++-- lldb/source/Target/Process.cpp | 110 +++++++++++++++++---- lldb/source/Target/StopInfo.cpp | 53 +++++++++- lldb/source/Target/Target.cpp | 1 + lldb/source/Target/Thread.cpp | 13 ++- lldb/source/Target/ThreadPlanCallFunction.cpp | 53 +++++++--- .../source/Target/ThreadPlanCallUserExpression.cpp | 5 +- lldb/test/expression_command/test/TestExprs.py | 17 +++- lldb/test/expression_command/test/main.cpp | 11 +++ 28 files changed, 425 insertions(+), 105 deletions(-) diff --git a/lldb/include/lldb/API/SBExpressionOptions.h b/lldb/include/lldb/API/SBExpressionOptions.h index f3dce3c..c0a748d 100644 --- a/lldb/include/lldb/API/SBExpressionOptions.h +++ b/lldb/include/lldb/API/SBExpressionOptions.h @@ -42,6 +42,12 @@ public: void SetUnwindOnError (bool unwind = false); + bool + GetIgnoreBreakpoints () const; + + void + SetIgnoreBreakpoints (bool ignore = false); + lldb::DynamicValueType GetFetchDynamicValue () const; diff --git a/lldb/include/lldb/Expression/ClangFunction.h b/lldb/include/lldb/Expression/ClangFunction.h index 68093eb..348ba37 100644 --- a/lldb/include/lldb/Expression/ClangFunction.h +++ b/lldb/include/lldb/Expression/ClangFunction.h @@ -246,7 +246,7 @@ public: /// If the timeout expires, true if other threads should run. If /// the function may try to take locks, this is useful. /// - /// @param[in] discard_on_error + /// @param[in] unwind_on_error /// If true, and the execution stops before completion, we unwind the /// function call, and return the program state to what it was before the /// execution. If false, we leave the program in the stopped state. @@ -272,7 +272,8 @@ public: lldb::addr_t &void_arg, bool stop_others, bool try_all_threads, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, uint32_t timeout_usec, Stream &errors, lldb::addr_t* this_arg = 0); @@ -404,7 +405,8 @@ public: bool stop_others, uint32_t timeout_usec, bool try_all_threads, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, Value &results); //------------------------------------------------------------------ @@ -426,9 +428,12 @@ public: /// @param[in] stop_others /// True if other threads should pause during execution. /// - /// @param[in] discard_on_error + /// @param[in] unwind_on_error /// True if the thread plan may simply be discarded if an error occurs. /// + /// @param[in] ignore_breakpoints + /// True if the expression execution will ignore breakpoint hits and continue executing. + /// /// @param[in] this_arg /// If non-NULL (and cmd_arg is NULL), the function is invoked like a C++ /// method, with the value pointed to by the pointer as its 'this' @@ -447,7 +452,8 @@ public: lldb::addr_t &args_addr_ref, Stream &errors, bool stop_others, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, lldb::addr_t *this_arg = 0, lldb::addr_t *cmd_arg = 0); @@ -470,7 +476,7 @@ public: /// @param[in] stop_others /// True if other threads should pause during execution. /// - /// @param[in] discard_on_error + /// @param[in] unwind_on_error /// True if the thread plan may simply be discarded if an error occurs. /// /// @return @@ -481,14 +487,16 @@ public: lldb::addr_t &args_addr_ref, Stream &errors, bool stop_others, - bool discard_on_error = true) + bool unwind_on_error = true, + bool ignore_breakpoints = true) { return ClangFunction::GetThreadPlanToCallFunction (exe_ctx, m_jit_start_addr, args_addr_ref, errors, stop_others, - discard_on_error); + unwind_on_error, + ignore_breakpoints); } //------------------------------------------------------------------ diff --git a/lldb/include/lldb/Expression/ClangUserExpression.h b/lldb/include/lldb/Expression/ClangUserExpression.h index fca26d9..b7484fa 100644 --- a/lldb/include/lldb/Expression/ClangUserExpression.h +++ b/lldb/include/lldb/Expression/ClangUserExpression.h @@ -117,11 +117,14 @@ public: /// The execution context to use when looking up entities that /// are needed for parsing (locations of variables, etc.) /// - /// @param[in] discard_on_error + /// @param[in] unwind_on_error /// If true, and the execution stops before completion, we unwind the /// function call, and return the program state to what it was before the /// execution. If false, we leave the program in the stopped state. /// + /// @param[in] ignore_breakpoints + /// If true, ignore breakpoints while executing the expression. + /// /// @param[in] shared_ptr_to_me /// This is a shared pointer to this ClangUserExpression. This is /// needed because Execute can push a thread plan that will hold onto @@ -150,7 +153,8 @@ public: ExecutionResults Execute (Stream &error_stream, ExecutionContext &exe_ctx, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, ClangUserExpressionSP &shared_ptr_to_me, lldb::ClangExpressionVariableSP &result, bool try_all_threads, @@ -305,10 +309,13 @@ public: /// the expression. Currently restricted to those languages /// supported by Clang. /// - /// @param[in] discard_on_error + /// @param[in] unwind_on_error /// True if the thread's state should be restored in the case /// of an error. /// + /// @param[in] ignore_breakpoints + /// If true, ignore breakpoints while executing the expression. + /// /// @param[in] result_type /// If not eResultTypeAny, the type of the desired result. Will /// result in parse errors if impossible. @@ -341,7 +348,8 @@ public: lldb_private::ExecutionPolicy execution_policy, lldb::LanguageType language, ResultType desired_type, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, const char *expr_cstr, const char *expr_prefix, lldb::ValueObjectSP &result_valobj_sp, @@ -353,7 +361,8 @@ public: lldb_private::ExecutionPolicy execution_policy, lldb::LanguageType language, ResultType desired_type, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, const char *expr_cstr, const char *expr_prefix, lldb::ValueObjectSP &result_valobj_sp, diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 1750243..502be1d 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -73,6 +73,18 @@ public: void SetPythonOSPluginPath (const FileSpec &file); + + bool + GetIgnoreBreakpointsInExpressions () const; + + void + SetIgnoreBreakpointsInExpressions (bool ignore); + + bool + GetUnwindOnErrorInExpressions () const; + + void + SetUnwindOnErrorInExpressions (bool ignore); }; typedef STD_SHARED_PTR(ProcessProperties) ProcessPropertiesSP; @@ -2414,7 +2426,8 @@ public: lldb::ThreadPlanSP &thread_plan_sp, bool stop_others, bool run_others, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, uint32_t timeout_usec, Stream &errors); diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 354e7a9..7e47012 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -151,6 +151,7 @@ public: m_execution_policy(eExecutionPolicyOnlyWhenNeeded), m_coerce_to_id(false), m_unwind_on_error(true), + m_ignore_breakpoints (false), m_keep_in_memory(false), m_run_others(true), m_use_dynamic(lldb::eNoDynamicValues), @@ -197,6 +198,19 @@ public: } bool + DoesIgnoreBreakpoints () const + { + return m_ignore_breakpoints; + } + + EvaluateExpressionOptions& + SetIgnoreBreakpoints (bool ignore = false) + { + m_ignore_breakpoints = ignore; + return *this; + } + + bool DoesKeepInMemory () const { return m_keep_in_memory; @@ -252,6 +266,7 @@ private: ExecutionPolicy m_execution_policy; bool m_coerce_to_id; bool m_unwind_on_error; + bool m_ignore_breakpoints; bool m_keep_in_memory; bool m_run_others; lldb::DynamicValueType m_use_dynamic; diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index 1e2e584..0a0e6c9 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -648,7 +648,8 @@ public: Address& function, lldb::addr_t arg, bool stop_other_threads, - bool discard_on_error = false); + bool unwind_on_error = false, + bool ignore_breakpoints = true); //------------------------------------------------------------------ // Thread Plan accessors: diff --git a/lldb/include/lldb/Target/ThreadPlanCallFunction.h b/lldb/include/lldb/Target/ThreadPlanCallFunction.h index c2f84df..5ca790b 100644 --- a/lldb/include/lldb/Target/ThreadPlanCallFunction.h +++ b/lldb/include/lldb/Target/ThreadPlanCallFunction.h @@ -31,7 +31,8 @@ public: const ClangASTType &return_type, lldb::addr_t arg, bool stop_other_threads, - bool discard_on_error = true, + bool unwind_on_error = true, + bool ignore_breakpoints = false, lldb::addr_t *this_arg = 0, lldb::addr_t *cmd_arg = 0); @@ -39,7 +40,8 @@ public: Address &function, const ClangASTType &return_type, bool stop_other_threads, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, lldb::addr_t *arg1_ptr = NULL, lldb::addr_t *arg2_ptr = NULL, lldb::addr_t *arg3_ptr = NULL, @@ -170,7 +172,8 @@ private: lldb::ValueObjectSP m_return_valobj_sp; // If this contains a valid pointer, use the ABI to extract values when complete bool m_takedown_done; // We want to ensure we only do the takedown once. This ensures that. lldb::addr_t m_stop_address; // This is the address we stopped at. Also set in DoTakedown; - bool m_discard_on_error; + bool m_unwind_on_error; + bool m_ignore_breakpoints; DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallFunction); }; diff --git a/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h b/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h index c2f4ab6..7a7ec33 100644 --- a/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h +++ b/lldb/include/lldb/Target/ThreadPlanCallUserExpression.h @@ -29,7 +29,8 @@ public: Address &function, lldb::addr_t arg, bool stop_other_threads, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, lldb::addr_t *this_arg, lldb::addr_t *cmd_arg, ClangUserExpression::ClangUserExpressionSP &user_expression_sp); diff --git a/lldb/include/lldb/lldb-private-enumerations.h b/lldb/include/lldb/lldb-private-enumerations.h index 8839b44..fd3fe90 100644 --- a/lldb/include/lldb/lldb-private-enumerations.h +++ b/lldb/include/lldb/lldb-private-enumerations.h @@ -131,6 +131,7 @@ typedef enum ExecutionResults eExecutionCompleted, eExecutionDiscarded, eExecutionInterrupted, + eExecutionHitBreakpoint, eExecutionTimedOut } ExecutionResults; diff --git a/lldb/scripts/Python/interface/SBExpressionOptions.i b/lldb/scripts/Python/interface/SBExpressionOptions.i index 3190402..87e3ffe 100644 --- a/lldb/scripts/Python/interface/SBExpressionOptions.i +++ b/lldb/scripts/Python/interface/SBExpressionOptions.i @@ -41,6 +41,14 @@ public: void SetUnwindOnError (bool unwind = false); + bool + GetIgnoreBreakpoints () const; + + %feature("docstring", "Sets whether to ignore breakpoint hits while running expressions.") SetUnwindOnError; + + void + SetIgnoreBreakpoints (bool ignore = false); + lldb::DynamicValueType GetFetchDynamicValue () const; diff --git a/lldb/source/API/SBExpressionOptions.cpp b/lldb/source/API/SBExpressionOptions.cpp index 9733d25..127b0cf 100644 --- a/lldb/source/API/SBExpressionOptions.cpp +++ b/lldb/source/API/SBExpressionOptions.cpp @@ -65,6 +65,18 @@ SBExpressionOptions::SetUnwindOnError (bool unwind) m_opaque_ap->SetUnwindOnError (unwind); } +bool +SBExpressionOptions::GetIgnoreBreakpoints () const +{ + return m_opaque_ap->DoesIgnoreBreakpoints (); +} + +void +SBExpressionOptions::SetIgnoreBreakpoints (bool ignore) +{ + m_opaque_ap->SetIgnoreBreakpoints (ignore); +} + lldb::DynamicValueType SBExpressionOptions::GetFetchDynamicValue () const { diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index 9689b40..abaf4c6 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -53,6 +53,7 @@ OptionDefinition CommandObjectExpression::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "all-threads", 'a', required_argument, NULL, 0, eArgTypeBoolean, "Should we run all threads if the execution doesn't complete on one thread."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "ignore-breakpoints", 'i', required_argument, NULL, 0, eArgTypeBoolean, "Ignore breakpoint hits while running expressions"}, { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "timeout", 't', required_argument, NULL, 0, eArgTypeUnsignedInteger, "Timeout value for running the expression."}, { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "unwind-on-error", 'u', required_argument, NULL, 0, eArgTypeBoolean, "Clean up program state if the expression causes a crash, breakpoint hit or signal."}, }; @@ -94,6 +95,16 @@ CommandObjectExpression::CommandOptions::SetOptionValue (CommandInterpreter &int } break; + case 'i': + { + bool success; + bool tmp_value = Args::StringToBoolean(option_arg, true, &success); + if (success) + ignore_breakpoints = tmp_value; + else + error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg); + break; + } case 't': { bool success; @@ -109,8 +120,10 @@ CommandObjectExpression::CommandOptions::SetOptionValue (CommandInterpreter &int case 'u': { bool success; - unwind_on_error = Args::StringToBoolean(option_arg, true, &success); - if (!success) + bool tmp_value = Args::StringToBoolean(option_arg, true, &success); + if (success) + unwind_on_error = tmp_value; + else error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg); break; } @@ -125,7 +138,18 @@ CommandObjectExpression::CommandOptions::SetOptionValue (CommandInterpreter &int void CommandObjectExpression::CommandOptions::OptionParsingStarting (CommandInterpreter &interpreter) { - unwind_on_error = true; + Process *process = interpreter.GetExecutionContext().GetProcessPtr(); + if (process != NULL) + { + ignore_breakpoints = process->GetIgnoreBreakpointsInExpressions(); + unwind_on_error = process->GetUnwindOnErrorInExpressions(); + } + else + { + ignore_breakpoints = false; + unwind_on_error = true; + } + show_summary = true; try_all_threads = true; timeout = 0; @@ -306,6 +330,7 @@ CommandObjectExpression::EvaluateExpression EvaluateExpressionOptions options; options.SetCoerceToId(m_varobj_options.use_objc) .SetUnwindOnError(m_command_options.unwind_on_error) + .SetIgnoreBreakpoints (m_command_options.ignore_breakpoints) .SetKeepInMemory(keep_in_memory) .SetUseDynamic(m_varobj_options.use_dynamic) .SetRunOthers(m_command_options.try_all_threads) @@ -316,7 +341,8 @@ CommandObjectExpression::EvaluateExpression result_valobj_sp, options); - if (exe_results == eExecutionInterrupted && !m_command_options.unwind_on_error) + if ((exe_results == eExecutionInterrupted && !m_command_options.unwind_on_error) + ||(exe_results == eExecutionHitBreakpoint && !m_command_options.ignore_breakpoints)) { uint32_t start_frame = 0; uint32_t num_frames = 1; diff --git a/lldb/source/Commands/CommandObjectExpression.h b/lldb/source/Commands/CommandObjectExpression.h index b10e208..3964f2d 100644 --- a/lldb/source/Commands/CommandObjectExpression.h +++ b/lldb/source/Commands/CommandObjectExpression.h @@ -52,6 +52,7 @@ public: static OptionDefinition g_option_table[]; bool unwind_on_error; + bool ignore_breakpoints; bool show_types; bool show_summary; uint32_t timeout; diff --git a/lldb/source/Expression/ClangFunction.cpp b/lldb/source/Expression/ClangFunction.cpp index 49e09c4..2d69941 100644 --- a/lldb/source/Expression/ClangFunction.cpp +++ b/lldb/source/Expression/ClangFunction.cpp @@ -402,7 +402,8 @@ ClangFunction::GetThreadPlanToCallFunction (ExecutionContext &exe_ctx, lldb::addr_t &args_addr, Stream &errors, bool stop_others, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, lldb::addr_t *this_arg, lldb::addr_t *cmd_arg) { @@ -422,7 +423,8 @@ ClangFunction::GetThreadPlanToCallFunction (ExecutionContext &exe_ctx, ClangASTType(), args_addr, stop_others, - discard_on_error, + unwind_on_error, + ignore_breakpoints, this_arg, cmd_arg); new_plan->SetIsMasterPlan(true); @@ -479,8 +481,10 @@ ExecutionResults ClangFunction::ExecuteFunction(ExecutionContext &exe_ctx, Stream &errors, bool stop_others, Value &results) { const bool try_all_threads = false; - const bool discard_on_error = true; - return ExecuteFunction (exe_ctx, NULL, errors, stop_others, 0UL, try_all_threads, discard_on_error, results); + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + return ExecuteFunction (exe_ctx, NULL, errors, stop_others, 0UL, try_all_threads, + unwind_on_error, ignore_breakpoints, results); } ExecutionResults @@ -492,9 +496,10 @@ ClangFunction::ExecuteFunction( Value &results) { const bool stop_others = true; - const bool discard_on_error = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; return ExecuteFunction (exe_ctx, NULL, errors, stop_others, timeout_usec, - try_all_threads, discard_on_error, results); + try_all_threads, unwind_on_error, ignore_breakpoints, results); } // This is the static function @@ -505,7 +510,8 @@ ClangFunction::ExecuteFunction ( lldb::addr_t &void_arg, bool stop_others, bool try_all_threads, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, uint32_t timeout_usec, Stream &errors, lldb::addr_t *this_arg) @@ -515,7 +521,8 @@ ClangFunction::ExecuteFunction ( void_arg, errors, stop_others, - discard_on_error, + unwind_on_error, + ignore_breakpoints, this_arg)); if (!call_plan_sp) return eExecutionSetupError; @@ -528,7 +535,8 @@ ClangFunction::ExecuteFunction ( ExecutionResults results = exe_ctx.GetProcessRef().RunThreadPlan (exe_ctx, call_plan_sp, stop_others, try_all_threads, - discard_on_error, + unwind_on_error, + ignore_breakpoints, timeout_usec, errors); @@ -546,7 +554,8 @@ ClangFunction::ExecuteFunction( bool stop_others, uint32_t timeout_usec, bool try_all_threads, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, Value &results) { using namespace clang; @@ -573,7 +582,8 @@ ClangFunction::ExecuteFunction( args_addr, stop_others, try_all_threads, - discard_on_error, + unwind_on_error, + ignore_breakpoints, timeout_usec, errors); diff --git a/lldb/source/Expression/ClangUserExpression.cpp b/lldb/source/Expression/ClangUserExpression.cpp index 81db328..d228569 100644 --- a/lldb/source/Expression/ClangUserExpression.cpp +++ b/lldb/source/Expression/ClangUserExpression.cpp @@ -537,12 +537,16 @@ ClangUserExpression::GetThreadPlanToExecuteJITExpression (Stream &error_stream, // ClangUserExpression resources before the thread plan finishes execution in the target. But because we are // forcing unwind_on_error to be true here, in practical terms that can't happen. + const bool stop_others = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = false; return ClangFunction::GetThreadPlanToCallFunction (exe_ctx, m_jit_start_addr, struct_address, error_stream, - true, - true, + stop_others, + unwind_on_error, + ignore_breakpoints, (m_needs_object_ptr ? &object_ptr : NULL), (m_needs_object_ptr && m_objectivec) ? &cmd_ptr : NULL); } @@ -593,7 +597,8 @@ ClangUserExpression::FinalizeJITExecution (Stream &error_stream, ExecutionResults ClangUserExpression::Execute (Stream &error_stream, ExecutionContext &exe_ctx, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, ClangUserExpression::ClangUserExpressionSP &shared_ptr_to_me, lldb::ClangExpressionVariableSP &result, bool run_others, @@ -624,7 +629,8 @@ ClangUserExpression::Execute (Stream &error_stream, wrapper_address, struct_address, stop_others, - discard_on_error, + unwind_on_error, + ignore_breakpoints, (m_needs_object_ptr ? &object_ptr : NULL), ((m_needs_object_ptr && m_objectivec) ? &cmd_ptr : NULL), shared_ptr_to_me)); @@ -644,7 +650,8 @@ ClangUserExpression::Execute (Stream &error_stream, call_plan_sp, stop_others, try_all_threads, - discard_on_error, + unwind_on_error, + ignore_breakpoints, timeout_usec, error_stream); @@ -654,7 +661,7 @@ ClangUserExpression::Execute (Stream &error_stream, if (log) log->Printf("-- [ClangUserExpression::Execute] Execution of expression completed --"); - if (execution_result == eExecutionInterrupted) + if (execution_result == eExecutionInterrupted || execution_result == eExecutionHitBreakpoint) { const char *error_desc = NULL; @@ -669,7 +676,8 @@ ClangUserExpression::Execute (Stream &error_stream, else error_stream.Printf ("Execution was interrupted."); - if (discard_on_error) + if ((execution_result == eExecutionInterrupted && unwind_on_error) + || (execution_result == eExecutionHitBreakpoint && ignore_breakpoints)) error_stream.Printf ("\nThe process has been returned to the state before execution."); else error_stream.Printf ("\nThe process has been left at the point where it was interrupted."); @@ -702,7 +710,8 @@ ClangUserExpression::Evaluate (ExecutionContext &exe_ctx, lldb_private::ExecutionPolicy execution_policy, lldb::LanguageType language, ResultType desired_type, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, const char *expr_cstr, const char *expr_prefix, lldb::ValueObjectSP &result_valobj_sp, @@ -714,7 +723,8 @@ ClangUserExpression::Evaluate (ExecutionContext &exe_ctx, execution_policy, language, desired_type, - discard_on_error, + unwind_on_error, + ignore_breakpoints, expr_cstr, expr_prefix, result_valobj_sp, @@ -728,7 +738,8 @@ ClangUserExpression::EvaluateWithError (ExecutionContext &exe_ctx, lldb_private::ExecutionPolicy execution_policy, lldb::LanguageType language, ResultType desired_type, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, const char *expr_cstr, const char *expr_prefix, lldb::ValueObjectSP &result_valobj_sp, @@ -807,7 +818,8 @@ ClangUserExpression::EvaluateWithError (ExecutionContext &exe_ctx, execution_results = user_expression_sp->Execute (error_stream, exe_ctx, - discard_on_error, + unwind_on_error, + ignore_breakpoints, user_expression_sp, expr_result, run_others, diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 9f1c6cc..f248fdf 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -1320,6 +1320,7 @@ CommandInterpreter::PreprocessCommand (std::string &command) EvaluateExpressionOptions options; options.SetCoerceToId(false) .SetUnwindOnError(true) + .SetIgnoreBreakpoints(true) .SetKeepInMemory(false) .SetRunOthers(true) .SetTimeoutUsec(0); @@ -1375,6 +1376,9 @@ CommandInterpreter::PreprocessCommand (std::string &command) case eExecutionInterrupted: error.SetErrorStringWithFormat("expression interrupted for the expression '%s'", expr_str.c_str()); break; + case eExecutionHitBreakpoint: + error.SetErrorStringWithFormat("expression hit breakpoint for the expression '%s'", expr_str.c_str()); + break; case eExecutionTimedOut: error.SetErrorStringWithFormat("expression timed out for the expression '%s'", expr_str.c_str()); break; diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp index a49b213..cacdfdb 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp @@ -134,9 +134,10 @@ AppleObjCRuntime::GetObjectDescription (Stream &strm, Value &value, ExecutionCon lldb::addr_t wrapper_struct_addr = LLDB_INVALID_ADDRESS; func.InsertFunction(exe_ctx, wrapper_struct_addr, error_stream); - bool unwind_on_error = true; - bool try_all_threads = true; - bool stop_others = true; + const bool unwind_on_error = true; + const bool try_all_threads = true; + const bool stop_others = true; + const bool ignore_breakpoints = true; ExecutionResults results = func.ExecuteFunction (exe_ctx, &wrapper_struct_addr, @@ -144,7 +145,8 @@ AppleObjCRuntime::GetObjectDescription (Stream &strm, Value &value, ExecutionCon stop_others, 0 /* no timeout */, try_all_threads, - unwind_on_error, + unwind_on_error, + ignore_breakpoints, ret); if (results != eExecutionCompleted) { diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index b7edba8..b811829 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -220,9 +220,10 @@ AppleObjCRuntimeV2::RunFunctionToFindClassName(addr_t object_addr, Thread *threa if (!m_get_class_name_function->WriteFunctionArguments (exe_ctx, m_get_class_name_args, find_class_name_address, dispatch_values, errors)) return false; - bool stop_others = true; - bool try_all_threads = true; - bool unwind_on_error = true; + const bool stop_others = true; + const bool try_all_threads = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; ExecutionResults results = m_get_class_name_function->ExecuteFunction (exe_ctx, &m_get_class_name_args, @@ -230,7 +231,8 @@ AppleObjCRuntimeV2::RunFunctionToFindClassName(addr_t object_addr, Thread *threa stop_others, 100000, try_all_threads, - unwind_on_error, + unwind_on_error, + ignore_breakpoints, void_ptr_value); if (results != eExecutionCompleted) diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp index 18667d6..956b0fc 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp @@ -84,8 +84,15 @@ AppleThreadPlanStepThroughObjCTrampoline::InitializeClangFunction () } m_impl_function = m_trampoline_handler->GetLookupImplementationWrapperFunction(); ExecutionContext exc_ctx; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; m_thread.CalculateExecutionContext(exc_ctx); - m_func_sp.reset(m_impl_function->GetThreadPlanToCallFunction (exc_ctx, m_args_addr, errors, m_stop_others)); + m_func_sp.reset(m_impl_function->GetThreadPlanToCallFunction (exc_ctx, + m_args_addr, + errors, + m_stop_others, + unwind_on_error, + ignore_breakpoints)); m_func_sp->SetOkayToDiscard(true); m_thread.QueueThreadPlan (m_func_sp, false); } diff --git a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp index 040058b..905ab25 100644 --- a/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp +++ b/lldb/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -48,7 +48,8 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, const uint32_t range_scope = eSymbolContextFunction | eSymbolContextSymbol; const bool use_inline_block_range = false; const bool stop_other_threads = true; - const bool discard_on_error = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; const bool try_all_threads = true; const uint32_t timeout_usec = 500000; @@ -80,7 +81,8 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, mmap_range.GetBaseAddress(), ClangASTType (clang_ast_context->getASTContext(), clang_void_ptr_type), stop_other_threads, - discard_on_error, + unwind_on_error, + ignore_breakpoints, &addr, &length, &prot_arg, @@ -104,7 +106,8 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, call_plan_sp, stop_other_threads, try_all_threads, - discard_on_error, + unwind_on_error, + ignore_breakpoints, timeout_usec, error_strm); if (result == eExecutionCompleted) @@ -152,7 +155,8 @@ bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr, const uint32_t range_scope = eSymbolContextFunction | eSymbolContextSymbol; const bool use_inline_block_range = false; const bool stop_other_threads = true; - const bool discard_on_error = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; const bool try_all_threads = true; const uint32_t timeout_usec = 500000; @@ -163,7 +167,8 @@ bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr, munmap_range.GetBaseAddress(), ClangASTType(), stop_other_threads, - discard_on_error, + unwind_on_error, + ignore_breakpoints, &addr, &length)); if (call_plan_sp) @@ -182,7 +187,8 @@ bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr, call_plan_sp, stop_other_threads, try_all_threads, - discard_on_error, + unwind_on_error, + ignore_breakpoints, timeout_usec, error_strm); if (result == eExecutionCompleted) diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index e95fbf8..18493de 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -97,6 +97,8 @@ g_properties[] = { "disable-memory-cache" , OptionValue::eTypeBoolean, false, DISABLE_MEM_CACHE_DEFAULT, NULL, NULL, "Disable reading and caching of memory in fixed-size units." }, { "extra-startup-command", OptionValue::eTypeArray , false, OptionValue::eTypeString, NULL, NULL, "A list containing extra commands understood by the particular process plugin used. " "For instance, to turn on debugserver logging set this to \"QSetLogging:bitmask=LOG_DEFAULT;\"" }, + { "ignore-breakpoints-in-expressions", OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, breakpoints will be ignored during expression evaluation." }, + { "unwind-on-error-in-expressions", OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, errors in expression evaluation will unwind the stack back to the state before the call." }, { "python-os-plugin-path", OptionValue::eTypeFileSpec, false, true, NULL, NULL, "A path to a python OS plug-in module file that contains a OperatingSystemPlugIn class." }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; @@ -104,6 +106,8 @@ g_properties[] = enum { ePropertyDisableMemCache, ePropertyExtraStartCommand, + ePropertyIgnoreBreakpointsInExpressions, + ePropertyUnwindOnErrorInExpressions, ePropertyPythonOSPluginPath }; @@ -164,6 +168,35 @@ ProcessProperties::SetPythonOSPluginPath (const FileSpec &file) m_collection_sp->SetPropertyAtIndexAsFileSpec(NULL, idx, file); } + +bool +ProcessProperties::GetIgnoreBreakpointsInExpressions () const +{ + const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions; + return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value != 0); +} + +void +ProcessProperties::SetIgnoreBreakpointsInExpressions (bool ignore) +{ + const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions; + m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, ignore); +} + +bool +ProcessProperties::GetUnwindOnErrorInExpressions () const +{ + const uint32_t idx = ePropertyUnwindOnErrorInExpressions; + return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value != 0); +} + +void +ProcessProperties::SetUnwindOnErrorInExpressions (bool ignore) +{ + const uint32_t idx = ePropertyUnwindOnErrorInExpressions; + m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, ignore); +} + void ProcessInstanceInfo::Dump (Stream &s, Platform *platform) const { @@ -1668,7 +1701,8 @@ Process::LoadImage (const FileSpec &image_spec, Error &error) { ExecutionContext exe_ctx; frame_sp->CalculateExecutionContext (exe_ctx); - bool unwind_on_error = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; StreamString expr; expr.Printf("dlopen (\"%s\", 2)", path); const char *prefix = "extern \"C\" void* dlopen (const char *path, int mode);\n"; @@ -1678,6 +1712,7 @@ Process::LoadImage (const FileSpec &image_spec, Error &error) lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny, unwind_on_error, + ignore_breakpoints, expr.GetData(), prefix, result_valobj_sp, @@ -1743,7 +1778,8 @@ Process::UnloadImage (uint32_t image_token) { ExecutionContext exe_ctx; frame_sp->CalculateExecutionContext (exe_ctx); - bool unwind_on_error = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; StreamString expr; expr.Printf("dlclose ((void *)0x%" PRIx64 ")", image_addr); const char *prefix = "extern \"C\" int dlclose(void* handle);\n"; @@ -1753,6 +1789,7 @@ Process::UnloadImage (uint32_t image_token) lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny, unwind_on_error, + ignore_breakpoints, expr.GetData(), prefix, result_valobj_sp, @@ -4293,7 +4330,8 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, lldb::ThreadPlanSP &thread_plan_sp, bool stop_others, bool run_others, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, uint32_t timeout_usec, Stream &errors) { @@ -4419,8 +4457,10 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, bool first_timeout = true; bool do_resume = true; + bool handle_running_event = true; const uint64_t default_one_thread_timeout_usec = 250000; uint64_t computed_timeout = 0; + bool stopped_by_breakpoint = false; // This while loop must exit out the bottom, there's cleanup that we need to do when we are done. // So don't call return anywhere within it. @@ -4431,16 +4471,19 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, // The only exception is if we get two running events with no intervening // stop, which can happen, we will just wait for then next stop event. - if (do_resume) + if (do_resume || handle_running_event) { // Do the initial resume and wait for the running event before going further. - Error resume_error = PrivateResume (); - if (!resume_error.Success()) + if (do_resume) { - errors.Printf("Error resuming inferior: \"%s\".\n", resume_error.AsCString()); - return_value = eExecutionSetupError; - break; + Error resume_error = PrivateResume (); + if (!resume_error.Success()) + { + errors.Printf("Error resuming inferior: \"%s\".\n", resume_error.AsCString()); + return_value = eExecutionSetupError; + break; + } } real_timeout = TimeValue::Now(); @@ -4526,6 +4569,7 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, if (log) log->PutCString ("Process::RunThreadPlan(): handled an extra running event."); do_resume = true; + handle_running_event = true; } // Now wait for the process to stop again: @@ -4601,10 +4645,24 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, } else { - if (log) - log->PutCString ("Process::RunThreadPlan(): thread plan didn't successfully complete."); - - return_value = eExecutionInterrupted; + // Something restarted the target, so just wait for it to stop for real. + if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get())) + { + if (log) + log->PutCString ("Process::RunThreadPlan(): Somebody stopped and then restarted, we'll continue waiting."); + keep_going = true; + do_resume = false; + handle_running_event = true; + } + else + { + if (log) + log->PutCString ("Process::RunThreadPlan(): thread plan didn't successfully complete."); + if (stop_reason == eStopReasonBreakpoint) + return_value = eExecutionHitBreakpoint; + else + return_value = eExecutionInterrupted; + } } } } @@ -4619,6 +4677,7 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, case lldb::eStateRunning: do_resume = false; keep_going = true; + handle_running_event = false; break; default: @@ -4815,7 +4874,6 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, } } - } // END WAIT LOOP // If we had to start up a temporary private state thread to run this thread plan, shut it down now. @@ -4832,15 +4890,22 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, } - // Restore the thread state if we are going to discard the plan execution. + // Restore the thread state if we are going to discard the plan execution. There are three cases where this + // could happen: + // 1) The execution successfully completed + // 2) We hit a breakpoint, and ignore_breakpoints was true + // 3) We got some other error, and discard_on_error was true + bool should_unwind = (return_value == eExecutionInterrupted && unwind_on_error) + || (return_value == eExecutionHitBreakpoint && ignore_breakpoints); - if (return_value == eExecutionCompleted || discard_on_error) + if (return_value == eExecutionCompleted + || should_unwind) { thread_plan_sp->RestoreThreadState(); } // Now do some processing on the results of the run: - if (return_value == eExecutionInterrupted) + if (return_value == eExecutionInterrupted || return_value == eExecutionHitBreakpoint) { if (log) { @@ -4933,7 +4998,7 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, log->Printf("Process::RunThreadPlan(): execution interrupted: %s", s.GetData()); } - if (discard_on_error && thread_plan_sp) + if (should_unwind && thread_plan_sp) { if (log) log->Printf ("Process::RunThreadPlan: ExecutionInterrupted - discarding thread plans up to %p.", thread_plan_sp.get()); @@ -4951,7 +5016,7 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, if (log) log->PutCString("Process::RunThreadPlan(): execution set up error."); - if (discard_on_error && thread_plan_sp) + if (unwind_on_error && thread_plan_sp) { thread->DiscardThreadPlansUpToPlan (thread_plan_sp); thread_plan_sp->SetPrivate (orig_plan_private); @@ -4975,10 +5040,10 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, { if (log) log->PutCString("Process::RunThreadPlan(): thread plan stopped in mid course"); - if (discard_on_error && thread_plan_sp) + if (unwind_on_error && thread_plan_sp) { if (log) - log->PutCString("Process::RunThreadPlan(): discarding thread plan 'cause discard_on_error is set."); + log->PutCString("Process::RunThreadPlan(): discarding thread plan 'cause unwind_on_error is set."); thread->DiscardThreadPlansUpToPlan (thread_plan_sp); thread_plan_sp->SetPrivate (orig_plan_private); } @@ -5037,6 +5102,9 @@ Process::ExecutionResultAsCString (ExecutionResults result) case eExecutionInterrupted: result_name = "eExecutionInterrupted"; break; + case eExecutionHitBreakpoint: + result_name = "eExecutionHitBreakpoint"; + break; case eExecutionSetupError: result_name = "eExecutionSetupError"; break; diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp index 77a960a..3774c51 100644 --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -275,6 +275,47 @@ protected: m_should_stop = false; ExecutionContext exe_ctx (m_thread.GetStackFrameAtIndex(0)); + Process *process = exe_ctx.GetProcessPtr(); + if (process->GetModIDRef().IsLastResumeForUserExpression()) + { + // If we are in the middle of evaluating an expression, don't run asynchronous breakpoint commands or + // expressions. That could lead to infinite recursion if the command or condition re-calls the function + // with this breakpoint. + // TODO: We can keep a list of the breakpoints we've seen while running expressions in the nested + // PerformAction calls that can arise when the action runs a function that hits another breakpoint, + // and only stop running commands when we see the same breakpoint hit a second time. + + m_should_stop_is_valid = true; + if (log) + log->Printf ("StopInfoBreakpoint::PerformAction - Hit a breakpoint while running an expression," + " not running commands to avoid recursion."); + bool ignoring_breakpoints = process->GetIgnoreBreakpointsInExpressions(); + if (ignoring_breakpoints) + { + m_should_stop = false; + // Internal breakpoints will always stop. + for (size_t j = 0; j < num_owners; j++) + { + lldb::BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(j); + if (bp_loc_sp->GetBreakpoint().IsInternal()) + { + m_should_stop = true; + break; + } + } + } + else + { + m_should_stop = true; + } + if (log) + log->Printf ("StopInfoBreakpoint::PerformAction - in expression, continuing: %s.", + m_should_stop ? "true" : "false"); + process->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf("Warning: hit breakpoint while " + "running function, skipping commands and conditions to prevent recursion."); + return; + } + StoppointCallbackContext context (event_ptr, exe_ctx, false); for (size_t j = 0; j < num_owners; j++) @@ -294,13 +335,15 @@ protected: ExecutionResults result_code; ValueObjectSP result_value_sp; - const bool discard_on_error = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; Error error; result_code = ClangUserExpression::EvaluateWithError (exe_ctx, eExecutionPolicyOnlyWhenNeeded, lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny, - discard_on_error, + unwind_on_error, + ignore_breakpoints, bp_loc_sp->GetConditionText(), NULL, result_value_sp, @@ -575,13 +618,15 @@ protected: // constructor errors up to the debugger's Async I/O. ExecutionResults result_code; ValueObjectSP result_value_sp; - const bool discard_on_error = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; Error error; result_code = ClangUserExpression::EvaluateWithError (exe_ctx, eExecutionPolicyOnlyWhenNeeded, lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny, - discard_on_error, + unwind_on_error, + ignore_breakpoints, wp_sp->GetConditionText(), NULL, result_value_sp, diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 219453b..6238d2e 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -1757,6 +1757,7 @@ Target::EvaluateExpression lldb::eLanguageTypeUnknown, options.DoesCoerceToId() ? ClangUserExpression::eResultTypeId : ClangUserExpression::eResultTypeAny, options.DoesUnwindOnError(), + options.DoesIgnoreBreakpoints(), expr_cstr, prefix, result_valobj_sp, diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index 3137cf5..88760d5 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -1283,9 +1283,16 @@ Thread::QueueThreadPlanForCallFunction (bool abort_other_plans, Address& function, lldb::addr_t arg, bool stop_other_threads, - bool discard_on_error) -{ - ThreadPlanSP thread_plan_sp (new ThreadPlanCallFunction (*this, function, ClangASTType(), arg, stop_other_threads, discard_on_error)); + bool unwind_on_error, + bool ignore_breakpoints) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanCallFunction (*this, + function, + ClangASTType(), + arg, + stop_other_threads, + unwind_on_error, + ignore_breakpoints)); QueueThreadPlan (thread_plan_sp, abort_other_plans); return thread_plan_sp.get(); } diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp index 9e76864..1f4edb4 100644 --- a/lldb/source/Target/ThreadPlanCallFunction.cpp +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -123,7 +123,8 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread, const ClangASTType &return_type, addr_t arg, bool stop_other_threads, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, addr_t *this_arg, addr_t *cmd_arg) : ThreadPlan (ThreadPlan::eKindCallFunction, "Call function plan", thread, eVoteNoOpinion, eVoteNoOpinion), @@ -134,7 +135,8 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread, m_return_type (return_type), m_takedown_done (false), m_stop_address (LLDB_INVALID_ADDRESS), - m_discard_on_error (discard_on_error) + m_unwind_on_error (unwind_on_error), + m_ignore_breakpoints (ignore_breakpoints) { lldb::addr_t start_load_addr; ABI *abi; @@ -183,7 +185,8 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread, Address &function, const ClangASTType &return_type, bool stop_other_threads, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, addr_t *arg1_ptr, addr_t *arg2_ptr, addr_t *arg3_ptr, @@ -198,7 +201,8 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread, m_return_type (return_type), m_takedown_done (false), m_stop_address (LLDB_INVALID_ADDRESS), - m_discard_on_error (discard_on_error) + m_unwind_on_error (unwind_on_error), + m_ignore_breakpoints (ignore_breakpoints) { lldb::addr_t start_load_addr; ABI *abi; @@ -339,7 +343,10 @@ ThreadPlanCallFunction::PlanExplainsStop () // If our subplan knows why we stopped, even if it's done (which would forward the question to us) // we answer yes. if (m_subplan_sp.get() != NULL && m_subplan_sp->PlanExplainsStop()) + { + SetPlanComplete(); return true; + } // Check if the breakpoint is one of ours. @@ -353,7 +360,7 @@ ThreadPlanCallFunction::PlanExplainsStop () return true; // If we don't want to discard this plan, than any stop we don't understand should be propagated up the stack. - if (!m_discard_on_error) + if (!m_unwind_on_error) return false; // Otherwise, check the case where we stopped for an internal breakpoint, in that case, continue on. @@ -385,7 +392,7 @@ ThreadPlanCallFunction::PlanExplainsStop () return false; } - if (m_discard_on_error) + if (m_ignore_breakpoints) { DoTakedown(false); return true; @@ -399,9 +406,10 @@ ThreadPlanCallFunction::PlanExplainsStop () // If we want to discard the plan, then we say we explain the stop // but if we are going to be discarded, let whoever is above us // explain the stop. + SetPlanComplete(false); if (m_subplan_sp) { - if (m_discard_on_error) + if (m_unwind_on_error) { DoTakedown(false); return true; @@ -417,7 +425,11 @@ ThreadPlanCallFunction::PlanExplainsStop () bool ThreadPlanCallFunction::ShouldStop (Event *event_ptr) { - if (IsPlanComplete() || PlanExplainsStop()) + // We do some computation in PlanExplainsStop that may or may not set the plan as complete. + // We need to do that here to make sure our state is correct. + PlanExplainsStop(); + + if (IsPlanComplete()) { ReportRegisterState ("Function completed. Register state was:"); @@ -481,10 +493,16 @@ ThreadPlanCallFunction::WillStop () bool ThreadPlanCallFunction::MischiefManaged () { + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + + if (PlanExplainsStop() && !IsPlanComplete()) + { + if (log) + log->Printf ("ThreadPlanCallFunction: Got into MischiefManaged, explained stop but was not complete."); + } + if (IsPlanComplete()) { - LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - if (log) log->Printf("ThreadPlanCallFunction(%p): Completed call function plan.", this); @@ -527,12 +545,19 @@ ThreadPlanCallFunction::BreakpointsExplainStop() { StopInfoSP stop_info_sp = GetPrivateStopReason(); - if (m_cxx_language_runtime && - m_cxx_language_runtime->ExceptionBreakpointsExplainStop(stop_info_sp)) + if ((m_cxx_language_runtime && + m_cxx_language_runtime->ExceptionBreakpointsExplainStop(stop_info_sp)) + ||(m_objc_language_runtime && + m_objc_language_runtime->ExceptionBreakpointsExplainStop(stop_info_sp))) + { + SetPlanComplete(false); return true; + } + + // Finally, if the process is set to ignore breakpoints in function calls, + // then we explain all breakpoint stops. - if (m_objc_language_runtime && - m_objc_language_runtime->ExceptionBreakpointsExplainStop(stop_info_sp)) + if (m_ignore_breakpoints) return true; return false; diff --git a/lldb/source/Target/ThreadPlanCallUserExpression.cpp b/lldb/source/Target/ThreadPlanCallUserExpression.cpp index f739d2a..6e47808 100644 --- a/lldb/source/Target/ThreadPlanCallUserExpression.cpp +++ b/lldb/source/Target/ThreadPlanCallUserExpression.cpp @@ -40,11 +40,12 @@ ThreadPlanCallUserExpression::ThreadPlanCallUserExpression (Thread &thread, Address &function, lldb::addr_t arg, bool stop_other_threads, - bool discard_on_error, + bool unwind_on_error, + bool ignore_breakpoints, lldb::addr_t *this_arg, lldb::addr_t *cmd_arg, ClangUserExpression::ClangUserExpressionSP &user_expression_sp) : - ThreadPlanCallFunction (thread, function, ClangASTType(), arg, stop_other_threads, discard_on_error, this_arg, cmd_arg), + ThreadPlanCallFunction (thread, function, ClangASTType(), arg, stop_other_threads, unwind_on_error, ignore_breakpoints, this_arg, cmd_arg), m_user_expression_sp (user_expression_sp) { // User expressions are generally "User generated" so we should set them up to stop when done. diff --git a/lldb/test/expression_command/test/TestExprs.py b/lldb/test/expression_command/test/TestExprs.py index b086c2a..ffbd4d6 100644 --- a/lldb/test/expression_command/test/TestExprs.py +++ b/lldb/test/expression_command/test/TestExprs.py @@ -157,6 +157,22 @@ class BasicExprCommandsTestCase(TestBase): startstr = "'Z'") self.DebugSBValue(val) + callee_break = target.BreakpointCreateByName ("a_function_to_call", None) + self.assertTrue(callee_break.GetNumLocations() > 0) + + # Make sure ignoring breakpoints works from the command line: + self.expect("expression -i true -- a_function_to_call()", + substrs = ['(int) $', ' 1']) + self.assertTrue (callee_break.GetHitCount() == 1) + + # Now try ignoring breakpoints using the SB API's: + options = lldb.SBExpressionOptions() + options.SetIgnoreBreakpoints(True) + value = frame.EvaluateExpression('a_function_to_call()', options) + self.assertTrue (value.IsValid()) + self.assertTrue (value.GetValueAsSigned(0) == 2) + self.assertTrue (callee_break.GetHitCount() == 2) + # rdar://problem/8686536 # CommandInterpreter::HandleCommand is stripping \'s from input for WantsRawCommand commands def test_expr_commands_can_handle_quotes(self): @@ -207,7 +223,6 @@ class BasicExprCommandsTestCase(TestBase): substrs = ['(int) $', '6']) - if __name__ == '__main__': import atexit lldb.SBDebugger.Initialize() diff --git a/lldb/test/expression_command/test/main.cpp b/lldb/test/expression_command/test/main.cpp index f83bb9a..22208a8 100644 --- a/lldb/test/expression_command/test/main.cpp +++ b/lldb/test/expression_command/test/main.cpp @@ -1,5 +1,14 @@ #include +static int static_value = 0; + +int +a_function_to_call() +{ + static_value++; + return static_value; +} + int main (int argc, char const *argv[]) { printf ("Hello world!\n"); @@ -29,5 +38,7 @@ int main (int argc, char const *argv[]) expression printf ("two: %llu, one: %i\n", 2ull, 1) expression random() % 255l #endif + + a_function_to_call(); return 0; } -- 2.7.4