1 //===-- ThreadPlanStepUntil.cpp ---------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Target/ThreadPlanStepUntil.h"
10 
11 #include "lldb/Breakpoint/Breakpoint.h"
12 #include "lldb/Symbol/SymbolContextScope.h"
13 #include "lldb/Target/Process.h"
14 #include "lldb/Target/RegisterContext.h"
15 #include "lldb/Target/StopInfo.h"
16 #include "lldb/Target/Target.h"
17 #include "lldb/Utility/Log.h"
18 
19 using namespace lldb;
20 using namespace lldb_private;
21 
22 // ThreadPlanStepUntil: Run until we reach a given line number or step out of
23 // the current frame
24 
25 ThreadPlanStepUntil::ThreadPlanStepUntil(Thread &thread,
26                                          lldb::addr_t *address_list,
27                                          size_t num_addresses, bool stop_others,
28                                          uint32_t frame_idx)
29     : ThreadPlan(ThreadPlan::eKindStepUntil, "Step until", thread,
30                  eVoteNoOpinion, eVoteNoOpinion),
31       m_step_from_insn(LLDB_INVALID_ADDRESS),
32       m_return_bp_id(LLDB_INVALID_BREAK_ID),
33       m_return_addr(LLDB_INVALID_ADDRESS), m_stepped_out(false),
34       m_should_stop(false), m_ran_analyze(false), m_explains_stop(false),
35       m_until_points(), m_stop_others(stop_others) {
36   // Stash away our "until" addresses:
37   TargetSP target_sp(m_thread.CalculateTarget());
38 
39   StackFrameSP frame_sp(m_thread.GetStackFrameAtIndex(frame_idx));
40   if (frame_sp) {
41     m_step_from_insn = frame_sp->GetStackID().GetPC();
42     lldb::user_id_t thread_id = m_thread.GetID();
43 
44     // Find the return address and set a breakpoint there:
45     // FIXME - can we do this more securely if we know first_insn?
46 
47     StackFrameSP return_frame_sp(m_thread.GetStackFrameAtIndex(frame_idx + 1));
48     if (return_frame_sp) {
49       // TODO: add inline functionality
50       m_return_addr = return_frame_sp->GetStackID().GetPC();
51       Breakpoint *return_bp =
52           target_sp->CreateBreakpoint(m_return_addr, true, false).get();
53 
54       if (return_bp != nullptr) {
55         if (return_bp->IsHardware() && !return_bp->HasResolvedLocations())
56           m_could_not_resolve_hw_bp = true;
57         return_bp->SetThreadID(thread_id);
58         m_return_bp_id = return_bp->GetID();
59         return_bp->SetBreakpointKind("until-return-backstop");
60       }
61     }
62 
63     m_stack_id = frame_sp->GetStackID();
64 
65     // Now set breakpoints on all our return addresses:
66     for (size_t i = 0; i < num_addresses; i++) {
67       Breakpoint *until_bp =
68           target_sp->CreateBreakpoint(address_list[i], true, false).get();
69       if (until_bp != nullptr) {
70         until_bp->SetThreadID(thread_id);
71         m_until_points[address_list[i]] = until_bp->GetID();
72         until_bp->SetBreakpointKind("until-target");
73       } else {
74         m_until_points[address_list[i]] = LLDB_INVALID_BREAK_ID;
75       }
76     }
77   }
78 }
79 
80 ThreadPlanStepUntil::~ThreadPlanStepUntil() { Clear(); }
81 
82 void ThreadPlanStepUntil::Clear() {
83   TargetSP target_sp(m_thread.CalculateTarget());
84   if (target_sp) {
85     if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {
86       target_sp->RemoveBreakpointByID(m_return_bp_id);
87       m_return_bp_id = LLDB_INVALID_BREAK_ID;
88     }
89 
90     until_collection::iterator pos, end = m_until_points.end();
91     for (pos = m_until_points.begin(); pos != end; pos++) {
92       target_sp->RemoveBreakpointByID((*pos).second);
93     }
94   }
95   m_until_points.clear();
96   m_could_not_resolve_hw_bp = false;
97 }
98 
99 void ThreadPlanStepUntil::GetDescription(Stream *s,
100                                          lldb::DescriptionLevel level) {
101   if (level == lldb::eDescriptionLevelBrief) {
102     s->Printf("step until");
103     if (m_stepped_out)
104       s->Printf(" - stepped out");
105   } else {
106     if (m_until_points.size() == 1)
107       s->Printf("Stepping from address 0x%" PRIx64 " until we reach 0x%" PRIx64
108                 " using breakpoint %d",
109                 (uint64_t)m_step_from_insn,
110                 (uint64_t)(*m_until_points.begin()).first,
111                 (*m_until_points.begin()).second);
112     else {
113       until_collection::iterator pos, end = m_until_points.end();
114       s->Printf("Stepping from address 0x%" PRIx64 " until we reach one of:",
115                 (uint64_t)m_step_from_insn);
116       for (pos = m_until_points.begin(); pos != end; pos++) {
117         s->Printf("\n\t0x%" PRIx64 " (bp: %d)", (uint64_t)(*pos).first,
118                   (*pos).second);
119       }
120     }
121     s->Printf(" stepped out address is 0x%" PRIx64 ".",
122               (uint64_t)m_return_addr);
123   }
124 }
125 
126 bool ThreadPlanStepUntil::ValidatePlan(Stream *error) {
127   if (m_could_not_resolve_hw_bp) {
128     if (error)
129       error->PutCString(
130           "Could not create hardware breakpoint for thread plan.");
131     return false;
132   } else if (m_return_bp_id == LLDB_INVALID_BREAK_ID) {
133     if (error)
134       error->PutCString("Could not create return breakpoint.");
135     return false;
136   } else {
137     until_collection::iterator pos, end = m_until_points.end();
138     for (pos = m_until_points.begin(); pos != end; pos++) {
139       if (!LLDB_BREAK_ID_IS_VALID((*pos).second))
140         return false;
141     }
142     return true;
143   }
144 }
145 
146 void ThreadPlanStepUntil::AnalyzeStop() {
147   if (m_ran_analyze)
148     return;
149 
150   StopInfoSP stop_info_sp = GetPrivateStopInfo();
151   m_should_stop = true;
152   m_explains_stop = false;
153 
154   if (stop_info_sp) {
155     StopReason reason = stop_info_sp->GetStopReason();
156 
157     if (reason == eStopReasonBreakpoint) {
158       // If this is OUR breakpoint, we're fine, otherwise we don't know why
159       // this happened...
160       BreakpointSiteSP this_site =
161           m_thread.GetProcess()->GetBreakpointSiteList().FindByID(
162               stop_info_sp->GetValue());
163       if (!this_site) {
164         m_explains_stop = false;
165         return;
166       }
167 
168       if (this_site->IsBreakpointAtThisSite(m_return_bp_id)) {
169         // If we are at our "step out" breakpoint, and the stack depth has
170         // shrunk, then this is indeed our stop. If the stack depth has grown,
171         // then we've hit our step out breakpoint recursively. If we are the
172         // only breakpoint at that location, then we do explain the stop, and
173         // we'll just continue. If there was another breakpoint here, then we
174         // don't explain the stop, but we won't mark ourselves Completed,
175         // because maybe that breakpoint will continue, and then we'll finish
176         // the "until".
177         bool done;
178         StackID cur_frame_zero_id;
179 
180         done = (m_stack_id < cur_frame_zero_id);
181 
182         if (done) {
183           m_stepped_out = true;
184           SetPlanComplete();
185         } else
186           m_should_stop = false;
187 
188         if (this_site->GetNumberOfOwners() == 1)
189           m_explains_stop = true;
190         else
191           m_explains_stop = false;
192         return;
193       } else {
194         // Check if we've hit one of our "until" breakpoints.
195         until_collection::iterator pos, end = m_until_points.end();
196         for (pos = m_until_points.begin(); pos != end; pos++) {
197           if (this_site->IsBreakpointAtThisSite((*pos).second)) {
198             // If we're at the right stack depth, then we're done.
199 
200             bool done;
201             StackID frame_zero_id =
202                 m_thread.GetStackFrameAtIndex(0)->GetStackID();
203 
204             if (frame_zero_id == m_stack_id)
205               done = true;
206             else if (frame_zero_id < m_stack_id)
207               done = false;
208             else {
209               StackFrameSP older_frame_sp = m_thread.GetStackFrameAtIndex(1);
210 
211               // But if we can't even unwind one frame we should just get out
212               // of here & stop...
213               if (older_frame_sp) {
214                 const SymbolContext &older_context =
215                     older_frame_sp->GetSymbolContext(eSymbolContextEverything);
216                 SymbolContext stack_context;
217                 m_stack_id.GetSymbolContextScope()->CalculateSymbolContext(
218                     &stack_context);
219 
220                 done = (older_context == stack_context);
221               } else
222                 done = false;
223             }
224 
225             if (done)
226               SetPlanComplete();
227             else
228               m_should_stop = false;
229 
230             // Otherwise we've hit this breakpoint recursively.  If we're the
231             // only breakpoint here, then we do explain the stop, and we'll
232             // continue. If not then we should let higher plans handle this
233             // stop.
234             if (this_site->GetNumberOfOwners() == 1)
235               m_explains_stop = true;
236             else {
237               m_should_stop = true;
238               m_explains_stop = false;
239             }
240             return;
241           }
242         }
243       }
244       // If we get here we haven't hit any of our breakpoints, so let the
245       // higher plans take care of the stop.
246       m_explains_stop = false;
247       return;
248     } else if (IsUsuallyUnexplainedStopReason(reason)) {
249       m_explains_stop = false;
250     } else {
251       m_explains_stop = true;
252     }
253   }
254 }
255 
256 bool ThreadPlanStepUntil::DoPlanExplainsStop(Event *event_ptr) {
257   // We don't explain signals or breakpoints (breakpoints that handle stepping
258   // in or out will be handled by a child plan.
259   AnalyzeStop();
260   return m_explains_stop;
261 }
262 
263 bool ThreadPlanStepUntil::ShouldStop(Event *event_ptr) {
264   // If we've told our self in ExplainsStop that we plan to continue, then do
265   // so here.  Otherwise, as long as this thread has stopped for a reason, we
266   // will stop.
267 
268   StopInfoSP stop_info_sp = GetPrivateStopInfo();
269   if (!stop_info_sp || stop_info_sp->GetStopReason() == eStopReasonNone)
270     return false;
271 
272   AnalyzeStop();
273   return m_should_stop;
274 }
275 
276 bool ThreadPlanStepUntil::StopOthers() { return m_stop_others; }
277 
278 StateType ThreadPlanStepUntil::GetPlanRunState() { return eStateRunning; }
279 
280 bool ThreadPlanStepUntil::DoWillResume(StateType resume_state,
281                                        bool current_plan) {
282   if (current_plan) {
283     TargetSP target_sp(m_thread.CalculateTarget());
284     if (target_sp) {
285       Breakpoint *return_bp =
286           target_sp->GetBreakpointByID(m_return_bp_id).get();
287       if (return_bp != nullptr)
288         return_bp->SetEnabled(true);
289 
290       until_collection::iterator pos, end = m_until_points.end();
291       for (pos = m_until_points.begin(); pos != end; pos++) {
292         Breakpoint *until_bp =
293             target_sp->GetBreakpointByID((*pos).second).get();
294         if (until_bp != nullptr)
295           until_bp->SetEnabled(true);
296       }
297     }
298   }
299 
300   m_should_stop = true;
301   m_ran_analyze = false;
302   m_explains_stop = false;
303   return true;
304 }
305 
306 bool ThreadPlanStepUntil::WillStop() {
307   TargetSP target_sp(m_thread.CalculateTarget());
308   if (target_sp) {
309     Breakpoint *return_bp = target_sp->GetBreakpointByID(m_return_bp_id).get();
310     if (return_bp != nullptr)
311       return_bp->SetEnabled(false);
312 
313     until_collection::iterator pos, end = m_until_points.end();
314     for (pos = m_until_points.begin(); pos != end; pos++) {
315       Breakpoint *until_bp = target_sp->GetBreakpointByID((*pos).second).get();
316       if (until_bp != nullptr)
317         until_bp->SetEnabled(false);
318     }
319   }
320   return true;
321 }
322 
323 bool ThreadPlanStepUntil::MischiefManaged() {
324   // I'm letting "PlanExplainsStop" do all the work, and just reporting that
325   // here.
326   bool done = false;
327   if (IsPlanComplete()) {
328     Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
329     LLDB_LOGF(log, "Completed step until plan.");
330 
331     Clear();
332     done = true;
333   }
334   if (done)
335     ThreadPlan::MischiefManaged();
336 
337   return done;
338 }
339