diff options
Diffstat (limited to 'lldb/source/Target/ThreadPlanStepRange.cpp')
| -rw-r--r-- | lldb/source/Target/ThreadPlanStepRange.cpp | 488 | 
1 files changed, 488 insertions, 0 deletions
diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp new file mode 100644 index 000000000000..27513a34eadb --- /dev/null +++ b/lldb/source/Target/ThreadPlanStepRange.cpp @@ -0,0 +1,488 @@ +//===-- ThreadPlanStepRange.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/ThreadPlanStepRange.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanStepRange: Step through a stack range, either stepping over or +// into based on the value of \a type. + +ThreadPlanStepRange::ThreadPlanStepRange(ThreadPlanKind kind, const char *name, +                                         Thread &thread, +                                         const AddressRange &range, +                                         const SymbolContext &addr_context, +                                         lldb::RunMode stop_others, +                                         bool given_ranges_only) +    : ThreadPlan(kind, name, thread, eVoteNoOpinion, eVoteNoOpinion), +      m_addr_context(addr_context), m_address_ranges(), +      m_stop_others(stop_others), m_stack_id(), m_parent_stack_id(), +      m_no_more_plans(false), m_first_run_event(true), m_use_fast_step(false), +      m_given_ranges_only(given_ranges_only) { +  m_use_fast_step = GetTarget().GetUseFastStepping(); +  AddRange(range); +  m_stack_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); +  StackFrameSP parent_stack = m_thread.GetStackFrameAtIndex(1); +  if (parent_stack) +    m_parent_stack_id = parent_stack->GetStackID(); +} + +ThreadPlanStepRange::~ThreadPlanStepRange() { ClearNextBranchBreakpoint(); } + +void ThreadPlanStepRange::DidPush() { +  // See if we can find a "next range" breakpoint: +  SetNextBranchBreakpoint(); +} + +bool ThreadPlanStepRange::ValidatePlan(Stream *error) { +  if (m_could_not_resolve_hw_bp) { +    if (error) +      error->PutCString( +          "Could not create hardware breakpoint for thread plan."); +    return false; +  } +  return true; +} + +Vote ThreadPlanStepRange::ShouldReportStop(Event *event_ptr) { +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + +  const Vote vote = IsPlanComplete() ? eVoteYes : eVoteNo; +  LLDB_LOGF(log, "ThreadPlanStepRange::ShouldReportStop() returning vote %i\n", +            vote); +  return vote; +} + +void ThreadPlanStepRange::AddRange(const AddressRange &new_range) { +  // For now I'm just adding the ranges.  At some point we may want to condense +  // the ranges if they overlap, though I don't think it is likely to be very +  // important. +  m_address_ranges.push_back(new_range); + +  // Fill the slot for this address range with an empty DisassemblerSP in the +  // instruction ranges. I want the indices to match, but I don't want to do +  // the work to disassemble this range if I don't step into it. +  m_instruction_ranges.push_back(DisassemblerSP()); +} + +void ThreadPlanStepRange::DumpRanges(Stream *s) { +  size_t num_ranges = m_address_ranges.size(); +  if (num_ranges == 1) { +    m_address_ranges[0].Dump(s, m_thread.CalculateTarget().get(), +                             Address::DumpStyleLoadAddress); +  } else { +    for (size_t i = 0; i < num_ranges; i++) { +      s->Printf(" %" PRIu64 ": ", uint64_t(i)); +      m_address_ranges[i].Dump(s, m_thread.CalculateTarget().get(), +                               Address::DumpStyleLoadAddress); +    } +  } +} + +bool ThreadPlanStepRange::InRange() { +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); +  bool ret_value = false; + +  lldb::addr_t pc_load_addr = m_thread.GetRegisterContext()->GetPC(); + +  size_t num_ranges = m_address_ranges.size(); +  for (size_t i = 0; i < num_ranges; i++) { +    ret_value = m_address_ranges[i].ContainsLoadAddress( +        pc_load_addr, m_thread.CalculateTarget().get()); +    if (ret_value) +      break; +  } + +  if (!ret_value && !m_given_ranges_only) { +    // See if we've just stepped to another part of the same line number... +    StackFrame *frame = m_thread.GetStackFrameAtIndex(0).get(); + +    SymbolContext new_context( +        frame->GetSymbolContext(eSymbolContextEverything)); +    if (m_addr_context.line_entry.IsValid() && +        new_context.line_entry.IsValid()) { +      if (m_addr_context.line_entry.original_file == +          new_context.line_entry.original_file) { +        if (m_addr_context.line_entry.line == new_context.line_entry.line) { +          m_addr_context = new_context; +          const bool include_inlined_functions = +              GetKind() == eKindStepOverRange; +          AddRange(m_addr_context.line_entry.GetSameLineContiguousAddressRange( +              include_inlined_functions)); +          ret_value = true; +          if (log) { +            StreamString s; +            m_addr_context.line_entry.Dump(&s, m_thread.CalculateTarget().get(), +                                           true, Address::DumpStyleLoadAddress, +                                           Address::DumpStyleLoadAddress, true); + +            LLDB_LOGF( +                log, +                "Step range plan stepped to another range of same line: %s", +                s.GetData()); +          } +        } else if (new_context.line_entry.line == 0) { +          new_context.line_entry.line = m_addr_context.line_entry.line; +          m_addr_context = new_context; +          const bool include_inlined_functions = +              GetKind() == eKindStepOverRange; +          AddRange(m_addr_context.line_entry.GetSameLineContiguousAddressRange( +              include_inlined_functions)); +          ret_value = true; +          if (log) { +            StreamString s; +            m_addr_context.line_entry.Dump(&s, m_thread.CalculateTarget().get(), +                                           true, Address::DumpStyleLoadAddress, +                                           Address::DumpStyleLoadAddress, true); + +            LLDB_LOGF(log, +                      "Step range plan stepped to a range at linenumber 0 " +                      "stepping through that range: %s", +                      s.GetData()); +          } +        } else if (new_context.line_entry.range.GetBaseAddress().GetLoadAddress( +                       m_thread.CalculateTarget().get()) != pc_load_addr) { +          // Another thing that sometimes happens here is that we step out of +          // one line into the MIDDLE of another line.  So far I mostly see +          // this due to bugs in the debug information. But we probably don't +          // want to be in the middle of a line range, so in that case reset +          // the stepping range to the line we've stepped into the middle of +          // and continue. +          m_addr_context = new_context; +          m_address_ranges.clear(); +          AddRange(m_addr_context.line_entry.range); +          ret_value = true; +          if (log) { +            StreamString s; +            m_addr_context.line_entry.Dump(&s, m_thread.CalculateTarget().get(), +                                           true, Address::DumpStyleLoadAddress, +                                           Address::DumpStyleLoadAddress, true); + +            LLDB_LOGF(log, +                      "Step range plan stepped to the middle of new " +                      "line(%d): %s, continuing to clear this line.", +                      new_context.line_entry.line, s.GetData()); +          } +        } +      } +    } +  } + +  if (!ret_value && log) +    LLDB_LOGF(log, "Step range plan out of range to 0x%" PRIx64, pc_load_addr); + +  return ret_value; +} + +bool ThreadPlanStepRange::InSymbol() { +  lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC(); +  if (m_addr_context.function != nullptr) { +    return m_addr_context.function->GetAddressRange().ContainsLoadAddress( +        cur_pc, m_thread.CalculateTarget().get()); +  } else if (m_addr_context.symbol && m_addr_context.symbol->ValueIsAddress()) { +    AddressRange range(m_addr_context.symbol->GetAddressRef(), +                       m_addr_context.symbol->GetByteSize()); +    return range.ContainsLoadAddress(cur_pc, m_thread.CalculateTarget().get()); +  } +  return false; +} + +// FIXME: This should also handle inlining if we aren't going to do inlining in +// the +// main stack. +// +// Ideally we should remember the whole stack frame list, and then compare that +// to the current list. + +lldb::FrameComparison ThreadPlanStepRange::CompareCurrentFrameToStartFrame() { +  FrameComparison frame_order; + +  StackID cur_frame_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); + +  if (cur_frame_id == m_stack_id) { +    frame_order = eFrameCompareEqual; +  } else if (cur_frame_id < m_stack_id) { +    frame_order = eFrameCompareYounger; +  } else { +    StackFrameSP cur_parent_frame = m_thread.GetStackFrameAtIndex(1); +    StackID cur_parent_id; +    if (cur_parent_frame) +      cur_parent_id = cur_parent_frame->GetStackID(); +    if (m_parent_stack_id.IsValid() && cur_parent_id.IsValid() && +        m_parent_stack_id == cur_parent_id) +      frame_order = eFrameCompareSameParent; +    else +      frame_order = eFrameCompareOlder; +  } +  return frame_order; +} + +bool ThreadPlanStepRange::StopOthers() { +  return (m_stop_others == lldb::eOnlyThisThread || +          m_stop_others == lldb::eOnlyDuringStepping); +} + +InstructionList *ThreadPlanStepRange::GetInstructionsForAddress( +    lldb::addr_t addr, size_t &range_index, size_t &insn_offset) { +  size_t num_ranges = m_address_ranges.size(); +  for (size_t i = 0; i < num_ranges; i++) { +    if (m_address_ranges[i].ContainsLoadAddress(addr, &GetTarget())) { +      // Some joker added a zero size range to the stepping range... +      if (m_address_ranges[i].GetByteSize() == 0) +        return nullptr; + +      if (!m_instruction_ranges[i]) { +        // Disassemble the address range given: +        ExecutionContext exe_ctx(m_thread.GetProcess()); +        const char *plugin_name = nullptr; +        const char *flavor = nullptr; +        const bool prefer_file_cache = true; +        m_instruction_ranges[i] = Disassembler::DisassembleRange( +            GetTarget().GetArchitecture(), plugin_name, flavor, exe_ctx, +            m_address_ranges[i], prefer_file_cache); +      } +      if (!m_instruction_ranges[i]) +        return nullptr; +      else { +        // Find where we are in the instruction list as well.  If we aren't at +        // an instruction, return nullptr. In this case, we're probably lost, +        // and shouldn't try to do anything fancy. + +        insn_offset = +            m_instruction_ranges[i] +                ->GetInstructionList() +                .GetIndexOfInstructionAtLoadAddress(addr, GetTarget()); +        if (insn_offset == UINT32_MAX) +          return nullptr; +        else { +          range_index = i; +          return &m_instruction_ranges[i]->GetInstructionList(); +        } +      } +    } +  } +  return nullptr; +} + +void ThreadPlanStepRange::ClearNextBranchBreakpoint() { +  if (m_next_branch_bp_sp) { +    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); +    LLDB_LOGF(log, "Removing next branch breakpoint: %d.", +              m_next_branch_bp_sp->GetID()); +    GetTarget().RemoveBreakpointByID(m_next_branch_bp_sp->GetID()); +    m_next_branch_bp_sp.reset(); +    m_could_not_resolve_hw_bp = false; +  } +} + +bool ThreadPlanStepRange::SetNextBranchBreakpoint() { +  if (m_next_branch_bp_sp) +    return true; + +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); +  // Stepping through ranges using breakpoints doesn't work yet, but with this +  // off we fall back to instruction single stepping. +  if (!m_use_fast_step) +    return false; + +  lldb::addr_t cur_addr = GetThread().GetRegisterContext()->GetPC(); +  // Find the current address in our address ranges, and fetch the disassembly +  // if we haven't already: +  size_t pc_index; +  size_t range_index; +  InstructionList *instructions = +      GetInstructionsForAddress(cur_addr, range_index, pc_index); +  if (instructions == nullptr) +    return false; +  else { +    Target &target = GetThread().GetProcess()->GetTarget(); +    const bool ignore_calls = GetKind() == eKindStepOverRange; +    uint32_t branch_index = +        instructions->GetIndexOfNextBranchInstruction(pc_index, target, +                                                      ignore_calls); + +    Address run_to_address; + +    // If we didn't find a branch, run to the end of the range. +    if (branch_index == UINT32_MAX) { +      uint32_t last_index = instructions->GetSize() - 1; +      if (last_index - pc_index > 1) { +        InstructionSP last_inst = +            instructions->GetInstructionAtIndex(last_index); +        size_t last_inst_size = last_inst->GetOpcode().GetByteSize(); +        run_to_address = last_inst->GetAddress(); +        run_to_address.Slide(last_inst_size); +      } +    } else if (branch_index - pc_index > 1) { +      run_to_address = +          instructions->GetInstructionAtIndex(branch_index)->GetAddress(); +    } + +    if (run_to_address.IsValid()) { +      const bool is_internal = true; +      m_next_branch_bp_sp = +          GetTarget().CreateBreakpoint(run_to_address, is_internal, false); +      if (m_next_branch_bp_sp) { + +        if (m_next_branch_bp_sp->IsHardware() && +            !m_next_branch_bp_sp->HasResolvedLocations()) +          m_could_not_resolve_hw_bp = true; + +        if (log) { +          lldb::break_id_t bp_site_id = LLDB_INVALID_BREAK_ID; +          BreakpointLocationSP bp_loc = +              m_next_branch_bp_sp->GetLocationAtIndex(0); +          if (bp_loc) { +            BreakpointSiteSP bp_site = bp_loc->GetBreakpointSite(); +            if (bp_site) { +              bp_site_id = bp_site->GetID(); +            } +          } +          LLDB_LOGF(log, +                    "ThreadPlanStepRange::SetNextBranchBreakpoint - Setting " +                    "breakpoint %d (site %d) to run to address 0x%" PRIx64, +                    m_next_branch_bp_sp->GetID(), bp_site_id, +                    run_to_address.GetLoadAddress( +                        &m_thread.GetProcess()->GetTarget())); +        } + +        m_next_branch_bp_sp->SetThreadID(m_thread.GetID()); +        m_next_branch_bp_sp->SetBreakpointKind("next-branch-location"); + +        return true; +      } else +        return false; +    } +  } +  return false; +} + +bool ThreadPlanStepRange::NextRangeBreakpointExplainsStop( +    lldb::StopInfoSP stop_info_sp) { +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); +  if (!m_next_branch_bp_sp) +    return false; + +  break_id_t bp_site_id = stop_info_sp->GetValue(); +  BreakpointSiteSP bp_site_sp = +      m_thread.GetProcess()->GetBreakpointSiteList().FindByID(bp_site_id); +  if (!bp_site_sp) +    return false; +  else if (!bp_site_sp->IsBreakpointAtThisSite(m_next_branch_bp_sp->GetID())) +    return false; +  else { +    // If we've hit the next branch breakpoint, then clear it. +    size_t num_owners = bp_site_sp->GetNumberOfOwners(); +    bool explains_stop = true; +    // If all the owners are internal, then we are probably just stepping over +    // this range from multiple threads, or multiple frames, so we want to +    // continue.  If one is not internal, then we should not explain the stop, +    // and let the user breakpoint handle the stop. +    for (size_t i = 0; i < num_owners; i++) { +      if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal()) { +        explains_stop = false; +        break; +      } +    } +    LLDB_LOGF(log, +              "ThreadPlanStepRange::NextRangeBreakpointExplainsStop - Hit " +              "next range breakpoint which has %" PRIu64 +              " owners - explains stop: %u.", +              (uint64_t)num_owners, explains_stop); +    ClearNextBranchBreakpoint(); +    return explains_stop; +  } +} + +bool ThreadPlanStepRange::WillStop() { return true; } + +StateType ThreadPlanStepRange::GetPlanRunState() { +  if (m_next_branch_bp_sp) +    return eStateRunning; +  else +    return eStateStepping; +} + +bool ThreadPlanStepRange::MischiefManaged() { +  // If we have pushed some plans between ShouldStop & MischiefManaged, then +  // we're not done... +  // I do this check first because we might have stepped somewhere that will +  // fool InRange into +  // thinking it needs to step past the end of that line.  This happens, for +  // instance, when stepping over inlined code that is in the middle of the +  // current line. + +  if (!m_no_more_plans) +    return false; + +  bool done = true; +  if (!IsPlanComplete()) { +    if (InRange()) { +      done = false; +    } else { +      FrameComparison frame_order = CompareCurrentFrameToStartFrame(); +      done = (frame_order != eFrameCompareOlder) ? m_no_more_plans : true; +    } +  } + +  if (done) { +    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); +    LLDB_LOGF(log, "Completed step through range plan."); +    ClearNextBranchBreakpoint(); +    ThreadPlan::MischiefManaged(); +    return true; +  } else { +    return false; +  } +} + +bool ThreadPlanStepRange::IsPlanStale() { +  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); +  FrameComparison frame_order = CompareCurrentFrameToStartFrame(); + +  if (frame_order == eFrameCompareOlder) { +    if (log) { +      LLDB_LOGF(log, "ThreadPlanStepRange::IsPlanStale returning true, we've " +                     "stepped out."); +    } +    return true; +  } else if (frame_order == eFrameCompareEqual && InSymbol()) { +    // If we are not in a place we should step through, we've gotten stale. One +    // tricky bit here is that some stubs don't push a frame, so we should. +    // check that we are in the same symbol. +    if (!InRange()) { +      // Set plan Complete when we reach next instruction just after the range +      lldb::addr_t addr = m_thread.GetRegisterContext()->GetPC() - 1; +      size_t num_ranges = m_address_ranges.size(); +      for (size_t i = 0; i < num_ranges; i++) { +        bool in_range = m_address_ranges[i].ContainsLoadAddress( +            addr, m_thread.CalculateTarget().get()); +        if (in_range) { +          SetPlanComplete(); +        } +      } +      return true; +    } +  } +  return false; +}  | 
