brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.0 KiB · d49a01b Raw
574 lines · cpp
1//===-- ThreadPlanStepOut.cpp ---------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Target/ThreadPlanStepOut.h"10#include "lldb/Breakpoint/Breakpoint.h"11#include "lldb/Core/Value.h"12#include "lldb/Symbol/Block.h"13#include "lldb/Symbol/Function.h"14#include "lldb/Symbol/Symbol.h"15#include "lldb/Symbol/Type.h"16#include "lldb/Target/ABI.h"17#include "lldb/Target/Process.h"18#include "lldb/Target/RegisterContext.h"19#include "lldb/Target/StopInfo.h"20#include "lldb/Target/Target.h"21#include "lldb/Target/ThreadPlanStepOverRange.h"22#include "lldb/Target/ThreadPlanStepThrough.h"23#include "lldb/Utility/LLDBLog.h"24#include "lldb/Utility/Log.h"25#include "lldb/ValueObject/ValueObjectConstResult.h"26 27#include <memory>28 29using namespace lldb;30using namespace lldb_private;31 32uint32_t ThreadPlanStepOut::s_default_flag_values = 0;33 34/// Computes the target frame this plan should step out to.35static StackFrameSP36ComputeTargetFrame(Thread &thread, uint32_t start_frame_idx,37                   std::vector<StackFrameSP> &skipped_frames) {38  uint32_t frame_idx = start_frame_idx + 1;39  StackFrameSP return_frame_sp = thread.GetStackFrameAtIndex(frame_idx);40  if (!return_frame_sp)41    return nullptr;42 43  while (return_frame_sp->IsArtificial() || return_frame_sp->IsHidden()) {44    skipped_frames.push_back(return_frame_sp);45 46    frame_idx++;47    return_frame_sp = thread.GetStackFrameAtIndex(frame_idx);48 49    // We never expect to see an artificial frame without a regular ancestor.50    // Defensively refuse to step out.51    if (!return_frame_sp) {52      LLDB_LOG(GetLog(LLDBLog::Step),53               "Can't step out of frame with artificial ancestors");54      return nullptr;55    }56  }57  return return_frame_sp;58}59 60// ThreadPlanStepOut: Step out of the current frame61ThreadPlanStepOut::ThreadPlanStepOut(62    Thread &thread, SymbolContext *context, bool first_insn, bool stop_others,63    Vote report_stop_vote, Vote report_run_vote, uint32_t frame_idx,64    LazyBool step_out_avoids_code_without_debug_info,65    bool continue_to_next_branch, bool gather_return_value)66    : ThreadPlan(ThreadPlan::eKindStepOut, "Step out", thread, report_stop_vote,67                 report_run_vote),68      ThreadPlanShouldStopHere(this), m_step_from_insn(LLDB_INVALID_ADDRESS),69      m_return_bp_id(LLDB_INVALID_BREAK_ID),70      m_return_addr(LLDB_INVALID_ADDRESS), m_stop_others(stop_others),71      m_immediate_step_from_function(nullptr),72      m_calculate_return_value(gather_return_value) {73  SetFlagsToDefault();74  SetupAvoidNoDebug(step_out_avoids_code_without_debug_info);75 76  m_step_from_insn = thread.GetRegisterContext()->GetPC(0);77 78  StackFrameSP return_frame_sp =79      ComputeTargetFrame(thread, frame_idx, m_stepped_past_frames);80  StackFrameSP immediate_return_from_sp(thread.GetStackFrameAtIndex(frame_idx));81 82  SetupReturnAddress(return_frame_sp, immediate_return_from_sp, frame_idx,83                     continue_to_next_branch);84}85 86ThreadPlanStepOut::ThreadPlanStepOut(Thread &thread, bool stop_others,87                                     Vote report_stop_vote,88                                     Vote report_run_vote, uint32_t frame_idx,89                                     bool continue_to_next_branch,90                                     bool gather_return_value)91    : ThreadPlan(ThreadPlan::eKindStepOut, "Step out", thread, report_stop_vote,92                 report_run_vote),93      ThreadPlanShouldStopHere(this), m_return_bp_id(LLDB_INVALID_BREAK_ID),94      m_return_addr(LLDB_INVALID_ADDRESS), m_stop_others(stop_others),95      m_immediate_step_from_function(nullptr),96      m_calculate_return_value(gather_return_value) {97  SetFlagsToDefault();98  m_step_from_insn = thread.GetRegisterContext()->GetPC(0);99 100  StackFrameSP return_frame_sp = thread.GetStackFrameAtIndex(frame_idx + 1);101  StackFrameSP immediate_return_from_sp =102      thread.GetStackFrameAtIndex(frame_idx);103 104  SetupReturnAddress(return_frame_sp, immediate_return_from_sp, frame_idx,105                     continue_to_next_branch);106}107 108void ThreadPlanStepOut::SetupReturnAddress(109    StackFrameSP return_frame_sp, StackFrameSP immediate_return_from_sp,110    uint32_t frame_idx, bool continue_to_next_branch) {111  if (!return_frame_sp || !immediate_return_from_sp)112    return; // we can't do anything here.  ValidatePlan() will return false.113 114  m_step_out_to_id = return_frame_sp->GetStackID();115  m_immediate_step_from_id = immediate_return_from_sp->GetStackID();116 117  // If the frame directly below the one we are returning to is inlined, we118  // have to be a little more careful.  It is non-trivial to determine the real119  // "return code address" for an inlined frame, so we have to work our way to120  // that frame and then step out.121  if (immediate_return_from_sp->IsInlined()) {122    if (frame_idx > 0) {123      // First queue a plan that gets us to this inlined frame, and when we get124      // there we'll queue a second plan that walks us out of this frame.125      m_step_out_to_inline_plan_sp = std::make_shared<ThreadPlanStepOut>(126          GetThread(), nullptr, false, m_stop_others, eVoteNoOpinion,127          eVoteNoOpinion, frame_idx - 1, eLazyBoolNo, continue_to_next_branch);128      static_cast<ThreadPlanStepOut *>(m_step_out_to_inline_plan_sp.get())129          ->SetShouldStopHereCallbacks(nullptr, nullptr);130      m_step_out_to_inline_plan_sp->SetPrivate(true);131    } else {132      // If we're already at the inlined frame we're stepping through, then133      // just do that now.134      QueueInlinedStepPlan(false);135    }136  } else {137    // Find the return address and set a breakpoint there:138    // FIXME - can we do this more securely if we know first_insn?139 140    Address return_address(return_frame_sp->GetFrameCodeAddress());141    if (continue_to_next_branch) {142      SymbolContext return_address_sc;143      AddressRange range;144      Address return_address_decr_pc = return_address;145      if (return_address_decr_pc.GetOffset() > 0)146        return_address_decr_pc.Slide(-1);147 148      return_address_decr_pc.CalculateSymbolContext(149          &return_address_sc, lldb::eSymbolContextLineEntry);150      if (return_address_sc.line_entry.IsValid()) {151        const bool include_inlined_functions = false;152        range = return_address_sc.line_entry.GetSameLineContiguousAddressRange(153            include_inlined_functions);154        if (range.GetByteSize() > 0) {155          return_address = m_process.AdvanceAddressToNextBranchInstruction(156              return_address, range);157        }158      }159    }160    m_return_addr = return_address.GetLoadAddress(&m_process.GetTarget());161 162    if (m_return_addr == LLDB_INVALID_ADDRESS)163      return;164 165    // Perform some additional validation on the return address.166    uint32_t permissions = 0;167    Log *log = GetLog(LLDBLog::Step);168    if (!m_process.GetLoadAddressPermissions(m_return_addr, permissions)) {169      LLDB_LOGF(log, "ThreadPlanStepOut(%p): Return address (0x%" PRIx64170                ") permissions not found.", static_cast<void *>(this),171                m_return_addr);172    } else if (!(permissions & ePermissionsExecutable)) {173      m_constructor_errors.Printf("Return address (0x%" PRIx64174                                  ") did not point to executable memory.",175                                  m_return_addr);176      LLDB_LOGF(log, "ThreadPlanStepOut(%p): %s", static_cast<void *>(this),177                m_constructor_errors.GetData());178      return;179    }180 181    Breakpoint *return_bp = 182        GetTarget().CreateBreakpoint(m_return_addr, true, false).get();183 184    if (return_bp != nullptr) {185      if (return_bp->IsHardware() && !return_bp->HasResolvedLocations())186        m_could_not_resolve_hw_bp = true;187      return_bp->SetThreadID(m_tid);188      m_return_bp_id = return_bp->GetID();189      return_bp->SetBreakpointKind("step-out");190    }191 192    if (immediate_return_from_sp) {193      const SymbolContext &sc =194          immediate_return_from_sp->GetSymbolContext(eSymbolContextFunction);195      if (sc.function) {196        m_immediate_step_from_function = sc.function;197      }198    }199  }200}201 202void ThreadPlanStepOut::SetupAvoidNoDebug(203    LazyBool step_out_avoids_code_without_debug_info) {204  bool avoid_nodebug = true;205  switch (step_out_avoids_code_without_debug_info) {206  case eLazyBoolYes:207    avoid_nodebug = true;208    break;209  case eLazyBoolNo:210    avoid_nodebug = false;211    break;212  case eLazyBoolCalculate:213    avoid_nodebug = GetThread().GetStepOutAvoidsNoDebug();214    break;215  }216  if (avoid_nodebug)217    GetFlags().Set(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);218  else219    GetFlags().Clear(ThreadPlanShouldStopHere::eStepOutAvoidNoDebug);220}221 222void ThreadPlanStepOut::DidPush() {223  Thread &thread = GetThread();224  if (m_step_out_to_inline_plan_sp)225    thread.QueueThreadPlan(m_step_out_to_inline_plan_sp, false);226  else if (m_step_through_inline_plan_sp)227    thread.QueueThreadPlan(m_step_through_inline_plan_sp, false);228}229 230ThreadPlanStepOut::~ThreadPlanStepOut() {231  if (m_return_bp_id != LLDB_INVALID_BREAK_ID)232    GetTarget().RemoveBreakpointByID(m_return_bp_id);233}234 235void ThreadPlanStepOut::GetDescription(Stream *s,236                                       lldb::DescriptionLevel level) {237  if (level == lldb::eDescriptionLevelBrief)238    s->Printf("step out");239  else {240    if (m_step_out_to_inline_plan_sp)241      s->Printf("Stepping out to inlined frame so we can walk through it.");242    else if (m_step_through_inline_plan_sp)243      s->Printf("Stepping out by stepping through inlined function.");244    else {245      s->Printf("Stepping out from ");246      Address tmp_address;247      if (tmp_address.SetLoadAddress(m_step_from_insn, &GetTarget())) {248        tmp_address.Dump(s, &m_process, Address::DumpStyleResolvedDescription,249                         Address::DumpStyleLoadAddress);250      } else {251        s->Printf("address 0x%" PRIx64 "", (uint64_t)m_step_from_insn);252      }253 254      // FIXME: find some useful way to present the m_return_id, since there may255      // be multiple copies of the256      // same function on the stack.257 258      s->Printf(" returning to frame at ");259      if (tmp_address.SetLoadAddress(m_return_addr, &GetTarget())) {260        tmp_address.Dump(s, &m_process, Address::DumpStyleResolvedDescription,261                         Address::DumpStyleLoadAddress);262      } else {263        s->Printf("address 0x%" PRIx64 "", (uint64_t)m_return_addr);264      }265 266      if (level == eDescriptionLevelVerbose)267        s->Printf(" using breakpoint site %d", m_return_bp_id);268    }269  }270 271  if (m_stepped_past_frames.empty())272    return;273 274  s->Printf("\n");275  for (StackFrameSP frame_sp : m_stepped_past_frames) {276    s->Printf("Stepped out past: ");277    frame_sp->DumpUsingSettingsFormat(s);278  }279}280 281bool ThreadPlanStepOut::ValidatePlan(Stream *error) {282  if (m_step_out_to_inline_plan_sp)283    return m_step_out_to_inline_plan_sp->ValidatePlan(error);284 285  if (m_step_through_inline_plan_sp)286    return m_step_through_inline_plan_sp->ValidatePlan(error);287 288  if (m_could_not_resolve_hw_bp) {289    if (error)290      error->PutCString(291          "Could not create hardware breakpoint for thread plan.");292    return false;293  }294 295  if (m_return_bp_id == LLDB_INVALID_BREAK_ID) {296    if (error) {297      error->PutCString("Could not create return address breakpoint.");298      if (m_constructor_errors.GetSize() > 0) {299        error->PutCString(" ");300        error->PutCString(m_constructor_errors.GetString());301      }302    }303    return false;304  }305 306  return true;307}308 309bool ThreadPlanStepOut::DoPlanExplainsStop(Event *event_ptr) {310  // If the step out plan is done, then we just need to step through the311  // inlined frame.312  if (m_step_out_to_inline_plan_sp) {313    return m_step_out_to_inline_plan_sp->MischiefManaged();314  } else if (m_step_through_inline_plan_sp) {315    if (m_step_through_inline_plan_sp->MischiefManaged()) {316      CalculateReturnValue();317      SetPlanComplete();318      return true;319    } else320      return false;321  } else if (m_step_out_further_plan_sp) {322    return m_step_out_further_plan_sp->MischiefManaged();323  }324 325  // We don't explain signals or breakpoints (breakpoints that handle stepping326  // in or out will be handled by a child plan.327 328  StopInfoSP stop_info_sp = GetPrivateStopInfo();329  if (stop_info_sp) {330    StopReason reason = stop_info_sp->GetStopReason();331    if (reason == eStopReasonBreakpoint) {332      // If this is OUR breakpoint, we're fine, otherwise we don't know why333      // this happened...334      BreakpointSiteSP site_sp(335          m_process.GetBreakpointSiteList().FindByID(stop_info_sp->GetValue()));336      if (site_sp && site_sp->IsBreakpointAtThisSite(m_return_bp_id)) {337        bool done;338 339        StackID frame_zero_id =340            GetThread().GetStackFrameAtIndex(0)->GetStackID();341 342        if (m_step_out_to_id == frame_zero_id)343          done = true;344        else if (m_step_out_to_id < frame_zero_id) {345          // Either we stepped past the breakpoint, or the stack ID calculation346          // was incorrect and we should probably stop.347          done = true;348        } else {349          done = (m_immediate_step_from_id < frame_zero_id);350        }351 352        if (done) {353          if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) {354            CalculateReturnValue();355            SetPlanComplete();356          }357        }358 359        // If there was only one owner, then we're done.  But if we also hit360        // some user breakpoint on our way out, we should mark ourselves as361        // done, but also not claim to explain the stop, since it is more362        // important to report the user breakpoint than the step out363        // completion.364 365        if (site_sp->GetNumberOfConstituents() == 1)366          return true;367      }368      return false;369    } else if (IsUsuallyUnexplainedStopReason(reason))370      return false;371    else372      return true;373  }374  return true;375}376 377bool ThreadPlanStepOut::ShouldStop(Event *event_ptr) {378  if (IsPlanComplete())379    return true;380 381  bool done = false;382  if (m_step_out_to_inline_plan_sp) {383    if (m_step_out_to_inline_plan_sp->MischiefManaged()) {384      // Now step through the inlined stack we are in:385      if (QueueInlinedStepPlan(true)) {386        // If we can't queue a plan to do this, then just call ourselves done.387        m_step_out_to_inline_plan_sp.reset();388        SetPlanComplete(false);389        return true;390      } else391        done = true;392    } else393      return m_step_out_to_inline_plan_sp->ShouldStop(event_ptr);394  } else if (m_step_through_inline_plan_sp) {395    if (m_step_through_inline_plan_sp->MischiefManaged())396      done = true;397    else398      return m_step_through_inline_plan_sp->ShouldStop(event_ptr);399  } else if (m_step_out_further_plan_sp) {400    if (m_step_out_further_plan_sp->MischiefManaged()) {401      m_step_out_further_plan_sp.reset();402      done = true;403    } else404      return m_step_out_further_plan_sp->ShouldStop(event_ptr);405  }406 407  if (!done) {408    StopInfoSP stop_info_sp = GetPrivateStopInfo();409    if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint) {410      StackID frame_zero_id = GetThread().GetStackFrameAtIndex(0)->GetStackID();411      done = !(frame_zero_id < m_step_out_to_id);412    }413  }414 415  // The normal step out computations think we are done, so all we need to do416  // is consult the ShouldStopHere, and we are done.417 418  if (done) {419    if (InvokeShouldStopHereCallback(eFrameCompareOlder, m_status)) {420      CalculateReturnValue();421      SetPlanComplete();422    } else {423      m_step_out_further_plan_sp =424          QueueStepOutFromHerePlan(m_flags, eFrameCompareOlder, m_status);425      done = false;426    }427  }428 429  return done;430}431 432bool ThreadPlanStepOut::StopOthers() { return m_stop_others; }433 434StateType ThreadPlanStepOut::GetPlanRunState() { return eStateRunning; }435 436bool ThreadPlanStepOut::DoWillResume(StateType resume_state,437                                     bool current_plan) {438  if (m_step_out_to_inline_plan_sp || m_step_through_inline_plan_sp)439    return true;440 441  if (m_return_bp_id == LLDB_INVALID_BREAK_ID)442    return false;443 444  if (current_plan) {445    Breakpoint *return_bp = GetTarget().GetBreakpointByID(m_return_bp_id).get();446    if (return_bp != nullptr)447      return_bp->SetEnabled(true);448  }449  return true;450}451 452bool ThreadPlanStepOut::WillStop() {453  if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {454    Breakpoint *return_bp = GetTarget().GetBreakpointByID(m_return_bp_id).get();455    if (return_bp != nullptr)456      return_bp->SetEnabled(false);457  }458 459  return true;460}461 462bool ThreadPlanStepOut::MischiefManaged() {463  if (IsPlanComplete()) {464    // Did I reach my breakpoint?  If so I'm done.465    //466    // I also check the stack depth, since if we've blown past the breakpoint467    // for some468    // reason and we're now stopping for some other reason altogether, then469    // we're done with this step out operation.470 471    Log *log = GetLog(LLDBLog::Step);472    if (log)473      LLDB_LOGF(log, "Completed step out plan.");474    if (m_return_bp_id != LLDB_INVALID_BREAK_ID) {475      GetTarget().RemoveBreakpointByID(m_return_bp_id);476      m_return_bp_id = LLDB_INVALID_BREAK_ID;477    }478 479    ThreadPlan::MischiefManaged();480    return true;481  } else {482    return false;483  }484}485 486bool ThreadPlanStepOut::QueueInlinedStepPlan(bool queue_now) {487  // Now figure out the range of this inlined block, and set up a "step through488  // range" plan for that.  If we've been provided with a context, then use the489  // block in that context.490  Thread &thread = GetThread();491  StackFrameSP immediate_return_from_sp(thread.GetStackFrameAtIndex(0));492  if (!immediate_return_from_sp)493    return false;494 495  Log *log = GetLog(LLDBLog::Step);496  if (log) {497    StreamString s;498    immediate_return_from_sp->Dump(&s, true, false);499    LLDB_LOGF(log, "Queuing inlined frame to step past: %s.", s.GetData());500  }501 502  Block *from_block = immediate_return_from_sp->GetFrameBlock();503  if (from_block) {504    Block *inlined_block = from_block->GetContainingInlinedBlock();505    if (inlined_block) {506      size_t num_ranges = inlined_block->GetNumRanges();507      AddressRange inline_range;508      if (inlined_block->GetRangeAtIndex(0, inline_range)) {509        SymbolContext inlined_sc;510        inlined_block->CalculateSymbolContext(&inlined_sc);511        inlined_sc.target_sp = GetTarget().shared_from_this();512        RunMode run_mode =513            m_stop_others ? lldb::eOnlyThisThread : lldb::eAllThreads;514        const LazyBool avoid_no_debug = eLazyBoolNo;515 516        m_step_through_inline_plan_sp =517            std::make_shared<ThreadPlanStepOverRange>(518                thread, inline_range, inlined_sc, run_mode, avoid_no_debug);519        ThreadPlanStepOverRange *step_through_inline_plan_ptr =520            static_cast<ThreadPlanStepOverRange *>(521                m_step_through_inline_plan_sp.get());522        m_step_through_inline_plan_sp->SetPrivate(true);523 524        step_through_inline_plan_ptr->SetOkayToDiscard(true);525        StreamString errors;526        if (!step_through_inline_plan_ptr->ValidatePlan(&errors)) {527          // FIXME: Log this failure.528          delete step_through_inline_plan_ptr;529          return false;530        }531 532        for (size_t i = 1; i < num_ranges; i++) {533          if (inlined_block->GetRangeAtIndex(i, inline_range))534            step_through_inline_plan_ptr->AddRange(inline_range);535        }536 537        if (queue_now)538          thread.QueueThreadPlan(m_step_through_inline_plan_sp, false);539        return true;540      }541    }542  }543 544  return false;545}546 547void ThreadPlanStepOut::CalculateReturnValue() {548  if (m_return_valobj_sp)549    return;550 551  if (!m_calculate_return_value)552    return;553 554  if (m_immediate_step_from_function != nullptr) {555    CompilerType return_compiler_type =556        m_immediate_step_from_function->GetCompilerType()557            .GetFunctionReturnType();558    if (return_compiler_type) {559      lldb::ABISP abi_sp = m_process.GetABI();560      if (abi_sp)561        m_return_valobj_sp =562            abi_sp->GetReturnValueObject(GetThread(), return_compiler_type);563    }564  }565}566 567bool ThreadPlanStepOut::IsPlanStale() {568  // If we are still lower on the stack than the frame we are returning to,569  // then there's something for us to do.  Otherwise, we're stale.570 571  StackID frame_zero_id = GetThread().GetStackFrameAtIndex(0)->GetStackID();572  return !(frame_zero_id < m_step_out_to_id);573}574