Skip to content

Commit

Permalink
[llvm-objdump] Display locations of variables alongside disassembly
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ostannard committed Mar 16, 2020
1 parent 8b409ea commit 3a5dded
Show file tree
Hide file tree
Showing 14 changed files with 2,806 additions and 75 deletions.
11 changes: 11 additions & 0 deletions llvm/docs/CommandGuide/llvm-objdump.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ class DWARFExpression {
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:
Expand Down
14 changes: 10 additions & 4 deletions llvm/include/llvm/Support/FormattedStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,17 @@ class formatted_raw_ostream : public raw_ostream {
/// \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();
Expand Down
70 changes: 70 additions & 0 deletions llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
29 changes: 24 additions & 5 deletions llvm/lib/Support/FormattedStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,42 @@
//===----------------------------------------------------------------------===//

#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>

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;
Expand Down
10 changes: 10 additions & 0 deletions llvm/test/tools/llvm-objdump/ARM/Inputs/debug.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
int foo(int a, int b, int c) {
int x = a + b;
int y = x + c;
return y;
}

int bar(int a) {
a++;
return a;
}
3 changes: 3 additions & 0 deletions llvm/test/tools/llvm-objdump/ARM/Inputs/wide-char.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int foo(int *) {
return *;
}
Loading

0 comments on commit 3a5dded

Please sign in to comment.