[Lldb-commits] [lldb] r174095 - Add "thread return -x" to unwind the innermost user called expression (if you happen to have stopped in it due to a crash.)
Jim Ingham
jingham at apple.com
Thu Jan 31 13:46:01 PST 2013
Author: jingham
Date: Thu Jan 31 15:46:01 2013
New Revision: 174095
URL: http://llvm.org/viewvc/llvm-project?rev=174095&view=rev
Log:
Add "thread return -x" to unwind the innermost user called expression (if you happen to have stopped in it due to a crash.)
Make the message when you hit an crash while evaluating an expression a little clearer, and mention "thread return -x".
rdar://problem/13110464
Modified:
lldb/trunk/include/lldb/Target/Thread.h
lldb/trunk/source/Commands/CommandObjectFrame.cpp
lldb/trunk/source/Commands/CommandObjectThread.cpp
lldb/trunk/source/Expression/ClangUserExpression.cpp
lldb/trunk/source/Target/Thread.cpp
Modified: lldb/trunk/include/lldb/Target/Thread.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/Thread.h?rev=174095&r1=174094&r2=174095&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/Thread.h (original)
+++ lldb/trunk/include/lldb/Target/Thread.h Thu Jan 31 15:46:01 2013
@@ -394,9 +394,13 @@ public:
uint32_t
SetSelectedFrame (lldb_private::StackFrame *frame, bool broadcast = false);
+
bool
SetSelectedFrameByIndex (uint32_t frame_idx, bool broadcast = false);
+ bool
+ SetSelectedFrameByIndexNoisily (uint32_t frame_idx, Stream &output_stream);
+
void
SetDefaultFileAndLineToSelectedFrame()
{
@@ -663,6 +667,17 @@ public:
//------------------------------------------------------------------
ThreadPlan *
GetCurrentPlan ();
+
+ //------------------------------------------------------------------
+ /// Unwinds the thread stack for the innermost expression plan currently
+ /// on the thread plan stack.
+ ///
+ /// @return
+ /// An error if the thread plan could not be unwound.
+ //------------------------------------------------------------------
+
+ Error
+ UnwindInnermostExpression();
private:
bool
Modified: lldb/trunk/source/Commands/CommandObjectFrame.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectFrame.cpp?rev=174095&r1=174094&r2=174095&view=diff
==============================================================================
--- lldb/trunk/source/Commands/CommandObjectFrame.cpp (original)
+++ lldb/trunk/source/Commands/CommandObjectFrame.cpp Thu Jan 31 15:46:01 2013
@@ -261,33 +261,19 @@ protected:
}
}
- const bool broadcast = true;
- bool success = thread->SetSelectedFrameByIndex (frame_idx, broadcast);
+ bool success = thread->SetSelectedFrameByIndexNoisily (frame_idx, result.GetOutputStream());
if (success)
{
m_exe_ctx.SetFrameSP(thread->GetSelectedFrame ());
- StackFrame *frame = m_exe_ctx.GetFramePtr();
- if (frame)
- {
- bool already_shown = false;
- SymbolContext frame_sc(frame->GetSymbolContext(eSymbolContextLineEntry));
- if (m_interpreter.GetDebugger().GetUseExternalEditor() && frame_sc.line_entry.file && frame_sc.line_entry.line != 0)
- {
- already_shown = Host::OpenFileInExternalEditor (frame_sc.line_entry.file, frame_sc.line_entry.line);
- }
-
- bool show_frame_info = true;
- bool show_source = !already_shown;
- if (frame->GetStatus (result.GetOutputStream(), show_frame_info, show_source))
- {
- result.SetStatus (eReturnStatusSuccessFinishResult);
- return result.Succeeded();
- }
- }
+ result.SetStatus (eReturnStatusSuccessFinishResult);
+ }
+ else
+ {
+ result.AppendErrorWithFormat ("Frame index (%u) out of range.\n", frame_idx);
+ result.SetStatus (eReturnStatusFailed);
}
- result.AppendErrorWithFormat ("Frame index (%u) out of range.\n", frame_idx);
- result.SetStatus (eReturnStatusFailed);
- return false;
+
+ return result.Succeeded();
}
protected:
Modified: lldb/trunk/source/Commands/CommandObjectThread.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectThread.cpp?rev=174095&r1=174094&r2=174095&view=diff
==============================================================================
--- lldb/trunk/source/Commands/CommandObjectThread.cpp (original)
+++ lldb/trunk/source/Commands/CommandObjectThread.cpp Thu Jan 31 15:46:01 2013
@@ -1248,25 +1248,104 @@ protected:
}
};
+//-------------------------------------------------------------------------
+// CommandObjectThreadReturn
+//-------------------------------------------------------------------------
+
class CommandObjectThreadReturn : public CommandObjectRaw
{
public:
+ class CommandOptions : public Options
+ {
+ public:
+
+ CommandOptions (CommandInterpreter &interpreter) :
+ Options (interpreter),
+ m_from_expression (false)
+ {
+ // Keep default values of all options in one place: OptionParsingStarting ()
+ OptionParsingStarting ();
+ }
+
+ virtual
+ ~CommandOptions ()
+ {
+ }
+
+ virtual Error
+ SetOptionValue (uint32_t option_idx, const char *option_arg)
+ {
+ Error error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option)
+ {
+ case 'x':
+ {
+ bool success;
+ bool tmp_value = Args::StringToBoolean (option_arg, false, &success);
+ if (success)
+ m_from_expression = tmp_value;
+ else
+ {
+ error.SetErrorStringWithFormat ("invalid boolean value '%s' for 'x' option", option_arg);
+ }
+ }
+ break;
+ default:
+ error.SetErrorStringWithFormat("invalid short option character '%c'", short_option);
+ break;
+
+ }
+ return error;
+ }
+
+ void
+ OptionParsingStarting ()
+ {
+ m_from_expression = false;
+ }
+
+ const OptionDefinition*
+ GetDefinitions ()
+ {
+ return g_option_table;
+ }
+
+ bool m_from_expression;
+
+ // Options table: Required for subclasses of Options.
+
+ static OptionDefinition g_option_table[];
+
+ // Instance variables to hold the values for command options.
+ };
+
+ virtual
+ Options *
+ GetOptions ()
+ {
+ return &m_options;
+ }
+
CommandObjectThreadReturn (CommandInterpreter &interpreter) :
CommandObjectRaw (interpreter,
"thread return",
- "Return from the currently selected frame, short-circuiting execution of the frames below it, with an optional return value.",
+ "Return from the currently selected frame, short-circuiting execution of the frames below it, with an optional return value,"
+ " or with the -x option from the innermost function evaluation.",
"thread return",
eFlagRequiresFrame |
eFlagTryTargetAPILock |
eFlagProcessMustBeLaunched |
- eFlagProcessMustBePaused )
+ eFlagProcessMustBePaused ),
+ m_options (interpreter)
{
CommandArgumentEntry arg;
CommandArgumentData expression_arg;
// Define the first (and only) variant of this arg.
expression_arg.arg_type = eArgTypeExpression;
- expression_arg.arg_repetition = eArgRepeatPlain;
+ expression_arg.arg_repetition = eArgRepeatOptional;
// There is only one variant this argument could be; put it into the argument entry.
arg.push_back (expression_arg);
@@ -1289,6 +1368,38 @@ protected:
CommandReturnObject &result
)
{
+ // I am going to handle this by hand, because I don't want you to have to say:
+ // "thread return -- -5".
+ if (command[0] == '-' && command[1] == 'x')
+ {
+ if (command && command[2] != '\0')
+ result.AppendWarning("Return values ignored when returning from user called expressions");
+
+ Thread *thread = m_exe_ctx.GetThreadPtr();
+ Error error;
+ error = thread->UnwindInnermostExpression();
+ if (!error.Success())
+ {
+ result.AppendErrorWithFormat ("Unwinding expression failed - %s.", error.AsCString());
+ result.SetStatus (eReturnStatusFailed);
+ }
+ else
+ {
+ bool success = thread->SetSelectedFrameByIndexNoisily (0, result.GetOutputStream());
+ if (success)
+ {
+ m_exe_ctx.SetFrameSP(thread->GetSelectedFrame ());
+ result.SetStatus (eReturnStatusSuccessFinishResult);
+ }
+ else
+ {
+ result.AppendErrorWithFormat ("Could not select 0th frame after unwinding expression.");
+ result.SetStatus (eReturnStatusFailed);
+ }
+ }
+ return result.Succeeded();
+ }
+
ValueObjectSP return_valobj_sp;
StackFrameSP frame_sp = m_exe_ctx.GetFrameSP();
@@ -1340,8 +1451,16 @@ protected:
result.SetStatus (eReturnStatusSuccessFinishResult);
return true;
}
+
+ CommandOptions m_options;
};
+OptionDefinition
+CommandObjectThreadReturn::CommandOptions::g_option_table[] =
+{
+{ LLDB_OPT_SET_ALL, false, "from-expression", 'x', no_argument, NULL, 0, eArgTypeNone, "Return from the innermost expression evaluation."},
+{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
+};
//-------------------------------------------------------------------------
// CommandObjectMultiwordThread
Modified: lldb/trunk/source/Expression/ClangUserExpression.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Expression/ClangUserExpression.cpp?rev=174095&r1=174094&r2=174095&view=diff
==============================================================================
--- lldb/trunk/source/Expression/ClangUserExpression.cpp (original)
+++ lldb/trunk/source/Expression/ClangUserExpression.cpp Thu Jan 31 15:46:01 2013
@@ -759,9 +759,9 @@ ClangUserExpression::Execute (Stream &er
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.");
+ error_stream.Printf ("\nThe process has been returned to the state before expression evaluation.");
else
- error_stream.Printf ("\nThe process has been left at the point where it was interrupted.");
+ error_stream.Printf ("\nThe process has been left at the point where it was interrupted, use \"thread return -x\" to return to the state before expression evaluation.");
return execution_result;
}
Modified: lldb/trunk/source/Target/Thread.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/Thread.cpp?rev=174095&r1=174094&r2=174095&view=diff
==============================================================================
--- lldb/trunk/source/Target/Thread.cpp (original)
+++ lldb/trunk/source/Target/Thread.cpp Thu Jan 31 15:46:01 2013
@@ -323,6 +323,33 @@ Thread::SetSelectedFrameByIndex (uint32_
return false;
}
+bool
+Thread::SetSelectedFrameByIndexNoisily (uint32_t frame_idx, Stream &output_stream)
+{
+ const bool broadcast = true;
+ bool success = SetSelectedFrameByIndex (frame_idx, broadcast);
+ if (success)
+ {
+ StackFrameSP frame_sp = GetSelectedFrame();
+ if (frame_sp)
+ {
+ bool already_shown = false;
+ SymbolContext frame_sc(frame_sp->GetSymbolContext(eSymbolContextLineEntry));
+ if (GetProcess()->GetTarget().GetDebugger().GetUseExternalEditor() && frame_sc.line_entry.file && frame_sc.line_entry.line != 0)
+ {
+ already_shown = Host::OpenFileInExternalEditor (frame_sc.line_entry.file, frame_sc.line_entry.line);
+ }
+
+ bool show_frame_info = true;
+ bool show_source = !already_shown;
+ return frame_sp->GetStatus (output_stream, show_frame_info, show_source);
+ }
+ return false;
+ }
+ else
+ return false;
+}
+
lldb::StopInfoSP
Thread::GetStopInfo ()
@@ -1165,6 +1192,28 @@ Thread::PlanIsBasePlan (ThreadPlan *plan
return m_plan_stack[0].get() == plan_ptr;
}
+Error
+Thread::UnwindInnermostExpression()
+{
+ Error error;
+ int stack_size = m_plan_stack.size();
+
+ // If the input plan is NULL, discard all plans. Otherwise make sure this plan is in the
+ // stack, and if so discard up to and including it.
+
+ for (int i = stack_size - 1; i > 0; i--)
+ {
+ if (m_plan_stack[i]->GetKind() == ThreadPlan::eKindCallFunction)
+ {
+ DiscardThreadPlansUpToPlan(m_plan_stack[i].get());
+ return error;
+ }
+ }
+ error.SetErrorString("No expressions currently active on this thread");
+ return error;
+}
+
+
ThreadPlan *
Thread::QueueFundamentalPlan (bool abort_other_plans)
{
@@ -1478,6 +1527,11 @@ Thread::ReturnFromFrame (lldb::StackFram
Thread *thread = frame_sp->GetThread().get();
uint32_t older_frame_idx = frame_sp->GetFrameIndex() + 1;
StackFrameSP older_frame_sp = thread->GetStackFrameAtIndex(older_frame_idx);
+ if (!older_frame_sp)
+ {
+ return_error.SetErrorString("No older frame to return to.");
+ return return_error;
+ }
if (return_value_sp)
{
@@ -1519,20 +1573,29 @@ Thread::ReturnFromFrame (lldb::StackFram
// Now write the return registers for the chosen frame:
// Note, we can't use ReadAllRegisterValues->WriteAllRegisterValues, since the read & write
- // cook their data
- bool copy_success = thread->GetStackFrameAtIndex(0)->GetRegisterContext()->CopyFromRegisterContext(older_frame_sp->GetRegisterContext());
- if (copy_success)
- {
- thread->DiscardThreadPlans(true);
- thread->ClearStackFrames();
- if (broadcast && EventTypeHasListeners(eBroadcastBitStackChanged))
- BroadcastEvent(eBroadcastBitStackChanged, new ThreadEventData (this->shared_from_this()));
- return return_error;
+ // cook their data
+
+ StackFrameSP youngest_frame_sp = thread->GetStackFrameAtIndex(0);
+ if (youngest_frame_sp)
+ {
+ bool copy_success = youngest_frame_sp->GetRegisterContext()->CopyFromRegisterContext(older_frame_sp->GetRegisterContext());
+ if (copy_success)
+ {
+ thread->DiscardThreadPlans(true);
+ thread->ClearStackFrames();
+ if (broadcast && EventTypeHasListeners(eBroadcastBitStackChanged))
+ BroadcastEvent(eBroadcastBitStackChanged, new ThreadEventData (this->shared_from_this()));
+ return return_error;
+ }
+ else
+ {
+ return_error.SetErrorString("Could not reset register values.");
+ return return_error;
+ }
}
else
{
- return_error.SetErrorString("Could not reset register values.");
- return return_error;
+ return_error.SetErrorString("Returned past top frame.");
}
}
More information about the lldb-commits
mailing list