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.
<rdar://problem/12986644>
<rdar://problem/9119325>
git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@172503 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/API/SBExpressionOptions.cpp b/source/API/SBExpressionOptions.cpp
index 9733d25..127b0cf 100644
--- a/source/API/SBExpressionOptions.cpp
+++ b/source/API/SBExpressionOptions.cpp
@@ -65,6 +65,18 @@
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/source/Commands/CommandObjectExpression.cpp b/source/Commands/CommandObjectExpression.cpp
index 9689b40..abaf4c6 100644
--- a/source/Commands/CommandObjectExpression.cpp
+++ b/source/Commands/CommandObjectExpression.cpp
@@ -53,6 +53,7 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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/source/Commands/CommandObjectExpression.h b/source/Commands/CommandObjectExpression.h
index b10e208..3964f2d 100644
--- a/source/Commands/CommandObjectExpression.h
+++ b/source/Commands/CommandObjectExpression.h
@@ -52,6 +52,7 @@
static OptionDefinition g_option_table[];
bool unwind_on_error;
+ bool ignore_breakpoints;
bool show_types;
bool show_summary;
uint32_t timeout;
diff --git a/source/Expression/ClangFunction.cpp b/source/Expression/ClangFunction.cpp
index 49e09c4..2d69941 100644
--- a/source/Expression/ClangFunction.cpp
+++ b/source/Expression/ClangFunction.cpp
@@ -402,7 +402,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
args_addr,
stop_others,
try_all_threads,
- discard_on_error,
+ unwind_on_error,
+ ignore_breakpoints,
timeout_usec,
errors);
diff --git a/source/Expression/ClangUserExpression.cpp b/source/Expression/ClangUserExpression.cpp
index 81db328..d228569 100644
--- a/source/Expression/ClangUserExpression.cpp
+++ b/source/Expression/ClangUserExpression.cpp
@@ -537,12 +537,16 @@
// 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 @@
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 @@
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 @@
call_plan_sp,
stop_others,
try_all_threads,
- discard_on_error,
+ unwind_on_error,
+ ignore_breakpoints,
timeout_usec,
error_stream);
@@ -654,7 +661,7 @@
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 @@
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 @@
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 @@
execution_policy,
language,
desired_type,
- discard_on_error,
+ unwind_on_error,
+ ignore_breakpoints,
expr_cstr,
expr_prefix,
result_valobj_sp,
@@ -728,7 +738,8 @@
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 @@
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/source/Interpreter/CommandInterpreter.cpp b/source/Interpreter/CommandInterpreter.cpp
index 9f1c6cc..f248fdf 100644
--- a/source/Interpreter/CommandInterpreter.cpp
+++ b/source/Interpreter/CommandInterpreter.cpp
@@ -1320,6 +1320,7 @@
EvaluateExpressionOptions options;
options.SetCoerceToId(false)
.SetUnwindOnError(true)
+ .SetIgnoreBreakpoints(true)
.SetKeepInMemory(false)
.SetRunOthers(true)
.SetTimeoutUsec(0);
@@ -1375,6 +1376,9 @@
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/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
index a49b213..cacdfdb 100644
--- a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
+++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
@@ -134,9 +134,10 @@
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 @@
stop_others,
0 /* no timeout */,
try_all_threads,
- unwind_on_error,
+ unwind_on_error,
+ ignore_breakpoints,
ret);
if (results != eExecutionCompleted)
{
diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
index b7edba8..b811829 100644
--- a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -220,9 +220,10 @@
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 @@
stop_others,
100000,
try_all_threads,
- unwind_on_error,
+ unwind_on_error,
+ ignore_breakpoints,
void_ptr_value);
if (results != eExecutionCompleted)
diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp
index 18667d6..956b0fc 100644
--- a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp
+++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp
@@ -84,8 +84,15 @@
}
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/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
index 040058b..905ab25 100644
--- a/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
+++ b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp
@@ -48,7 +48,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/source/Target/Process.cpp b/source/Target/Process.cpp
index e95fbf8..18493de 100644
--- a/source/Target/Process.cpp
+++ b/source/Target/Process.cpp
@@ -97,6 +97,8 @@
{ "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 @@
enum {
ePropertyDisableMemCache,
ePropertyExtraStartCommand,
+ ePropertyIgnoreBreakpointsInExpressions,
+ ePropertyUnwindOnErrorInExpressions,
ePropertyPythonOSPluginPath
};
@@ -164,6 +168,35 @@
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 @@
{
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 @@
lldb::eLanguageTypeUnknown,
ClangUserExpression::eResultTypeAny,
unwind_on_error,
+ ignore_breakpoints,
expr.GetData(),
prefix,
result_valobj_sp,
@@ -1743,7 +1778,8 @@
{
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 @@
lldb::eLanguageTypeUnknown,
ClangUserExpression::eResultTypeAny,
unwind_on_error,
+ ignore_breakpoints,
expr.GetData(),
prefix,
result_valobj_sp,
@@ -4293,7 +4330,8 @@
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 @@
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 @@
// 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 @@
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 @@
}
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 @@
case lldb::eStateRunning:
do_resume = false;
keep_going = true;
+ handle_running_event = false;
break;
default:
@@ -4815,7 +4874,6 @@
}
}
-
} // 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 @@
}
- // 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 @@
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 @@
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 @@
{
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 @@
case eExecutionInterrupted:
result_name = "eExecutionInterrupted";
break;
+ case eExecutionHitBreakpoint:
+ result_name = "eExecutionHitBreakpoint";
+ break;
case eExecutionSetupError:
result_name = "eExecutionSetupError";
break;
diff --git a/source/Target/StopInfo.cpp b/source/Target/StopInfo.cpp
index 77a960a..3774c51 100644
--- a/source/Target/StopInfo.cpp
+++ b/source/Target/StopInfo.cpp
@@ -275,6 +275,47 @@
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 @@
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 @@
// 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/source/Target/Target.cpp b/source/Target/Target.cpp
index 219453b..6238d2e 100644
--- a/source/Target/Target.cpp
+++ b/source/Target/Target.cpp
@@ -1757,6 +1757,7 @@
lldb::eLanguageTypeUnknown,
options.DoesCoerceToId() ? ClangUserExpression::eResultTypeId : ClangUserExpression::eResultTypeAny,
options.DoesUnwindOnError(),
+ options.DoesIgnoreBreakpoints(),
expr_cstr,
prefix,
result_valobj_sp,
diff --git a/source/Target/Thread.cpp b/source/Target/Thread.cpp
index 3137cf5..88760d5 100644
--- a/source/Target/Thread.cpp
+++ b/source/Target/Thread.cpp
@@ -1283,9 +1283,16 @@
Address& function,
lldb::addr_t arg,
bool stop_other_threads,
- bool discard_on_error)
+ bool unwind_on_error,
+ bool ignore_breakpoints)
{
- ThreadPlanSP thread_plan_sp (new ThreadPlanCallFunction (*this, function, ClangASTType(), arg, stop_other_threads, discard_on_error));
+ 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/source/Target/ThreadPlanCallFunction.cpp b/source/Target/ThreadPlanCallFunction.cpp
index 9e76864..1f4edb4 100644
--- a/source/Target/ThreadPlanCallFunction.cpp
+++ b/source/Target/ThreadPlanCallFunction.cpp
@@ -123,7 +123,8 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
return false;
}
- if (m_discard_on_error)
+ if (m_ignore_breakpoints)
{
DoTakedown(false);
return true;
@@ -399,9 +406,10 @@
// 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 @@
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 @@
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 @@
{
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;
+ }
- if (m_objc_language_runtime &&
- m_objc_language_runtime->ExceptionBreakpointsExplainStop(stop_info_sp))
+ // Finally, if the process is set to ignore breakpoints in function calls,
+ // then we explain all breakpoint stops.
+
+ if (m_ignore_breakpoints)
return true;
return false;
diff --git a/source/Target/ThreadPlanCallUserExpression.cpp b/source/Target/ThreadPlanCallUserExpression.cpp
index f739d2a..6e47808 100644
--- a/source/Target/ThreadPlanCallUserExpression.cpp
+++ b/source/Target/ThreadPlanCallUserExpression.cpp
@@ -40,11 +40,12 @@
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.