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         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 
72 ThreadPlanStepOut::~ThreadPlanStepOut ()
73 {
74     if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
75         m_thread.GetProcess().GetTarget().RemoveBreakpointByID(m_return_bp_id);
76 }
77 
78 void
79 ThreadPlanStepOut::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 
92 bool
93 ThreadPlanStepOut::ValidatePlan (Stream *error)
94 {
95     if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
96         return false;
97     else
98         return true;
99 }
100 
101 bool
102 ThreadPlanStepOut::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.
106     StopInfo *stop_info = m_thread.GetStopInfo();
107     if (stop_info)
108     {
109         StopReason reason = stop_info->GetStopReason();
110         switch (reason)
111         {
112         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))
117             {
118                 // 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.
122 
123                 if (site_sp->GetNumberOfOwners() == 1)
124                     return true;
125 
126                 SetPlanComplete();
127             }
128             return false;
129         }
130         case eStopReasonWatchpoint:
131         case eStopReasonSignal:
132         case eStopReasonException:
133             return false;
134 
135         default:
136             return true;
137         }
138     }
139     return true;
140 }
141 
142 bool
143 ThreadPlanStepOut::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 
156 bool
157 ThreadPlanStepOut::StopOthers ()
158 {
159     return m_stop_others;
160 }
161 
162 StateType
163 ThreadPlanStepOut::RunState ()
164 {
165     return eStateRunning;
166 }
167 
168 bool
169 ThreadPlanStepOut::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 
184 bool
185 ThreadPlanStepOut::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 
193 bool
194 ThreadPlanStepOut::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 
223