blob: d78411ee936b19c16965bea8181b6c7169a546b4 [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"
Jim Ingham43d39062011-10-15 00:57:28 +000023#include "lldb/Target/ThreadPlanStepOverRange.h"
Chris Lattner24943d22010-06-08 16:52:24 +000024
25using namespace lldb;
26using namespace lldb_private;
27
28//----------------------------------------------------------------------
29// ThreadPlanStepOut: Step out of the current frame
30//----------------------------------------------------------------------
31
32ThreadPlanStepOut::ThreadPlanStepOut
33(
34 Thread &thread,
35 SymbolContext *context,
36 bool first_insn,
37 bool stop_others,
38 Vote stop_vote,
Greg Clayton1ebdcc72011-01-21 06:11:58 +000039 Vote run_vote,
40 uint32_t frame_idx
Chris Lattner24943d22010-06-08 16:52:24 +000041) :
Jim Ingham5a47e8b2010-06-19 04:45:32 +000042 ThreadPlan (ThreadPlan::eKindStepOut, "Step out", thread, stop_vote, run_vote),
Chris Lattner24943d22010-06-08 16:52:24 +000043 m_step_from_context (context),
44 m_step_from_insn (LLDB_INVALID_ADDRESS),
Benjamin Kramer36a08102010-07-16 12:32:33 +000045 m_return_bp_id (LLDB_INVALID_BREAK_ID),
Chris Lattner24943d22010-06-08 16:52:24 +000046 m_return_addr (LLDB_INVALID_ADDRESS),
47 m_first_insn (first_insn),
Jim Ingham43d39062011-10-15 00:57:28 +000048 m_stop_others (stop_others),
49 m_step_through_inline_plan_sp(),
50 m_step_out_plan_sp ()
51
Chris Lattner24943d22010-06-08 16:52:24 +000052{
53 m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0);
54
Greg Clayton1ebdcc72011-01-21 06:11:58 +000055 StackFrameSP return_frame_sp (m_thread.GetStackFrameAtIndex(frame_idx + 1));
Jim Ingham43d39062011-10-15 00:57:28 +000056 StackFrameSP immediate_return_from_sp (m_thread.GetStackFrameAtIndex (frame_idx));
57
58 m_stack_depth = m_thread.GetStackFrameCount() - frame_idx;
59
60 // If the frame directly below the one we are returning to is inlined, we have to be
61 // a little more careful. It is non-trivial to determine the real "return code address" for
62 // an inlined frame, so we have to work our way to that frame and then step out.
63 if (immediate_return_from_sp && immediate_return_from_sp->IsInlined())
Chris Lattner24943d22010-06-08 16:52:24 +000064 {
Jim Ingham43d39062011-10-15 00:57:28 +000065 if (frame_idx > 0)
66 {
67 // First queue a plan that gets us to this inlined frame, and when we get there we'll queue a second
68 // plan that walks us out of this frame.
69 m_step_out_plan_sp.reset (new ThreadPlanStepOut(m_thread, NULL, false, stop_others, eVoteNoOpinion, eVoteNoOpinion, frame_idx - 1));
70 m_step_out_plan_sp->SetOkayToDiscard(true);
71 }
72 else
73 {
74 // If we're already at the inlined frame we're stepping through, then just do that now.
75 QueueInlinedStepPlan(false);
76 }
77
78 }
79 else if (return_frame_sp)
80 {
81 // Find the return address and set a breakpoint there:
82 // FIXME - can we do this more securely if we know first_insn?
83
84 m_return_addr = return_frame_sp->GetFrameCodeAddress().GetLoadAddress(&m_thread.GetProcess().GetTarget());
Chris Lattner24943d22010-06-08 16:52:24 +000085 Breakpoint *return_bp = m_thread.GetProcess().GetTarget().CreateBreakpoint (m_return_addr, true).get();
86 if (return_bp != NULL)
87 {
88 return_bp->SetThreadID(m_thread.GetID());
89 m_return_bp_id = return_bp->GetID();
90 }
Chris Lattner24943d22010-06-08 16:52:24 +000091 }
92
Jim Ingham43d39062011-10-15 00:57:28 +000093}
94
95void
96ThreadPlanStepOut::DidPush()
97{
98 if (m_step_out_plan_sp)
99 m_thread.QueueThreadPlan(m_step_out_plan_sp, false);
100 else if (m_step_through_inline_plan_sp)
101 m_thread.QueueThreadPlan(m_step_through_inline_plan_sp, false);
Chris Lattner24943d22010-06-08 16:52:24 +0000102}
103
104ThreadPlanStepOut::~ThreadPlanStepOut ()
105{
106 if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
107 m_thread.GetProcess().GetTarget().RemoveBreakpointByID(m_return_bp_id);
108}
109
110void
111ThreadPlanStepOut::GetDescription (Stream *s, lldb::DescriptionLevel level)
112{
113 if (level == lldb::eDescriptionLevelBrief)
114 s->Printf ("step out");
115 else
116 {
Jim Ingham43d39062011-10-15 00:57:28 +0000117 if (m_step_out_plan_sp)
118 s->Printf ("Stepping out to inlined frame at depth: %d so we can walk through it.", m_stack_depth);
119 else if (m_step_through_inline_plan_sp)
120 s->Printf ("Stepping out by stepping through inlined function.");
121 else
122 s->Printf ("Stepping out from address 0x%llx to return address 0x%llx at depth: %d using breakpoint site %d",
123 (uint64_t)m_step_from_insn,
124 (uint64_t)m_return_addr,
125 m_stack_depth,
126 m_return_bp_id);
Chris Lattner24943d22010-06-08 16:52:24 +0000127 }
128}
129
130bool
131ThreadPlanStepOut::ValidatePlan (Stream *error)
132{
Jim Ingham43d39062011-10-15 00:57:28 +0000133 if (m_step_out_plan_sp)
134 return m_step_out_plan_sp->ValidatePlan (error);
135 else if (m_step_through_inline_plan_sp)
136 return m_step_through_inline_plan_sp->ValidatePlan (error);
137 else if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
138 {
139 error->PutCString("Could not create return address breakpoint.");
Chris Lattner24943d22010-06-08 16:52:24 +0000140 return false;
Jim Ingham43d39062011-10-15 00:57:28 +0000141 }
Chris Lattner24943d22010-06-08 16:52:24 +0000142 else
143 return true;
144}
145
146bool
147ThreadPlanStepOut::PlanExplainsStop ()
148{
Jim Ingham43d39062011-10-15 00:57:28 +0000149 // If one of our child plans just finished, then we do explain the stop.
150 if (m_step_out_plan_sp)
151 {
152 if (m_step_out_plan_sp->MischiefManaged())
153 {
154 // If this one is done, then we are all done.
155 SetPlanComplete();
156 return true;
157 }
158 else
159 return false;
160 }
161 else if (m_step_through_inline_plan_sp)
162 {
163 if (m_step_through_inline_plan_sp->MischiefManaged())
164 return true;
165 else
166 return false;
167 }
168
Chris Lattner24943d22010-06-08 16:52:24 +0000169 // We don't explain signals or breakpoints (breakpoints that handle stepping in or
170 // out will be handled by a child plan.
Jim Ingham43d39062011-10-15 00:57:28 +0000171
Jim Ingham6297a3a2010-10-20 00:39:53 +0000172 StopInfoSP stop_info_sp = GetPrivateStopReason();
173 if (stop_info_sp)
Chris Lattner24943d22010-06-08 16:52:24 +0000174 {
Jim Ingham6297a3a2010-10-20 00:39:53 +0000175 StopReason reason = stop_info_sp->GetStopReason();
Chris Lattner24943d22010-06-08 16:52:24 +0000176 switch (reason)
177 {
Greg Clayton643ee732010-08-04 01:40:35 +0000178 case eStopReasonBreakpoint:
179 {
180 // If this is OUR breakpoint, we're fine, otherwise we don't know why this happened...
Jim Ingham6297a3a2010-10-20 00:39:53 +0000181 BreakpointSiteSP site_sp (m_thread.GetProcess().GetBreakpointSiteList().FindByID (stop_info_sp->GetValue()));
Greg Clayton643ee732010-08-04 01:40:35 +0000182 if (site_sp && site_sp->IsBreakpointAtThisSite (m_return_bp_id))
Chris Lattner24943d22010-06-08 16:52:24 +0000183 {
Greg Clayton1ebdcc72011-01-21 06:11:58 +0000184 const uint32_t num_frames = m_thread.GetStackFrameCount();
Johnny Chena14755a2011-02-04 17:21:08 +0000185 if (m_stack_depth > num_frames)
Greg Clayton1ebdcc72011-01-21 06:11:58 +0000186 SetPlanComplete();
187
Greg Clayton643ee732010-08-04 01:40:35 +0000188 // If there was only one owner, then we're done. But if we also hit some
189 // user breakpoint on our way out, we should mark ourselves as done, but
190 // also not claim to explain the stop, since it is more important to report
191 // the user breakpoint than the step out completion.
Chris Lattner24943d22010-06-08 16:52:24 +0000192
Greg Clayton643ee732010-08-04 01:40:35 +0000193 if (site_sp->GetNumberOfOwners() == 1)
194 return true;
195
Chris Lattner24943d22010-06-08 16:52:24 +0000196 }
Greg Clayton643ee732010-08-04 01:40:35 +0000197 return false;
198 }
199 case eStopReasonWatchpoint:
200 case eStopReasonSignal:
201 case eStopReasonException:
202 return false;
203
204 default:
205 return true;
Chris Lattner24943d22010-06-08 16:52:24 +0000206 }
207 }
208 return true;
209}
210
211bool
212ThreadPlanStepOut::ShouldStop (Event *event_ptr)
213{
Jim Ingham43d39062011-10-15 00:57:28 +0000214 if (IsPlanComplete())
215 {
216 return true;
217 }
218 else if (m_stack_depth > m_thread.GetStackFrameCount())
219 {
220 SetPlanComplete();
221 return true;
222 }
223 else
224 {
225 if (m_step_out_plan_sp)
226 {
227 if (m_step_out_plan_sp->MischiefManaged())
228 {
229 // Now step through the inlined stack we are in:
230 if (QueueInlinedStepPlan(true))
231 {
232 return false;
233 }
234 else
235 {
236 SetPlanComplete ();
237 return true;
238 }
239 }
240 else
241 return m_step_out_plan_sp->ShouldStop(event_ptr);
242 }
243 else if (m_step_through_inline_plan_sp)
244 {
245 if (m_step_through_inline_plan_sp->MischiefManaged())
246 {
247 SetPlanComplete();
248 return true;
249 }
250 else
251 return m_step_through_inline_plan_sp->ShouldStop(event_ptr);
252 }
253 else
254 return false;
255 }
Chris Lattner24943d22010-06-08 16:52:24 +0000256}
257
258bool
259ThreadPlanStepOut::StopOthers ()
260{
261 return m_stop_others;
262}
263
264StateType
Jim Ingham745ac7a2010-11-11 19:26:09 +0000265ThreadPlanStepOut::GetPlanRunState ()
Chris Lattner24943d22010-06-08 16:52:24 +0000266{
267 return eStateRunning;
268}
269
270bool
271ThreadPlanStepOut::WillResume (StateType resume_state, bool current_plan)
272{
273 ThreadPlan::WillResume (resume_state, current_plan);
Jim Ingham43d39062011-10-15 00:57:28 +0000274 if (m_step_out_plan_sp || m_step_through_inline_plan_sp)
275 return true;
276
Chris Lattner24943d22010-06-08 16:52:24 +0000277 if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
278 return false;
279
280 if (current_plan)
281 {
282 Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get();
283 if (return_bp != NULL)
284 return_bp->SetEnabled (true);
285 }
286 return true;
287}
288
289bool
290ThreadPlanStepOut::WillStop ()
291{
Jim Ingham43d39062011-10-15 00:57:28 +0000292 if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
293 {
294 Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get();
295 if (return_bp != NULL)
296 return_bp->SetEnabled (false);
297 }
298
Chris Lattner24943d22010-06-08 16:52:24 +0000299 return true;
300}
301
302bool
303ThreadPlanStepOut::MischiefManaged ()
304{
Jim Ingham43d39062011-10-15 00:57:28 +0000305 if (IsPlanComplete())
Chris Lattner24943d22010-06-08 16:52:24 +0000306 {
307 // Did I reach my breakpoint? If so I'm done.
308 //
309 // I also check the stack depth, since if we've blown past the breakpoint for some
310 // reason and we're now stopping for some other reason altogether, then we're done
311 // with this step out operation.
312
Greg Claytone005f2c2010-11-06 01:53:30 +0000313 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
Chris Lattner24943d22010-06-08 16:52:24 +0000314 if (log)
315 log->Printf("Completed step out plan.");
Jim Ingham43d39062011-10-15 00:57:28 +0000316 if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
317 {
318 m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_return_bp_id);
319 m_return_bp_id = LLDB_INVALID_BREAK_ID;
320 }
321
Chris Lattner24943d22010-06-08 16:52:24 +0000322 ThreadPlan::MischiefManaged ();
323 return true;
324 }
325 else
326 {
327 return false;
328 }
329}
330
Jim Ingham43d39062011-10-15 00:57:28 +0000331bool
332ThreadPlanStepOut::QueueInlinedStepPlan (bool queue_now)
333{
334 // Now figure out the range of this inlined block, and set up a "step through range"
335 // plan for that. If we've been provided with a context, then use the block in that
336 // context.
337 StackFrameSP immediate_return_from_sp (m_thread.GetStackFrameAtIndex (0));
338 if (!immediate_return_from_sp)
339 return false;
340
341 LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
342 if (log)
343 {
344 StreamString s;
345 immediate_return_from_sp->Dump(&s, true, false);
346 log->Printf("Queuing inlined frame to step past: %s.", s.GetData());
347 }
348
349 Block *from_block = immediate_return_from_sp->GetFrameBlock();
350 if (from_block)
351 {
352 Block *inlined_block = from_block->GetContainingInlinedBlock();
353 if (inlined_block)
354 {
355 size_t num_ranges = inlined_block->GetNumRanges();
356 AddressRange inline_range;
357 if (inlined_block->GetRangeAtIndex(0, inline_range))
358 {
359 SymbolContext inlined_sc;
360 inlined_block->CalculateSymbolContext(&inlined_sc);
361 RunMode run_mode = m_stop_others ? lldb::eOnlyThisThread : lldb::eAllThreads;
362 ThreadPlanStepOverRange *step_through_inline_plan_ptr = new ThreadPlanStepOverRange(m_thread,
363 inline_range,
364 inlined_sc,
365 run_mode);
366 step_through_inline_plan_ptr->SetOkayToDiscard(true);
367 StreamString errors;
368 if (!step_through_inline_plan_ptr->ValidatePlan(&errors))
369 {
370 //FIXME: Log this failure.
371 delete step_through_inline_plan_ptr;
372 return false;
373 }
374
375 for (size_t i = 1; i < num_ranges; i++)
376 {
377 if (inlined_block->GetRangeAtIndex (i, inline_range))
378 step_through_inline_plan_ptr->AddRange (inline_range);
379 }
380 m_step_through_inline_plan_sp.reset (step_through_inline_plan_ptr);
381 if (queue_now)
382 m_thread.QueueThreadPlan (m_step_through_inline_plan_sp, false);
383 return true;
384 }
385 }
386 }
387
388 return false;
389}