Added support for stepping out of a frame. If you have 10 stack frames, and you

select frame #3, you can then do a step out and be able to go directly to the
frame above frame #3! 

Added StepOverUntil and StepOutOfFrame to the SBThread API to allow more powerful
stepping.

llvm-svn: 123970
This commit is contained in:
Greg Clayton 2011-01-21 06:11:58 +00:00
parent 47ff14b091
commit 481cef25dc
16 changed files with 329 additions and 85 deletions

View File

@ -72,17 +72,18 @@ public:
GetDescription (lldb::SBStream &description) const;
protected:
#ifndef SWIG
friend class SBArguments;
friend class SBDebugger;
friend class SBCommunication;
friend class SBHostOS;
friend class SBInputReader;
friend class SBProcess;
friend class SBThread;
friend class SBTarget;
friend class SBValue;
#ifndef SWIG
lldb_private::Error *
get();

View File

@ -146,6 +146,9 @@ private:
lldb_private::StackFrame *
get() const;
const lldb::StackFrameSP &
get_sp() const;
#endif

View File

@ -84,9 +84,17 @@ public:
void
StepOut ();
void
StepOutOfFrame (lldb::SBFrame &frame);
void
StepInstruction(bool step_over);
SBError
StepOverUntil (lldb::SBFrame &frame,
lldb::SBFileSpec &file_spec,
uint32_t line);
void
RunToAddress (lldb::addr_t addr);

View File

@ -481,8 +481,9 @@ public:
SymbolContext *addr_context,
bool first_insn,
bool stop_other_threads,
lldb::Vote stop_vote = lldb::eVoteYes,
lldb::Vote run_vote = lldb::eVoteNoOpinion);
lldb::Vote stop_vote, // = lldb::eVoteYes,
lldb::Vote run_vote, // = lldb::eVoteNoOpinion);
uint32_t frame_idx);
//------------------------------------------------------------------
/// Gets the plan used to step through the code that steps from a function
@ -529,7 +530,8 @@ public:
QueueThreadPlanForStepUntil (bool abort_other_plans,
lldb::addr_t *address_list,
size_t num_addresses,
bool stop_others);
bool stop_others,
uint32_t frame_idx);
virtual ThreadPlan *
QueueThreadPlanForCallFunction (bool abort_other_plans,

View File

@ -40,13 +40,14 @@ public:
bool first_insn,
bool stop_others,
lldb::Vote stop_vote,
lldb::Vote run_vote);
lldb::Vote run_vote,
uint32_t frame_idx);
protected:
private:
SymbolContext *m_step_from_context;
lldb::addr_t m_step_from_insn;
uint64_t m_stack_depth;
uint32_t m_stack_depth;
lldb::break_id_t m_return_bp_id;
lldb::addr_t m_return_addr;
bool m_first_insn;
@ -58,7 +59,8 @@ private:
bool first_insn,
bool stop_others,
lldb::Vote stop_vote,
lldb::Vote run_vote);
lldb::Vote run_vote,
uint32_t frame_idx);
// Need an appropriate marker for the current stack so we can tell step out
// from step in.

View File

@ -45,7 +45,8 @@ protected:
ThreadPlanStepUntil (Thread &thread,
lldb::addr_t *address_list,
size_t num_addresses,
bool stop_others);
bool stop_others,
uint32_t frame_idx = 0);
void AnalyzeStop(void);
private:
@ -69,7 +70,8 @@ private:
Thread::QueueThreadPlanForStepUntil (bool abort_other_plans,
lldb::addr_t *address_list,
size_t num_addresses,
bool stop_others);
bool stop_others,
uint32_t frame_idx);
// Need an appropriate marker for the current stack so we can tell step out
// from step in.

View File

@ -511,6 +511,11 @@ SBFrame::get() const
return m_opaque_sp.get();
}
const lldb::StackFrameSP &
SBFrame::get_sp() const
{
return m_opaque_sp;
}
SBThread
SBFrame::GetThread () const

View File

@ -492,7 +492,52 @@ SBThread::StepOut ()
bool abort_other_plans = true;
bool stop_other_threads = true;
m_opaque_sp->QueueThreadPlanForStepOut (abort_other_plans, NULL, false, stop_other_threads, eVoteYes, eVoteNoOpinion);
m_opaque_sp->QueueThreadPlanForStepOut (abort_other_plans,
NULL,
false,
stop_other_threads,
eVoteYes,
eVoteNoOpinion,
0);
Process &process = m_opaque_sp->GetProcess();
process.GetThreadList().SetSelectedThreadByID (m_opaque_sp->GetID());
Error error (process.Resume());
if (error.Success())
{
// If we are doing synchronous mode, then wait for the
// process to stop yet again!
if (process.GetTarget().GetDebugger().GetAsyncExecution () == false)
process.WaitForProcessToStop (NULL);
}
}
}
void
SBThread::StepOutOfFrame (lldb::SBFrame &sb_frame)
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
if (log)
{
SBStream frame_desc_strm;
sb_frame.GetDescription (frame_desc_strm);
log->Printf ("SBThread(%p)::StepOutOfFrame (frame = SBFrame(%p): %s)", m_opaque_sp.get(), sb_frame.get(), frame_desc_strm.GetData());
}
if (m_opaque_sp)
{
Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex());
bool abort_other_plans = true;
bool stop_other_threads = true;
m_opaque_sp->QueueThreadPlanForStepOut (abort_other_plans,
NULL,
false,
stop_other_threads,
eVoteYes,
eVoteNoOpinion,
sb_frame->GetFrameIndex());
Process &process = m_opaque_sp->GetProcess();
process.GetThreadList().SetSelectedThreadByID (m_opaque_sp->GetID());
@ -559,9 +604,141 @@ SBThread::RunToAddress (lldb::addr_t addr)
process.WaitForProcessToStop (NULL);
}
}
}
SBError
SBThread::StepOverUntil (lldb::SBFrame &sb_frame,
lldb::SBFileSpec &sb_file_spec,
uint32_t line)
{
SBError sb_error;
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
char path[PATH_MAX];
if (log)
{
SBStream frame_desc_strm;
sb_frame.GetDescription (frame_desc_strm);
sb_file_spec->GetPath (path, sizeof(path));
log->Printf ("SBThread(%p)::StepOverUntil (frame = SBFrame(%p): %s, file+line = %s:%u)",
m_opaque_sp.get(),
sb_frame.get(),
frame_desc_strm.GetData(),
path, line);
}
if (m_opaque_sp)
{
Mutex::Locker api_locker (m_opaque_sp->GetProcess().GetTarget().GetAPIMutex());
if (line == 0)
{
sb_error.SetErrorString("invalid line argument");
return sb_error;
}
StackFrameSP frame_sp;
if (sb_frame.IsValid())
frame_sp = sb_frame.get_sp();
else
{
frame_sp = m_opaque_sp->GetSelectedFrame ();
if (!frame_sp)
frame_sp = m_opaque_sp->GetStackFrameAtIndex (0);
}
SymbolContext frame_sc;
if (!frame_sp)
{
sb_error.SetErrorString("no valid frames in thread to step");
return sb_error;
}
// If we have a frame, get its line
frame_sc = frame_sp->GetSymbolContext (eSymbolContextCompUnit |
eSymbolContextFunction |
eSymbolContextLineEntry |
eSymbolContextSymbol );
if (frame_sc.comp_unit == NULL)
{
sb_error.SetErrorStringWithFormat("frame %u doesn't have debug information", frame_sp->GetFrameIndex());
return sb_error;
}
FileSpec step_file_spec;
if (sb_file_spec.IsValid())
{
// The file spec passed in was valid, so use it
step_file_spec = sb_file_spec.ref();
}
else
{
if (frame_sc.line_entry.IsValid())
step_file_spec = frame_sc.line_entry.file;
else
{
sb_error.SetErrorString("invalid file argument or no file for frame");
return sb_error;
}
}
std::vector<addr_t> step_over_until_addrs;
const bool abort_other_plans = true;
const bool stop_other_threads = true;
const bool check_inlines = true;
const bool exact = false;
SymbolContextList sc_list;
const uint32_t num_matches = frame_sc.comp_unit->ResolveSymbolContext (step_file_spec, line, check_inlines, exact, eSymbolContextLineEntry, sc_list);
if (num_matches > 0)
{
SymbolContext sc;
for (uint32_t i=0; i<num_matches; ++i)
{
if (sc_list.GetContextAtIndex(i, sc))
{
addr_t step_addr = sc.line_entry.range.GetBaseAddress().GetLoadAddress(&m_opaque_sp->GetProcess().GetTarget());
if (step_addr != LLDB_INVALID_ADDRESS)
{
step_over_until_addrs.push_back(step_addr);
}
}
}
}
if (step_over_until_addrs.empty())
{
step_file_spec.GetPath (path, sizeof(path));
sb_error.SetErrorStringWithFormat("No line entries for %s:u", path, line);
}
else
{
m_opaque_sp->QueueThreadPlanForStepUntil (abort_other_plans,
&step_over_until_addrs[0],
step_over_until_addrs.size(),
stop_other_threads,
frame_sp->GetFrameIndex());
m_opaque_sp->GetProcess().GetThreadList().SetSelectedThreadByID (m_opaque_sp->GetID());
sb_error.ref() = m_opaque_sp->GetProcess().Resume();
if (sb_error->Success())
{
// If we are doing synchronous mode, then wait for the
// process to stop yet again!
if (m_opaque_sp->GetProcess().GetTarget().GetDebugger().GetAsyncExecution () == false)
m_opaque_sp->GetProcess().WaitForProcessToStop (NULL);
}
}
}
else
{
sb_error.SetErrorString("this SBThread object is invalid");
}
return sb_error;
}
bool
SBThread::Suspend()
{
@ -606,10 +783,10 @@ SBThread::GetProcess ()
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
if (log)
{
SBStream sstr;
process.GetDescription (sstr);
SBStream frame_desc_strm;
process.GetDescription (frame_desc_strm);
log->Printf ("SBThread(%p)::GetProcess () => SBProcess(%p): %s", m_opaque_sp.get(),
process.get(), sstr.GetData());
process.get(), frame_desc_strm.GetData());
}
return process;
@ -647,10 +824,10 @@ SBThread::GetFrameAtIndex (uint32_t idx)
if (log)
{
SBStream sstr;
sb_frame.GetDescription (sstr);
SBStream frame_desc_strm;
sb_frame.GetDescription (frame_desc_strm);
log->Printf ("SBThread(%p)::GetFrameAtIndex (idx=%d) => SBFrame(%p): %s",
m_opaque_sp.get(), idx, sb_frame.get(), sstr.GetData());
m_opaque_sp.get(), idx, sb_frame.get(), frame_desc_strm.GetData());
}
return sb_frame;
@ -670,10 +847,10 @@ SBThread::GetSelectedFrame ()
if (log)
{
SBStream sstr;
sb_frame.GetDescription (sstr);
SBStream frame_desc_strm;
sb_frame.GetDescription (frame_desc_strm);
log->Printf ("SBThread(%p)::GetSelectedFrame () => SBFrame(%p): %s",
m_opaque_sp.get(), sb_frame.get(), sstr.GetData());
m_opaque_sp.get(), sb_frame.get(), frame_desc_strm.GetData());
}
return sb_frame;
@ -698,10 +875,10 @@ SBThread::SetSelectedFrame (uint32_t idx)
if (log)
{
SBStream sstr;
sb_frame.GetDescription (sstr);
SBStream frame_desc_strm;
sb_frame.GetDescription (frame_desc_strm);
log->Printf ("SBThread(%p)::SetSelectedFrame (idx=%u) => SBFrame(%p): %s",
m_opaque_sp.get(), idx, sb_frame.get(), sstr.GetData());
m_opaque_sp.get(), idx, sb_frame.get(), frame_desc_strm.GetData());
}
return sb_frame;
}

View File

@ -729,7 +729,13 @@ public:
{
ThreadPlan *new_plan;
new_plan = thread->QueueThreadPlanForStepOut (abort_other_plans, NULL, false, bool_stop_other_threads, eVoteYes, eVoteNoOpinion);
new_plan = thread->QueueThreadPlanForStepOut (abort_other_plans,
NULL,
false,
bool_stop_other_threads,
eVoteYes,
eVoteNoOpinion,
thread->GetSelectedFrameIndex());
// FIXME: This will keep the step plan on the thread stack when we hit a breakpoint while stepping over.
// Maybe there should be a parameter to control this.
new_plan->SetOkayToDiscard(false);
@ -1208,7 +1214,7 @@ public:
index_ptr++;
}
new_plan = thread->QueueThreadPlanForStepUntil (abort_other_plans, &address_list.front(), address_list.size(), m_options.m_stop_others);
new_plan = thread->QueueThreadPlanForStepUntil (abort_other_plans, &address_list.front(), address_list.size(), m_options.m_stop_others, thread->GetSelectedFrameIndex ());
new_plan->SetOkayToDiscard(false);
}
else

View File

@ -129,7 +129,13 @@ AppleThreadPlanStepThroughObjCTrampoline::ShouldStop (Event *event_ptr)
log->Printf ("Implementation lookup returned msgForward function: 0x%llx, stopping.", target_addr);
SymbolContext sc = m_thread.GetStackFrameAtIndex(0)->GetSymbolContext(eSymbolContextEverything);
m_run_to_sp.reset(new ThreadPlanStepOut(m_thread, &sc, true, m_stop_others, eVoteNoOpinion, eVoteNoOpinion));
m_run_to_sp.reset(new ThreadPlanStepOut (m_thread,
&sc,
true,
m_stop_others,
eVoteNoOpinion,
eVoteNoOpinion,
0));
m_thread.QueueThreadPlan(m_run_to_sp, false);
m_run_to_sp->SetPrivate(true);
return false;

View File

@ -661,7 +661,12 @@ Thread::QueueFundamentalPlan (bool abort_other_plans)
}
ThreadPlan *
Thread::QueueThreadPlanForStepSingleInstruction (bool step_over, bool abort_other_plans, bool stop_other_threads)
Thread::QueueThreadPlanForStepSingleInstruction
(
bool step_over,
bool abort_other_plans,
bool stop_other_threads
)
{
ThreadPlanSP thread_plan_sp (new ThreadPlanStepInstruction (*this, step_over, stop_other_threads, eVoteNoOpinion, eVoteNoOpinion));
QueueThreadPlan (thread_plan_sp, abort_other_plans);
@ -706,10 +711,24 @@ Thread::QueueThreadPlanForStepOverBreakpointPlan (bool abort_other_plans)
}
ThreadPlan *
Thread::QueueThreadPlanForStepOut (bool abort_other_plans, SymbolContext *addr_context, bool first_insn,
bool stop_other_threads, Vote stop_vote, Vote run_vote)
Thread::QueueThreadPlanForStepOut
(
bool abort_other_plans,
SymbolContext *addr_context,
bool first_insn,
bool stop_other_threads,
Vote stop_vote,
Vote run_vote,
uint32_t frame_idx
)
{
ThreadPlanSP thread_plan_sp (new ThreadPlanStepOut (*this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote));
ThreadPlanSP thread_plan_sp (new ThreadPlanStepOut (*this,
addr_context,
first_insn,
stop_other_threads,
stop_vote,
run_vote,
frame_idx));
QueueThreadPlan (thread_plan_sp, abort_other_plans);
return thread_plan_sp.get();
}
@ -763,9 +782,10 @@ ThreadPlan *
Thread::QueueThreadPlanForStepUntil (bool abort_other_plans,
lldb::addr_t *address_list,
size_t num_addresses,
bool stop_other_threads)
bool stop_other_threads,
uint32_t frame_idx)
{
ThreadPlanSP thread_plan_sp (new ThreadPlanStepUntil (*this, address_list, num_addresses, stop_other_threads));
ThreadPlanSP thread_plan_sp (new ThreadPlanStepUntil (*this, address_list, num_addresses, stop_other_threads, frame_idx));
QueueThreadPlan (thread_plan_sp, abort_other_plans);
return thread_plan_sp.get();

View File

@ -270,9 +270,13 @@ ThreadPlanStepInRange::DefaultShouldStopHereCallback (ThreadPlan *current_plan,
if (should_step_out)
{
// FIXME: Make sure the ThreadPlanForStepOut does the right thing with inlined functions.
return current_plan->GetThread().QueueThreadPlanForStepOut (false, NULL, true,
return current_plan->GetThread().QueueThreadPlanForStepOut (false,
NULL,
true,
current_plan->StopOthers(),
eVoteNo, eVoteNoOpinion);
eVoteNo,
eVoteNoOpinion,
0); // Frame index
}
return NULL;

View File

@ -129,7 +129,7 @@ ThreadPlanStepInstruction::ShouldStop (Event *event_ptr)
s.Address (return_addr, m_thread.GetProcess().GetAddressByteSize());
log->Printf("%s.", s.GetData());
}
m_thread.QueueThreadPlanForStepOut(false, NULL, true, m_stop_other_threads, eVoteNo, eVoteNoOpinion);
m_thread.QueueThreadPlanForStepOut(false, NULL, true, m_stop_other_threads, eVoteNo, eVoteNoOpinion, 0);
return false;
}
else

View File

@ -35,7 +35,8 @@ ThreadPlanStepOut::ThreadPlanStepOut
bool first_insn,
bool stop_others,
Vote stop_vote,
Vote run_vote
Vote run_vote,
uint32_t frame_idx
) :
ThreadPlan (ThreadPlan::eKindStepOut, "Step out", thread, stop_vote, run_vote),
m_step_from_context (context),
@ -50,24 +51,20 @@ ThreadPlanStepOut::ThreadPlanStepOut
// Find the return address and set a breakpoint there:
// FIXME - can we do this more securely if we know first_insn?
StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get();
if (return_frame)
StackFrameSP return_frame_sp (m_thread.GetStackFrameAtIndex(frame_idx + 1));
if (return_frame_sp)
{
// TODO: check for inlined frames and do the right thing...
m_return_addr = return_frame->GetRegisterContext()->GetPC();
m_return_addr = return_frame_sp->GetRegisterContext()->GetPC();
Breakpoint *return_bp = m_thread.GetProcess().GetTarget().CreateBreakpoint (m_return_addr, true).get();
if (return_bp != NULL)
{
return_bp->SetThreadID(m_thread.GetID());
m_return_bp_id = return_bp->GetID();
}
else
{
m_return_bp_id = LLDB_INVALID_BREAK_ID;
}
}
m_stack_depth = m_thread.GetStackFrameCount();
m_stack_depth = m_thread.GetStackFrameCount() - frame_idx;
}
ThreadPlanStepOut::~ThreadPlanStepOut ()
@ -116,6 +113,10 @@ ThreadPlanStepOut::PlanExplainsStop ()
BreakpointSiteSP site_sp (m_thread.GetProcess().GetBreakpointSiteList().FindByID (stop_info_sp->GetValue()));
if (site_sp && site_sp->IsBreakpointAtThisSite (m_return_bp_id))
{
const uint32_t num_frames = m_thread.GetStackFrameCount();
if (m_stack_depth > num_frames);
SetPlanComplete();
// If there was only one owner, then we're done. But if we also hit some
// user breakpoint on our way out, we should mark ourselves as done, but
// also not claim to explain the stop, since it is more important to report
@ -124,7 +125,6 @@ ThreadPlanStepOut::PlanExplainsStop ()
if (site_sp->GetNumberOfOwners() == 1)
return true;
SetPlanComplete();
}
return false;
}
@ -143,9 +143,7 @@ ThreadPlanStepOut::PlanExplainsStop ()
bool
ThreadPlanStepOut::ShouldStop (Event *event_ptr)
{
if (IsPlanComplete()
|| m_thread.GetRegisterContext()->GetPC() == m_return_addr
|| m_stack_depth > m_thread.GetStackFrameCount())
if (IsPlanComplete() || m_stack_depth > m_thread.GetStackFrameCount())
{
SetPlanComplete();
return true;

View File

@ -104,7 +104,13 @@ ThreadPlanStepOverRange::ShouldStop (Event *event_ptr)
}
else if (FrameIsYounger())
{
new_plan = m_thread.QueueThreadPlanForStepOut (false, NULL, true, stop_others, lldb::eVoteNo, lldb::eVoteNoOpinion);
new_plan = m_thread.QueueThreadPlanForStepOut (false,
NULL,
true,
stop_others,
lldb::eVoteNo,
lldb::eVoteNoOpinion,
0);
}
else if (!InSymbol())
{

View File

@ -37,7 +37,8 @@ ThreadPlanStepUntil::ThreadPlanStepUntil
Thread &thread,
lldb::addr_t *address_list,
size_t num_addresses,
bool stop_others
bool stop_others,
uint32_t frame_idx
) :
ThreadPlan (ThreadPlan::eKindStepUntil, "Step until", thread, eVoteNoOpinion, eVoteNoOpinion),
m_stack_depth (0),
@ -56,27 +57,29 @@ ThreadPlanStepUntil::ThreadPlanStepUntil
// Stash away our "until" addresses:
Target &target = m_thread.GetProcess().GetTarget();
m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0);
StackFrameSP frame_sp (m_thread.GetStackFrameAtIndex (frame_idx));
if (frame_sp)
{
m_step_from_insn = frame_sp->GetStackID().GetPC();
lldb::user_id_t thread_id = m_thread.GetID();
// Find the return address and set a breakpoint there:
// FIXME - can we do this more securely if we know first_insn?
StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get();
StackFrameSP return_frame_sp (m_thread.GetStackFrameAtIndex(frame_idx + 1));
if (return_frame_sp)
{
// TODO: add inline functionality
m_return_addr = return_frame->GetRegisterContext()->GetPC();
m_return_addr = return_frame_sp->GetStackID().GetPC();
Breakpoint *return_bp = target.CreateBreakpoint (m_return_addr, true).get();
if (return_bp != NULL)
{
return_bp->SetThreadID(thread_id);
m_return_bp_id = return_bp->GetID();
}
else
{
m_return_bp_id = LLDB_INVALID_BREAK_ID;
}
m_stack_depth = m_thread.GetStackFrameCount();
m_stack_depth = m_thread.GetStackFrameCount() - frame_idx;
// Now set breakpoints on all our return addresses:
for (int i = 0; i < num_addresses; i++)
@ -92,6 +95,7 @@ ThreadPlanStepUntil::ThreadPlanStepUntil
m_until_points[address_list[i]] = LLDB_INVALID_BREAK_ID;
}
}
}
}
ThreadPlanStepUntil::~ThreadPlanStepUntil ()