1 //===-- 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"
21 #include "lldb/Target/StopInfo.h"
22 #include "lldb/Target/Target.h"
23 
24 using namespace lldb;
25 using namespace lldb_private;
26 
27 //----------------------------------------------------------------------
28 // ThreadPlanStepOut: Step out of the current frame
29 //----------------------------------------------------------------------
30 
31 ThreadPlanStepOut::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 ) :
40     ThreadPlan (ThreadPlan::eKindStepOut, "Step out", thread, stop_vote, run_vote),
41     m_step_from_context (context),
42     m_step_from_insn (LLDB_INVALID_ADDRESS),
43     m_return_bp_id (LLDB_INVALID_BREAK_ID),
44     m_return_addr (LLDB_INVALID_ADDRESS),
45     m_first_insn (first_insn),
46     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         // TODO: check for inlined frames and do the right thing...
57         m_return_addr = return_frame->GetRegisterContext()->GetPC();
58         Breakpoint *return_bp = m_thread.GetProcess().GetTarget().CreateBreakpoint (m_return_addr, true).get();
59         if (return_bp != NULL)
60         {
61             return_bp->SetThreadID(m_thread.GetID());
62             m_return_bp_id = return_bp->GetID();
63         }
64         else
65         {
66             m_return_bp_id = LLDB_INVALID_BREAK_ID;
67         }
68     }
69 
70     m_stack_depth = m_thread.GetStackFrameCount();
71 }
72 
73 ThreadPlanStepOut::~ThreadPlanStepOut ()
74 {
75     if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
76         m_thread.GetProcess().GetTarget().RemoveBreakpointByID(m_return_bp_id);
77 }
78 
79 void
80 ThreadPlanStepOut::GetDescription (Stream *s, lldb::DescriptionLevel level)
81 {
82     if (level == lldb::eDescriptionLevelBrief)
83         s->Printf ("step out");
84     else
85     {
86         s->Printf ("Stepping out from address 0x%llx to return address 0x%llx using breakpoint site %d",
87                    (uint64_t)m_step_from_insn,
88                    (uint64_t)m_return_addr,
89                    m_return_bp_id);
90     }
91 }
92 
93 bool
94 ThreadPlanStepOut::ValidatePlan (Stream *error)
95 {
96     if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
97         return false;
98     else
99         return true;
100 }
101 
102 bool
103 ThreadPlanStepOut::PlanExplainsStop ()
104 {
105     // We don't explain signals or breakpoints (breakpoints that handle stepping in or
106     // out will be handled by a child plan.
107     StopInfoSP stop_info_sp = GetPrivateStopReason();
108     if (stop_info_sp)
109     {
110         StopReason reason = stop_info_sp->GetStopReason();
111         switch (reason)
112         {
113         case eStopReasonBreakpoint:
114         {
115             // If this is OUR breakpoint, we're fine, otherwise we don't know why this happened...
116             BreakpointSiteSP site_sp (m_thread.GetProcess().GetBreakpointSiteList().FindByID (stop_info_sp->GetValue()));
117             if (site_sp && site_sp->IsBreakpointAtThisSite (m_return_bp_id))
118             {
119                 // If there was only one owner, then we're done.  But if we also hit some
120                 // user breakpoint on our way out, we should mark ourselves as done, but
121                 // also not claim to explain the stop, since it is more important to report
122                 // the user breakpoint than the step out completion.
123 
124                 if (site_sp->GetNumberOfOwners() == 1)
125                     return true;
126 
127                 SetPlanComplete();
128             }
129             return false;
130         }
131         case eStopReasonWatchpoint:
132         case eStopReasonSignal:
133         case eStopReasonException:
134             return false;
135 
136         default:
137             return true;
138         }
139     }
140     return true;
141 }
142 
143 bool
144 ThreadPlanStepOut::ShouldStop (Event *event_ptr)
145 {
146     if (IsPlanComplete()
147         || m_thread.GetRegisterContext()->GetPC() == m_return_addr
148         || m_stack_depth > m_thread.GetStackFrameCount())
149     {
150         SetPlanComplete();
151         return true;
152     }
153     else
154         return false;
155 }
156 
157 bool
158 ThreadPlanStepOut::StopOthers ()
159 {
160     return m_stop_others;
161 }
162 
163 StateType
164 ThreadPlanStepOut::RunState ()
165 {
166     return eStateRunning;
167 }
168 
169 bool
170 ThreadPlanStepOut::WillResume (StateType resume_state, bool current_plan)
171 {
172     ThreadPlan::WillResume (resume_state, current_plan);
173     if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
174         return false;
175 
176     if (current_plan)
177     {
178         Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get();
179         if (return_bp != NULL)
180             return_bp->SetEnabled (true);
181     }
182     return true;
183 }
184 
185 bool
186 ThreadPlanStepOut::WillStop ()
187 {
188     Breakpoint *return_bp = m_thread.GetProcess().GetTarget().GetBreakpointByID(m_return_bp_id).get();
189     if (return_bp != NULL)
190         return_bp->SetEnabled (false);
191     return true;
192 }
193 
194 bool
195 ThreadPlanStepOut::MischiefManaged ()
196 {
197     if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
198     {
199         // If I couldn't set this breakpoint, then I'm just going to jettison myself.
200         return true;
201     }
202     else if (IsPlanComplete())
203     {
204         // Did I reach my breakpoint?  If so I'm done.
205         //
206         // I also check the stack depth, since if we've blown past the breakpoint for some
207         // reason and we're now stopping for some other reason altogether, then we're done
208         // with this step out operation.
209 
210         Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP);
211         if (log)
212             log->Printf("Completed step out plan.");
213         m_thread.GetProcess().GetTarget().RemoveBreakpointByID (m_return_bp_id);
214         m_return_bp_id = LLDB_INVALID_BREAK_ID;
215         ThreadPlan::MischiefManaged ();
216         return true;
217     }
218     else
219     {
220         return false;
221     }
222 }
223 
224