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"
240b57cec5SDimitry Andric #include "lldb/Utility/Log.h"
250b57cec5SDimitry Andric
260b57cec5SDimitry Andric #include <memory>
270b57cec5SDimitry Andric
280b57cec5SDimitry Andric using namespace lldb;
290b57cec5SDimitry Andric using namespace lldb_private;
300b57cec5SDimitry Andric
310b57cec5SDimitry Andric uint32_t ThreadPlanStepOut::s_default_flag_values = 0;
320b57cec5SDimitry Andric
330b57cec5SDimitry 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)340b57cec5SDimitry Andric ThreadPlanStepOut::ThreadPlanStepOut(
350b57cec5SDimitry Andric Thread &thread, SymbolContext *context, bool first_insn, bool stop_others,
36*5f7ddb14SDimitry Andric Vote report_stop_vote, Vote report_run_vote, uint32_t frame_idx,
370b57cec5SDimitry Andric LazyBool step_out_avoids_code_without_debug_info,
380b57cec5SDimitry Andric bool continue_to_next_branch, bool gather_return_value)
39*5f7ddb14SDimitry Andric : ThreadPlan(ThreadPlan::eKindStepOut, "Step out", thread, report_stop_vote,
40*5f7ddb14SDimitry Andric report_run_vote),
410b57cec5SDimitry Andric ThreadPlanShouldStopHere(this), m_step_from_insn(LLDB_INVALID_ADDRESS),
420b57cec5SDimitry Andric m_return_bp_id(LLDB_INVALID_BREAK_ID),
430b57cec5SDimitry Andric m_return_addr(LLDB_INVALID_ADDRESS), m_stop_others(stop_others),
440b57cec5SDimitry Andric m_immediate_step_from_function(nullptr),
450b57cec5SDimitry Andric m_calculate_return_value(gather_return_value) {
460b57cec5SDimitry Andric Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
470b57cec5SDimitry Andric SetFlagsToDefault();
480b57cec5SDimitry Andric SetupAvoidNoDebug(step_out_avoids_code_without_debug_info);
490b57cec5SDimitry Andric
505ffd83dbSDimitry Andric m_step_from_insn = thread.GetRegisterContext()->GetPC(0);
510b57cec5SDimitry Andric
520b57cec5SDimitry Andric uint32_t return_frame_index = frame_idx + 1;
535ffd83dbSDimitry Andric StackFrameSP return_frame_sp(thread.GetStackFrameAtIndex(return_frame_index));
545ffd83dbSDimitry Andric StackFrameSP immediate_return_from_sp(thread.GetStackFrameAtIndex(frame_idx));
550b57cec5SDimitry Andric
560b57cec5SDimitry Andric if (!return_frame_sp || !immediate_return_from_sp)
570b57cec5SDimitry Andric return; // we can't do anything here. ValidatePlan() will return false.
580b57cec5SDimitry Andric
590b57cec5SDimitry Andric // While stepping out, behave as-if artificial frames are not present.
600b57cec5SDimitry Andric while (return_frame_sp->IsArtificial()) {
610b57cec5SDimitry Andric m_stepped_past_frames.push_back(return_frame_sp);
620b57cec5SDimitry Andric
630b57cec5SDimitry Andric ++return_frame_index;
645ffd83dbSDimitry Andric return_frame_sp = thread.GetStackFrameAtIndex(return_frame_index);
650b57cec5SDimitry Andric
660b57cec5SDimitry Andric // We never expect to see an artificial frame without a regular ancestor.
670b57cec5SDimitry Andric // If this happens, log the issue and defensively refuse to step out.
680b57cec5SDimitry Andric if (!return_frame_sp) {
690b57cec5SDimitry Andric LLDB_LOG(log, "Can't step out of frame with artificial ancestors");
700b57cec5SDimitry Andric return;
710b57cec5SDimitry Andric }
720b57cec5SDimitry Andric }
730b57cec5SDimitry Andric
740b57cec5SDimitry Andric m_step_out_to_id = return_frame_sp->GetStackID();
750b57cec5SDimitry Andric m_immediate_step_from_id = immediate_return_from_sp->GetStackID();
760b57cec5SDimitry Andric
770b57cec5SDimitry Andric // If the frame directly below the one we are returning to is inlined, we
780b57cec5SDimitry Andric // have to be a little more careful. It is non-trivial to determine the real
790b57cec5SDimitry Andric // "return code address" for an inlined frame, so we have to work our way to
800b57cec5SDimitry Andric // that frame and then step out.
810b57cec5SDimitry Andric if (immediate_return_from_sp->IsInlined()) {
820b57cec5SDimitry Andric if (frame_idx > 0) {
830b57cec5SDimitry Andric // First queue a plan that gets us to this inlined frame, and when we get
840b57cec5SDimitry Andric // there we'll queue a second plan that walks us out of this frame.
850b57cec5SDimitry Andric m_step_out_to_inline_plan_sp = std::make_shared<ThreadPlanStepOut>(
865ffd83dbSDimitry Andric thread, nullptr, false, stop_others, eVoteNoOpinion, eVoteNoOpinion,
870b57cec5SDimitry Andric frame_idx - 1, eLazyBoolNo, continue_to_next_branch);
880b57cec5SDimitry Andric static_cast<ThreadPlanStepOut *>(m_step_out_to_inline_plan_sp.get())
890b57cec5SDimitry Andric ->SetShouldStopHereCallbacks(nullptr, nullptr);
900b57cec5SDimitry Andric m_step_out_to_inline_plan_sp->SetPrivate(true);
910b57cec5SDimitry Andric } else {
920b57cec5SDimitry Andric // If we're already at the inlined frame we're stepping through, then
930b57cec5SDimitry Andric // just do that now.
940b57cec5SDimitry Andric QueueInlinedStepPlan(false);
950b57cec5SDimitry Andric }
960b57cec5SDimitry Andric } else {
970b57cec5SDimitry Andric // Find the return address and set a breakpoint there:
980b57cec5SDimitry Andric // FIXME - can we do this more securely if we know first_insn?
990b57cec5SDimitry Andric
1000b57cec5SDimitry Andric Address return_address(return_frame_sp->GetFrameCodeAddress());
1010b57cec5SDimitry Andric if (continue_to_next_branch) {
1020b57cec5SDimitry Andric SymbolContext return_address_sc;
1030b57cec5SDimitry Andric AddressRange range;
1040b57cec5SDimitry Andric Address return_address_decr_pc = return_address;
1050b57cec5SDimitry Andric if (return_address_decr_pc.GetOffset() > 0)
1060b57cec5SDimitry Andric return_address_decr_pc.Slide(-1);
1070b57cec5SDimitry Andric
1080b57cec5SDimitry Andric return_address_decr_pc.CalculateSymbolContext(
1090b57cec5SDimitry Andric &return_address_sc, lldb::eSymbolContextLineEntry);
1100b57cec5SDimitry Andric if (return_address_sc.line_entry.IsValid()) {
1110b57cec5SDimitry Andric const bool include_inlined_functions = false;
1120b57cec5SDimitry Andric range = return_address_sc.line_entry.GetSameLineContiguousAddressRange(
1130b57cec5SDimitry Andric include_inlined_functions);
1140b57cec5SDimitry Andric if (range.GetByteSize() > 0) {
1155ffd83dbSDimitry Andric return_address = m_process.AdvanceAddressToNextBranchInstruction(
1160b57cec5SDimitry Andric return_address, range);
1170b57cec5SDimitry Andric }
1180b57cec5SDimitry Andric }
1190b57cec5SDimitry Andric }
1205ffd83dbSDimitry Andric m_return_addr = return_address.GetLoadAddress(&m_process.GetTarget());
1210b57cec5SDimitry Andric
1220b57cec5SDimitry Andric if (m_return_addr == LLDB_INVALID_ADDRESS)
1230b57cec5SDimitry Andric return;
1240b57cec5SDimitry Andric
125480093f4SDimitry Andric // Perform some additional validation on the return address.
126480093f4SDimitry Andric uint32_t permissions = 0;
1275ffd83dbSDimitry Andric if (!m_process.GetLoadAddressPermissions(m_return_addr, permissions)) {
1285ffd83dbSDimitry Andric LLDB_LOGF(log, "ThreadPlanStepOut(%p): Return address (0x%" PRIx64
1295ffd83dbSDimitry Andric ") permissions not found.", static_cast<void *>(this),
130480093f4SDimitry Andric m_return_addr);
131480093f4SDimitry Andric } else if (!(permissions & ePermissionsExecutable)) {
132480093f4SDimitry Andric m_constructor_errors.Printf("Return address (0x%" PRIx64
133480093f4SDimitry Andric ") did not point to executable memory.",
134480093f4SDimitry Andric m_return_addr);
135480093f4SDimitry Andric LLDB_LOGF(log, "ThreadPlanStepOut(%p): %s", static_cast<void *>(this),
136480093f4SDimitry Andric m_constructor_errors.GetData());
137480093f4SDimitry Andric return;
138480093f4SDimitry Andric }
139480093f4SDimitry Andric
1405ffd83dbSDimitry Andric Breakpoint *return_bp =
1415ffd83dbSDimitry Andric GetTarget().CreateBreakpoint(m_return_addr, true, false).get();
1420b57cec5SDimitry Andric
1430b57cec5SDimitry Andric if (return_bp != nullptr) {
1440b57cec5SDimitry Andric if (return_bp->IsHardware() && !return_bp->HasResolvedLocations())
1450b57cec5SDimitry Andric m_could_not_resolve_hw_bp = true;
1465ffd83dbSDimitry Andric return_bp->SetThreadID(m_tid);
1470b57cec5SDimitry Andric m_return_bp_id = return_bp->GetID();
1480b57cec5SDimitry Andric return_bp->SetBreakpointKind("step-out");
1490b57cec5SDimitry Andric }
1500b57cec5SDimitry Andric
1510b57cec5SDimitry Andric if (immediate_return_from_sp) {
1520b57cec5SDimitry Andric const SymbolContext &sc =
1530b57cec5SDimitry Andric immediate_return_from_sp->GetSymbolContext(eSymbolContextFunction);
1540b57cec5SDimitry Andric if (sc.function) {
1550b57cec5SDimitry Andric m_immediate_step_from_function = sc.function;
1560b57cec5SDimitry Andric }
1570b57cec5SDimitry Andric }
1580b57cec5SDimitry Andric }
1590b57cec5SDimitry Andric }
1600b57cec5SDimitry Andric
SetupAvoidNoDebug(LazyBool step_out_avoids_code_without_debug_info)1610b57cec5SDimitry Andric void ThreadPlanStepOut::SetupAvoidNoDebug(
1620b57cec5SDimitry Andric LazyBool step_out_avoids_code_without_debug_info) {
1630b57cec5SDimitry Andric bool avoid_nodebug = true;
1640b57cec5SDimitry Andric switch (step_out_avoids_code_without_debug_info) {
1650b57cec5SDimitry Andric case eLazyBoolYes:
1660b57cec5SDimitry Andric avoid_nodebug = true;
1670b57cec5SDimitry Andric break;
1680b57cec5SDimitry Andric case eLazyBoolNo:
1690b57cec5SDimitry Andric avoid_nodebug = false;
1700b57cec5SDimitry Andric break;
1710b57cec5SDimitry Andric case eLazyBoolCalculate:
1725ffd83dbSDimitry Andric avoid_nodebug = GetThread().GetStepOutAvoidsNoDebug();
1730b57cec5SDimitry Andric break;
1740b57cec5SDimitry Andric }
1750b57cec5SDimitry Andric if (avoid_nodebug)
1760b57cec5SDimitry Andric GetFlags().Set(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);
1770b57cec5SDimitry Andric else
1780b57cec5SDimitry Andric GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);
1790b57cec5SDimitry Andric }
1800b57cec5SDimitry Andric
DidPush()1810b57cec5SDimitry Andric void ThreadPlanStepOut::DidPush() {
1825ffd83dbSDimitry Andric Thread &thread = GetThread();
1830b57cec5SDimitry Andric if (m_step_out_to_inline_plan_sp)
1845ffd83dbSDimitry Andric thread.QueueThreadPlan(m_step_out_to_inline_plan_sp, false);
1850b57cec5SDimitry Andric else if (m_step_through_inline_plan_sp)
1865ffd83dbSDimitry Andric thread.QueueThreadPlan(m_step_through_inline_plan_sp, false);
1870b57cec5SDimitry Andric }
1880b57cec5SDimitry Andric
~ThreadPlanStepOut()1890b57cec5SDimitry Andric ThreadPlanStepOut::~ThreadPlanStepOut() {
1900b57cec5SDimitry Andric if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
1915ffd83dbSDimitry Andric GetTarget().RemoveBreakpointByID(m_return_bp_id);
1920b57cec5SDimitry Andric }
1930b57cec5SDimitry Andric
GetDescription(Stream * s,lldb::DescriptionLevel level)1940b57cec5SDimitry Andric void ThreadPlanStepOut::GetDescription(Stream *s,
1950b57cec5SDimitry Andric lldb::DescriptionLevel level) {
1960b57cec5SDimitry Andric if (level == lldb::eDescriptionLevelBrief)
1970b57cec5SDimitry Andric s->Printf("step out");
1980b57cec5SDimitry Andric else {
1990b57cec5SDimitry Andric if (m_step_out_to_inline_plan_sp)
2000b57cec5SDimitry Andric s->Printf("Stepping out to inlined frame so we can walk through it.");
2010b57cec5SDimitry Andric else if (m_step_through_inline_plan_sp)
2020b57cec5SDimitry Andric s->Printf("Stepping out by stepping through inlined function.");
2030b57cec5SDimitry Andric else {
2040b57cec5SDimitry Andric s->Printf("Stepping out from ");
2050b57cec5SDimitry Andric Address tmp_address;
2060b57cec5SDimitry Andric if (tmp_address.SetLoadAddress(m_step_from_insn, &GetTarget())) {
2075ffd83dbSDimitry Andric tmp_address.Dump(s, &m_process, Address::DumpStyleResolvedDescription,
2080b57cec5SDimitry Andric Address::DumpStyleLoadAddress);
2090b57cec5SDimitry Andric } else {
2100b57cec5SDimitry Andric s->Printf("address 0x%" PRIx64 "", (uint64_t)m_step_from_insn);
2110b57cec5SDimitry Andric }
2120b57cec5SDimitry Andric
2130b57cec5SDimitry Andric // FIXME: find some useful way to present the m_return_id, since there may
2140b57cec5SDimitry Andric // be multiple copies of the
2150b57cec5SDimitry Andric // same function on the stack.
2160b57cec5SDimitry Andric
2170b57cec5SDimitry Andric s->Printf(" returning to frame at ");
2180b57cec5SDimitry Andric if (tmp_address.SetLoadAddress(m_return_addr, &GetTarget())) {
2195ffd83dbSDimitry Andric tmp_address.Dump(s, &m_process, Address::DumpStyleResolvedDescription,
2200b57cec5SDimitry Andric Address::DumpStyleLoadAddress);
2210b57cec5SDimitry Andric } else {
2220b57cec5SDimitry Andric s->Printf("address 0x%" PRIx64 "", (uint64_t)m_return_addr);
2230b57cec5SDimitry Andric }
2240b57cec5SDimitry Andric
2250b57cec5SDimitry Andric if (level == eDescriptionLevelVerbose)
2260b57cec5SDimitry Andric s->Printf(" using breakpoint site %d", m_return_bp_id);
2270b57cec5SDimitry Andric }
2280b57cec5SDimitry Andric }
2290b57cec5SDimitry Andric
2305ffd83dbSDimitry Andric if (m_stepped_past_frames.empty())
2315ffd83dbSDimitry Andric return;
2325ffd83dbSDimitry Andric
2330b57cec5SDimitry Andric s->Printf("\n");
2340b57cec5SDimitry Andric for (StackFrameSP frame_sp : m_stepped_past_frames) {
2350b57cec5SDimitry Andric s->Printf("Stepped out past: ");
2360b57cec5SDimitry Andric frame_sp->DumpUsingSettingsFormat(s);
2370b57cec5SDimitry Andric }
2380b57cec5SDimitry Andric }
2390b57cec5SDimitry Andric
ValidatePlan(Stream * error)2400b57cec5SDimitry Andric bool ThreadPlanStepOut::ValidatePlan(Stream *error) {
2410b57cec5SDimitry Andric if (m_step_out_to_inline_plan_sp)
2420b57cec5SDimitry Andric return m_step_out_to_inline_plan_sp->ValidatePlan(error);
2430b57cec5SDimitry Andric
2440b57cec5SDimitry Andric if (m_step_through_inline_plan_sp)
2450b57cec5SDimitry Andric return m_step_through_inline_plan_sp->ValidatePlan(error);
2460b57cec5SDimitry Andric
2470b57cec5SDimitry Andric if (m_could_not_resolve_hw_bp) {
2480b57cec5SDimitry Andric if (error)
2490b57cec5SDimitry Andric error->PutCString(
2500b57cec5SDimitry Andric "Could not create hardware breakpoint for thread plan.");
2510b57cec5SDimitry Andric return false;
2520b57cec5SDimitry Andric }
2530b57cec5SDimitry Andric
2540b57cec5SDimitry Andric if (m_return_bp_id == LLDB_INVALID_BREAK_ID) {
255480093f4SDimitry Andric if (error) {
2560b57cec5SDimitry Andric error->PutCString("Could not create return address breakpoint.");
257480093f4SDimitry Andric if (m_constructor_errors.GetSize() > 0) {
258480093f4SDimitry Andric error->PutCString(" ");
259480093f4SDimitry Andric error->PutCString(m_constructor_errors.GetString());
260480093f4SDimitry Andric }
261480093f4SDimitry Andric }
2620b57cec5SDimitry Andric return false;
2630b57cec5SDimitry Andric }
2640b57cec5SDimitry Andric
2650b57cec5SDimitry Andric return true;
2660b57cec5SDimitry Andric }
2670b57cec5SDimitry Andric
DoPlanExplainsStop(Event * event_ptr)2680b57cec5SDimitry Andric bool ThreadPlanStepOut::DoPlanExplainsStop(Event *event_ptr) {
2690b57cec5SDimitry Andric // If the step out plan is done, then we just need to step through the
2700b57cec5SDimitry Andric // inlined frame.
2710b57cec5SDimitry Andric if (m_step_out_to_inline_plan_sp) {
2720b57cec5SDimitry Andric return m_step_out_to_inline_plan_sp->MischiefManaged();
2730b57cec5SDimitry Andric } else if (m_step_through_inline_plan_sp) {
2740b57cec5SDimitry Andric if (m_step_through_inline_plan_sp->MischiefManaged()) {
2750b57cec5SDimitry Andric CalculateReturnValue();
2760b57cec5SDimitry Andric SetPlanComplete();
2770b57cec5SDimitry Andric return true;
2780b57cec5SDimitry Andric } else
2790b57cec5SDimitry Andric return false;
2800b57cec5SDimitry Andric } else if (m_step_out_further_plan_sp) {
2810b57cec5SDimitry Andric return m_step_out_further_plan_sp->MischiefManaged();
2820b57cec5SDimitry Andric }
2830b57cec5SDimitry Andric
2840b57cec5SDimitry Andric // We don't explain signals or breakpoints (breakpoints that handle stepping
2850b57cec5SDimitry Andric // in or out will be handled by a child plan.
2860b57cec5SDimitry Andric
2870b57cec5SDimitry Andric StopInfoSP stop_info_sp = GetPrivateStopInfo();
2880b57cec5SDimitry Andric if (stop_info_sp) {
2890b57cec5SDimitry Andric StopReason reason = stop_info_sp->GetStopReason();
2900b57cec5SDimitry Andric if (reason == eStopReasonBreakpoint) {
2910b57cec5SDimitry Andric // If this is OUR breakpoint, we're fine, otherwise we don't know why
2920b57cec5SDimitry Andric // this happened...
2930b57cec5SDimitry Andric BreakpointSiteSP site_sp(
2945ffd83dbSDimitry Andric m_process.GetBreakpointSiteList().FindByID(stop_info_sp->GetValue()));
2950b57cec5SDimitry Andric if (site_sp && site_sp->IsBreakpointAtThisSite(m_return_bp_id)) {
2960b57cec5SDimitry Andric bool done;
2970b57cec5SDimitry Andric
2985ffd83dbSDimitry Andric StackID frame_zero_id =
2995ffd83dbSDimitry Andric GetThread().GetStackFrameAtIndex(0)->GetStackID();
3000b57cec5SDimitry Andric
3010b57cec5SDimitry Andric if (m_step_out_to_id == frame_zero_id)
3020b57cec5SDimitry Andric done = true;
3030b57cec5SDimitry Andric else if (m_step_out_to_id < frame_zero_id) {
3040b57cec5SDimitry Andric // Either we stepped past the breakpoint, or the stack ID calculation
3050b57cec5SDimitry Andric // was incorrect and we should probably stop.
3060b57cec5SDimitry Andric done = true;
3070b57cec5SDimitry Andric } else {
3080b57cec5SDimitry Andric done = (m_immediate_step_from_id < frame_zero_id);
3090b57cec5SDimitry Andric }
3100b57cec5SDimitry Andric
3110b57cec5SDimitry Andric if (done) {
3120b57cec5SDimitry Andric if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) {
3130b57cec5SDimitry Andric CalculateReturnValue();
3140b57cec5SDimitry Andric SetPlanComplete();
3150b57cec5SDimitry Andric }
3160b57cec5SDimitry Andric }
3170b57cec5SDimitry Andric
3180b57cec5SDimitry Andric // If there was only one owner, then we're done. But if we also hit
3190b57cec5SDimitry Andric // some user breakpoint on our way out, we should mark ourselves as
3200b57cec5SDimitry Andric // done, but also not claim to explain the stop, since it is more
3210b57cec5SDimitry Andric // important to report the user breakpoint than the step out
3220b57cec5SDimitry Andric // completion.
3230b57cec5SDimitry Andric
3240b57cec5SDimitry Andric if (site_sp->GetNumberOfOwners() == 1)
3250b57cec5SDimitry Andric return true;
3260b57cec5SDimitry Andric }
3270b57cec5SDimitry Andric return false;
3280b57cec5SDimitry Andric } else if (IsUsuallyUnexplainedStopReason(reason))
3290b57cec5SDimitry Andric return false;
3300b57cec5SDimitry Andric else
3310b57cec5SDimitry Andric return true;
3320b57cec5SDimitry Andric }
3330b57cec5SDimitry Andric return true;
3340b57cec5SDimitry Andric }
3350b57cec5SDimitry Andric
ShouldStop(Event * event_ptr)3360b57cec5SDimitry Andric bool ThreadPlanStepOut::ShouldStop(Event *event_ptr) {
3370b57cec5SDimitry Andric if (IsPlanComplete())
3380b57cec5SDimitry Andric return true;
3390b57cec5SDimitry Andric
3400b57cec5SDimitry Andric bool done = false;
3410b57cec5SDimitry Andric if (m_step_out_to_inline_plan_sp) {
3420b57cec5SDimitry Andric if (m_step_out_to_inline_plan_sp->MischiefManaged()) {
3430b57cec5SDimitry Andric // Now step through the inlined stack we are in:
3440b57cec5SDimitry Andric if (QueueInlinedStepPlan(true)) {
3450b57cec5SDimitry Andric // If we can't queue a plan to do this, then just call ourselves done.
3460b57cec5SDimitry Andric m_step_out_to_inline_plan_sp.reset();
3470b57cec5SDimitry Andric SetPlanComplete(false);
3480b57cec5SDimitry Andric return true;
3490b57cec5SDimitry Andric } else
3500b57cec5SDimitry Andric done = true;
3510b57cec5SDimitry Andric } else
3520b57cec5SDimitry Andric return m_step_out_to_inline_plan_sp->ShouldStop(event_ptr);
3530b57cec5SDimitry Andric } else if (m_step_through_inline_plan_sp) {
3540b57cec5SDimitry Andric if (m_step_through_inline_plan_sp->MischiefManaged())
3550b57cec5SDimitry Andric done = true;
3560b57cec5SDimitry Andric else
3570b57cec5SDimitry Andric return m_step_through_inline_plan_sp->ShouldStop(event_ptr);
3580b57cec5SDimitry Andric } else if (m_step_out_further_plan_sp) {
3590b57cec5SDimitry Andric if (m_step_out_further_plan_sp->MischiefManaged())
3600b57cec5SDimitry Andric m_step_out_further_plan_sp.reset();
3610b57cec5SDimitry Andric else
3620b57cec5SDimitry Andric return m_step_out_further_plan_sp->ShouldStop(event_ptr);
3630b57cec5SDimitry Andric }
3640b57cec5SDimitry Andric
3650b57cec5SDimitry Andric if (!done) {
3665ffd83dbSDimitry Andric StackID frame_zero_id = GetThread().GetStackFrameAtIndex(0)->GetStackID();
3670b57cec5SDimitry Andric done = !(frame_zero_id < m_step_out_to_id);
3680b57cec5SDimitry Andric }
3690b57cec5SDimitry Andric
3700b57cec5SDimitry Andric // The normal step out computations think we are done, so all we need to do
3710b57cec5SDimitry Andric // is consult the ShouldStopHere, and we are done.
3720b57cec5SDimitry Andric
3730b57cec5SDimitry Andric if (done) {
3740b57cec5SDimitry Andric if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) {
3750b57cec5SDimitry Andric CalculateReturnValue();
3760b57cec5SDimitry Andric SetPlanComplete();
3770b57cec5SDimitry Andric } else {
3780b57cec5SDimitry Andric m_step_out_further_plan_sp =
3790b57cec5SDimitry Andric QueueStepOutFromHerePlan(m_flags, eFrameCompareOlder, m_status);
3800b57cec5SDimitry Andric done = false;
3810b57cec5SDimitry Andric }
3820b57cec5SDimitry Andric }
3830b57cec5SDimitry Andric
3840b57cec5SDimitry Andric return done;
3850b57cec5SDimitry Andric }
3860b57cec5SDimitry Andric
StopOthers()3870b57cec5SDimitry Andric bool ThreadPlanStepOut::StopOthers() { return m_stop_others; }
3880b57cec5SDimitry Andric
GetPlanRunState()3890b57cec5SDimitry Andric StateType ThreadPlanStepOut::GetPlanRunState() { return eStateRunning; }
3900b57cec5SDimitry Andric
DoWillResume(StateType resume_state,bool current_plan)3910b57cec5SDimitry Andric bool ThreadPlanStepOut::DoWillResume(StateType resume_state,
3920b57cec5SDimitry Andric bool current_plan) {
3930b57cec5SDimitry Andric if (m_step_out_to_inline_plan_sp || m_step_through_inline_plan_sp)
3940b57cec5SDimitry Andric return true;
3950b57cec5SDimitry Andric
3960b57cec5SDimitry Andric if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
3970b57cec5SDimitry Andric return false;
3980b57cec5SDimitry Andric
3990b57cec5SDimitry Andric if (current_plan) {
4005ffd83dbSDimitry Andric Breakpoint *return_bp = GetTarget().GetBreakpointByID(m_return_bp_id).get();
4010b57cec5SDimitry Andric if (return_bp != nullptr)
4020b57cec5SDimitry Andric return_bp->SetEnabled(true);
4030b57cec5SDimitry Andric }
4040b57cec5SDimitry Andric return true;
4050b57cec5SDimitry Andric }
4060b57cec5SDimitry Andric
WillStop()4070b57cec5SDimitry Andric bool ThreadPlanStepOut::WillStop() {
4080b57cec5SDimitry Andric if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {
4095ffd83dbSDimitry Andric Breakpoint *return_bp = GetTarget().GetBreakpointByID(m_return_bp_id).get();
4100b57cec5SDimitry Andric if (return_bp != nullptr)
4110b57cec5SDimitry Andric return_bp->SetEnabled(false);
4120b57cec5SDimitry Andric }
4130b57cec5SDimitry Andric
4140b57cec5SDimitry Andric return true;
4150b57cec5SDimitry Andric }
4160b57cec5SDimitry Andric
MischiefManaged()4170b57cec5SDimitry Andric bool ThreadPlanStepOut::MischiefManaged() {
4180b57cec5SDimitry Andric if (IsPlanComplete()) {
4190b57cec5SDimitry Andric // Did I reach my breakpoint? If so I'm done.
4200b57cec5SDimitry Andric //
4210b57cec5SDimitry Andric // I also check the stack depth, since if we've blown past the breakpoint
4220b57cec5SDimitry Andric // for some
4230b57cec5SDimitry Andric // reason and we're now stopping for some other reason altogether, then
4240b57cec5SDimitry Andric // we're done with this step out operation.
4250b57cec5SDimitry Andric
4260b57cec5SDimitry Andric Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
4270b57cec5SDimitry Andric if (log)
4289dba64beSDimitry Andric LLDB_LOGF(log, "Completed step out plan.");
4290b57cec5SDimitry Andric if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {
4305ffd83dbSDimitry Andric GetTarget().RemoveBreakpointByID(m_return_bp_id);
4310b57cec5SDimitry Andric m_return_bp_id = LLDB_INVALID_BREAK_ID;
4320b57cec5SDimitry Andric }
4330b57cec5SDimitry Andric
4340b57cec5SDimitry Andric ThreadPlan::MischiefManaged();
4350b57cec5SDimitry Andric return true;
4360b57cec5SDimitry Andric } else {
4370b57cec5SDimitry Andric return false;
4380b57cec5SDimitry Andric }
4390b57cec5SDimitry Andric }
4400b57cec5SDimitry Andric
QueueInlinedStepPlan(bool queue_now)4410b57cec5SDimitry Andric bool ThreadPlanStepOut::QueueInlinedStepPlan(bool queue_now) {
4420b57cec5SDimitry Andric // Now figure out the range of this inlined block, and set up a "step through
4430b57cec5SDimitry Andric // range" plan for that. If we've been provided with a context, then use the
4440b57cec5SDimitry Andric // block in that context.
4455ffd83dbSDimitry Andric Thread &thread = GetThread();
4465ffd83dbSDimitry Andric StackFrameSP immediate_return_from_sp(thread.GetStackFrameAtIndex(0));
4470b57cec5SDimitry Andric if (!immediate_return_from_sp)
4480b57cec5SDimitry Andric return false;
4490b57cec5SDimitry Andric
4500b57cec5SDimitry Andric Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
4510b57cec5SDimitry Andric if (log) {
4520b57cec5SDimitry Andric StreamString s;
4530b57cec5SDimitry Andric immediate_return_from_sp->Dump(&s, true, false);
4549dba64beSDimitry Andric LLDB_LOGF(log, "Queuing inlined frame to step past: %s.", s.GetData());
4550b57cec5SDimitry Andric }
4560b57cec5SDimitry Andric
4570b57cec5SDimitry Andric Block *from_block = immediate_return_from_sp->GetFrameBlock();
4580b57cec5SDimitry Andric if (from_block) {
4590b57cec5SDimitry Andric Block *inlined_block = from_block->GetContainingInlinedBlock();
4600b57cec5SDimitry Andric if (inlined_block) {
4610b57cec5SDimitry Andric size_t num_ranges = inlined_block->GetNumRanges();
4620b57cec5SDimitry Andric AddressRange inline_range;
4630b57cec5SDimitry Andric if (inlined_block->GetRangeAtIndex(0, inline_range)) {
4640b57cec5SDimitry Andric SymbolContext inlined_sc;
4650b57cec5SDimitry Andric inlined_block->CalculateSymbolContext(&inlined_sc);
4660b57cec5SDimitry Andric inlined_sc.target_sp = GetTarget().shared_from_this();
4670b57cec5SDimitry Andric RunMode run_mode =
4680b57cec5SDimitry Andric m_stop_others ? lldb::eOnlyThisThread : lldb::eAllThreads;
4690b57cec5SDimitry Andric const LazyBool avoid_no_debug = eLazyBoolNo;
4700b57cec5SDimitry Andric
4710b57cec5SDimitry Andric m_step_through_inline_plan_sp =
4720b57cec5SDimitry Andric std::make_shared<ThreadPlanStepOverRange>(
4735ffd83dbSDimitry Andric thread, inline_range, inlined_sc, run_mode, avoid_no_debug);
4740b57cec5SDimitry Andric ThreadPlanStepOverRange *step_through_inline_plan_ptr =
4750b57cec5SDimitry Andric static_cast<ThreadPlanStepOverRange *>(
4760b57cec5SDimitry Andric m_step_through_inline_plan_sp.get());
4770b57cec5SDimitry Andric m_step_through_inline_plan_sp->SetPrivate(true);
4780b57cec5SDimitry Andric
4790b57cec5SDimitry Andric step_through_inline_plan_ptr->SetOkayToDiscard(true);
4800b57cec5SDimitry Andric StreamString errors;
4810b57cec5SDimitry Andric if (!step_through_inline_plan_ptr->ValidatePlan(&errors)) {
4820b57cec5SDimitry Andric // FIXME: Log this failure.
4830b57cec5SDimitry Andric delete step_through_inline_plan_ptr;
4840b57cec5SDimitry Andric return false;
4850b57cec5SDimitry Andric }
4860b57cec5SDimitry Andric
4870b57cec5SDimitry Andric for (size_t i = 1; i < num_ranges; i++) {
4880b57cec5SDimitry Andric if (inlined_block->GetRangeAtIndex(i, inline_range))
4890b57cec5SDimitry Andric step_through_inline_plan_ptr->AddRange(inline_range);
4900b57cec5SDimitry Andric }
4910b57cec5SDimitry Andric
4920b57cec5SDimitry Andric if (queue_now)
4935ffd83dbSDimitry Andric thread.QueueThreadPlan(m_step_through_inline_plan_sp, false);
4940b57cec5SDimitry Andric return true;
4950b57cec5SDimitry Andric }
4960b57cec5SDimitry Andric }
4970b57cec5SDimitry Andric }
4980b57cec5SDimitry Andric
4990b57cec5SDimitry Andric return false;
5000b57cec5SDimitry Andric }
5010b57cec5SDimitry Andric
CalculateReturnValue()5020b57cec5SDimitry Andric void ThreadPlanStepOut::CalculateReturnValue() {
5030b57cec5SDimitry Andric if (m_return_valobj_sp)
5040b57cec5SDimitry Andric return;
5050b57cec5SDimitry Andric
5060b57cec5SDimitry Andric if (!m_calculate_return_value)
5070b57cec5SDimitry Andric return;
5080b57cec5SDimitry Andric
5090b57cec5SDimitry Andric if (m_immediate_step_from_function != nullptr) {
5100b57cec5SDimitry Andric CompilerType return_compiler_type =
5110b57cec5SDimitry Andric m_immediate_step_from_function->GetCompilerType()
5120b57cec5SDimitry Andric .GetFunctionReturnType();
5130b57cec5SDimitry Andric if (return_compiler_type) {
5145ffd83dbSDimitry Andric lldb::ABISP abi_sp = m_process.GetABI();
5150b57cec5SDimitry Andric if (abi_sp)
5160b57cec5SDimitry Andric m_return_valobj_sp =
5175ffd83dbSDimitry Andric abi_sp->GetReturnValueObject(GetThread(), return_compiler_type);
5180b57cec5SDimitry Andric }
5190b57cec5SDimitry Andric }
5200b57cec5SDimitry Andric }
5210b57cec5SDimitry Andric
IsPlanStale()5220b57cec5SDimitry Andric bool ThreadPlanStepOut::IsPlanStale() {
5230b57cec5SDimitry Andric // If we are still lower on the stack than the frame we are returning to,
5240b57cec5SDimitry Andric // then there's something for us to do. Otherwise, we're stale.
5250b57cec5SDimitry Andric
5265ffd83dbSDimitry Andric StackID frame_zero_id = GetThread().GetStackFrameAtIndex(0)->GetStackID();
5270b57cec5SDimitry Andric return !(frame_zero_id < m_step_out_to_id);
5280b57cec5SDimitry Andric }
529