forked from OSchip/llvm-project
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:
parent
c94285a1a0
commit
30fadafefe
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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:");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue