Commit 3a5ddeda authored by Oliver Stannard's avatar Oliver Stannard
Browse files

[llvm-objdump] Display locations of variables alongside disassembly

This adds the --debug-vars option to llvm-objdump, which prints
locations (registers/memory) of source-level variables alongside the
disassembly based on DWARF info. A vertical line is printed for each
live-range, with a label at the top giving the variable name and
location, and the position and length of the line indicating the program
counter range in which it is valid.

Currently, this only works for object files, not executables or shared
libraries.

Differential revision: https://reviews.llvm.org/D70720
parent 8b409eab
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -119,6 +119,17 @@ OPTIONS

  Demangle symbol names in the output.

.. option:: --debug-vars=<format>

  Print the locations (in registers or memory) of source-level variables
  alongside disassembly. ``format`` may be ``unicode`` or ``ascii``, defaulting
  to ``unicode`` if omitted.

.. option:: --debug-vars-indent=<width>

  Distance to indent the source-level variable display, relative to the start
  of the disassembly. Defaults to 40 characters.

.. option:: -j, --section=<section1[,section2,...]>

  Perform commands on the specified sections only. For Mach-O use
+6 −0
Original line number Diff line number Diff line
@@ -137,6 +137,12 @@ public:
  void print(raw_ostream &OS, const MCRegisterInfo *RegInfo, DWARFUnit *U,
             bool IsEH = false) const;

  /// Print the expression in a format intended to be compact and useful to a
  /// user, but not perfectly unambiguous, or capable of representing every
  /// valid DWARF expression. Returns true if the expression was sucessfully
  /// printed.
  bool printCompact(raw_ostream &OS, const MCRegisterInfo &RegInfo);

  bool verify(DWARFUnit *U);

private:
+10 −4
Original line number Diff line number Diff line
@@ -105,11 +105,17 @@ public:
  /// \param NewCol - The column to move to.
  formatted_raw_ostream &PadToColumn(unsigned NewCol);

  /// getColumn - Return the column number
  unsigned getColumn() { return Position.first; }
  unsigned getColumn() {
    // Calculate current position, taking buffer contents into account.
    ComputePosition(getBufferStart(), GetNumBytesInBuffer());
    return Position.first;
  }

  /// getLine - Return the line number
  unsigned getLine() { return Position.second; }
  unsigned getLine() {
    // Calculate current position, taking buffer contents into account.
    ComputePosition(getBufferStart(), GetNumBytesInBuffer());
    return Position.second;
  }

  raw_ostream &resetColor() override {
    TheStream->resetColor();
+70 −0
Original line number Diff line number Diff line
@@ -351,4 +351,74 @@ bool DWARFExpression::verify(DWARFUnit *U) {
  return true;
}

/// A user-facing string representation of a DWARF expression. This might be an
/// Address expression, in which case it will be implicitly dereferenced, or a
/// Value expression.
struct PrintedExpr {
  enum ExprKind {
    Address,
    Value,
  };
  ExprKind Kind;
  SmallString<16> String;

  PrintedExpr(ExprKind K = Address) : Kind(K) {}
};

static bool printCompactDWARFExpr(raw_ostream &OS, DWARFExpression::iterator I,
                                  const DWARFExpression::iterator E,
                                  const MCRegisterInfo &MRI) {
  SmallVector<PrintedExpr, 4> Stack;

  while (I != E) {
    DWARFExpression::Operation &Op = *I;
    uint8_t Opcode = Op.getCode();
    switch (Opcode) {
    case dwarf::DW_OP_regx: {
      // DW_OP_regx: A register, with the register num given as an operand.
      // Printed as the plain register name.
      uint64_t DwarfRegNum = Op.getRawOperand(0);
      Optional<unsigned> LLVMRegNum = MRI.getLLVMRegNum(DwarfRegNum, false);
      if (!LLVMRegNum) {
        OS << "<unknown register " << DwarfRegNum << ">";
        return false;
      }
      raw_svector_ostream S(Stack.emplace_back(PrintedExpr::Value).String);
      S << MRI.getName(*LLVMRegNum);
      break;
    }
    default:
      if (Opcode >= dwarf::DW_OP_reg0 && Opcode <= dwarf::DW_OP_reg31) {
        // DW_OP_reg<N>: A register, with the register num implied by the
        // opcode. Printed as the plain register name.
        uint64_t DwarfRegNum = Opcode - dwarf::DW_OP_reg0;
        Optional<unsigned> LLVMRegNum = MRI.getLLVMRegNum(DwarfRegNum, false);
        if (!LLVMRegNum) {
          OS << "<unknown register " << DwarfRegNum << ">";
          return false;
        }
        raw_svector_ostream S(Stack.emplace_back(PrintedExpr::Value).String);
        S << MRI.getName(*LLVMRegNum);
      } else {
        // If we hit an unknown operand, we don't know its effect on the stack,
        // so bail out on the whole expression.
        OS << "<unknown op " << dwarf::OperationEncodingString(Opcode) << " ("
           << (int)Opcode << ")>";
        return false;
      }
      break;
    }
    ++I;
  }

  assert(Stack.size() == 1 && "expected one value on stack");
  OS << Stack.front().String;

  return true;
}

bool DWARFExpression::printCompact(raw_ostream &OS, const MCRegisterInfo &MRI) {
  return printCompactDWARFExpr(OS, begin(), end(), MRI);
}

} // namespace llvm
+24 −5
Original line number Diff line number Diff line
@@ -11,7 +11,9 @@
//===----------------------------------------------------------------------===//

#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Locale.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

@@ -19,15 +21,32 @@ using namespace llvm;

/// UpdatePosition - Examine the given char sequence and figure out which
/// column we end up in after output, and how many line breaks are contained.
///
static void UpdatePosition(std::pair<unsigned, unsigned> &Position, const char *Ptr, size_t Size) {
/// This assumes that the input string is well-formed UTF-8, and takes into
/// account unicode characters which render as multiple columns wide.
static void UpdatePosition(std::pair<unsigned, unsigned> &Position,
                           const char *Ptr, size_t Size) {
  unsigned &Column = Position.first;
  unsigned &Line = Position.second;

  // Keep track of the current column and line by scanning the string for
  // special characters
  for (const char *End = Ptr + Size; Ptr != End; ++Ptr) {
    ++Column;
  // special characters.
  unsigned NumBytes;
  for (const char *End = Ptr + Size; Ptr < End; Ptr += NumBytes) {
    NumBytes = getNumBytesForUTF8(*Ptr);

    // The string should never end part way through a multi-byte sequence.
    assert((Ptr + NumBytes) <= End && "Malformed multi-byte sequence");

    int Width = sys::locale::columnWidth(StringRef(Ptr, NumBytes));
    // columnWidth returns -1 for non-printing characters.
    if (Width != -1)
      Column += Width;

    // If this is the final byte of a multi-byte sequence, it can't be any of
    // the special whitespace characters below.
    if (NumBytes > 1)
      continue;

    switch (*Ptr) {
    case '\n':
      Line += 1;
Loading