forked from OSchip/llvm-project
[llvm-pdbutil] Dump inline call site line table annotations
This ports and improves on some existing llvm-readobj -codeview dumping functionality that llvm-pdbutil lacked. Helpful for comparing inline line tables between MSVC and clang. llvm-svn: 362037
This commit is contained in:
parent
c8f2efe065
commit
86bad3f924
|
@ -13,6 +13,7 @@
|
|||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/iterator.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/CodeView.h"
|
||||
|
@ -155,15 +156,19 @@ public:
|
|||
uint32_t RecordOffset;
|
||||
};
|
||||
|
||||
struct BinaryAnnotationIterator {
|
||||
struct AnnotationData {
|
||||
BinaryAnnotationsOpCode OpCode;
|
||||
StringRef Name;
|
||||
uint32_t U1;
|
||||
uint32_t U2;
|
||||
int32_t S1;
|
||||
};
|
||||
struct DecodedAnnotation {
|
||||
StringRef Name;
|
||||
ArrayRef<uint8_t> Bytes;
|
||||
BinaryAnnotationsOpCode OpCode;
|
||||
uint32_t U1 = 0;
|
||||
uint32_t U2 = 0;
|
||||
int32_t S1 = 0;
|
||||
};
|
||||
|
||||
struct BinaryAnnotationIterator
|
||||
: public iterator_facade_base<BinaryAnnotationIterator,
|
||||
std::forward_iterator_tag,
|
||||
DecodedAnnotation> {
|
||||
BinaryAnnotationIterator() = default;
|
||||
BinaryAnnotationIterator(ArrayRef<uint8_t> Annotations) : Data(Annotations) {}
|
||||
BinaryAnnotationIterator(const BinaryAnnotationIterator &Other)
|
||||
|
@ -173,10 +178,6 @@ struct BinaryAnnotationIterator {
|
|||
return Data == Other.Data;
|
||||
}
|
||||
|
||||
bool operator!=(const BinaryAnnotationIterator &Other) const {
|
||||
return !(*this == Other);
|
||||
}
|
||||
|
||||
BinaryAnnotationIterator &operator=(const BinaryAnnotationIterator Other) {
|
||||
Data = Other.Data;
|
||||
return *this;
|
||||
|
@ -193,13 +194,7 @@ struct BinaryAnnotationIterator {
|
|||
return *this;
|
||||
}
|
||||
|
||||
BinaryAnnotationIterator operator++(int) {
|
||||
BinaryAnnotationIterator Orig(*this);
|
||||
++(*this);
|
||||
return Orig;
|
||||
}
|
||||
|
||||
const AnnotationData &operator*() {
|
||||
const DecodedAnnotation &operator*() {
|
||||
ParseCurrentAnnotation();
|
||||
return Current.getValue();
|
||||
}
|
||||
|
@ -241,17 +236,17 @@ private:
|
|||
(ThirdByte << 8) | FourthByte;
|
||||
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
static int32_t DecodeSignedOperand(uint32_t Operand) {
|
||||
if (Operand & 1)
|
||||
return -(Operand >> 1);
|
||||
return Operand >> 1;
|
||||
};
|
||||
}
|
||||
|
||||
static int32_t DecodeSignedOperand(ArrayRef<uint8_t> &Annotations) {
|
||||
return DecodeSignedOperand(GetCompressedAnnotation(Annotations));
|
||||
};
|
||||
}
|
||||
|
||||
bool ParseCurrentAnnotation() {
|
||||
if (Current.hasValue())
|
||||
|
@ -259,7 +254,7 @@ private:
|
|||
|
||||
Next = Data;
|
||||
uint32_t Op = GetCompressedAnnotation(Next);
|
||||
AnnotationData Result;
|
||||
DecodedAnnotation Result;
|
||||
Result.OpCode = static_cast<BinaryAnnotationsOpCode>(Op);
|
||||
switch (Result.OpCode) {
|
||||
case BinaryAnnotationsOpCode::Invalid:
|
||||
|
@ -324,11 +319,12 @@ private:
|
|||
break;
|
||||
}
|
||||
}
|
||||
Result.Bytes = Data.take_front(Data.size() - Next.size());
|
||||
Current = Result;
|
||||
return true;
|
||||
}
|
||||
|
||||
Optional<AnnotationData> Current;
|
||||
Optional<DecodedAnnotation> Current;
|
||||
ArrayRef<uint8_t> Data;
|
||||
ArrayRef<uint8_t> Next;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# RUN: llvm-mc -triple=i686-pc-win32 -filetype=obj < %s | llvm-readobj --codeview | FileCheck %s
|
||||
# RUN: llvm-mc -triple=i686-pc-win32 -filetype=obj %s -o %t.o
|
||||
# RUN: llvm-readobj --codeview %t.o | FileCheck %s
|
||||
# RUN: llvm-objdump -d %t.o | FileCheck %s --check-prefix=ASM
|
||||
# RUN: llvm-pdbutil dump -symbols %t.o | FileCheck %s --check-prefix=PDB
|
||||
.text
|
||||
.def @feat.00;
|
||||
.scl 3;
|
||||
|
@ -43,6 +46,40 @@ Lfunc_begin0:
|
|||
retl
|
||||
Lfunc_end0:
|
||||
|
||||
# Check the disassembly so we have accurate instruction offsets in hex.
|
||||
# ASM-LABEL: ?baz@@YAXXZ:
|
||||
# ASM-NEXT: 0: {{.*}} pushl %eax
|
||||
# ASM-NEXT: 1: {{.*}} addl $6, 0
|
||||
# ASM-NEXT: 8: {{.*}} addl $4, 0
|
||||
# ASM-NEXT: f: {{.*}} movl $1, (%esp)
|
||||
# ASM-NEXT: 16: {{.*}} leal (%esp), %eax
|
||||
# ASM-NEXT: 19: {{.*}} addl %eax, 0
|
||||
# ASM-NEXT: 1f: {{.*}} addl $2, 0
|
||||
# ASM-NEXT: 26: {{.*}} addl $3, 0
|
||||
# ASM-NEXT: 2d: {{.*}} addl $5, 0
|
||||
# ASM-NEXT: 34: {{.*}} addl $7, 0
|
||||
# ASM-NEXT: 3b: {{.*}} popl %eax
|
||||
# ASM-NEXT: 3c: {{.*}} retl
|
||||
|
||||
# PDB: S_GPROC32_ID {{.*}} `baz`
|
||||
# PDB: S_INLINESITE
|
||||
# PDB-NEXT: inlinee = 0x1003 (bar), parent = 0, end = 0
|
||||
# PDB-NEXT: 0B08 code 0x8 (+0x8) line 0 (-0)
|
||||
# PDB-NEXT: 0B27 code 0xF (+0x7) line 1 (+1)
|
||||
# PDB-NEXT: 0602 line 2 (+1)
|
||||
# PDB-NEXT: 031E code 0x2D (+0x1E)
|
||||
# PDB-NEXT: 0407 code end 0x34 (+0x7)
|
||||
# PDB: S_INLINESITE
|
||||
# PDB-NEXT: inlinee = 0x1004 (foo), parent = 0, end = 0
|
||||
# PDB-NEXT: 0B0F code 0xF (+0xF) line 0 (-0)
|
||||
# PDB-NEXT: 0B2A code 0x19 (+0xA) line 1 (+1)
|
||||
# PDB-NEXT: 0B26 code 0x1F (+0x6) line 2 (+1)
|
||||
# PDB-NEXT: 0B27 code 0x26 (+0x7) line 3 (+1)
|
||||
# PDB-NEXT: 0407 code end 0x2D (+0x7)
|
||||
# PEB: S_INLINESITE_END
|
||||
# PEB: S_INLINESITE_END
|
||||
# PEB: S_PROC_ID_END
|
||||
|
||||
.section .debug$T,"dr"
|
||||
.long 4
|
||||
.short 6
|
||||
|
|
|
@ -650,13 +650,85 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
|
|||
|
||||
Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, InlineSiteSym &IS) {
|
||||
AutoIndent Indent(P, 7);
|
||||
auto Bytes = makeArrayRef(IS.AnnotationData);
|
||||
StringRef Annotations(reinterpret_cast<const char *>(Bytes.begin()),
|
||||
Bytes.size());
|
||||
|
||||
P.formatLine("inlinee = {0}, parent = {1}, end = {2}", idIndex(IS.Inlinee),
|
||||
IS.Parent, IS.End);
|
||||
P.formatLine("annotations = {0}", toHex(Annotations));
|
||||
|
||||
// Break down the annotation byte code and calculate code and line offsets.
|
||||
// FIXME: It would be helpful if we could look up the initial file and inlinee
|
||||
// lines offset using the inlinee index above.
|
||||
uint32_t CodeOffset = 0;
|
||||
int32_t LineOffset = 0;
|
||||
for (auto &Annot : IS.annotations()) {
|
||||
P.formatLine(" {0}", fmt_align(toHex(Annot.Bytes), AlignStyle::Left, 9));
|
||||
|
||||
auto formatCodeOffset = [&](uint32_t Delta) {
|
||||
CodeOffset += Delta;
|
||||
P.format(" code 0x{0} (+0x{1})", utohexstr(CodeOffset), utohexstr(Delta));
|
||||
};
|
||||
auto formatCodeLength = [&](uint32_t Length) {
|
||||
// Notably, changing the code length does not affect the code offset.
|
||||
P.format(" code end 0x{0} (+0x{1})", utohexstr(CodeOffset + Length),
|
||||
utohexstr(Length));
|
||||
};
|
||||
auto formatLineOffset = [&](int32_t Delta) {
|
||||
LineOffset += Delta;
|
||||
char Sign = Delta > 0 ? '+' : '-';
|
||||
P.format(" line {0} ({1}{2})", LineOffset, Sign, std::abs(Delta));
|
||||
};
|
||||
|
||||
// Use the opcode to interpret the integer values.
|
||||
switch (Annot.OpCode) {
|
||||
case BinaryAnnotationsOpCode::Invalid:
|
||||
break;
|
||||
case BinaryAnnotationsOpCode::CodeOffset:
|
||||
case BinaryAnnotationsOpCode::ChangeCodeOffset:
|
||||
formatCodeOffset(Annot.U1);
|
||||
break;
|
||||
case BinaryAnnotationsOpCode::ChangeLineOffset:
|
||||
formatLineOffset(Annot.S1);
|
||||
break;
|
||||
case BinaryAnnotationsOpCode::ChangeCodeLength:
|
||||
formatCodeLength(Annot.U1);
|
||||
break;
|
||||
case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset:
|
||||
formatCodeOffset(Annot.U1);
|
||||
formatLineOffset(Annot.S1);
|
||||
break;
|
||||
case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset:
|
||||
formatCodeOffset(Annot.U2);
|
||||
formatCodeLength(Annot.U1);
|
||||
break;
|
||||
|
||||
case BinaryAnnotationsOpCode::ChangeFile: {
|
||||
uint32_t FileOffset = Annot.U1;
|
||||
StringRef Filename = "<unknown>";
|
||||
if (SymGroup) {
|
||||
if (Expected<StringRef> MaybeFile =
|
||||
SymGroup->getNameFromStringTable(FileOffset))
|
||||
Filename = *MaybeFile;
|
||||
else
|
||||
return MaybeFile.takeError();
|
||||
}
|
||||
P.format(" setfile {0} 0x{1}", utohexstr(FileOffset));
|
||||
break;
|
||||
}
|
||||
|
||||
// The rest of these are hard to convince MSVC to emit, so they are not as
|
||||
// well understood.
|
||||
case BinaryAnnotationsOpCode::ChangeCodeOffsetBase:
|
||||
formatCodeOffset(Annot.U1);
|
||||
break;
|
||||
case BinaryAnnotationsOpCode::ChangeLineEndDelta:
|
||||
case BinaryAnnotationsOpCode::ChangeRangeKind:
|
||||
case BinaryAnnotationsOpCode::ChangeColumnStart:
|
||||
case BinaryAnnotationsOpCode::ChangeColumnEnd:
|
||||
P.format(" {0} {1}", Annot.Name, Annot.U1);
|
||||
break;
|
||||
case BinaryAnnotationsOpCode::ChangeColumnEndDelta:
|
||||
P.format(" {0} {1}", Annot.Name, Annot.S1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue