If a hand-called function is interrupted by hitting a breakpoint, then

when you continue to finish off the function call, the expression result
will be included as part of the thread stop info.

llvm-svn: 212506
This commit is contained in:
Jim Ingham 2014-07-08 01:07:32 +00:00
parent c94285a1a0
commit 30fadafefe
11 changed files with 243 additions and 20 deletions

View File

@ -157,7 +157,9 @@ public:
CreateStopReasonToTrace (Thread &thread);
static lldb::StopInfoSP
CreateStopReasonWithPlan (lldb::ThreadPlanSP &plan, lldb::ValueObjectSP return_valobj_sp);
CreateStopReasonWithPlan (lldb::ThreadPlanSP &plan,
lldb::ValueObjectSP return_valobj_sp,
lldb::ClangExpressionVariableSP expression_variable_sp);
static lldb::StopInfoSP
CreateStopReasonWithException (Thread &thread, const char *description);
@ -168,6 +170,9 @@ public:
static lldb::ValueObjectSP
GetReturnValueObject (lldb::StopInfoSP &stop_info_sp);
static lldb::ClangExpressionVariableSP
GetExpressionVariable (lldb::StopInfoSP &stop_info_sp);
protected:
// Perform any action that is associated with this stop. This is done as the
// Event is removed from the event queue. ProcessEventData::DoOnRemoval does the job.

View File

@ -965,6 +965,17 @@ public:
lldb::ValueObjectSP
GetReturnValueObject ();
//------------------------------------------------------------------
/// Gets the outer-most expression variable from the completed plans
///
/// @return
/// A ClangExpressionVariableSP, either empty if there is no
/// plan completed an expression during the current stop
/// or the expression variable that was made for the completed expression.
//------------------------------------------------------------------
lldb::ClangExpressionVariableSP
GetExpressionVariable ();
//------------------------------------------------------------------
/// Checks whether the given plan is in the completed plans for this
/// stop.

View File

@ -501,11 +501,26 @@ public:
return m_thread.GetStopInfo ();
}
// If the completion of the thread plan stepped out of a function, the return value of the function
// might have been captured by the thread plan (currently only ThreadPlanStepOut does this.)
// If so, the ReturnValueObject can be retrieved from here.
virtual lldb::ValueObjectSP
GetReturnValueObject ()
{
return lldb::ValueObjectSP();
}
// If the thread plan managing the evaluation of a user expression lives longer than the command
// that instigated the expression (generally because the expression evaluation hit a breakpoint, and
// the user regained control at that point) a subsequent process control command step/continue/etc. might
// complete the expression evaluations. If so, the result of the expression evaluation will show up here.
virtual lldb::ClangExpressionVariableSP
GetExpressionVariable ()
{
return lldb::ClangExpressionVariableSP();
}
// If a thread plan stores the state before it was run, then you might
// want to restore the state when it is done. This will do that job.

View File

@ -40,21 +40,35 @@ public:
GetDescription (Stream *s, lldb::DescriptionLevel level);
virtual void
WillPop ()
{
ThreadPlanCallFunction::WillPop();
if (m_user_expression_sp)
m_user_expression_sp.reset();
}
WillPop ();
virtual lldb::StopInfoSP
GetRealStopInfo();
virtual bool
MischiefManaged ();
void
TransferExpressionOwnership ()
{
m_manage_materialization = true;
}
virtual lldb::ClangExpressionVariableSP
GetExpressionVariable ()
{
return m_result_var_sp;
}
protected:
private:
ClangUserExpression::ClangUserExpressionSP m_user_expression_sp; // This is currently just used to ensure the
// User expression the initiated this ThreadPlan
// lives as long as the thread plan does.
bool m_manage_materialization = false;
lldb::ClangExpressionVariableSP m_result_var_sp; // If we are left to manage the materialization,
// then stuff the result expression variable here.
DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallUserExpression);
};

View File

@ -111,6 +111,7 @@ g_language_enumerators[] =
"{, ${thread.info.trace_messages} messages}" \
"{, stop reason = ${thread.stop-reason}}"\
"{\\nReturn value: ${thread.return-value}}"\
"{\\nCompleted expression: ${thread.completed-expression}}"\
"\\n"
#define DEFAULT_FRAME_FORMAT "frame #${frame.index}: ${frame.pc}"\
@ -2075,6 +2076,19 @@ FormatPromptRecurse
}
}
}
else if (IsToken (var_name_begin, "completed-expression}"))
{
StopInfoSP stop_info_sp = thread->GetStopInfo ();
if (stop_info_sp && stop_info_sp->IsValid())
{
ClangExpressionVariableSP expression_var_sp = StopInfo::GetExpressionVariable (stop_info_sp);
if (expression_var_sp && expression_var_sp->GetValueObject())
{
expression_var_sp->GetValueObject()->Dump(s);
var_success = true;
}
}
}
else if (IsToken (var_name_begin, "script:"))
{
var_name_begin += ::strlen("script:");

View File

@ -884,17 +884,19 @@ ClangUserExpression::Execute (Stream &error_stream,
}
args.push_back(struct_address);
lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression (exe_ctx.GetThreadRef(),
wrapper_address,
args,
options,
shared_ptr_to_me));
ThreadPlanCallUserExpression *user_expression_plan =
new ThreadPlanCallUserExpression (exe_ctx.GetThreadRef(),
wrapper_address,
args,
options,
shared_ptr_to_me);
lldb::ThreadPlanSP call_plan_sp(user_expression_plan);
if (!call_plan_sp || !call_plan_sp->ValidatePlan (&error_stream))
return lldb::eExpressionSetupError;
lldb::addr_t function_stack_pointer = static_cast<ThreadPlanCallFunction *>(call_plan_sp.get())->GetFunctionStackPointer();
lldb::addr_t function_stack_pointer = user_expression_plan->GetFunctionStackPointer();
function_stack_bottom = function_stack_pointer - Host::GetPageSize();
function_stack_top = function_stack_pointer;
@ -935,8 +937,12 @@ ClangUserExpression::Execute (Stream &error_stream,
|| (execution_result == lldb::eExpressionHitBreakpoint && options.DoesIgnoreBreakpoints()))
error_stream.PutCString ("\nThe process has been returned to the state before expression evaluation.");
else
{
if (execution_result == lldb::eExpressionHitBreakpoint)
user_expression_plan->TransferExpressionOwnership();
error_stream.PutCString ("\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;
}

View File

@ -997,10 +997,11 @@ class StopInfoThreadPlan : public StopInfo
{
public:
StopInfoThreadPlan (ThreadPlanSP &plan_sp, ValueObjectSP &return_valobj_sp) :
StopInfoThreadPlan (ThreadPlanSP &plan_sp, ValueObjectSP &return_valobj_sp, ClangExpressionVariableSP &expression_variable_sp) :
StopInfo (plan_sp->GetThread(), LLDB_INVALID_UID),
m_plan_sp (plan_sp),
m_return_valobj_sp (return_valobj_sp)
m_return_valobj_sp (return_valobj_sp),
m_expression_variable_sp (expression_variable_sp)
{
}
@ -1032,6 +1033,12 @@ public:
return m_return_valobj_sp;
}
ClangExpressionVariableSP
GetExpressionVariable()
{
return m_expression_variable_sp;
}
protected:
virtual bool
ShouldStop (Event *event_ptr)
@ -1045,6 +1052,7 @@ protected:
private:
ThreadPlanSP m_plan_sp;
ValueObjectSP m_return_valobj_sp;
ClangExpressionVariableSP m_expression_variable_sp;
};
class StopInfoExec : public StopInfo
@ -1123,9 +1131,11 @@ StopInfo::CreateStopReasonToTrace (Thread &thread)
}
StopInfoSP
StopInfo::CreateStopReasonWithPlan (ThreadPlanSP &plan_sp, ValueObjectSP return_valobj_sp)
StopInfo::CreateStopReasonWithPlan (ThreadPlanSP &plan_sp,
ValueObjectSP return_valobj_sp,
ClangExpressionVariableSP expression_variable_sp)
{
return StopInfoSP (new StopInfoThreadPlan (plan_sp, return_valobj_sp));
return StopInfoSP (new StopInfoThreadPlan (plan_sp, return_valobj_sp, expression_variable_sp));
}
StopInfoSP
@ -1151,3 +1161,15 @@ StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp)
else
return ValueObjectSP();
}
ClangExpressionVariableSP
StopInfo::GetExpressionVariable(StopInfoSP &stop_info_sp)
{
if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonPlanComplete)
{
StopInfoThreadPlan *plan_stop_info = static_cast<StopInfoThreadPlan *>(stop_info_sp.get());
return plan_stop_info->GetExpressionVariable();
}
else
return ClangExpressionVariableSP();
}

View File

@ -420,7 +420,7 @@ Thread::GetStopInfo ()
const uint32_t stop_id = process_sp ? process_sp->GetStopID() : UINT32_MAX;
if (plan_sp && plan_sp->PlanSucceeded())
{
return StopInfo::CreateStopReasonWithPlan (plan_sp, GetReturnValueObject());
return StopInfo::CreateStopReasonWithPlan (plan_sp, GetReturnValueObject(), GetExpressionVariable());
}
else
{
@ -1184,6 +1184,22 @@ Thread::GetReturnValueObject ()
return ValueObjectSP();
}
ClangExpressionVariableSP
Thread::GetExpressionVariable ()
{
if (!m_completed_plan_stack.empty())
{
for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--)
{
ClangExpressionVariableSP expression_variable_sp;
expression_variable_sp = m_completed_plan_stack[i]->GetExpressionVariable();
if (expression_variable_sp)
return expression_variable_sp;
}
}
return ClangExpressionVariableSP();
}
bool
Thread::IsThreadPlanDone (ThreadPlan *plan)
{

View File

@ -56,7 +56,54 @@ ThreadPlanCallUserExpression::~ThreadPlanCallUserExpression ()
void
ThreadPlanCallUserExpression::GetDescription (Stream *s, lldb::DescriptionLevel level)
{
ThreadPlanCallFunction::GetDescription (s, level);
if (level == eDescriptionLevelBrief)
s->Printf("User Expression thread plan");
else
ThreadPlanCallFunction::GetDescription (s, level);
}
void
ThreadPlanCallUserExpression::WillPop ()
{
ThreadPlanCallFunction::WillPop();
if (m_user_expression_sp)
m_user_expression_sp.reset();
}
bool
ThreadPlanCallUserExpression::MischiefManaged ()
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
if (IsPlanComplete())
{
if (log)
log->Printf("ThreadPlanCallFunction(%p): Completed call function plan.",
static_cast<void*>(this));
if (m_manage_materialization && PlanSucceeded() && m_user_expression_sp)
{
lldb::addr_t function_stack_top;
lldb::addr_t function_stack_bottom;
lldb::addr_t function_stack_pointer = GetFunctionStackPointer();
function_stack_bottom = function_stack_pointer - Host::GetPageSize();
function_stack_top = function_stack_pointer;
StreamString error_stream;
ExecutionContext exe_ctx(GetThread());
m_user_expression_sp->FinalizeJITExecution(error_stream, exe_ctx, m_result_var_sp, function_stack_bottom, function_stack_top);
}
ThreadPlan::MischiefManaged ();
return true;
}
else
{
return false;
}
}
StopInfoSP

View File

@ -0,0 +1,59 @@
"""
Test calling a function, stopping in the call, continue and gather the result on stop.
"""
import unittest2
import lldb
import lldbutil
from lldbtest import *
class ExprCommandCallStopContinueTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line number to break for main.c.
self.line = line_number('main.cpp',
'// Please test these expressions while stopped at this line:')
self.func_line = line_number ('main.cpp',
'{ 5, "five" }')
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@dsym_test
def test_with_dsym(self):
"""Test gathering result from interrupted function call."""
self.buildDsym()
self.call_function()
@dwarf_test
def test_with_dwarf(self):
"""Test gathering result from interrupted function call."""
self.buildDwarf()
self.call_function()
def call_function(self):
"""Test gathering result from interrupted function call."""
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
# Some versions of GCC encode two locations for the 'return' statement in main.cpp
lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1, loc_exact=True)
self.runCmd("run", RUN_SUCCEEDED)
lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.func_line, num_expected_locations=-1, loc_exact=True)
self.expect("expr -i false -- returnsFive()", error=True,
substrs = ['Execution was interrupted, reason: breakpoint'])
self.runCmd("continue", "Continue completed")
self.expect ("thread list",
substrs = ['stop reason = User Expression thread plan',
r'Completed expression: (Five) $0 = (number = 5, name = "five")'])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -1,11 +1,25 @@
#include <iostream>
#include <string>
struct Five
{
int number;
const char *name;
};
Five
returnsFive()
{
Five my_five = { 5, "five" };
return my_five;
}
int main (int argc, char const *argv[])
{
std::string str = "Hello world";
std::cout << str << std::endl;
std::cout << str.c_str() << std::endl;
Five main_five = returnsFive();
#if 0
print str
print str.c_str()