BytesOutputStyle.cpp revision 360784
1//===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "BytesOutputStyle.h"
10
11#include "FormatUtil.h"
12#include "StreamUtil.h"
13#include "llvm-pdbutil.h"
14
15#include "llvm/DebugInfo/CodeView/Formatters.h"
16#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
17#include "llvm/DebugInfo/MSF/MSFCommon.h"
18#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
19#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
20#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
21#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
22#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
23#include "llvm/DebugInfo/PDB/Native/RawError.h"
24#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
25#include "llvm/Support/BinaryStreamReader.h"
26#include "llvm/Support/FormatAdapters.h"
27#include "llvm/Support/FormatVariadic.h"
28
29using namespace llvm;
30using namespace llvm::codeview;
31using namespace llvm::msf;
32using namespace llvm::pdb;
33
34namespace {
35struct StreamSpec {
36  uint32_t SI = 0;
37  uint32_t Begin = 0;
38  uint32_t Size = 0;
39};
40} // namespace
41
42static Expected<StreamSpec> parseStreamSpec(StringRef Str) {
43  StreamSpec Result;
44  if (Str.consumeInteger(0, Result.SI))
45    return make_error<RawError>(raw_error_code::invalid_format,
46                                "Invalid Stream Specification");
47  if (Str.consume_front(":")) {
48    if (Str.consumeInteger(0, Result.Begin))
49      return make_error<RawError>(raw_error_code::invalid_format,
50                                  "Invalid Stream Specification");
51  }
52  if (Str.consume_front("@")) {
53    if (Str.consumeInteger(0, Result.Size))
54      return make_error<RawError>(raw_error_code::invalid_format,
55                                  "Invalid Stream Specification");
56  }
57
58  if (!Str.empty())
59    return make_error<RawError>(raw_error_code::invalid_format,
60                                "Invalid Stream Specification");
61  return Result;
62}
63
64static SmallVector<StreamSpec, 2> parseStreamSpecs(LinePrinter &P) {
65  SmallVector<StreamSpec, 2> Result;
66
67  for (auto &Str : opts::bytes::DumpStreamData) {
68    auto ESS = parseStreamSpec(Str);
69    if (!ESS) {
70      P.formatLine("Error parsing stream spec {0}: {1}", Str,
71                   toString(ESS.takeError()));
72      continue;
73    }
74    Result.push_back(*ESS);
75  }
76  return Result;
77}
78
79static void printHeader(LinePrinter &P, const Twine &S) {
80  P.NewLine();
81  P.formatLine("{0,=60}", S);
82  P.formatLine("{0}", fmt_repeat('=', 60));
83}
84
85BytesOutputStyle::BytesOutputStyle(PDBFile &File)
86    : File(File), P(2, false, outs()) {}
87
88Error BytesOutputStyle::dump() {
89
90  if (opts::bytes::DumpBlockRange.hasValue()) {
91    auto &R = *opts::bytes::DumpBlockRange;
92    uint32_t Max = R.Max.getValueOr(R.Min);
93
94    if (Max < R.Min)
95      return make_error<StringError>(
96          "Invalid block range specified.  Max < Min",
97          inconvertibleErrorCode());
98    if (Max >= File.getBlockCount())
99      return make_error<StringError>(
100          "Invalid block range specified.  Requested block out of bounds",
101          inconvertibleErrorCode());
102
103    dumpBlockRanges(R.Min, Max);
104    P.NewLine();
105  }
106
107  if (opts::bytes::DumpByteRange.hasValue()) {
108    auto &R = *opts::bytes::DumpByteRange;
109    uint32_t Max = R.Max.getValueOr(File.getFileSize());
110
111    if (Max < R.Min)
112      return make_error<StringError>("Invalid byte range specified.  Max < Min",
113                                     inconvertibleErrorCode());
114    if (Max >= File.getFileSize())
115      return make_error<StringError>(
116          "Invalid byte range specified.  Requested byte larger than file size",
117          inconvertibleErrorCode());
118
119    dumpByteRanges(R.Min, Max);
120    P.NewLine();
121  }
122
123  if (opts::bytes::Fpm) {
124    dumpFpm();
125    P.NewLine();
126  }
127
128  if (!opts::bytes::DumpStreamData.empty()) {
129    dumpStreamBytes();
130    P.NewLine();
131  }
132
133  if (opts::bytes::NameMap) {
134    dumpNameMap();
135    P.NewLine();
136  }
137
138  if (opts::bytes::SectionContributions) {
139    dumpSectionContributions();
140    P.NewLine();
141  }
142
143  if (opts::bytes::SectionMap) {
144    dumpSectionMap();
145    P.NewLine();
146  }
147
148  if (opts::bytes::ModuleInfos) {
149    dumpModuleInfos();
150    P.NewLine();
151  }
152
153  if (opts::bytes::FileInfo) {
154    dumpFileInfo();
155    P.NewLine();
156  }
157
158  if (opts::bytes::TypeServerMap) {
159    dumpTypeServerMap();
160    P.NewLine();
161  }
162
163  if (opts::bytes::ECData) {
164    dumpECData();
165    P.NewLine();
166  }
167
168  if (!opts::bytes::TypeIndex.empty()) {
169    dumpTypeIndex(StreamTPI, opts::bytes::TypeIndex);
170    P.NewLine();
171  }
172
173  if (!opts::bytes::IdIndex.empty()) {
174    dumpTypeIndex(StreamIPI, opts::bytes::IdIndex);
175    P.NewLine();
176  }
177
178  if (opts::bytes::ModuleSyms) {
179    dumpModuleSyms();
180    P.NewLine();
181  }
182
183  if (opts::bytes::ModuleC11) {
184    dumpModuleC11();
185    P.NewLine();
186  }
187
188  if (opts::bytes::ModuleC13) {
189    dumpModuleC13();
190    P.NewLine();
191  }
192
193  return Error::success();
194}
195
196void BytesOutputStyle::dumpNameMap() {
197  printHeader(P, "Named Stream Map");
198
199  AutoIndent Indent(P);
200
201  auto &InfoS = Err(File.getPDBInfoStream());
202  BinarySubstreamRef NS = InfoS.getNamedStreamsBuffer();
203  auto Layout = File.getStreamLayout(StreamPDB);
204  P.formatMsfStreamData("Named Stream Map", File, Layout, NS);
205}
206
207void BytesOutputStyle::dumpBlockRanges(uint32_t Min, uint32_t Max) {
208  printHeader(P, "MSF Blocks");
209
210  AutoIndent Indent(P);
211  for (uint32_t I = Min; I <= Max; ++I) {
212    uint64_t Base = I;
213    Base *= File.getBlockSize();
214
215    auto ExpectedData = File.getBlockData(I, File.getBlockSize());
216    if (!ExpectedData) {
217      P.formatLine("Could not get block {0}.  Reason = {1}", I,
218                   toString(ExpectedData.takeError()));
219      continue;
220    }
221    std::string Label = formatv("Block {0}", I).str();
222    P.formatBinary(Label, *ExpectedData, Base, 0);
223  }
224}
225
226void BytesOutputStyle::dumpSectionContributions() {
227  printHeader(P, "Section Contributions");
228
229  AutoIndent Indent(P);
230
231  auto &DbiS = Err(File.getPDBDbiStream());
232  BinarySubstreamRef NS = DbiS.getSectionContributionData();
233  auto Layout = File.getStreamLayout(StreamDBI);
234  P.formatMsfStreamData("Section Contributions", File, Layout, NS);
235}
236
237void BytesOutputStyle::dumpSectionMap() {
238  printHeader(P, "Section Map");
239
240  AutoIndent Indent(P);
241
242  auto &DbiS = Err(File.getPDBDbiStream());
243  BinarySubstreamRef NS = DbiS.getSecMapSubstreamData();
244  auto Layout = File.getStreamLayout(StreamDBI);
245  P.formatMsfStreamData("Section Map", File, Layout, NS);
246}
247
248void BytesOutputStyle::dumpModuleInfos() {
249  printHeader(P, "Module Infos");
250
251  AutoIndent Indent(P);
252
253  auto &DbiS = Err(File.getPDBDbiStream());
254  BinarySubstreamRef NS = DbiS.getModiSubstreamData();
255  auto Layout = File.getStreamLayout(StreamDBI);
256  P.formatMsfStreamData("Module Infos", File, Layout, NS);
257}
258
259void BytesOutputStyle::dumpFileInfo() {
260  printHeader(P, "File Info");
261
262  AutoIndent Indent(P);
263
264  auto &DbiS = Err(File.getPDBDbiStream());
265  BinarySubstreamRef NS = DbiS.getFileInfoSubstreamData();
266  auto Layout = File.getStreamLayout(StreamDBI);
267  P.formatMsfStreamData("File Info", File, Layout, NS);
268}
269
270void BytesOutputStyle::dumpTypeServerMap() {
271  printHeader(P, "Type Server Map");
272
273  AutoIndent Indent(P);
274
275  auto &DbiS = Err(File.getPDBDbiStream());
276  BinarySubstreamRef NS = DbiS.getTypeServerMapSubstreamData();
277  auto Layout = File.getStreamLayout(StreamDBI);
278  P.formatMsfStreamData("Type Server Map", File, Layout, NS);
279}
280
281void BytesOutputStyle::dumpECData() {
282  printHeader(P, "Edit and Continue Data");
283
284  AutoIndent Indent(P);
285
286  auto &DbiS = Err(File.getPDBDbiStream());
287  BinarySubstreamRef NS = DbiS.getECSubstreamData();
288  auto Layout = File.getStreamLayout(StreamDBI);
289  P.formatMsfStreamData("Edit and Continue Data", File, Layout, NS);
290}
291
292void BytesOutputStyle::dumpTypeIndex(uint32_t StreamIdx,
293                                     ArrayRef<uint32_t> Indices) {
294  assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
295  assert(!Indices.empty());
296
297  bool IsTpi = (StreamIdx == StreamTPI);
298
299  StringRef Label = IsTpi ? "Type (TPI) Records" : "Index (IPI) Records";
300  printHeader(P, Label);
301  auto &Stream = Err(IsTpi ? File.getPDBTpiStream() : File.getPDBIpiStream());
302
303  AutoIndent Indent(P);
304
305  auto Substream = Stream.getTypeRecordsSubstream();
306  auto &Types = Err(initializeTypes(StreamIdx));
307  auto Layout = File.getStreamLayout(StreamIdx);
308  for (const auto &Id : Indices) {
309    TypeIndex TI(Id);
310    if (TI.toArrayIndex() >= Types.capacity()) {
311      P.formatLine("Error: TypeIndex {0} does not exist", TI);
312      continue;
313    }
314
315    auto Type = Types.getType(TI);
316    uint32_t Offset = Types.getOffsetOfType(TI);
317    auto OneType = Substream.slice(Offset, Type.length());
318    P.formatMsfStreamData(formatv("Type {0}", TI).str(), File, Layout, OneType);
319  }
320}
321
322template <typename CallbackT>
323static void iterateOneModule(PDBFile &File, LinePrinter &P,
324                             const DbiModuleList &Modules, uint32_t I,
325                             uint32_t Digits, uint32_t IndentLevel,
326                             CallbackT Callback) {
327  if (I >= Modules.getModuleCount()) {
328    P.formatLine("Mod {0:4} | Invalid module index ",
329                 fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)));
330    return;
331  }
332
333  auto Modi = Modules.getModuleDescriptor(I);
334  P.formatLine("Mod {0:4} | `{1}`: ",
335               fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)),
336               Modi.getModuleName());
337
338  uint16_t ModiStream = Modi.getModuleStreamIndex();
339  AutoIndent Indent2(P, IndentLevel);
340  if (ModiStream == kInvalidStreamIndex)
341    return;
342
343  auto ModStreamData = File.createIndexedStream(ModiStream);
344  ModuleDebugStreamRef ModStream(Modi, std::move(ModStreamData));
345  if (auto EC = ModStream.reload()) {
346    P.formatLine("Could not parse debug information.");
347    return;
348  }
349  auto Layout = File.getStreamLayout(ModiStream);
350  Callback(I, ModStream, Layout);
351}
352
353template <typename CallbackT>
354static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel,
355                           CallbackT Callback) {
356  AutoIndent Indent(P);
357  if (!File.hasPDBDbiStream()) {
358    P.formatLine("DBI Stream not present");
359    return;
360  }
361
362  ExitOnError Err("Unexpected error processing modules");
363
364  auto &Stream = Err(File.getPDBDbiStream());
365
366  const DbiModuleList &Modules = Stream.modules();
367
368  if (opts::bytes::ModuleIndex.getNumOccurrences() > 0) {
369    iterateOneModule(File, P, Modules, opts::bytes::ModuleIndex, 1, IndentLevel,
370                     Callback);
371  } else {
372    uint32_t Count = Modules.getModuleCount();
373    uint32_t Digits = NumDigits(Count);
374    for (uint32_t I = 0; I < Count; ++I) {
375      iterateOneModule(File, P, Modules, I, Digits, IndentLevel, Callback);
376    }
377  }
378}
379
380void BytesOutputStyle::dumpModuleSyms() {
381  printHeader(P, "Module Symbols");
382
383  AutoIndent Indent(P);
384
385  iterateModules(File, P, 2,
386                 [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
387                        const MSFStreamLayout &Layout) {
388                   auto Symbols = Stream.getSymbolsSubstream();
389                   P.formatMsfStreamData("Symbols", File, Layout, Symbols);
390                 });
391}
392
393void BytesOutputStyle::dumpModuleC11() {
394  printHeader(P, "C11 Debug Chunks");
395
396  AutoIndent Indent(P);
397
398  iterateModules(File, P, 2,
399                 [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
400                        const MSFStreamLayout &Layout) {
401                   auto Chunks = Stream.getC11LinesSubstream();
402                   P.formatMsfStreamData("C11 Debug Chunks", File, Layout,
403                                         Chunks);
404                 });
405}
406
407void BytesOutputStyle::dumpModuleC13() {
408  printHeader(P, "Debug Chunks");
409
410  AutoIndent Indent(P);
411
412  iterateModules(
413      File, P, 2,
414      [this](uint32_t Modi, const ModuleDebugStreamRef &Stream,
415             const MSFStreamLayout &Layout) {
416        auto Chunks = Stream.getC13LinesSubstream();
417        if (opts::bytes::SplitChunks) {
418          for (const auto &SS : Stream.subsections()) {
419            BinarySubstreamRef ThisChunk;
420            std::tie(ThisChunk, Chunks) = Chunks.split(SS.getRecordLength());
421            P.formatMsfStreamData(formatChunkKind(SS.kind()), File, Layout,
422                                  ThisChunk);
423          }
424        } else {
425          P.formatMsfStreamData("Debug Chunks", File, Layout, Chunks);
426        }
427      });
428}
429
430void BytesOutputStyle::dumpByteRanges(uint32_t Min, uint32_t Max) {
431  printHeader(P, "MSF Bytes");
432
433  AutoIndent Indent(P);
434
435  BinaryStreamReader Reader(File.getMsfBuffer());
436  ArrayRef<uint8_t> Data;
437  consumeError(Reader.skip(Min));
438  uint32_t Size = Max - Min + 1;
439  auto EC = Reader.readBytes(Data, Size);
440  assert(!EC);
441  consumeError(std::move(EC));
442  P.formatBinary("Bytes", Data, Min);
443}
444
445Expected<codeview::LazyRandomTypeCollection &>
446BytesOutputStyle::initializeTypes(uint32_t StreamIdx) {
447  auto &TypeCollection = (StreamIdx == StreamTPI) ? TpiTypes : IpiTypes;
448  if (TypeCollection)
449    return *TypeCollection;
450
451  auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream()
452                                      : File.getPDBIpiStream();
453  if (!Tpi)
454    return Tpi.takeError();
455
456  auto &Types = Tpi->typeArray();
457  uint32_t Count = Tpi->getNumTypeRecords();
458  auto Offsets = Tpi->getTypeIndexOffsets();
459  TypeCollection =
460      std::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets);
461
462  return *TypeCollection;
463}
464
465void BytesOutputStyle::dumpFpm() {
466  printHeader(P, "Free Page Map");
467
468  msf::MSFStreamLayout FpmLayout = File.getFpmStreamLayout();
469  P.formatMsfStreamBlocks(File, FpmLayout);
470}
471
472void BytesOutputStyle::dumpStreamBytes() {
473  if (StreamPurposes.empty())
474    discoverStreamPurposes(File, StreamPurposes);
475
476  printHeader(P, "Stream Data");
477  ExitOnError Err("Unexpected error reading stream data");
478
479  auto Specs = parseStreamSpecs(P);
480
481  for (const auto &Spec : Specs) {
482    AutoIndent Indent(P);
483    if (Spec.SI >= StreamPurposes.size()) {
484      P.formatLine("Stream {0}: Not present", Spec.SI);
485      continue;
486    }
487    P.formatMsfStreamData("Data", File, Spec.SI,
488                          StreamPurposes[Spec.SI].getShortName(), Spec.Begin,
489                          Spec.Size);
490  }
491}
492