From 524187c8cc0ba55bc3b8b3abb22a1652f677afd0 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Wed, 29 May 2019 21:26:25 +0000 Subject: [PATCH] [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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@362037 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/DebugInfo/CodeView/SymbolRecord.h | 44 +++++++------- test/MC/COFF/cv-inline-linetable.s | 39 +++++++++++- tools/llvm-pdbutil/MinimalSymbolDumper.cpp | 82 ++++++++++++++++++++++++-- 3 files changed, 135 insertions(+), 30 deletions(-) diff --git a/include/llvm/DebugInfo/CodeView/SymbolRecord.h b/include/llvm/DebugInfo/CodeView/SymbolRecord.h index b98ada221a4..5e9a7432b9b 100644 --- a/include/llvm/DebugInfo/CodeView/SymbolRecord.h +++ b/include/llvm/DebugInfo/CodeView/SymbolRecord.h @@ -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 Bytes; + BinaryAnnotationsOpCode OpCode; + uint32_t U1 = 0; + uint32_t U2 = 0; + int32_t S1 = 0; +}; +struct BinaryAnnotationIterator + : public iterator_facade_base { BinaryAnnotationIterator() = default; BinaryAnnotationIterator(ArrayRef 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 &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(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 Current; + Optional Current; ArrayRef Data; ArrayRef Next; }; diff --git a/test/MC/COFF/cv-inline-linetable.s b/test/MC/COFF/cv-inline-linetable.s index 460c2e9ba2a..f226d6fe21f 100644 --- a/test/MC/COFF/cv-inline-linetable.s +++ b/test/MC/COFF/cv-inline-linetable.s @@ -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 diff --git a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp index 50d70c070d2..d3c3f3da9c0 100644 --- a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -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(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 = ""; + if (SymGroup) { + if (Expected 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(); } -- 2.11.0