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