15ffd83dbSDimitry Andric //===-- ThreadPlanStepOut.cpp ---------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "lldb/Target/ThreadPlanStepOut.h"
100b57cec5SDimitry Andric #include "lldb/Breakpoint/Breakpoint.h"
110b57cec5SDimitry Andric #include "lldb/Core/Value.h"
120b57cec5SDimitry Andric #include "lldb/Core/ValueObjectConstResult.h"
130b57cec5SDimitry Andric #include "lldb/Symbol/Block.h"
140b57cec5SDimitry Andric #include "lldb/Symbol/Function.h"
150b57cec5SDimitry Andric #include "lldb/Symbol/Symbol.h"
160b57cec5SDimitry Andric #include "lldb/Symbol/Type.h"
170b57cec5SDimitry Andric #include "lldb/Target/ABI.h"
180b57cec5SDimitry Andric #include "lldb/Target/Process.h"
190b57cec5SDimitry Andric #include "lldb/Target/RegisterContext.h"
200b57cec5SDimitry Andric #include "lldb/Target/StopInfo.h"
210b57cec5SDimitry Andric #include "lldb/Target/Target.h"
220b57cec5SDimitry Andric #include "lldb/Target/ThreadPlanStepOverRange.h"
230b57cec5SDimitry Andric #include "lldb/Target/ThreadPlanStepThrough.h"
2481ad6265SDimitry Andric #include "lldb/Utility/LLDBLog.h"
250b57cec5SDimitry Andric #include "lldb/Utility/Log.h"
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric #include <memory>
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric using namespace lldb;
300b57cec5SDimitry Andric using namespace lldb_private;
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric uint32_t ThreadPlanStepOut::s_default_flag_values = 0;
330b57cec5SDimitry Andric 
340b57cec5SDimitry Andric // ThreadPlanStepOut: Step out of the current frame
ThreadPlanStepOut(Thread & thread,SymbolContext * context,bool first_insn,bool stop_others,Vote report_stop_vote,Vote report_run_vote,uint32_t frame_idx,LazyBool step_out_avoids_code_without_debug_info,bool continue_to_next_branch,bool gather_return_value)350b57cec5SDimitry Andric ThreadPlanStepOut::ThreadPlanStepOut(
360b57cec5SDimitry Andric     Thread &thread, SymbolContext *context, bool first_insn, bool stop_others,
37fe6060f1SDimitry Andric     Vote report_stop_vote, Vote report_run_vote, uint32_t frame_idx,
380b57cec5SDimitry Andric     LazyBool step_out_avoids_code_without_debug_info,
390b57cec5SDimitry Andric     bool continue_to_next_branch, bool gather_return_value)
40fe6060f1SDimitry Andric     : ThreadPlan(ThreadPlan::eKindStepOut, "Step out", thread, report_stop_vote,
41fe6060f1SDimitry Andric                  report_run_vote),
420b57cec5SDimitry Andric       ThreadPlanShouldStopHere(this), m_step_from_insn(LLDB_INVALID_ADDRESS),
430b57cec5SDimitry Andric       m_return_bp_id(LLDB_INVALID_BREAK_ID),
440b57cec5SDimitry Andric       m_return_addr(LLDB_INVALID_ADDRESS), m_stop_others(stop_others),
450b57cec5SDimitry Andric       m_immediate_step_from_function(nullptr),
460b57cec5SDimitry Andric       m_calculate_return_value(gather_return_value) {
4781ad6265SDimitry Andric   Log *log = GetLog(LLDBLog::Step);
480b57cec5SDimitry Andric   SetFlagsToDefault();
490b57cec5SDimitry Andric   SetupAvoidNoDebug(step_out_avoids_code_without_debug_info);
500b57cec5SDimitry Andric 
515ffd83dbSDimitry Andric   m_step_from_insn = thread.GetRegisterContext()->GetPC(0);
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric   uint32_t return_frame_index = frame_idx + 1;
545ffd83dbSDimitry Andric   StackFrameSP return_frame_sp(thread.GetStackFrameAtIndex(return_frame_index));
555ffd83dbSDimitry Andric   StackFrameSP immediate_return_from_sp(thread.GetStackFrameAtIndex(frame_idx));
560b57cec5SDimitry Andric 
570b57cec5SDimitry Andric   if (!return_frame_sp || !immediate_return_from_sp)
580b57cec5SDimitry Andric     return; // we can't do anything here.  ValidatePlan() will return false.
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric   // While stepping out, behave as-if artificial frames are not present.
610b57cec5SDimitry Andric   while (return_frame_sp->IsArtificial()) {
620b57cec5SDimitry Andric     m_stepped_past_frames.push_back(return_frame_sp);
630b57cec5SDimitry Andric 
640b57cec5SDimitry Andric     ++return_frame_index;
655ffd83dbSDimitry Andric     return_frame_sp = thread.GetStackFrameAtIndex(return_frame_index);
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric     // We never expect to see an artificial frame without a regular ancestor.
680b57cec5SDimitry Andric     // If this happens, log the issue and defensively refuse to step out.
690b57cec5SDimitry Andric     if (!return_frame_sp) {
700b57cec5SDimitry Andric       LLDB_LOG(log, "Can't step out of frame with artificial ancestors");
710b57cec5SDimitry Andric       return;
720b57cec5SDimitry Andric     }
730b57cec5SDimitry Andric   }
740b57cec5SDimitry Andric 
750b57cec5SDimitry Andric   m_step_out_to_id = return_frame_sp->GetStackID();
760b57cec5SDimitry Andric   m_immediate_step_from_id = immediate_return_from_sp->GetStackID();
770b57cec5SDimitry Andric 
780b57cec5SDimitry Andric   // If the frame directly below the one we are returning to is inlined, we
790b57cec5SDimitry Andric   // have to be a little more careful.  It is non-trivial to determine the real
800b57cec5SDimitry Andric   // "return code address" for an inlined frame, so we have to work our way to
810b57cec5SDimitry Andric   // that frame and then step out.
820b57cec5SDimitry Andric   if (immediate_return_from_sp->IsInlined()) {
830b57cec5SDimitry Andric     if (frame_idx > 0) {
840b57cec5SDimitry Andric       // First queue a plan that gets us to this inlined frame, and when we get
850b57cec5SDimitry Andric       // there we'll queue a second plan that walks us out of this frame.
860b57cec5SDimitry Andric       m_step_out_to_inline_plan_sp = std::make_shared<ThreadPlanStepOut>(
875ffd83dbSDimitry Andric           thread, nullptr, false, stop_others, eVoteNoOpinion, eVoteNoOpinion,
880b57cec5SDimitry Andric           frame_idx - 1, eLazyBoolNo, continue_to_next_branch);
890b57cec5SDimitry Andric       static_cast<ThreadPlanStepOut *>(m_step_out_to_inline_plan_sp.get())
900b57cec5SDimitry Andric           ->SetShouldStopHereCallbacks(nullptr, nullptr);
910b57cec5SDimitry Andric       m_step_out_to_inline_plan_sp->SetPrivate(true);
920b57cec5SDimitry Andric     } else {
930b57cec5SDimitry Andric       // If we're already at the inlined frame we're stepping through, then
940b57cec5SDimitry Andric       // just do that now.
950b57cec5SDimitry Andric       QueueInlinedStepPlan(false);
960b57cec5SDimitry Andric     }
970b57cec5SDimitry Andric   } else {
980b57cec5SDimitry Andric     // Find the return address and set a breakpoint there:
990b57cec5SDimitry Andric     // FIXME - can we do this more securely if we know first_insn?
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric     Address return_address(return_frame_sp->GetFrameCodeAddress());
1020b57cec5SDimitry Andric     if (continue_to_next_branch) {
1030b57cec5SDimitry Andric       SymbolContext return_address_sc;
1040b57cec5SDimitry Andric       AddressRange range;
1050b57cec5SDimitry Andric       Address return_address_decr_pc = return_address;
1060b57cec5SDimitry Andric       if (return_address_decr_pc.GetOffset() > 0)
1070b57cec5SDimitry Andric         return_address_decr_pc.Slide(-1);
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric       return_address_decr_pc.CalculateSymbolContext(
1100b57cec5SDimitry Andric           &return_address_sc, lldb::eSymbolContextLineEntry);
1110b57cec5SDimitry Andric       if (return_address_sc.line_entry.IsValid()) {
1120b57cec5SDimitry Andric         const bool include_inlined_functions = false;
1130b57cec5SDimitry Andric         range = return_address_sc.line_entry.GetSameLineContiguousAddressRange(
1140b57cec5SDimitry Andric             include_inlined_functions);
1150b57cec5SDimitry Andric         if (range.GetByteSize() > 0) {
1165ffd83dbSDimitry Andric           return_address = m_process.AdvanceAddressToNextBranchInstruction(
1170b57cec5SDimitry Andric               return_address, range);
1180b57cec5SDimitry Andric         }
1190b57cec5SDimitry Andric       }
1200b57cec5SDimitry Andric     }
1215ffd83dbSDimitry Andric     m_return_addr = return_address.GetLoadAddress(&m_process.GetTarget());
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric     if (m_return_addr == LLDB_INVALID_ADDRESS)
1240b57cec5SDimitry Andric       return;
1250b57cec5SDimitry Andric 
126480093f4SDimitry Andric     // Perform some additional validation on the return address.
127480093f4SDimitry Andric     uint32_t permissions = 0;
1285ffd83dbSDimitry Andric     if (!m_process.GetLoadAddressPermissions(m_return_addr, permissions)) {
1295ffd83dbSDimitry Andric       LLDB_LOGF(log, "ThreadPlanStepOut(%p): Return address (0x%" PRIx64
1305ffd83dbSDimitry Andric                 ") permissions not found.", static_cast<void *>(this),
131480093f4SDimitry Andric                 m_return_addr);
132480093f4SDimitry Andric     } else if (!(permissions & ePermissionsExecutable)) {
133480093f4SDimitry Andric       m_constructor_errors.Printf("Return address (0x%" PRIx64
134480093f4SDimitry Andric                                   ") did not point to executable memory.",
135480093f4SDimitry Andric                                   m_return_addr);
136480093f4SDimitry Andric       LLDB_LOGF(log, "ThreadPlanStepOut(%p): %s", static_cast<void *>(this),
137480093f4SDimitry Andric                 m_constructor_errors.GetData());
138480093f4SDimitry Andric       return;
139480093f4SDimitry Andric     }
140480093f4SDimitry Andric 
1415ffd83dbSDimitry Andric     Breakpoint *return_bp =
1425ffd83dbSDimitry Andric         GetTarget().CreateBreakpoint(m_return_addr, true, false).get();
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric     if (return_bp != nullptr) {
1450b57cec5SDimitry Andric       if (return_bp->IsHardware() && !return_bp->HasResolvedLocations())
1460b57cec5SDimitry Andric         m_could_not_resolve_hw_bp = true;
1475ffd83dbSDimitry Andric       return_bp->SetThreadID(m_tid);
1480b57cec5SDimitry Andric       m_return_bp_id = return_bp->GetID();
1490b57cec5SDimitry Andric       return_bp->SetBreakpointKind("step-out");
1500b57cec5SDimitry Andric     }
1510b57cec5SDimitry Andric 
1520b57cec5SDimitry Andric     if (immediate_return_from_sp) {
1530b57cec5SDimitry Andric       const SymbolContext &sc =
1540b57cec5SDimitry Andric           immediate_return_from_sp->GetSymbolContext(eSymbolContextFunction);
1550b57cec5SDimitry Andric       if (sc.function) {
1560b57cec5SDimitry Andric         m_immediate_step_from_function = sc.function;
1570b57cec5SDimitry Andric       }
1580b57cec5SDimitry Andric     }
1590b57cec5SDimitry Andric   }
1600b57cec5SDimitry Andric }
1610b57cec5SDimitry Andric 
SetupAvoidNoDebug(LazyBool step_out_avoids_code_without_debug_info)1620b57cec5SDimitry Andric void ThreadPlanStepOut::SetupAvoidNoDebug(
1630b57cec5SDimitry Andric     LazyBool step_out_avoids_code_without_debug_info) {
1640b57cec5SDimitry Andric   bool avoid_nodebug = true;
1650b57cec5SDimitry Andric   switch (step_out_avoids_code_without_debug_info) {
1660b57cec5SDimitry Andric   case eLazyBoolYes:
1670b57cec5SDimitry Andric     avoid_nodebug = true;
1680b57cec5SDimitry Andric     break;
1690b57cec5SDimitry Andric   case eLazyBoolNo:
1700b57cec5SDimitry Andric     avoid_nodebug = false;
1710b57cec5SDimitry Andric     break;
1720b57cec5SDimitry Andric   case eLazyBoolCalculate:
1735ffd83dbSDimitry Andric     avoid_nodebug = GetThread().GetStepOutAvoidsNoDebug();
1740b57cec5SDimitry Andric     break;
1750b57cec5SDimitry Andric   }
1760b57cec5SDimitry Andric   if (avoid_nodebug)
1770b57cec5SDimitry Andric     GetFlags().Set(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);
1780b57cec5SDimitry Andric   else
1790b57cec5SDimitry Andric     GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);
1800b57cec5SDimitry Andric }
1810b57cec5SDimitry Andric 
DidPush()1820b57cec5SDimitry Andric void ThreadPlanStepOut::DidPush() {
1835ffd83dbSDimitry Andric   Thread &thread = GetThread();
1840b57cec5SDimitry Andric   if (m_step_out_to_inline_plan_sp)
1855ffd83dbSDimitry Andric     thread.QueueThreadPlan(m_step_out_to_inline_plan_sp, false);
1860b57cec5SDimitry Andric   else if (m_step_through_inline_plan_sp)
1875ffd83dbSDimitry Andric     thread.QueueThreadPlan(m_step_through_inline_plan_sp, false);
1880b57cec5SDimitry Andric }
1890b57cec5SDimitry Andric 
~ThreadPlanStepOut()1900b57cec5SDimitry Andric ThreadPlanStepOut::~ThreadPlanStepOut() {
1910b57cec5SDimitry Andric   if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
1925ffd83dbSDimitry Andric     GetTarget().RemoveBreakpointByID(m_return_bp_id);
1930b57cec5SDimitry Andric }
1940b57cec5SDimitry Andric 
GetDescription(Stream * s,lldb::DescriptionLevel level)1950b57cec5SDimitry Andric void ThreadPlanStepOut::GetDescription(Stream *s,
1960b57cec5SDimitry Andric                                        lldb::DescriptionLevel level) {
1970b57cec5SDimitry Andric   if (level == lldb::eDescriptionLevelBrief)
1980b57cec5SDimitry Andric     s->Printf("step out");
1990b57cec5SDimitry Andric   else {
2000b57cec5SDimitry Andric     if (m_step_out_to_inline_plan_sp)
2010b57cec5SDimitry Andric       s->Printf("Stepping out to inlined frame so we can walk through it.");
2020b57cec5SDimitry Andric     else if (m_step_through_inline_plan_sp)
2030b57cec5SDimitry Andric       s->Printf("Stepping out by stepping through inlined function.");
2040b57cec5SDimitry Andric     else {
2050b57cec5SDimitry Andric       s->Printf("Stepping out from ");
2060b57cec5SDimitry Andric       Address tmp_address;
2070b57cec5SDimitry Andric       if (tmp_address.SetLoadAddress(m_step_from_insn, &GetTarget())) {
2085ffd83dbSDimitry Andric         tmp_address.Dump(s, &m_process, Address::DumpStyleResolvedDescription,
2090b57cec5SDimitry Andric                          Address::DumpStyleLoadAddress);
2100b57cec5SDimitry Andric       } else {
2110b57cec5SDimitry Andric         s->Printf("address 0x%" PRIx64 "", (uint64_t)m_step_from_insn);
2120b57cec5SDimitry Andric       }
2130b57cec5SDimitry Andric 
2140b57cec5SDimitry Andric       // FIXME: find some useful way to present the m_return_id, since there may
2150b57cec5SDimitry Andric       // be multiple copies of the
2160b57cec5SDimitry Andric       // same function on the stack.
2170b57cec5SDimitry Andric 
2180b57cec5SDimitry Andric       s->Printf(" returning to frame at ");
2190b57cec5SDimitry Andric       if (tmp_address.SetLoadAddress(m_return_addr, &GetTarget())) {
2205ffd83dbSDimitry Andric         tmp_address.Dump(s, &m_process, Address::DumpStyleResolvedDescription,
2210b57cec5SDimitry Andric                          Address::DumpStyleLoadAddress);
2220b57cec5SDimitry Andric       } else {
2230b57cec5SDimitry Andric         s->Printf("address 0x%" PRIx64 "", (uint64_t)m_return_addr);
2240b57cec5SDimitry Andric       }
2250b57cec5SDimitry Andric 
2260b57cec5SDimitry Andric       if (level == eDescriptionLevelVerbose)
2270b57cec5SDimitry Andric         s->Printf(" using breakpoint site %d", m_return_bp_id);
2280b57cec5SDimitry Andric     }
2290b57cec5SDimitry Andric   }
2300b57cec5SDimitry Andric 
2315ffd83dbSDimitry Andric   if (m_stepped_past_frames.empty())
2325ffd83dbSDimitry Andric     return;
2335ffd83dbSDimitry Andric 
2340b57cec5SDimitry Andric   s->Printf("\n");
2350b57cec5SDimitry Andric   for (StackFrameSP frame_sp : m_stepped_past_frames) {
2360b57cec5SDimitry Andric     s->Printf("Stepped out past: ");
2370b57cec5SDimitry Andric     frame_sp->DumpUsingSettingsFormat(s);
2380b57cec5SDimitry Andric   }
2390b57cec5SDimitry Andric }
2400b57cec5SDimitry Andric 
ValidatePlan(Stream * error)2410b57cec5SDimitry Andric bool ThreadPlanStepOut::ValidatePlan(Stream *error) {
2420b57cec5SDimitry Andric   if (m_step_out_to_inline_plan_sp)
2430b57cec5SDimitry Andric     return m_step_out_to_inline_plan_sp->ValidatePlan(error);
2440b57cec5SDimitry Andric 
2450b57cec5SDimitry Andric   if (m_step_through_inline_plan_sp)
2460b57cec5SDimitry Andric     return m_step_through_inline_plan_sp->ValidatePlan(error);
2470b57cec5SDimitry Andric 
2480b57cec5SDimitry Andric   if (m_could_not_resolve_hw_bp) {
2490b57cec5SDimitry Andric     if (error)
2500b57cec5SDimitry Andric       error->PutCString(
2510b57cec5SDimitry Andric           "Could not create hardware breakpoint for thread plan.");
2520b57cec5SDimitry Andric     return false;
2530b57cec5SDimitry Andric   }
2540b57cec5SDimitry Andric 
2550b57cec5SDimitry Andric   if (m_return_bp_id == LLDB_INVALID_BREAK_ID) {
256480093f4SDimitry Andric     if (error) {
2570b57cec5SDimitry Andric       error->PutCString("Could not create return address breakpoint.");
258480093f4SDimitry Andric       if (m_constructor_errors.GetSize() > 0) {
259480093f4SDimitry Andric         error->PutCString(" ");
260480093f4SDimitry Andric         error->PutCString(m_constructor_errors.GetString());
261480093f4SDimitry Andric       }
262480093f4SDimitry Andric     }
2630b57cec5SDimitry Andric     return false;
2640b57cec5SDimitry Andric   }
2650b57cec5SDimitry Andric 
2660b57cec5SDimitry Andric   return true;
2670b57cec5SDimitry Andric }
2680b57cec5SDimitry Andric 
DoPlanExplainsStop(Event * event_ptr)2690b57cec5SDimitry Andric bool ThreadPlanStepOut::DoPlanExplainsStop(Event *event_ptr) {
2700b57cec5SDimitry Andric   // If the step out plan is done, then we just need to step through the
2710b57cec5SDimitry Andric   // inlined frame.
2720b57cec5SDimitry Andric   if (m_step_out_to_inline_plan_sp) {
2730b57cec5SDimitry Andric     return m_step_out_to_inline_plan_sp->MischiefManaged();
2740b57cec5SDimitry Andric   } else if (m_step_through_inline_plan_sp) {
2750b57cec5SDimitry Andric     if (m_step_through_inline_plan_sp->MischiefManaged()) {
2760b57cec5SDimitry Andric       CalculateReturnValue();
2770b57cec5SDimitry Andric       SetPlanComplete();
2780b57cec5SDimitry Andric       return true;
2790b57cec5SDimitry Andric     } else
2800b57cec5SDimitry Andric       return false;
2810b57cec5SDimitry Andric   } else if (m_step_out_further_plan_sp) {
2820b57cec5SDimitry Andric     return m_step_out_further_plan_sp->MischiefManaged();
2830b57cec5SDimitry Andric   }
2840b57cec5SDimitry Andric 
2850b57cec5SDimitry Andric   // We don't explain signals or breakpoints (breakpoints that handle stepping
2860b57cec5SDimitry Andric   // in or out will be handled by a child plan.
2870b57cec5SDimitry Andric 
2880b57cec5SDimitry Andric   StopInfoSP stop_info_sp = GetPrivateStopInfo();
2890b57cec5SDimitry Andric   if (stop_info_sp) {
2900b57cec5SDimitry Andric     StopReason reason = stop_info_sp->GetStopReason();
2910b57cec5SDimitry Andric     if (reason == eStopReasonBreakpoint) {
2920b57cec5SDimitry Andric       // If this is OUR breakpoint, we're fine, otherwise we don't know why
2930b57cec5SDimitry Andric       // this happened...
2940b57cec5SDimitry Andric       BreakpointSiteSP site_sp(
2955ffd83dbSDimitry Andric           m_process.GetBreakpointSiteList().FindByID(stop_info_sp->GetValue()));
2960b57cec5SDimitry Andric       if (site_sp && site_sp->IsBreakpointAtThisSite(m_return_bp_id)) {
2970b57cec5SDimitry Andric         bool done;
2980b57cec5SDimitry Andric 
2995ffd83dbSDimitry Andric         StackID frame_zero_id =
3005ffd83dbSDimitry Andric             GetThread().GetStackFrameAtIndex(0)->GetStackID();
3010b57cec5SDimitry Andric 
3020b57cec5SDimitry Andric         if (m_step_out_to_id == frame_zero_id)
3030b57cec5SDimitry Andric           done = true;
3040b57cec5SDimitry Andric         else if (m_step_out_to_id < frame_zero_id) {
3050b57cec5SDimitry Andric           // Either we stepped past the breakpoint, or the stack ID calculation
3060b57cec5SDimitry Andric           // was incorrect and we should probably stop.
3070b57cec5SDimitry Andric           done = true;
3080b57cec5SDimitry Andric         } else {
3090b57cec5SDimitry Andric           done = (m_immediate_step_from_id < frame_zero_id);
3100b57cec5SDimitry Andric         }
3110b57cec5SDimitry Andric 
3120b57cec5SDimitry Andric         if (done) {
3130b57cec5SDimitry Andric           if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) {
3140b57cec5SDimitry Andric             CalculateReturnValue();
3150b57cec5SDimitry Andric             SetPlanComplete();
3160b57cec5SDimitry Andric           }
3170b57cec5SDimitry Andric         }
3180b57cec5SDimitry Andric 
3190b57cec5SDimitry Andric         // If there was only one owner, then we're done.  But if we also hit
3200b57cec5SDimitry Andric         // some user breakpoint on our way out, we should mark ourselves as
3210b57cec5SDimitry Andric         // done, but also not claim to explain the stop, since it is more
3220b57cec5SDimitry Andric         // important to report the user breakpoint than the step out
3230b57cec5SDimitry Andric         // completion.
3240b57cec5SDimitry Andric 
3255f757f3fSDimitry Andric         if (site_sp->GetNumberOfConstituents() == 1)
3260b57cec5SDimitry Andric           return true;
3270b57cec5SDimitry Andric       }
3280b57cec5SDimitry Andric       return false;
3290b57cec5SDimitry Andric     } else if (IsUsuallyUnexplainedStopReason(reason))
3300b57cec5SDimitry Andric       return false;
3310b57cec5SDimitry Andric     else
3320b57cec5SDimitry Andric       return true;
3330b57cec5SDimitry Andric   }
3340b57cec5SDimitry Andric   return true;
3350b57cec5SDimitry Andric }
3360b57cec5SDimitry Andric 
ShouldStop(Event * event_ptr)3370b57cec5SDimitry Andric bool ThreadPlanStepOut::ShouldStop(Event *event_ptr) {
3380b57cec5SDimitry Andric   if (IsPlanComplete())
3390b57cec5SDimitry Andric     return true;
3400b57cec5SDimitry Andric 
3410b57cec5SDimitry Andric   bool done = false;
3420b57cec5SDimitry Andric   if (m_step_out_to_inline_plan_sp) {
3430b57cec5SDimitry Andric     if (m_step_out_to_inline_plan_sp->MischiefManaged()) {
3440b57cec5SDimitry Andric       // Now step through the inlined stack we are in:
3450b57cec5SDimitry Andric       if (QueueInlinedStepPlan(true)) {
3460b57cec5SDimitry Andric         // If we can't queue a plan to do this, then just call ourselves done.
3470b57cec5SDimitry Andric         m_step_out_to_inline_plan_sp.reset();
3480b57cec5SDimitry Andric         SetPlanComplete(false);
3490b57cec5SDimitry Andric         return true;
3500b57cec5SDimitry Andric       } else
3510b57cec5SDimitry Andric         done = true;
3520b57cec5SDimitry Andric     } else
3530b57cec5SDimitry Andric       return m_step_out_to_inline_plan_sp->ShouldStop(event_ptr);
3540b57cec5SDimitry Andric   } else if (m_step_through_inline_plan_sp) {
3550b57cec5SDimitry Andric     if (m_step_through_inline_plan_sp->MischiefManaged())
3560b57cec5SDimitry Andric       done = true;
3570b57cec5SDimitry Andric     else
3580b57cec5SDimitry Andric       return m_step_through_inline_plan_sp->ShouldStop(event_ptr);
3590b57cec5SDimitry Andric   } else if (m_step_out_further_plan_sp) {
3600b57cec5SDimitry Andric     if (m_step_out_further_plan_sp->MischiefManaged())
3610b57cec5SDimitry Andric       m_step_out_further_plan_sp.reset();
3620b57cec5SDimitry Andric     else
3630b57cec5SDimitry Andric       return m_step_out_further_plan_sp->ShouldStop(event_ptr);
3640b57cec5SDimitry Andric   }
3650b57cec5SDimitry Andric 
3660b57cec5SDimitry Andric   if (!done) {
3675ffd83dbSDimitry Andric     StackID frame_zero_id = GetThread().GetStackFrameAtIndex(0)->GetStackID();
3680b57cec5SDimitry Andric     done = !(frame_zero_id < m_step_out_to_id);
3690b57cec5SDimitry Andric   }
3700b57cec5SDimitry Andric 
3710b57cec5SDimitry Andric   // The normal step out computations think we are done, so all we need to do
3720b57cec5SDimitry Andric   // is consult the ShouldStopHere, and we are done.
3730b57cec5SDimitry Andric 
3740b57cec5SDimitry Andric   if (done) {
3750b57cec5SDimitry Andric     if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) {
3760b57cec5SDimitry Andric       CalculateReturnValue();
3770b57cec5SDimitry Andric       SetPlanComplete();
3780b57cec5SDimitry Andric     } else {
3790b57cec5SDimitry Andric       m_step_out_further_plan_sp =
3800b57cec5SDimitry Andric           QueueStepOutFromHerePlan(m_flags, eFrameCompareOlder, m_status);
3810b57cec5SDimitry Andric       done = false;
3820b57cec5SDimitry Andric     }
3830b57cec5SDimitry Andric   }
3840b57cec5SDimitry Andric 
3850b57cec5SDimitry Andric   return done;
3860b57cec5SDimitry Andric }
3870b57cec5SDimitry Andric 
StopOthers()3880b57cec5SDimitry Andric bool ThreadPlanStepOut::StopOthers() { return m_stop_others; }
3890b57cec5SDimitry Andric 
GetPlanRunState()3900b57cec5SDimitry Andric StateType ThreadPlanStepOut::GetPlanRunState() { return eStateRunning; }
3910b57cec5SDimitry Andric 
DoWillResume(StateType resume_state,bool current_plan)3920b57cec5SDimitry Andric bool ThreadPlanStepOut::DoWillResume(StateType resume_state,
3930b57cec5SDimitry Andric                                      bool current_plan) {
3940b57cec5SDimitry Andric   if (m_step_out_to_inline_plan_sp || m_step_through_inline_plan_sp)
3950b57cec5SDimitry Andric     return true;
3960b57cec5SDimitry Andric 
3970b57cec5SDimitry Andric   if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
3980b57cec5SDimitry Andric     return false;
3990b57cec5SDimitry Andric 
4000b57cec5SDimitry Andric   if (current_plan) {
4015ffd83dbSDimitry Andric     Breakpoint *return_bp = GetTarget().GetBreakpointByID(m_return_bp_id).get();
4020b57cec5SDimitry Andric     if (return_bp != nullptr)
4030b57cec5SDimitry Andric       return_bp->SetEnabled(true);
4040b57cec5SDimitry Andric   }
4050b57cec5SDimitry Andric   return true;
4060b57cec5SDimitry Andric }
4070b57cec5SDimitry Andric 
WillStop()4080b57cec5SDimitry Andric bool ThreadPlanStepOut::WillStop() {
4090b57cec5SDimitry Andric   if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {
4105ffd83dbSDimitry Andric     Breakpoint *return_bp = GetTarget().GetBreakpointByID(m_return_bp_id).get();
4110b57cec5SDimitry Andric     if (return_bp != nullptr)
4120b57cec5SDimitry Andric       return_bp->SetEnabled(false);
4130b57cec5SDimitry Andric   }
4140b57cec5SDimitry Andric 
4150b57cec5SDimitry Andric   return true;
4160b57cec5SDimitry Andric }
4170b57cec5SDimitry Andric 
MischiefManaged()4180b57cec5SDimitry Andric bool ThreadPlanStepOut::MischiefManaged() {
4190b57cec5SDimitry Andric   if (IsPlanComplete()) {
4200b57cec5SDimitry Andric     // Did I reach my breakpoint?  If so I'm done.
4210b57cec5SDimitry Andric     //
4220b57cec5SDimitry Andric     // I also check the stack depth, since if we've blown past the breakpoint
4230b57cec5SDimitry Andric     // for some
4240b57cec5SDimitry Andric     // reason and we're now stopping for some other reason altogether, then
4250b57cec5SDimitry Andric     // we're done with this step out operation.
4260b57cec5SDimitry Andric 
42781ad6265SDimitry Andric     Log *log = GetLog(LLDBLog::Step);
4280b57cec5SDimitry Andric     if (log)
4299dba64beSDimitry Andric       LLDB_LOGF(log, "Completed step out plan.");
4300b57cec5SDimitry Andric     if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {
4315ffd83dbSDimitry Andric       GetTarget().RemoveBreakpointByID(m_return_bp_id);
4320b57cec5SDimitry Andric       m_return_bp_id = LLDB_INVALID_BREAK_ID;
4330b57cec5SDimitry Andric     }
4340b57cec5SDimitry Andric 
4350b57cec5SDimitry Andric     ThreadPlan::MischiefManaged();
4360b57cec5SDimitry Andric     return true;
4370b57cec5SDimitry Andric   } else {
4380b57cec5SDimitry Andric     return false;
4390b57cec5SDimitry Andric   }
4400b57cec5SDimitry Andric }
4410b57cec5SDimitry Andric 
QueueInlinedStepPlan(bool queue_now)4420b57cec5SDimitry Andric bool ThreadPlanStepOut::QueueInlinedStepPlan(bool queue_now) {
4430b57cec5SDimitry Andric   // Now figure out the range of this inlined block, and set up a "step through
4440b57cec5SDimitry Andric   // range" plan for that.  If we've been provided with a context, then use the
4450b57cec5SDimitry Andric   // block in that context.
4465ffd83dbSDimitry Andric   Thread &thread = GetThread();
4475ffd83dbSDimitry Andric   StackFrameSP immediate_return_from_sp(thread.GetStackFrameAtIndex(0));
4480b57cec5SDimitry Andric   if (!immediate_return_from_sp)
4490b57cec5SDimitry Andric     return false;
4500b57cec5SDimitry Andric 
45181ad6265SDimitry Andric   Log *log = GetLog(LLDBLog::Step);
4520b57cec5SDimitry Andric   if (log) {
4530b57cec5SDimitry Andric     StreamString s;
4540b57cec5SDimitry Andric     immediate_return_from_sp->Dump(&s, true, false);
4559dba64beSDimitry Andric     LLDB_LOGF(log, "Queuing inlined frame to step past: %s.", s.GetData());
4560b57cec5SDimitry Andric   }
4570b57cec5SDimitry Andric 
4580b57cec5SDimitry Andric   Block *from_block = immediate_return_from_sp->GetFrameBlock();
4590b57cec5SDimitry Andric   if (from_block) {
4600b57cec5SDimitry Andric     Block *inlined_block = from_block->GetContainingInlinedBlock();
4610b57cec5SDimitry Andric     if (inlined_block) {
4620b57cec5SDimitry Andric       size_t num_ranges = inlined_block->GetNumRanges();
4630b57cec5SDimitry Andric       AddressRange inline_range;
4640b57cec5SDimitry Andric       if (inlined_block->GetRangeAtIndex(0, inline_range)) {
4650b57cec5SDimitry Andric         SymbolContext inlined_sc;
4660b57cec5SDimitry Andric         inlined_block->CalculateSymbolContext(&inlined_sc);
4670b57cec5SDimitry Andric         inlined_sc.target_sp = GetTarget().shared_from_this();
4680b57cec5SDimitry Andric         RunMode run_mode =
4690b57cec5SDimitry Andric             m_stop_others ? lldb::eOnlyThisThread : lldb::eAllThreads;
4700b57cec5SDimitry Andric         const LazyBool avoid_no_debug = eLazyBoolNo;
4710b57cec5SDimitry Andric 
4720b57cec5SDimitry Andric         m_step_through_inline_plan_sp =
4730b57cec5SDimitry Andric             std::make_shared<ThreadPlanStepOverRange>(
4745ffd83dbSDimitry Andric                 thread, inline_range, inlined_sc, run_mode, avoid_no_debug);
4750b57cec5SDimitry Andric         ThreadPlanStepOverRange *step_through_inline_plan_ptr =
4760b57cec5SDimitry Andric             static_cast<ThreadPlanStepOverRange *>(
4770b57cec5SDimitry Andric                 m_step_through_inline_plan_sp.get());
4780b57cec5SDimitry Andric         m_step_through_inline_plan_sp->SetPrivate(true);
4790b57cec5SDimitry Andric 
4800b57cec5SDimitry Andric         step_through_inline_plan_ptr->SetOkayToDiscard(true);
4810b57cec5SDimitry Andric         StreamString errors;
4820b57cec5SDimitry Andric         if (!step_through_inline_plan_ptr->ValidatePlan(&errors)) {
4830b57cec5SDimitry Andric           // FIXME: Log this failure.
4840b57cec5SDimitry Andric           delete step_through_inline_plan_ptr;
4850b57cec5SDimitry Andric           return false;
4860b57cec5SDimitry Andric         }
4870b57cec5SDimitry Andric 
4880b57cec5SDimitry Andric         for (size_t i = 1; i < num_ranges; i++) {
4890b57cec5SDimitry Andric           if (inlined_block->GetRangeAtIndex(i, inline_range))
4900b57cec5SDimitry Andric             step_through_inline_plan_ptr->AddRange(inline_range);
4910b57cec5SDimitry Andric         }
4920b57cec5SDimitry Andric 
4930b57cec5SDimitry Andric         if (queue_now)
4945ffd83dbSDimitry Andric           thread.QueueThreadPlan(m_step_through_inline_plan_sp, false);
4950b57cec5SDimitry Andric         return true;
4960b57cec5SDimitry Andric       }
4970b57cec5SDimitry Andric     }
4980b57cec5SDimitry Andric   }
4990b57cec5SDimitry Andric 
5000b57cec5SDimitry Andric   return false;
5010b57cec5SDimitry Andric }
5020b57cec5SDimitry Andric 
CalculateReturnValue()5030b57cec5SDimitry Andric void ThreadPlanStepOut::CalculateReturnValue() {
5040b57cec5SDimitry Andric   if (m_return_valobj_sp)
5050b57cec5SDimitry Andric     return;
5060b57cec5SDimitry Andric 
5070b57cec5SDimitry Andric   if (!m_calculate_return_value)
5080b57cec5SDimitry Andric     return;
5090b57cec5SDimitry Andric 
5100b57cec5SDimitry Andric   if (m_immediate_step_from_function != nullptr) {
5110b57cec5SDimitry Andric     CompilerType return_compiler_type =
5120b57cec5SDimitry Andric         m_immediate_step_from_function->GetCompilerType()
5130b57cec5SDimitry Andric             .GetFunctionReturnType();
5140b57cec5SDimitry Andric     if (return_compiler_type) {
5155ffd83dbSDimitry Andric       lldb::ABISP abi_sp = m_process.GetABI();
5160b57cec5SDimitry Andric       if (abi_sp)
5170b57cec5SDimitry Andric         m_return_valobj_sp =
5185ffd83dbSDimitry Andric             abi_sp->GetReturnValueObject(GetThread(), return_compiler_type);
5190b57cec5SDimitry Andric     }
5200b57cec5SDimitry Andric   }
5210b57cec5SDimitry Andric }
5220b57cec5SDimitry Andric 
IsPlanStale()5230b57cec5SDimitry Andric bool ThreadPlanStepOut::IsPlanStale() {
5240b57cec5SDimitry Andric   // If we are still lower on the stack than the frame we are returning to,
5250b57cec5SDimitry Andric   // then there's something for us to do.  Otherwise, we're stale.
5260b57cec5SDimitry Andric 
5275ffd83dbSDimitry Andric   StackID frame_zero_id = GetThread().GetStackFrameAtIndex(0)->GetStackID();
5280b57cec5SDimitry Andric   return !(frame_zero_id < m_step_out_to_id);
5290b57cec5SDimitry Andric }
530