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