diff options
Diffstat (limited to 'lldb/source/API/SBInstruction.cpp')
| -rw-r--r-- | lldb/source/API/SBInstruction.cpp | 383 | 
1 files changed, 383 insertions, 0 deletions
| diff --git a/lldb/source/API/SBInstruction.cpp b/lldb/source/API/SBInstruction.cpp new file mode 100644 index 000000000000..a9ef9fb59d24 --- /dev/null +++ b/lldb/source/API/SBInstruction.cpp @@ -0,0 +1,383 @@ +//===-- SBInstruction.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/API/SBInstruction.h" +#include "SBReproducerPrivate.h" + +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBFile.h" + +#include "lldb/API/SBInstruction.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBTarget.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/DataExtractor.h" + +#include <memory> + +// We recently fixed a leak in one of the Instruction subclasses where the +// instruction will only hold a weak reference to the disassembler to avoid a +// cycle that was keeping both objects alive (leak) and we need the +// InstructionImpl class to make sure our public API behaves as users would +// expect. Calls in our public API allow clients to do things like: +// +// 1  lldb::SBInstruction inst; +// 2  inst = target.ReadInstructions(pc, 1).GetInstructionAtIndex(0) +// 3  if (inst.DoesBranch()) +// 4  ... +// +// There was a temporary lldb::DisassemblerSP object created in the +// SBInstructionList that was returned by lldb.target.ReadInstructions() that +// will go away after line 2 but the "inst" object should be able to still +// answer questions about itself. So we make sure that any SBInstruction +// objects that are given out have a strong reference to the disassembler and +// the instruction so that the object can live and successfully respond to all +// queries. +class InstructionImpl { +public: +  InstructionImpl(const lldb::DisassemblerSP &disasm_sp, +                  const lldb::InstructionSP &inst_sp) +      : m_disasm_sp(disasm_sp), m_inst_sp(inst_sp) {} + +  lldb::InstructionSP GetSP() const { return m_inst_sp; } + +  bool IsValid() const { return (bool)m_inst_sp; } + +protected: +  lldb::DisassemblerSP m_disasm_sp; // Can be empty/invalid +  lldb::InstructionSP m_inst_sp; +}; + +using namespace lldb; +using namespace lldb_private; + +SBInstruction::SBInstruction() : m_opaque_sp() { +  LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBInstruction); +} + +SBInstruction::SBInstruction(const lldb::DisassemblerSP &disasm_sp, +                             const lldb::InstructionSP &inst_sp) +    : m_opaque_sp(new InstructionImpl(disasm_sp, inst_sp)) {} + +SBInstruction::SBInstruction(const SBInstruction &rhs) +    : m_opaque_sp(rhs.m_opaque_sp) { +  LLDB_RECORD_CONSTRUCTOR(SBInstruction, (const lldb::SBInstruction &), rhs); +} + +const SBInstruction &SBInstruction::operator=(const SBInstruction &rhs) { +  LLDB_RECORD_METHOD(const lldb::SBInstruction &, +                     SBInstruction, operator=,(const lldb::SBInstruction &), +                     rhs); + +  if (this != &rhs) +    m_opaque_sp = rhs.m_opaque_sp; +  return LLDB_RECORD_RESULT(*this); +} + +SBInstruction::~SBInstruction() {} + +bool SBInstruction::IsValid() { +  LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, IsValid); +  return this->operator bool(); +} +SBInstruction::operator bool() const { +  LLDB_RECORD_METHOD_CONST_NO_ARGS(bool, SBInstruction, operator bool); + +  return m_opaque_sp && m_opaque_sp->IsValid(); +} + +SBAddress SBInstruction::GetAddress() { +  LLDB_RECORD_METHOD_NO_ARGS(lldb::SBAddress, SBInstruction, GetAddress); + +  SBAddress sb_addr; +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp && inst_sp->GetAddress().IsValid()) +    sb_addr.SetAddress(&inst_sp->GetAddress()); +  return LLDB_RECORD_RESULT(sb_addr); +} + +const char *SBInstruction::GetMnemonic(SBTarget target) { +  LLDB_RECORD_METHOD(const char *, SBInstruction, GetMnemonic, (lldb::SBTarget), +                     target); + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) { +    ExecutionContext exe_ctx; +    TargetSP target_sp(target.GetSP()); +    std::unique_lock<std::recursive_mutex> lock; +    if (target_sp) { +      lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex()); + +      target_sp->CalculateExecutionContext(exe_ctx); +      exe_ctx.SetProcessSP(target_sp->GetProcessSP()); +    } +    return inst_sp->GetMnemonic(&exe_ctx); +  } +  return nullptr; +} + +const char *SBInstruction::GetOperands(SBTarget target) { +  LLDB_RECORD_METHOD(const char *, SBInstruction, GetOperands, (lldb::SBTarget), +                     target); + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) { +    ExecutionContext exe_ctx; +    TargetSP target_sp(target.GetSP()); +    std::unique_lock<std::recursive_mutex> lock; +    if (target_sp) { +      lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex()); + +      target_sp->CalculateExecutionContext(exe_ctx); +      exe_ctx.SetProcessSP(target_sp->GetProcessSP()); +    } +    return inst_sp->GetOperands(&exe_ctx); +  } +  return nullptr; +} + +const char *SBInstruction::GetComment(SBTarget target) { +  LLDB_RECORD_METHOD(const char *, SBInstruction, GetComment, (lldb::SBTarget), +                     target); + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) { +    ExecutionContext exe_ctx; +    TargetSP target_sp(target.GetSP()); +    std::unique_lock<std::recursive_mutex> lock; +    if (target_sp) { +      lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex()); + +      target_sp->CalculateExecutionContext(exe_ctx); +      exe_ctx.SetProcessSP(target_sp->GetProcessSP()); +    } +    return inst_sp->GetComment(&exe_ctx); +  } +  return nullptr; +} + +size_t SBInstruction::GetByteSize() { +  LLDB_RECORD_METHOD_NO_ARGS(size_t, SBInstruction, GetByteSize); + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) +    return inst_sp->GetOpcode().GetByteSize(); +  return 0; +} + +SBData SBInstruction::GetData(SBTarget target) { +  LLDB_RECORD_METHOD(lldb::SBData, SBInstruction, GetData, (lldb::SBTarget), +                     target); + +  lldb::SBData sb_data; +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) { +    DataExtractorSP data_extractor_sp(new DataExtractor()); +    if (inst_sp->GetData(*data_extractor_sp)) { +      sb_data.SetOpaque(data_extractor_sp); +    } +  } +  return LLDB_RECORD_RESULT(sb_data); +} + +bool SBInstruction::DoesBranch() { +  LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, DoesBranch); + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) +    return inst_sp->DoesBranch(); +  return false; +} + +bool SBInstruction::HasDelaySlot() { +  LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, HasDelaySlot); + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) +    return inst_sp->HasDelaySlot(); +  return false; +} + +bool SBInstruction::CanSetBreakpoint() { +  LLDB_RECORD_METHOD_NO_ARGS(bool, SBInstruction, CanSetBreakpoint); + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) +    return inst_sp->CanSetBreakpoint(); +  return false; +} + +lldb::InstructionSP SBInstruction::GetOpaque() { +  if (m_opaque_sp) +    return m_opaque_sp->GetSP(); +  else +    return lldb::InstructionSP(); +} + +void SBInstruction::SetOpaque(const lldb::DisassemblerSP &disasm_sp, +                              const lldb::InstructionSP &inst_sp) { +  m_opaque_sp = std::make_shared<InstructionImpl>(disasm_sp, inst_sp); +} + +bool SBInstruction::GetDescription(lldb::SBStream &s) { +  LLDB_RECORD_METHOD(bool, SBInstruction, GetDescription, (lldb::SBStream &), +                     s); + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) { +    SymbolContext sc; +    const Address &addr = inst_sp->GetAddress(); +    ModuleSP module_sp(addr.GetModule()); +    if (module_sp) +      module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, +                                                sc); +    // Use the "ref()" instead of the "get()" accessor in case the SBStream +    // didn't have a stream already created, one will get created... +    FormatEntity::Entry format; +    FormatEntity::Parse("${addr}: ", format); +    inst_sp->Dump(&s.ref(), 0, true, false, nullptr, &sc, nullptr, &format, 0); +    return true; +  } +  return false; +} + +void SBInstruction::Print(FILE *outp) { +  LLDB_RECORD_METHOD(void, SBInstruction, Print, (FILE *), outp); +  FileSP out = std::make_shared<NativeFile>(outp, /*take_ownership=*/false); +  Print(out); +} + +void SBInstruction::Print(SBFile out) { +  LLDB_RECORD_METHOD(void, SBInstruction, Print, (SBFile), out); +  Print(out.m_opaque_sp); +} + +void SBInstruction::Print(FileSP out_sp) { +  LLDB_RECORD_METHOD(void, SBInstruction, Print, (FileSP), out_sp); + +  if (!out_sp || !out_sp->IsValid()) +    return; + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) { +    SymbolContext sc; +    const Address &addr = inst_sp->GetAddress(); +    ModuleSP module_sp(addr.GetModule()); +    if (module_sp) +      module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, +                                                sc); +    StreamFile out_stream(out_sp); +    FormatEntity::Entry format; +    FormatEntity::Parse("${addr}: ", format); +    inst_sp->Dump(&out_stream, 0, true, false, nullptr, &sc, nullptr, &format, +                  0); +  } +} + +bool SBInstruction::EmulateWithFrame(lldb::SBFrame &frame, +                                     uint32_t evaluate_options) { +  LLDB_RECORD_METHOD(bool, SBInstruction, EmulateWithFrame, +                     (lldb::SBFrame &, uint32_t), frame, evaluate_options); + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) { +    lldb::StackFrameSP frame_sp(frame.GetFrameSP()); + +    if (frame_sp) { +      lldb_private::ExecutionContext exe_ctx; +      frame_sp->CalculateExecutionContext(exe_ctx); +      lldb_private::Target *target = exe_ctx.GetTargetPtr(); +      lldb_private::ArchSpec arch = target->GetArchitecture(); + +      return inst_sp->Emulate( +          arch, evaluate_options, (void *)frame_sp.get(), +          &lldb_private::EmulateInstruction::ReadMemoryFrame, +          &lldb_private::EmulateInstruction::WriteMemoryFrame, +          &lldb_private::EmulateInstruction::ReadRegisterFrame, +          &lldb_private::EmulateInstruction::WriteRegisterFrame); +    } +  } +  return false; +} + +bool SBInstruction::DumpEmulation(const char *triple) { +  LLDB_RECORD_METHOD(bool, SBInstruction, DumpEmulation, (const char *), +                     triple); + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp && triple) { +    return inst_sp->DumpEmulation(HostInfo::GetAugmentedArchSpec(triple)); +  } +  return false; +} + +bool SBInstruction::TestEmulation(lldb::SBStream &output_stream, +                                  const char *test_file) { +  LLDB_RECORD_METHOD(bool, SBInstruction, TestEmulation, +                     (lldb::SBStream &, const char *), output_stream, +                     test_file); + +  if (!m_opaque_sp) +    SetOpaque(lldb::DisassemblerSP(), +              lldb::InstructionSP(new PseudoInstruction())); + +  lldb::InstructionSP inst_sp(GetOpaque()); +  if (inst_sp) +    return inst_sp->TestEmulation(output_stream.get(), test_file); +  return false; +} + +namespace lldb_private { +namespace repro { + +template <> +void RegisterMethods<SBInstruction>(Registry &R) { +  LLDB_REGISTER_CONSTRUCTOR(SBInstruction, ()); +  LLDB_REGISTER_CONSTRUCTOR(SBInstruction, (const lldb::SBInstruction &)); +  LLDB_REGISTER_METHOD( +      const lldb::SBInstruction &, +      SBInstruction, operator=,(const lldb::SBInstruction &)); +  LLDB_REGISTER_METHOD(bool, SBInstruction, IsValid, ()); +  LLDB_REGISTER_METHOD_CONST(bool, SBInstruction, operator bool, ()); +  LLDB_REGISTER_METHOD(lldb::SBAddress, SBInstruction, GetAddress, ()); +  LLDB_REGISTER_METHOD(const char *, SBInstruction, GetMnemonic, +                       (lldb::SBTarget)); +  LLDB_REGISTER_METHOD(const char *, SBInstruction, GetOperands, +                       (lldb::SBTarget)); +  LLDB_REGISTER_METHOD(const char *, SBInstruction, GetComment, +                       (lldb::SBTarget)); +  LLDB_REGISTER_METHOD(size_t, SBInstruction, GetByteSize, ()); +  LLDB_REGISTER_METHOD(lldb::SBData, SBInstruction, GetData, +                       (lldb::SBTarget)); +  LLDB_REGISTER_METHOD(bool, SBInstruction, DoesBranch, ()); +  LLDB_REGISTER_METHOD(bool, SBInstruction, HasDelaySlot, ()); +  LLDB_REGISTER_METHOD(bool, SBInstruction, CanSetBreakpoint, ()); +  LLDB_REGISTER_METHOD(bool, SBInstruction, GetDescription, +                       (lldb::SBStream &)); +  LLDB_REGISTER_METHOD(void, SBInstruction, Print, (FILE *)); +  LLDB_REGISTER_METHOD(void, SBInstruction, Print, (SBFile)); +  LLDB_REGISTER_METHOD(void, SBInstruction, Print, (FileSP)); +  LLDB_REGISTER_METHOD(bool, SBInstruction, EmulateWithFrame, +                       (lldb::SBFrame &, uint32_t)); +  LLDB_REGISTER_METHOD(bool, SBInstruction, DumpEmulation, (const char *)); +  LLDB_REGISTER_METHOD(bool, SBInstruction, TestEmulation, +                       (lldb::SBStream &, const char *)); +} + +} +} | 
