blob: fd9f8faf99d81f5444d63d0973e25a00188d3f72 [file] [log] [blame]
Chris Lattner24943d22010-06-08 16:52:24 +00001//===-- ThreadPlanStepOut.cpp -----------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "lldb/Target/ThreadPlanStepOut.h"
11
12// C Includes
13// C++ Includes
14// Other libraries and framework includes
15// Project includes
16#include "lldb/Breakpoint/Breakpoint.h"
17#include "lldb/lldb-private-log.h"
18#include "lldb/Core/Log.h"
19#include "lldb/Target/Process.h"
20#include "lldb/Target/RegisterContext.h"
Greg Clayton643ee732010-08-04 01:40:35 +000021#include "lldb/Target/StopInfo.h"
Chris Lattner24943d22010-06-08 16:52:24 +000022#include "lldb/Target/Target.h"
23
24using namespace lldb;
25using namespace lldb_private;
26
27//----------------------------------------------------------------------
28// ThreadPlanStepOut: Step out of the current frame
29//----------------------------------------------------------------------
30
31ThreadPlanStepOut::ThreadPlanStepOut
32(
33 Thread &thread,
34 SymbolContext *context,
35 bool first_insn,
36 bool stop_others,
37 Vote stop_vote,
38 Vote run_vote
39) :
Jim Ingham5a47e8b2010-06-19 04:45:32 +000040 ThreadPlan (ThreadPlan::eKindStepOut, "Step out", thread, stop_vote, run_vote),
Chris Lattner24943d22010-06-08 16:52:24 +000041 m_step_from_context (context),
42 m_step_from_insn (LLDB_INVALID_ADDRESS),
Benjamin Kramer36a08102010-07-16 12:32:33 +000043 m_return_bp_id (LLDB_INVALID_BREAK_ID),
Chris Lattner24943d22010-06-08 16:52:24 +000044 m_return_addr (LLDB_INVALID_ADDRESS),
45 m_first_insn (first_insn),
Chris Lattner24943d22010-06-08 16:52:24 +000046 m_stop_others (stop_others)
47{
48 m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0);
49
50 // Find the return address and set a breakpoint there:
51 // FIXME - can we do this more securely if we know first_insn?
52
53 StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get();
54 if (return_frame)
55 {
56 m_return_addr = return_frame->GetPC().GetLoadAddress(&m_thread.GetProcess());
57 Breakpoint *return_bp = m_thread.GetProcess().GetTarget().CreateBreakpoint (m_return_addr, true).get();
58 if (return_bp != NULL)
59 {
60 return_bp->SetThreadID(m_thread.GetID());
61 m_return_bp_id = return_bp->GetID();
62 }
63 else
64 {
65 m_return_bp_id = LLDB_INVALID_BREAK_ID;
66 }
67 }
68
69 m_stack_depth = m_thread.GetStackFrameCount();
70}
71
72ThreadPlanStepOut::~ThreadPlanStepOut ()
73{
74 if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
75 m_thread.GetProcess().GetTarget().RemoveBreakpointByID(m_return_bp_id);
76}
77
78void
79ThreadPlanStepOut::GetDescription (Stream *s, lldb::DescriptionLevel level)
80{
81 if (level == lldb::eDescriptionLevelBrief)
82 s->Printf ("step out");
83 else
84 {
85 s->Printf ("Stepping out from address 0x%llx to return address 0x%llx using breakpoint site %d",
86 (uint64_t)m_step_from_insn,
87 (uint64_t)m_return_addr,
88 m_return_bp_id);
89 }
90}
91
92bool
93ThreadPlanStepOut::ValidatePlan (Stream *error)
94{
95 if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
96 return false;
97 else
98 return true;
99}
100
101bool
102ThreadPlanStepOut::PlanExplainsStop ()
103{
104 // We don't explain signals or breakpoints (breakpoints that handle stepping in or
105 // out will be handled by a child plan.
Greg Clayton643ee732010-08-04 01:40:35 +0000106 StopInfo *stop_info = m_thread.GetStopInfo();
107 if (stop_info)
Chris Lattner24943d22010-06-08 16:52:24 +0000108 {
Greg Clayton643ee732010-08-04 01:40:35 +0000109 StopReason reason = stop_info->GetStopReason();
Chris Lattner24943d22010-06-08 16:52:24 +0000110 switch (reason)
111 {
Greg Clayton643ee732010-08-04 01:40:35 +0000112 case eStopReasonBreakpoint:
113 {
114 // If this is OUR breakpoint, we're fine, otherwise we don't know why this happened...
115 BreakpointSiteSP site_sp (m_thread.GetProcess().GetBreakpointSiteList().FindByID (stop_info->GetValue()));
116 if (site_sp && site_sp->IsBreakpointAtThisSite (m_return_bp_id))
Chris Lattner24943d22010-06-08 16:52:24 +0000117 {
Greg Clayton643ee732010-08-04 01:40:35 +0000118 // If there was only one owner, then we're done. But if we also hit some
119 // user breakpoint on our way out, we should mark ourselves as done, but
120 // also not claim to explain the stop, since it is more important to report
121 // the user breakpoint than the step out completion.
Chris Lattner24943d22010-06-08 16:52:24 +0000122
Greg Clayton643ee732010-08-04 01:40:35 +0000123 if (site_sp->GetNumberOfOwners() == 1)
124 return true;
125
126 SetPlanComplete();
Chris Lattner24943d22010-06-08 16:52:24 +0000127 }
Greg Clayton643ee732010-08-04 01:40:35 +0000128 return false;
129 }
130 case eStopReasonWatchpoint:
131 case eStopReasonSignal:
132 case eStopReasonException:
133 return false;
134
135 default:
136 return true;
Chris Lattner24943d22010-06-08 16:52:24 +0000137 }
138 }
139 return true;
140}
141
142bool
143ThreadPlanStepOut::ShouldStop (Event *event_ptr)
144{
145 if (IsPlanComplete()
146 || m_thread.GetRegisterContext()->GetPC() == m_return_addr
147 || m_stack_depth > m_thread.GetStackFrameCount())
148 {
149 SetPlanComplete();
150 return true;
151 }
152 else
153 return false;
154}
155
156bool
157ThreadPlanStepOut::StopOthers ()
158{
159 return m_stop_others;
160}
161
162StateType
163ThreadPlanStepOut::RunState ()
164{
165 return eStateRunning;
166}
167
168bool
169ThreadPlanStepOut::WillResume (StateType resume_state, bool current_plan)
170{
171 ThreadPlan::WillResume (resume_state, current_plan);
172 if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
173 return false;
174
175 if (current_plan)
176 {
177 Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get();
178 if (return_bp != NULL)
179 return_bp->SetEnabled (true);
180 }
181 return true;
182}
183
184bool
185ThreadPlanStepOut::WillStop ()
186{
187 Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get();
188 if (return_bp != NULL)
189 return_bp->SetEnabled (false);
190 return true;
191}
192
193bool
194ThreadPlanStepOut::MischiefManaged ()
195{
196 if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
197 {
198 // If I couldn't set this breakpoint, then I'm just going to jettison myself.
199 return true;
200 }
201 else if (IsPlanComplete())
202 {
203 // Did I reach my breakpoint? If so I'm done.
204 //
205 // I also check the stack depth, since if we've blown past the breakpoint for some
206 // reason and we're now stopping for some other reason altogether, then we're done
207 // with this step out operation.
208
209 Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP);
210 if (log)
211 log->Printf("Completed step out plan.");
212 m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_return_bp_id);
213 m_return_bp_id = LLDB_INVALID_BREAK_ID;
214 ThreadPlan::MischiefManaged ();
215 return true;
216 }
217 else
218 {
219 return false;
220 }
221}
222