xref: /src/contrib/llvm-project/lldb/source/Target/ThreadPlanStepUntil.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
1cfca06d7SDimitry Andric //===-- ThreadPlanStepUntil.cpp -------------------------------------------===//
2f034231aSEd Maste //
35f29bb8aSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45f29bb8aSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55f29bb8aSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f034231aSEd Maste //
7f034231aSEd Maste //===----------------------------------------------------------------------===//
8f034231aSEd Maste 
9e81d9d49SDimitry Andric #include "lldb/Target/ThreadPlanStepUntil.h"
1074a628f7SDimitry Andric 
11f034231aSEd Maste #include "lldb/Breakpoint/Breakpoint.h"
1274a628f7SDimitry Andric #include "lldb/Symbol/SymbolContextScope.h"
13f034231aSEd Maste #include "lldb/Target/Process.h"
14f034231aSEd Maste #include "lldb/Target/RegisterContext.h"
15f034231aSEd Maste #include "lldb/Target/StopInfo.h"
16f034231aSEd Maste #include "lldb/Target/Target.h"
17145449b1SDimitry Andric #include "lldb/Utility/LLDBLog.h"
1874a628f7SDimitry Andric #include "lldb/Utility/Log.h"
19f034231aSEd Maste 
20f034231aSEd Maste using namespace lldb;
21f034231aSEd Maste using namespace lldb_private;
22f034231aSEd Maste 
2314f1b3e8SDimitry Andric // ThreadPlanStepUntil: Run until we reach a given line number or step out of
2414f1b3e8SDimitry Andric // the current frame
25f034231aSEd Maste 
ThreadPlanStepUntil(Thread & thread,lldb::addr_t * address_list,size_t num_addresses,bool stop_others,uint32_t frame_idx)2614f1b3e8SDimitry Andric ThreadPlanStepUntil::ThreadPlanStepUntil(Thread &thread,
27f034231aSEd Maste                                          lldb::addr_t *address_list,
2814f1b3e8SDimitry Andric                                          size_t num_addresses, bool stop_others,
2914f1b3e8SDimitry Andric                                          uint32_t frame_idx)
3014f1b3e8SDimitry Andric     : ThreadPlan(ThreadPlan::eKindStepUntil, "Step until", thread,
3114f1b3e8SDimitry Andric                  eVoteNoOpinion, eVoteNoOpinion),
32f034231aSEd Maste       m_step_from_insn(LLDB_INVALID_ADDRESS),
33f034231aSEd Maste       m_return_bp_id(LLDB_INVALID_BREAK_ID),
3414f1b3e8SDimitry Andric       m_return_addr(LLDB_INVALID_ADDRESS), m_stepped_out(false),
3514f1b3e8SDimitry Andric       m_should_stop(false), m_ran_analyze(false), m_explains_stop(false),
3614f1b3e8SDimitry Andric       m_until_points(), m_stop_others(stop_others) {
37f034231aSEd Maste   // Stash away our "until" addresses:
38cfca06d7SDimitry Andric   TargetSP target_sp(thread.CalculateTarget());
39f034231aSEd Maste 
40cfca06d7SDimitry Andric   StackFrameSP frame_sp(thread.GetStackFrameAtIndex(frame_idx));
4114f1b3e8SDimitry Andric   if (frame_sp) {
42f034231aSEd Maste     m_step_from_insn = frame_sp->GetStackID().GetPC();
43f034231aSEd Maste 
44f034231aSEd Maste     // Find the return address and set a breakpoint there:
45f034231aSEd Maste     // FIXME - can we do this more securely if we know first_insn?
46f034231aSEd Maste 
47cfca06d7SDimitry Andric     StackFrameSP return_frame_sp(thread.GetStackFrameAtIndex(frame_idx + 1));
4814f1b3e8SDimitry Andric     if (return_frame_sp) {
49f034231aSEd Maste       // TODO: add inline functionality
50f034231aSEd Maste       m_return_addr = return_frame_sp->GetStackID().GetPC();
5114f1b3e8SDimitry Andric       Breakpoint *return_bp =
5214f1b3e8SDimitry Andric           target_sp->CreateBreakpoint(m_return_addr, true, false).get();
5394994d37SDimitry Andric 
5414f1b3e8SDimitry Andric       if (return_bp != nullptr) {
5594994d37SDimitry Andric         if (return_bp->IsHardware() && !return_bp->HasResolvedLocations())
5694994d37SDimitry Andric           m_could_not_resolve_hw_bp = true;
57cfca06d7SDimitry Andric         return_bp->SetThreadID(m_tid);
58f034231aSEd Maste         m_return_bp_id = return_bp->GetID();
59f034231aSEd Maste         return_bp->SetBreakpointKind("until-return-backstop");
60f034231aSEd Maste       }
61f034231aSEd Maste     }
62f034231aSEd Maste 
630cac4ca3SEd Maste     m_stack_id = frame_sp->GetStackID();
64f034231aSEd Maste 
65f034231aSEd Maste     // Now set breakpoints on all our return addresses:
6614f1b3e8SDimitry Andric     for (size_t i = 0; i < num_addresses; i++) {
6714f1b3e8SDimitry Andric       Breakpoint *until_bp =
6814f1b3e8SDimitry Andric           target_sp->CreateBreakpoint(address_list[i], true, false).get();
6914f1b3e8SDimitry Andric       if (until_bp != nullptr) {
70cfca06d7SDimitry Andric         until_bp->SetThreadID(m_tid);
71f034231aSEd Maste         m_until_points[address_list[i]] = until_bp->GetID();
72f034231aSEd Maste         until_bp->SetBreakpointKind("until-target");
7314f1b3e8SDimitry Andric       } else {
74f034231aSEd Maste         m_until_points[address_list[i]] = LLDB_INVALID_BREAK_ID;
75f034231aSEd Maste       }
76f034231aSEd Maste     }
77f034231aSEd Maste   }
78f034231aSEd Maste }
79f034231aSEd Maste 
~ThreadPlanStepUntil()8014f1b3e8SDimitry Andric ThreadPlanStepUntil::~ThreadPlanStepUntil() { Clear(); }
81f034231aSEd Maste 
Clear()8214f1b3e8SDimitry Andric void ThreadPlanStepUntil::Clear() {
83cfca06d7SDimitry Andric   Target &target = GetTarget();
8414f1b3e8SDimitry Andric   if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {
85cfca06d7SDimitry Andric     target.RemoveBreakpointByID(m_return_bp_id);
86f034231aSEd Maste     m_return_bp_id = LLDB_INVALID_BREAK_ID;
87f034231aSEd Maste   }
88f034231aSEd Maste 
89f034231aSEd Maste   until_collection::iterator pos, end = m_until_points.end();
9014f1b3e8SDimitry Andric   for (pos = m_until_points.begin(); pos != end; pos++) {
91cfca06d7SDimitry Andric     target.RemoveBreakpointByID((*pos).second);
92f034231aSEd Maste   }
93f034231aSEd Maste   m_until_points.clear();
9494994d37SDimitry Andric   m_could_not_resolve_hw_bp = false;
95f034231aSEd Maste }
96f034231aSEd Maste 
GetDescription(Stream * s,lldb::DescriptionLevel level)9714f1b3e8SDimitry Andric void ThreadPlanStepUntil::GetDescription(Stream *s,
9814f1b3e8SDimitry Andric                                          lldb::DescriptionLevel level) {
9914f1b3e8SDimitry Andric   if (level == lldb::eDescriptionLevelBrief) {
100f034231aSEd Maste     s->Printf("step until");
101f034231aSEd Maste     if (m_stepped_out)
102f034231aSEd Maste       s->Printf(" - stepped out");
10314f1b3e8SDimitry Andric   } else {
104f034231aSEd Maste     if (m_until_points.size() == 1)
10514f1b3e8SDimitry Andric       s->Printf("Stepping from address 0x%" PRIx64 " until we reach 0x%" PRIx64
10614f1b3e8SDimitry Andric                 " using breakpoint %d",
107f034231aSEd Maste                 (uint64_t)m_step_from_insn,
108f034231aSEd Maste                 (uint64_t)(*m_until_points.begin()).first,
109f034231aSEd Maste                 (*m_until_points.begin()).second);
11014f1b3e8SDimitry Andric     else {
111f034231aSEd Maste       until_collection::iterator pos, end = m_until_points.end();
112f034231aSEd Maste       s->Printf("Stepping from address 0x%" PRIx64 " until we reach one of:",
113f034231aSEd Maste                 (uint64_t)m_step_from_insn);
11414f1b3e8SDimitry Andric       for (pos = m_until_points.begin(); pos != end; pos++) {
11514f1b3e8SDimitry Andric         s->Printf("\n\t0x%" PRIx64 " (bp: %d)", (uint64_t)(*pos).first,
11614f1b3e8SDimitry Andric                   (*pos).second);
117f034231aSEd Maste       }
118f034231aSEd Maste     }
11914f1b3e8SDimitry Andric     s->Printf(" stepped out address is 0x%" PRIx64 ".",
12014f1b3e8SDimitry Andric               (uint64_t)m_return_addr);
121f034231aSEd Maste   }
122f034231aSEd Maste }
123f034231aSEd Maste 
ValidatePlan(Stream * error)12414f1b3e8SDimitry Andric bool ThreadPlanStepUntil::ValidatePlan(Stream *error) {
12594994d37SDimitry Andric   if (m_could_not_resolve_hw_bp) {
12694994d37SDimitry Andric     if (error)
12794994d37SDimitry Andric       error->PutCString(
12894994d37SDimitry Andric           "Could not create hardware breakpoint for thread plan.");
129f034231aSEd Maste     return false;
13094994d37SDimitry Andric   } else if (m_return_bp_id == LLDB_INVALID_BREAK_ID) {
13194994d37SDimitry Andric     if (error)
13294994d37SDimitry Andric       error->PutCString("Could not create return breakpoint.");
13394994d37SDimitry Andric     return false;
13494994d37SDimitry Andric   } else {
135f034231aSEd Maste     until_collection::iterator pos, end = m_until_points.end();
13614f1b3e8SDimitry Andric     for (pos = m_until_points.begin(); pos != end; pos++) {
137f034231aSEd Maste       if (!LLDB_BREAK_ID_IS_VALID((*pos).second))
138f034231aSEd Maste         return false;
139f034231aSEd Maste     }
140f034231aSEd Maste     return true;
141f034231aSEd Maste   }
142f034231aSEd Maste }
143f034231aSEd Maste 
AnalyzeStop()14414f1b3e8SDimitry Andric void ThreadPlanStepUntil::AnalyzeStop() {
145f034231aSEd Maste   if (m_ran_analyze)
146f034231aSEd Maste     return;
147f034231aSEd Maste 
148f034231aSEd Maste   StopInfoSP stop_info_sp = GetPrivateStopInfo();
149f034231aSEd Maste   m_should_stop = true;
150f034231aSEd Maste   m_explains_stop = false;
151f034231aSEd Maste 
15214f1b3e8SDimitry Andric   if (stop_info_sp) {
153f034231aSEd Maste     StopReason reason = stop_info_sp->GetStopReason();
154f034231aSEd Maste 
15514f1b3e8SDimitry Andric     if (reason == eStopReasonBreakpoint) {
156f73363f1SDimitry Andric       // If this is OUR breakpoint, we're fine, otherwise we don't know why
157f73363f1SDimitry Andric       // this happened...
15814f1b3e8SDimitry Andric       BreakpointSiteSP this_site =
159cfca06d7SDimitry Andric           m_process.GetBreakpointSiteList().FindByID(stop_info_sp->GetValue());
16014f1b3e8SDimitry Andric       if (!this_site) {
161f034231aSEd Maste         m_explains_stop = false;
162f034231aSEd Maste         return;
163f034231aSEd Maste       }
164f034231aSEd Maste 
16514f1b3e8SDimitry Andric       if (this_site->IsBreakpointAtThisSite(m_return_bp_id)) {
16614f1b3e8SDimitry Andric         // If we are at our "step out" breakpoint, and the stack depth has
167f73363f1SDimitry Andric         // shrunk, then this is indeed our stop. If the stack depth has grown,
168f73363f1SDimitry Andric         // then we've hit our step out breakpoint recursively. If we are the
169f73363f1SDimitry Andric         // only breakpoint at that location, then we do explain the stop, and
170f73363f1SDimitry Andric         // we'll just continue. If there was another breakpoint here, then we
171f73363f1SDimitry Andric         // don't explain the stop, but we won't mark ourselves Completed,
172f73363f1SDimitry Andric         // because maybe that breakpoint will continue, and then we'll finish
173f73363f1SDimitry Andric         // the "until".
174f034231aSEd Maste         bool done;
175f034231aSEd Maste         StackID cur_frame_zero_id;
176f034231aSEd Maste 
177e81d9d49SDimitry Andric         done = (m_stack_id < cur_frame_zero_id);
178f034231aSEd Maste 
17914f1b3e8SDimitry Andric         if (done) {
180f034231aSEd Maste           m_stepped_out = true;
181f034231aSEd Maste           SetPlanComplete();
18214f1b3e8SDimitry Andric         } else
183f034231aSEd Maste           m_should_stop = false;
184f034231aSEd Maste 
185b1c73532SDimitry Andric         if (this_site->GetNumberOfConstituents() == 1)
186f034231aSEd Maste           m_explains_stop = true;
187f034231aSEd Maste         else
188f034231aSEd Maste           m_explains_stop = false;
189f034231aSEd Maste         return;
19014f1b3e8SDimitry Andric       } else {
191f034231aSEd Maste         // Check if we've hit one of our "until" breakpoints.
192f034231aSEd Maste         until_collection::iterator pos, end = m_until_points.end();
19314f1b3e8SDimitry Andric         for (pos = m_until_points.begin(); pos != end; pos++) {
19414f1b3e8SDimitry Andric           if (this_site->IsBreakpointAtThisSite((*pos).second)) {
195f034231aSEd Maste             // If we're at the right stack depth, then we're done.
196cfca06d7SDimitry Andric             Thread &thread = GetThread();
197f034231aSEd Maste             bool done;
19814f1b3e8SDimitry Andric             StackID frame_zero_id =
199cfca06d7SDimitry Andric                 thread.GetStackFrameAtIndex(0)->GetStackID();
200f034231aSEd Maste 
201f034231aSEd Maste             if (frame_zero_id == m_stack_id)
202f034231aSEd Maste               done = true;
203f034231aSEd Maste             else if (frame_zero_id < m_stack_id)
204f034231aSEd Maste               done = false;
20514f1b3e8SDimitry Andric             else {
206cfca06d7SDimitry Andric               StackFrameSP older_frame_sp = thread.GetStackFrameAtIndex(1);
207f034231aSEd Maste 
208f73363f1SDimitry Andric               // But if we can't even unwind one frame we should just get out
209f73363f1SDimitry Andric               // of here & stop...
21014f1b3e8SDimitry Andric               if (older_frame_sp) {
21114f1b3e8SDimitry Andric                 const SymbolContext &older_context =
21214f1b3e8SDimitry Andric                     older_frame_sp->GetSymbolContext(eSymbolContextEverything);
213f034231aSEd Maste                 SymbolContext stack_context;
21414f1b3e8SDimitry Andric                 m_stack_id.GetSymbolContextScope()->CalculateSymbolContext(
21514f1b3e8SDimitry Andric                     &stack_context);
216f034231aSEd Maste 
217e81d9d49SDimitry Andric                 done = (older_context == stack_context);
21814f1b3e8SDimitry Andric               } else
219f034231aSEd Maste                 done = false;
220f034231aSEd Maste             }
221f034231aSEd Maste 
222f034231aSEd Maste             if (done)
223f034231aSEd Maste               SetPlanComplete();
224f034231aSEd Maste             else
225f034231aSEd Maste               m_should_stop = false;
226f034231aSEd Maste 
227f034231aSEd Maste             // Otherwise we've hit this breakpoint recursively.  If we're the
22814f1b3e8SDimitry Andric             // only breakpoint here, then we do explain the stop, and we'll
229f73363f1SDimitry Andric             // continue. If not then we should let higher plans handle this
230f73363f1SDimitry Andric             // stop.
231b1c73532SDimitry Andric             if (this_site->GetNumberOfConstituents() == 1)
232f034231aSEd Maste               m_explains_stop = true;
23314f1b3e8SDimitry Andric             else {
234f034231aSEd Maste               m_should_stop = true;
235f034231aSEd Maste               m_explains_stop = false;
236f034231aSEd Maste             }
237f034231aSEd Maste             return;
238f034231aSEd Maste           }
239f034231aSEd Maste         }
240f034231aSEd Maste       }
241f73363f1SDimitry Andric       // If we get here we haven't hit any of our breakpoints, so let the
242f73363f1SDimitry Andric       // higher plans take care of the stop.
243f034231aSEd Maste       m_explains_stop = false;
244f034231aSEd Maste       return;
24514f1b3e8SDimitry Andric     } else if (IsUsuallyUnexplainedStopReason(reason)) {
246f034231aSEd Maste       m_explains_stop = false;
24714f1b3e8SDimitry Andric     } else {
248f034231aSEd Maste       m_explains_stop = true;
249f034231aSEd Maste     }
250f034231aSEd Maste   }
251f034231aSEd Maste }
252f034231aSEd Maste 
DoPlanExplainsStop(Event * event_ptr)25314f1b3e8SDimitry Andric bool ThreadPlanStepUntil::DoPlanExplainsStop(Event *event_ptr) {
25414f1b3e8SDimitry Andric   // We don't explain signals or breakpoints (breakpoints that handle stepping
255f73363f1SDimitry Andric   // in or out will be handled by a child plan.
256f034231aSEd Maste   AnalyzeStop();
257f034231aSEd Maste   return m_explains_stop;
258f034231aSEd Maste }
259f034231aSEd Maste 
ShouldStop(Event * event_ptr)26014f1b3e8SDimitry Andric bool ThreadPlanStepUntil::ShouldStop(Event *event_ptr) {
261f73363f1SDimitry Andric   // If we've told our self in ExplainsStop that we plan to continue, then do
262f73363f1SDimitry Andric   // so here.  Otherwise, as long as this thread has stopped for a reason, we
263f73363f1SDimitry Andric   // will stop.
264f034231aSEd Maste 
265f034231aSEd Maste   StopInfoSP stop_info_sp = GetPrivateStopInfo();
266f034231aSEd Maste   if (!stop_info_sp || stop_info_sp->GetStopReason() == eStopReasonNone)
267f034231aSEd Maste     return false;
268f034231aSEd Maste 
269f034231aSEd Maste   AnalyzeStop();
270f034231aSEd Maste   return m_should_stop;
271f034231aSEd Maste }
272f034231aSEd Maste 
StopOthers()27314f1b3e8SDimitry Andric bool ThreadPlanStepUntil::StopOthers() { return m_stop_others; }
274f034231aSEd Maste 
GetPlanRunState()27514f1b3e8SDimitry Andric StateType ThreadPlanStepUntil::GetPlanRunState() { return eStateRunning; }
276f034231aSEd Maste 
DoWillResume(StateType resume_state,bool current_plan)27714f1b3e8SDimitry Andric bool ThreadPlanStepUntil::DoWillResume(StateType resume_state,
27814f1b3e8SDimitry Andric                                        bool current_plan) {
27914f1b3e8SDimitry Andric   if (current_plan) {
280cfca06d7SDimitry Andric     Target &target = GetTarget();
281cfca06d7SDimitry Andric     Breakpoint *return_bp = target.GetBreakpointByID(m_return_bp_id).get();
282e81d9d49SDimitry Andric     if (return_bp != nullptr)
283f034231aSEd Maste       return_bp->SetEnabled(true);
284f034231aSEd Maste 
285f034231aSEd Maste     until_collection::iterator pos, end = m_until_points.end();
28614f1b3e8SDimitry Andric     for (pos = m_until_points.begin(); pos != end; pos++) {
287cfca06d7SDimitry Andric       Breakpoint *until_bp = target.GetBreakpointByID((*pos).second).get();
288e81d9d49SDimitry Andric       if (until_bp != nullptr)
289f034231aSEd Maste         until_bp->SetEnabled(true);
290f034231aSEd Maste     }
291f034231aSEd Maste   }
292f034231aSEd Maste 
293f034231aSEd Maste   m_should_stop = true;
294f034231aSEd Maste   m_ran_analyze = false;
295f034231aSEd Maste   m_explains_stop = false;
296f034231aSEd Maste   return true;
297f034231aSEd Maste }
298f034231aSEd Maste 
WillStop()29914f1b3e8SDimitry Andric bool ThreadPlanStepUntil::WillStop() {
300cfca06d7SDimitry Andric   Target &target = GetTarget();
301cfca06d7SDimitry Andric   Breakpoint *return_bp = target.GetBreakpointByID(m_return_bp_id).get();
302e81d9d49SDimitry Andric   if (return_bp != nullptr)
303f034231aSEd Maste     return_bp->SetEnabled(false);
304f034231aSEd Maste 
305f034231aSEd Maste   until_collection::iterator pos, end = m_until_points.end();
30614f1b3e8SDimitry Andric   for (pos = m_until_points.begin(); pos != end; pos++) {
307cfca06d7SDimitry Andric     Breakpoint *until_bp = target.GetBreakpointByID((*pos).second).get();
308e81d9d49SDimitry Andric     if (until_bp != nullptr)
309f034231aSEd Maste       until_bp->SetEnabled(false);
310f034231aSEd Maste   }
311f034231aSEd Maste   return true;
312f034231aSEd Maste }
313f034231aSEd Maste 
MischiefManaged()31414f1b3e8SDimitry Andric bool ThreadPlanStepUntil::MischiefManaged() {
31514f1b3e8SDimitry Andric   // I'm letting "PlanExplainsStop" do all the work, and just reporting that
31614f1b3e8SDimitry Andric   // here.
317f034231aSEd Maste   bool done = false;
31814f1b3e8SDimitry Andric   if (IsPlanComplete()) {
319145449b1SDimitry Andric     Log *log = GetLog(LLDBLog::Step);
320ead24645SDimitry Andric     LLDB_LOGF(log, "Completed step until plan.");
321f034231aSEd Maste 
322f034231aSEd Maste     Clear();
323f034231aSEd Maste     done = true;
324f034231aSEd Maste   }
325f034231aSEd Maste   if (done)
326f034231aSEd Maste     ThreadPlan::MischiefManaged();
327f034231aSEd Maste 
328f034231aSEd Maste   return done;
329f034231aSEd Maste }
330