llvm.org GIT mirror llvm / 0ebcf2f
[PDB] Add an explain subcommand. When investigating various things, we often have a file offset and what to know what's in the PDB at that address. For example we may be doing a binary comparison of two LLD-generated PDBs to look for sources of non-determinism, or we may wish to compare an LLD-generated PDB with a Microsoft generated PDB for sources of byte-for-byte incompatibility. In these cases, we can do a binary diff of the two files, and once we find a mismatched byte we can use explain to figure out what that byte is, immediately honining in on the problem. This patch implements this by trying to narrow the meaning of a particular file offset down as much as possible. Differential Revision: https://reviews.llvm.org/D44959 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@328799 91177308-0d34-0410-b5e6-96231b3b80d8 Zachary Turner 1 year, 5 months ago
6 changed file(s) with 371 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 ; RUN: llvm-pdbutil explain -offset=0 %p/Inputs/InjectedSource.pdb \
1 ; RUN: | FileCheck --check-prefix=ZERO %s
2 ; RUN: llvm-pdbutil explain -offset=40 %p/Inputs/InjectedSource.pdb \
3 ; RUN: | FileCheck --check-prefix=FORTY %s
4 ; RUN: llvm-pdbutil explain -offset=60 %p/Inputs/InjectedSource.pdb \
5 ; RUN: | FileCheck --check-prefix=SIXTY %s
6
7 ; RUN: llvm-pdbutil explain -offset=0x1000 %p/Inputs/InjectedSource.pdb \
8 ; RUN: | FileCheck --check-prefix=FPM1 %s
9 ; RUN: llvm-pdbutil explain -offset=0x1100 %p/Inputs/InjectedSource.pdb \
10 ; RUN: | FileCheck --check-prefix=EXTRANEOUSFPM %s
11 ; RUN: llvm-pdbutil explain -offset=0x2000 %p/Inputs/InjectedSource.pdb \
12 ; RUN: | FileCheck --check-prefix=FPM2 %s
13
14 ; RUN: llvm-pdbutil explain -offset=0x3000 %p/Inputs/InjectedSource.pdb \
15 ; RUN: | FileCheck --check-prefix=UNALLOCATED %s
16
17 ; RUN: llvm-pdbutil explain -offset=0x7000 %p/Inputs/InjectedSource.pdb \
18 ; RUN: | FileCheck --check-prefix=STREAM %s
19
20 ; RUN: llvm-pdbutil explain -offset=0x1A000 %p/Inputs/InjectedSource.pdb \
21 ; RUN: | FileCheck --check-prefix=STREAMDIR %s
22
23 ; RUN: llvm-pdbutil explain -offset=0x1B000 %p/Inputs/InjectedSource.pdb \
24 ; RUN: | FileCheck --check-prefix=DIRBLOCKLIST %s
25
26 ; RUN: llvm-pdbutil explain -offset=0x1D000 %p/Inputs/InjectedSource.pdb \
27 ; RUN: | FileCheck --check-prefix=INVALIDFILEOFFSET %s
28
29 ; RUN: llvm-pdbutil explain -offset=0xA100 %p/Inputs/InjectedSource.pdb \
30 ; RUN: | FileCheck --check-prefix=UNUSED %s
31
32
33 ZERO: Block:Offset = 0:0000.
34 ZERO-NEXT: Address is in block 0 (allocated).
35 ZERO-NEXT: This corresponds to offset 0 of MSF super block,
36 ZERO-NEXT: which is part of the MSF file magic.
37
38 FORTY: Block:Offset = 0:0028.
39 FORTY-NEXT: Address is in block 0 (allocated).
40 FORTY-NEXT: This corresponds to offset 40 of MSF super block,
41 FORTY-NEXT: which contains the number of bytes in the stream directory.
42
43 SIXTY: Block:Offset = 0:003C.
44 SIXTY-NEXT: Address is in block 0 (allocated).
45 SIXTY-NEXT: This corresponds to offset 60 of MSF super block,
46 SIXTY-NEXT: which is outside the range of valid data for the super block.
47
48 FPM1: Block:Offset = 1:0000.
49 FPM1-NEXT: Address is in block 1 (allocated).
50 FPM1-NEXT: Address is in FPM1 (Alt FPM)
51 FPM1-NEXT: Address describes the allocation status of blocks [0,8)
52
53 EXTRANEOUSFPM: Block:Offset = 1:0100.
54 EXTRANEOUSFPM-NEXT: Address is in block 1 (allocated).
55 EXTRANEOUSFPM-NEXT: Address is in FPM1 (Alt FPM)
56 EXTRANEOUSFPM-NEXT: Address is in extraneous FPM space.
57
58 FPM2: Block:Offset = 2:0000.
59 FPM2-NEXT: Address is in block 2 (allocated).
60 FPM2-NEXT: Address is in FPM2 (Main FPM)
61 FPM2-NEXT: Address describes the allocation status of blocks [0,8)
62
63 UNALLOCATED: Block:Offset = 3:0000.
64 UNALLOCATED-NEXT: Address is in block 3 (unallocated).
65
66 STREAM: Block:Offset = 7:0000.
67 STREAM-NEXT: Address is in block 7 (allocated).
68 STREAM-NEXT: Address is at offset 0/684 of Stream 12 (Module "* Linker *").
69
70 STREAMDIR: Block:Offset = 1A:0000.
71 STREAMDIR-NEXT: Address is in block 26 (allocated).
72 STREAMDIR-NEXT: Address is at offset 0/156 of Stream Directory.
73
74 DIRBLOCKLIST: Block:Offset = 1B:0000.
75 DIRBLOCKLIST-NEXT: Address is in block 27 (allocated).
76 DIRBLOCKLIST-NEXT: Address is at offset 0 of the directory block list
77
78 INVALIDFILEOFFSET: Address 118784 is not in the file (file size = 118784).
79
80 UNUSED: Block:Offset = A:0100.
81 UNUSED-NEXT: Address is in block 10 (allocated).
82 UNUSED-NEXT: Address is at offset 256/120 of Stream 11 (Section Header Data) in unused space.
1111 Analyze.cpp
1212 BytesOutputStyle.cpp
1313 DumpOutputStyle.cpp
14 ExplainOutputStyle.cpp
1415 InputFile.cpp
1516 llvm-pdbutil.cpp
1617 FormatUtil.cpp
0 //===- ExplainOutputStyle.cpp --------------------------------- *- C++ --*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "ExplainOutputStyle.h"
10
11 #include "FormatUtil.h"
12 #include "StreamUtil.h"
13 #include "llvm-pdbutil.h"
14
15 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
16 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
17
18 using namespace llvm;
19 using namespace llvm::codeview;
20 using namespace llvm::msf;
21 using namespace llvm::pdb;
22
23 ExplainOutputStyle::ExplainOutputStyle(PDBFile &File, uint64_t FileOffset)
24 : File(File), FileOffset(FileOffset),
25 BlockIndex(FileOffset / File.getBlockSize()),
26 OffsetInBlock(FileOffset - BlockIndex * File.getBlockSize()),
27 P(2, false, outs()) {}
28
29 Error ExplainOutputStyle::dump() {
30 P.formatLine("Explaining file offset {0} of file '{1}'.", FileOffset,
31 File.getFilePath());
32
33 bool IsAllocated = explainBlockStatus();
34 if (!IsAllocated)
35 return Error::success();
36
37 AutoIndent Indent(P);
38 if (isSuperBlock())
39 explainSuperBlockOffset();
40 else if (isFpmBlock())
41 explainFpmBlockOffset();
42 else if (isBlockMapBlock())
43 explainBlockMapOffset();
44 else if (isStreamDirectoryBlock())
45 explainStreamDirectoryOffset();
46 else if (auto Index = getBlockStreamIndex())
47 explainStreamOffset(*Index);
48 else
49 explainUnknownBlock();
50 return Error::success();
51 }
52
53 bool ExplainOutputStyle::isSuperBlock() const { return BlockIndex == 0; }
54
55 bool ExplainOutputStyle::isFpm1() const {
56 return ((BlockIndex - 1) % File.getBlockSize() == 0);
57 }
58 bool ExplainOutputStyle::isFpm2() const {
59 return ((BlockIndex - 2) % File.getBlockSize() == 0);
60 }
61
62 bool ExplainOutputStyle::isFpmBlock() const { return isFpm1() || isFpm2(); }
63
64 bool ExplainOutputStyle::isBlockMapBlock() const {
65 return BlockIndex == File.getBlockMapIndex();
66 }
67
68 bool ExplainOutputStyle::isStreamDirectoryBlock() const {
69 const auto &Layout = File.getMsfLayout();
70 return llvm::is_contained(Layout.DirectoryBlocks, BlockIndex);
71 }
72
73 Optional ExplainOutputStyle::getBlockStreamIndex() const {
74 const auto &Layout = File.getMsfLayout();
75 for (const auto &Entry : enumerate(Layout.StreamMap)) {
76 if (!llvm::is_contained(Entry.value(), BlockIndex))
77 continue;
78 return Entry.index();
79 }
80 return None;
81 }
82
83 bool ExplainOutputStyle::explainBlockStatus() {
84 if (FileOffset >= File.getFileSize()) {
85 P.formatLine("Address {0} is not in the file (file size = {1}).",
86 FileOffset, File.getFileSize());
87 return false;
88 }
89 P.formatLine("Block:Offset = {2:X-}:{1:X-4}.", FileOffset, OffsetInBlock,
90 BlockIndex);
91
92 bool IsFree = File.getMsfLayout().FreePageMap[BlockIndex];
93 P.formatLine("Address is in block {0} ({1}allocated).", BlockIndex,
94 IsFree ? "un" : "");
95 return !IsFree;
96 }
97
98 void ExplainOutputStyle::explainSuperBlockOffset() {
99 P.formatLine("This corresponds to offset {0} of MSF super block, ",
100 OffsetInBlock);
101 if (OffsetInBlock < sizeof(msf::Magic))
102 P.printLine("which is part of the MSF file magic.");
103 else if (OffsetInBlock < offsetof(SuperBlock, BlockSize))
104 P.printLine("which contains the block size of the file.");
105 else if (OffsetInBlock < offsetof(SuperBlock, FreeBlockMapBlock))
106 P.printLine("which contains the index of the FPM block (e.g. 1 or 2).");
107 else if (OffsetInBlock < offsetof(SuperBlock, NumBlocks))
108 P.printLine("which contains the number of blocks in the file.");
109 else if (OffsetInBlock < offsetof(SuperBlock, NumDirectoryBytes))
110 P.printLine("which contains the number of bytes in the stream directory.");
111 else if (OffsetInBlock < offsetof(SuperBlock, Unknown1))
112 P.printLine("whose purpose is unknown.");
113 else if (OffsetInBlock < offsetof(SuperBlock, BlockMapAddr))
114 P.printLine("which contains the file offset of the block map.");
115 else {
116 assert(OffsetInBlock > sizeof(SuperBlock));
117 P.printLine(
118 "which is outside the range of valid data for the super block.");
119 }
120 }
121
122 void ExplainOutputStyle::explainFpmBlockOffset() {
123 const MSFLayout &Layout = File.getMsfLayout();
124 uint32_t MainFpm = Layout.mainFpmBlock();
125 uint32_t AltFpm = Layout.alternateFpmBlock();
126
127 assert(isFpmBlock());
128 uint32_t Fpm = isFpm1() ? 1 : 2;
129 uint32_t FpmChunk = BlockIndex / File.getBlockSize();
130 assert((Fpm == MainFpm) || (Fpm == AltFpm));
131 (void)AltFpm;
132 bool IsMain = (Fpm == MainFpm);
133 P.formatLine("Address is in FPM{0} ({1} FPM)", Fpm, IsMain ? "Main" : "Alt");
134 uint32_t DescribedBlockStart =
135 8 * (FpmChunk * File.getBlockSize() + OffsetInBlock);
136 if (DescribedBlockStart > File.getBlockCount()) {
137 P.printLine("Address is in extraneous FPM space.");
138 return;
139 }
140
141 P.formatLine("Address describes the allocation status of blocks [{0},{1})",
142 DescribedBlockStart, DescribedBlockStart + 8);
143 }
144
145 static bool offsetIsInBlock(const PDBFile &File, uint64_t Offset,
146 uint32_t Block) {
147 uint64_t BlockOffset = uint64_t(Block) * File.getBlockSize();
148 uint64_t BlockOffset1 = BlockOffset + File.getBlockSize();
149 return (Offset >= BlockOffset && Offset < BlockOffset1);
150 }
151
152 void ExplainOutputStyle::explainBlockMapOffset() {
153 assert(offsetIsInBlock(File, FileOffset, File.getBlockMapIndex()));
154 uint64_t BlockMapOffset = File.getBlockMapOffset();
155 uint32_t OffsetInBlock = FileOffset - BlockMapOffset;
156 P.formatLine("Address is at offset {0} of the directory block list",
157 OffsetInBlock);
158 }
159
160 static uint32_t getOffsetInStream(ArrayRef StreamBlocks,
161 uint64_t FileOffset, uint32_t BlockSize) {
162 uint32_t BlockIndex = FileOffset / BlockSize;
163 uint32_t OffsetInBlock = FileOffset - BlockIndex * BlockSize;
164
165 auto Iter = llvm::find(StreamBlocks, BlockIndex);
166 assert(Iter != StreamBlocks.end());
167 uint32_t StreamBlockIndex = std::distance(StreamBlocks.begin(), Iter);
168 return StreamBlockIndex * BlockSize + OffsetInBlock;
169 }
170
171 void ExplainOutputStyle::explainStreamOffset(uint32_t Stream) {
172 SmallVector Streams;
173 discoverStreamPurposes(File, Streams);
174
175 assert(Stream <= Streams.size());
176 const StreamInfo &S = Streams[Stream];
177 const auto &Layout = File.getStreamLayout(Stream);
178 uint32_t StreamOff =
179 getOffsetInStream(Layout.Blocks, FileOffset, File.getBlockSize());
180 P.formatLine("Address is at offset {0}/{1} of Stream {2} ({3}){4}.",
181 StreamOff, Layout.Length, Stream, S.getLongName(),
182 (StreamOff > Layout.Length) ? " in unused space" : "");
183 }
184
185 void ExplainOutputStyle::explainStreamDirectoryOffset() {
186 auto DirectoryBlocks = File.getDirectoryBlockArray();
187 const auto &Layout = File.getMsfLayout();
188 uint32_t StreamOff =
189 getOffsetInStream(DirectoryBlocks, FileOffset, File.getBlockSize());
190 P.formatLine("Address is at offset {0}/{1} of Stream Directory{2}.",
191 StreamOff, uint32_t(Layout.SB->NumDirectoryBytes),
192 uint32_t(StreamOff > Layout.SB->NumDirectoryBytes)
193 ? " in unused space"
194 : "");
195 }
196
197 void ExplainOutputStyle::explainUnknownBlock() {
198 P.formatLine("Address has unknown purpose.");
199 }
0 //===- ExplainOutputStyle.h ----------------------------------- *- C++ --*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef LLVM_TOOLS_LLVMPDBDUMP_EXPLAINOUTPUTSTYLE_H
10 #define LLVM_TOOLS_LLVMPDBDUMP_EXPLAINOUTPUTSTYLE_H
11
12 #include "LinePrinter.h"
13 #include "OutputStyle.h"
14
15 #include
16
17 namespace llvm {
18
19 namespace pdb {
20
21 class PDBFile;
22
23 class ExplainOutputStyle : public OutputStyle {
24
25 public:
26 ExplainOutputStyle(PDBFile &File, uint64_t FileOffset);
27
28 Error dump() override;
29
30 private:
31 bool explainBlockStatus();
32
33 bool isFpm1() const;
34 bool isFpm2() const;
35
36 bool isSuperBlock() const;
37 bool isFpmBlock() const;
38 bool isBlockMapBlock() const;
39 bool isStreamDirectoryBlock() const;
40 Optional getBlockStreamIndex() const;
41
42 void explainSuperBlockOffset();
43 void explainFpmBlockOffset();
44 void explainBlockMapOffset();
45 void explainStreamDirectoryOffset();
46 void explainStreamOffset(uint32_t Stream);
47 void explainUnknownBlock();
48
49 PDBFile &File;
50 const uint64_t FileOffset;
51 const uint64_t BlockIndex;
52 const uint64_t OffsetInBlock;
53 LinePrinter P;
54 };
55 } // namespace pdb
56 } // namespace llvm
57
58 #endif
1515 #include "Analyze.h"
1616 #include "BytesOutputStyle.h"
1717 #include "DumpOutputStyle.h"
18 #include "ExplainOutputStyle.h"
1819 #include "InputFile.h"
1920 #include "LinePrinter.h"
2021 #include "OutputStyle.h"
110111 cl::SubCommand MergeSubcommand("merge",
111112 "Merge multiple PDBs into a single PDB");
112113
114 cl::SubCommand ExplainSubcommand("explain",
115 "Explain the meaning of a file offset");
116
113117 cl::OptionCategory TypeCategory("Symbol Type Options");
114118 cl::OptionCategory FilterCategory("Filtering and Sorting Options");
115119 cl::OptionCategory OtherOptions("Other Options");
604608 PdbOutputFile("pdb", cl::desc("the name of the PDB file to write"),
605609 cl::sub(MergeSubcommand));
606610 }
611
612 namespace explain {
613 cl::list InputFilename(cl::Positional,
614 cl::desc(""), cl::Required,
615 cl::sub(ExplainSubcommand));
616
617 cl::opt Offset("offset", cl::desc("The file offset to explain"),
618 cl::sub(ExplainSubcommand), cl::Required,
619 cl::OneOrMore);
620 } // namespace explain
607621 }
608622
609623 static ExitOnError ExitOnErr;
10711085 llvm::sys::path::replace_extension(OutFile, "merged.pdb");
10721086 }
10731087 ExitOnErr(Builder.commit(OutFile));
1088 }
1089
1090 static void explain() {
1091 std::unique_ptr Session;
1092 PDBFile &File = loadPDB(opts::explain::InputFilename.front(), Session);
1093 auto O = llvm::make_unique(File, opts::explain::Offset);
1094
1095 ExitOnErr(O->dump());
10741096 }
10751097
10761098 static bool parseRange(StringRef Str,
12471269 exit(1);
12481270 }
12491271 mergePdbs();
1272 } else if (opts::ExplainSubcommand) {
1273 explain();
12501274 }
12511275
12521276 outs().flush();
188188 extern llvm::cl::opt DumpModuleSyms;
189189 } // namespace pdb2yaml
190190
191 namespace explain {
192 extern llvm::cl::list InputFilename;
193 extern llvm::cl::opt Offset;
194 } // namespace explain
191195 }
192196
193197 #endif