1 //===- DumpOutputStyle.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 "DumpOutputStyle.h" 10 11 #include "FormatUtil.h" 12 #include "InputFile.h" 13 #include "MinimalSymbolDumper.h" 14 #include "MinimalTypeDumper.h" 15 #include "StreamUtil.h" 16 #include "TypeReferenceTracker.h" 17 #include "llvm-pdbutil.h" 18 19 #include "llvm/ADT/STLExtras.h" 20 #include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" 21 #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" 22 #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" 23 #include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" 24 #include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" 25 #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" 26 #include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" 27 #include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" 28 #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" 29 #include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" 30 #include "llvm/DebugInfo/CodeView/Formatters.h" 31 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" 32 #include "llvm/DebugInfo/CodeView/Line.h" 33 #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" 34 #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" 35 #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" 36 #include "llvm/DebugInfo/CodeView/TypeHashing.h" 37 #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" 38 #include "llvm/DebugInfo/MSF/MappedBlockStream.h" 39 #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" 40 #include "llvm/DebugInfo/PDB/Native/DbiStream.h" 41 #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" 42 #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" 43 #include "llvm/DebugInfo/PDB/Native/InfoStream.h" 44 #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" 45 #include "llvm/DebugInfo/PDB/Native/PDBFile.h" 46 #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" 47 #include "llvm/DebugInfo/PDB/Native/RawError.h" 48 #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" 49 #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" 50 #include "llvm/DebugInfo/PDB/Native/TpiStream.h" 51 #include "llvm/Object/COFF.h" 52 #include "llvm/Support/BinaryStreamReader.h" 53 #include "llvm/Support/FormatAdapters.h" 54 #include "llvm/Support/FormatVariadic.h" 55 56 #include <cctype> 57 58 using namespace llvm; 59 using namespace llvm::codeview; 60 using namespace llvm::msf; 61 using namespace llvm::pdb; 62 63 DumpOutputStyle::DumpOutputStyle(InputFile &File) 64 : File(File), P(2, false, outs()) { 65 if (opts::dump::DumpTypeRefStats) 66 RefTracker.reset(new TypeReferenceTracker(File)); 67 } 68 69 DumpOutputStyle::~DumpOutputStyle() {} 70 71 PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); } 72 object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); } 73 74 void DumpOutputStyle::printStreamNotValidForObj() { 75 AutoIndent Indent(P, 4); 76 P.formatLine("Dumping this stream is not valid for object files"); 77 } 78 79 void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) { 80 AutoIndent Indent(P, 4); 81 P.formatLine("{0} stream not present", StreamName); 82 } 83 84 Error DumpOutputStyle::dump() { 85 // Walk symbols & globals if we are supposed to mark types referenced. 86 if (opts::dump::DumpTypeRefStats) 87 RefTracker->mark(); 88 89 if (opts::dump::DumpSummary) { 90 if (auto EC = dumpFileSummary()) 91 return EC; 92 P.NewLine(); 93 } 94 95 if (opts::dump::DumpStreams) { 96 if (auto EC = dumpStreamSummary()) 97 return EC; 98 P.NewLine(); 99 } 100 101 if (opts::dump::DumpSymbolStats) { 102 if (auto EC = dumpSymbolStats()) 103 return EC; 104 P.NewLine(); 105 } 106 107 if (opts::dump::DumpUdtStats) { 108 if (auto EC = dumpUdtStats()) 109 return EC; 110 P.NewLine(); 111 } 112 113 if (opts::dump::DumpTypeStats) { 114 if (auto EC = dumpTypeStats()) 115 return EC; 116 P.NewLine(); 117 } 118 119 if (opts::dump::DumpNamedStreams) { 120 if (auto EC = dumpNamedStreams()) 121 return EC; 122 P.NewLine(); 123 } 124 125 if (opts::dump::DumpStringTable || opts::dump::DumpStringTableDetails) { 126 if (auto EC = dumpStringTable()) 127 return EC; 128 P.NewLine(); 129 } 130 131 if (opts::dump::DumpModules) { 132 if (auto EC = dumpModules()) 133 return EC; 134 } 135 136 if (opts::dump::DumpModuleFiles) { 137 if (auto EC = dumpModuleFiles()) 138 return EC; 139 } 140 141 if (opts::dump::DumpLines) { 142 if (auto EC = dumpLines()) 143 return EC; 144 } 145 146 if (opts::dump::DumpInlineeLines) { 147 if (auto EC = dumpInlineeLines()) 148 return EC; 149 } 150 151 if (opts::dump::DumpXmi) { 152 if (auto EC = dumpXmi()) 153 return EC; 154 } 155 156 if (opts::dump::DumpXme) { 157 if (auto EC = dumpXme()) 158 return EC; 159 } 160 161 if (opts::dump::DumpFpo) { 162 if (auto EC = dumpFpo()) 163 return EC; 164 } 165 166 if (File.isObj()) { 167 if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || 168 opts::dump::DumpTypeExtras) 169 if (auto EC = dumpTypesFromObjectFile()) 170 return EC; 171 } else { 172 if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || 173 opts::dump::DumpTypeExtras) { 174 if (auto EC = dumpTpiStream(StreamTPI)) 175 return EC; 176 } 177 178 if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() || 179 opts::dump::DumpIdExtras) { 180 if (auto EC = dumpTpiStream(StreamIPI)) 181 return EC; 182 } 183 } 184 185 if (opts::dump::DumpGSIRecords) { 186 if (auto EC = dumpGSIRecords()) 187 return EC; 188 } 189 190 if (opts::dump::DumpGlobals) { 191 if (auto EC = dumpGlobals()) 192 return EC; 193 } 194 195 if (opts::dump::DumpPublics) { 196 if (auto EC = dumpPublics()) 197 return EC; 198 } 199 200 if (opts::dump::DumpSymbols) { 201 auto EC = File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj(); 202 if (EC) 203 return EC; 204 } 205 206 if (opts::dump::DumpTypeRefStats) { 207 if (auto EC = dumpTypeRefStats()) 208 return EC; 209 } 210 211 if (opts::dump::DumpSectionHeaders) { 212 if (auto EC = dumpSectionHeaders()) 213 return EC; 214 } 215 216 if (opts::dump::DumpSectionContribs) { 217 if (auto EC = dumpSectionContribs()) 218 return EC; 219 } 220 221 if (opts::dump::DumpSectionMap) { 222 if (auto EC = dumpSectionMap()) 223 return EC; 224 } 225 226 return Error::success(); 227 } 228 229 static void printHeader(LinePrinter &P, const Twine &S) { 230 P.NewLine(); 231 P.formatLine("{0,=60}", S); 232 P.formatLine("{0}", fmt_repeat('=', 60)); 233 } 234 235 Error DumpOutputStyle::dumpFileSummary() { 236 printHeader(P, "Summary"); 237 238 if (File.isObj()) { 239 printStreamNotValidForObj(); 240 return Error::success(); 241 } 242 243 AutoIndent Indent(P); 244 ExitOnError Err("Invalid PDB Format: "); 245 246 P.formatLine("Block Size: {0}", getPdb().getBlockSize()); 247 P.formatLine("Number of blocks: {0}", getPdb().getBlockCount()); 248 P.formatLine("Number of streams: {0}", getPdb().getNumStreams()); 249 250 auto &PS = Err(getPdb().getPDBInfoStream()); 251 P.formatLine("Signature: {0}", PS.getSignature()); 252 P.formatLine("Age: {0}", PS.getAge()); 253 P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid)); 254 P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures())); 255 P.formatLine("Has Debug Info: {0}", getPdb().hasPDBDbiStream()); 256 P.formatLine("Has Types: {0}", getPdb().hasPDBTpiStream()); 257 P.formatLine("Has IDs: {0}", getPdb().hasPDBIpiStream()); 258 P.formatLine("Has Globals: {0}", getPdb().hasPDBGlobalsStream()); 259 P.formatLine("Has Publics: {0}", getPdb().hasPDBPublicsStream()); 260 if (getPdb().hasPDBDbiStream()) { 261 auto &DBI = Err(getPdb().getPDBDbiStream()); 262 P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked()); 263 P.formatLine("Has conflicting types: {0}", DBI.hasCTypes()); 264 P.formatLine("Is stripped: {0}", DBI.isStripped()); 265 } 266 267 return Error::success(); 268 } 269 270 static StatCollection getSymbolStats(const SymbolGroup &SG, 271 StatCollection &CumulativeStats) { 272 StatCollection Stats; 273 if (SG.getFile().isPdb() && SG.hasDebugStream()) { 274 // For PDB files, all symbols are packed into one stream. 275 for (const auto &S : SG.getPdbModuleStream().symbols(nullptr)) { 276 Stats.update(S.kind(), S.length()); 277 CumulativeStats.update(S.kind(), S.length()); 278 } 279 return Stats; 280 } 281 282 for (const auto &SS : SG.getDebugSubsections()) { 283 // For object files, all symbols are spread across multiple Symbol 284 // subsections of a given .debug$S section. 285 if (SS.kind() != DebugSubsectionKind::Symbols) 286 continue; 287 DebugSymbolsSubsectionRef Symbols; 288 BinaryStreamReader Reader(SS.getRecordData()); 289 cantFail(Symbols.initialize(Reader)); 290 for (const auto &S : Symbols) { 291 Stats.update(S.kind(), S.length()); 292 CumulativeStats.update(S.kind(), S.length()); 293 } 294 } 295 return Stats; 296 } 297 298 static StatCollection getChunkStats(const SymbolGroup &SG, 299 StatCollection &CumulativeStats) { 300 StatCollection Stats; 301 for (const auto &Chunk : SG.getDebugSubsections()) { 302 Stats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength()); 303 CumulativeStats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength()); 304 } 305 return Stats; 306 } 307 308 static inline std::string formatModuleDetailKind(DebugSubsectionKind K) { 309 return formatChunkKind(K, false); 310 } 311 312 static inline std::string formatModuleDetailKind(SymbolKind K) { 313 return formatSymbolKind(K); 314 } 315 316 // Get the stats sorted by size, descending. 317 std::vector<StatCollection::KindAndStat> 318 StatCollection::getStatsSortedBySize() const { 319 std::vector<KindAndStat> SortedStats(Individual.begin(), Individual.end()); 320 std::stable_sort(SortedStats.begin(), SortedStats.end(), 321 [](const KindAndStat &LHS, const KindAndStat &RHS) { 322 return LHS.second.Size > RHS.second.Size; 323 }); 324 return SortedStats; 325 } 326 327 template <typename Kind> 328 static void printModuleDetailStats(LinePrinter &P, StringRef Label, 329 const StatCollection &Stats) { 330 P.NewLine(); 331 P.formatLine(" {0}", Label); 332 AutoIndent Indent(P); 333 P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", "Total", 334 Stats.Totals.Count, Stats.Totals.Size); 335 P.formatLine("{0}", fmt_repeat('-', 74)); 336 337 for (const auto &K : Stats.getStatsSortedBySize()) { 338 std::string KindName = formatModuleDetailKind(Kind(K.first)); 339 P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", KindName, 340 K.second.Count, K.second.Size); 341 } 342 } 343 344 static bool isMyCode(const SymbolGroup &Group) { 345 if (Group.getFile().isObj()) 346 return true; 347 348 StringRef Name = Group.name(); 349 if (Name.startswith("Import:")) 350 return false; 351 if (Name.endswith_lower(".dll")) 352 return false; 353 if (Name.equals_lower("* linker *")) 354 return false; 355 if (Name.startswith_lower("f:\\binaries\\Intermediate\\vctools")) 356 return false; 357 if (Name.startswith_lower("f:\\dd\\vctools\\crt")) 358 return false; 359 return true; 360 } 361 362 static bool shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group) { 363 if (opts::dump::JustMyCode && !isMyCode(Group)) 364 return false; 365 366 // If the arg was not specified on the command line, always dump all modules. 367 if (opts::dump::DumpModi.getNumOccurrences() == 0) 368 return true; 369 370 // Otherwise, only dump if this is the same module specified. 371 return (opts::dump::DumpModi == Idx); 372 } 373 374 Error DumpOutputStyle::dumpStreamSummary() { 375 printHeader(P, "Streams"); 376 377 if (File.isObj()) { 378 printStreamNotValidForObj(); 379 return Error::success(); 380 } 381 382 AutoIndent Indent(P); 383 384 if (StreamPurposes.empty()) 385 discoverStreamPurposes(getPdb(), StreamPurposes); 386 387 uint32_t StreamCount = getPdb().getNumStreams(); 388 uint32_t MaxStreamSize = getPdb().getMaxStreamSize(); 389 390 for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { 391 P.formatLine( 392 "Stream {0} ({1} bytes): [{2}]", 393 fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)), 394 fmt_align(getPdb().getStreamByteSize(StreamIdx), AlignStyle::Right, 395 NumDigits(MaxStreamSize)), 396 StreamPurposes[StreamIdx].getLongName()); 397 398 if (opts::dump::DumpStreamBlocks) { 399 auto Blocks = getPdb().getStreamBlockList(StreamIdx); 400 std::vector<uint32_t> BV(Blocks.begin(), Blocks.end()); 401 P.formatLine(" {0} Blocks: [{1}]", 402 fmt_repeat(' ', NumDigits(StreamCount)), 403 make_range(BV.begin(), BV.end())); 404 } 405 } 406 407 return Error::success(); 408 } 409 410 static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File, 411 uint32_t Index) { 412 ExitOnError Err("Unexpected error: "); 413 414 auto &Dbi = Err(File.getPDBDbiStream()); 415 const auto &Modules = Dbi.modules(); 416 auto Modi = Modules.getModuleDescriptor(Index); 417 418 uint16_t ModiStream = Modi.getModuleStreamIndex(); 419 if (ModiStream == kInvalidStreamIndex) 420 return make_error<RawError>(raw_error_code::no_stream, 421 "Module stream not present"); 422 423 auto ModStreamData = File.createIndexedStream(ModiStream); 424 425 ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); 426 if (auto EC = ModS.reload()) 427 return make_error<RawError>(raw_error_code::corrupt_file, 428 "Invalid module stream"); 429 430 return std::move(ModS); 431 } 432 433 template <typename CallbackT> 434 static void 435 iterateOneModule(InputFile &File, const Optional<PrintScope> &HeaderScope, 436 const SymbolGroup &SG, uint32_t Modi, CallbackT Callback) { 437 if (HeaderScope) { 438 HeaderScope->P.formatLine( 439 "Mod {0:4} | `{1}`: ", 440 fmt_align(Modi, AlignStyle::Right, HeaderScope->LabelWidth), SG.name()); 441 } 442 443 AutoIndent Indent(HeaderScope); 444 Callback(Modi, SG); 445 } 446 447 template <typename CallbackT> 448 static void iterateSymbolGroups(InputFile &Input, 449 const Optional<PrintScope> &HeaderScope, 450 CallbackT Callback) { 451 AutoIndent Indent(HeaderScope); 452 453 ExitOnError Err("Unexpected error processing modules: "); 454 455 if (opts::dump::DumpModi.getNumOccurrences() > 0) { 456 assert(opts::dump::DumpModi.getNumOccurrences() == 1); 457 uint32_t Modi = opts::dump::DumpModi; 458 SymbolGroup SG(&Input, Modi); 459 iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(Modi)), SG, 460 Modi, Callback); 461 return; 462 } 463 464 uint32_t I = 0; 465 466 for (const auto &SG : Input.symbol_groups()) { 467 if (shouldDumpSymbolGroup(I, SG)) 468 iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(I)), SG, I, 469 Callback); 470 471 ++I; 472 } 473 } 474 475 template <typename SubsectionT> 476 static void iterateModuleSubsections( 477 InputFile &File, const Optional<PrintScope> &HeaderScope, 478 llvm::function_ref<void(uint32_t, const SymbolGroup &, SubsectionT &)> 479 Callback) { 480 481 iterateSymbolGroups(File, HeaderScope, 482 [&](uint32_t Modi, const SymbolGroup &SG) { 483 for (const auto &SS : SG.getDebugSubsections()) { 484 SubsectionT Subsection; 485 486 if (SS.kind() != Subsection.kind()) 487 continue; 488 489 BinaryStreamReader Reader(SS.getRecordData()); 490 if (auto EC = Subsection.initialize(Reader)) 491 continue; 492 Callback(Modi, SG, Subsection); 493 } 494 }); 495 } 496 497 static Expected<std::pair<std::unique_ptr<MappedBlockStream>, 498 ArrayRef<llvm::object::coff_section>>> 499 loadSectionHeaders(PDBFile &File, DbgHeaderType Type) { 500 if (!File.hasPDBDbiStream()) 501 return make_error<StringError>( 502 "Section headers require a DBI Stream, which could not be loaded", 503 inconvertibleErrorCode()); 504 505 auto &Dbi = cantFail(File.getPDBDbiStream()); 506 uint32_t SI = Dbi.getDebugStreamIndex(Type); 507 508 if (SI == kInvalidStreamIndex) 509 return make_error<StringError>( 510 "PDB does not contain the requested image section header type", 511 inconvertibleErrorCode()); 512 513 auto Stream = File.createIndexedStream(SI); 514 if (!Stream) 515 return make_error<StringError>("Could not load the required stream data", 516 inconvertibleErrorCode()); 517 518 ArrayRef<object::coff_section> Headers; 519 if (Stream->getLength() % sizeof(object::coff_section) != 0) 520 return make_error<StringError>( 521 "Section header array size is not a multiple of section header size", 522 inconvertibleErrorCode()); 523 524 uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section); 525 BinaryStreamReader Reader(*Stream); 526 cantFail(Reader.readArray(Headers, NumHeaders)); 527 return std::make_pair(std::move(Stream), Headers); 528 } 529 530 static std::vector<std::string> getSectionNames(PDBFile &File) { 531 auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr); 532 if (!ExpectedHeaders) 533 return {}; 534 535 std::unique_ptr<MappedBlockStream> Stream; 536 ArrayRef<object::coff_section> Headers; 537 std::tie(Stream, Headers) = std::move(*ExpectedHeaders); 538 std::vector<std::string> Names; 539 for (const auto &H : Headers) 540 Names.push_back(H.Name); 541 return Names; 542 } 543 544 static void dumpSectionContrib(LinePrinter &P, const SectionContrib &SC, 545 ArrayRef<std::string> SectionNames, 546 uint32_t FieldWidth) { 547 std::string NameInsert; 548 if (SC.ISect > 0 && SC.ISect <= SectionNames.size()) { 549 StringRef SectionName = SectionNames[SC.ISect - 1]; 550 NameInsert = formatv("[{0}]", SectionName).str(); 551 } else 552 NameInsert = "[???]"; 553 P.formatLine("SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " 554 "crc = {4}", 555 formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size), 556 fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc), 557 fmt_align(NameInsert, AlignStyle::Left, FieldWidth + 2)); 558 AutoIndent Indent(P, FieldWidth + 2); 559 P.formatLine(" {0}", 560 formatSectionCharacteristics(P.getIndentLevel() + 6, 561 SC.Characteristics, 3, " | ")); 562 } 563 564 static void dumpSectionContrib(LinePrinter &P, const SectionContrib2 &SC, 565 ArrayRef<std::string> SectionNames, 566 uint32_t FieldWidth) { 567 P.formatLine("SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " 568 "crc = {4}, coff section = {5}", 569 formatSegmentOffset(SC.Base.ISect, SC.Base.Off), 570 fmtle(SC.Base.Size), fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc), 571 fmtle(SC.Base.RelocCrc), fmtle(SC.ISectCoff)); 572 P.formatLine(" {0}", 573 formatSectionCharacteristics(P.getIndentLevel() + 6, 574 SC.Base.Characteristics, 3, " | ")); 575 } 576 577 Error DumpOutputStyle::dumpModules() { 578 printHeader(P, "Modules"); 579 580 if (File.isObj()) { 581 printStreamNotValidForObj(); 582 return Error::success(); 583 } 584 585 if (!getPdb().hasPDBDbiStream()) { 586 printStreamNotPresent("DBI"); 587 return Error::success(); 588 } 589 590 AutoIndent Indent(P); 591 ExitOnError Err("Unexpected error processing modules: "); 592 593 auto &Stream = Err(getPdb().getPDBDbiStream()); 594 595 const DbiModuleList &Modules = Stream.modules(); 596 iterateSymbolGroups( 597 File, PrintScope{P, 11}, [&](uint32_t Modi, const SymbolGroup &Strings) { 598 auto Desc = Modules.getModuleDescriptor(Modi); 599 if (opts::dump::DumpSectionContribs) { 600 std::vector<std::string> Sections = getSectionNames(getPdb()); 601 dumpSectionContrib(P, Desc.getSectionContrib(), Sections, 0); 602 } 603 P.formatLine("Obj: `{0}`: ", Desc.getObjFileName()); 604 P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}", 605 Desc.getModuleStreamIndex(), Desc.getNumberOfFiles(), 606 Desc.hasECInfo()); 607 StringRef PdbFilePath = 608 Err(Stream.getECName(Desc.getPdbFilePathNameIndex())); 609 StringRef SrcFilePath = 610 Err(Stream.getECName(Desc.getSourceFileNameIndex())); 611 P.formatLine("pdb file ni: {0} `{1}`, src file ni: {2} `{3}`", 612 Desc.getPdbFilePathNameIndex(), PdbFilePath, 613 Desc.getSourceFileNameIndex(), SrcFilePath); 614 }); 615 return Error::success(); 616 } 617 618 Error DumpOutputStyle::dumpModuleFiles() { 619 printHeader(P, "Files"); 620 621 if (File.isObj()) { 622 printStreamNotValidForObj(); 623 return Error::success(); 624 } 625 626 if (!getPdb().hasPDBDbiStream()) { 627 printStreamNotPresent("DBI"); 628 return Error::success(); 629 } 630 631 ExitOnError Err("Unexpected error processing modules: "); 632 633 iterateSymbolGroups(File, PrintScope{P, 11}, 634 [this, &Err](uint32_t Modi, const SymbolGroup &Strings) { 635 auto &Stream = Err(getPdb().getPDBDbiStream()); 636 637 const DbiModuleList &Modules = Stream.modules(); 638 for (const auto &F : Modules.source_files(Modi)) { 639 Strings.formatFromFileName(P, F); 640 } 641 }); 642 return Error::success(); 643 } 644 645 Error DumpOutputStyle::dumpSymbolStats() { 646 printHeader(P, "Module Stats"); 647 648 if (File.isPdb() && !getPdb().hasPDBDbiStream()) { 649 printStreamNotPresent("DBI"); 650 return Error::success(); 651 } 652 653 ExitOnError Err("Unexpected error processing modules: "); 654 655 StatCollection SymStats; 656 StatCollection ChunkStats; 657 658 Optional<PrintScope> Scope; 659 if (File.isPdb()) 660 Scope.emplace(P, 2); 661 662 iterateSymbolGroups(File, Scope, [&](uint32_t Modi, const SymbolGroup &SG) { 663 StatCollection SS = getSymbolStats(SG, SymStats); 664 StatCollection CS = getChunkStats(SG, ChunkStats); 665 666 if (SG.getFile().isPdb()) { 667 AutoIndent Indent(P); 668 auto Modules = cantFail(File.pdb().getPDBDbiStream()).modules(); 669 uint32_t ModCount = Modules.getModuleCount(); 670 DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi); 671 uint32_t StreamIdx = Desc.getModuleStreamIndex(); 672 673 if (StreamIdx == kInvalidStreamIndex) { 674 P.formatLine("Mod {0} (debug info not present): [{1}]", 675 fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)), 676 Desc.getModuleName()); 677 return; 678 } 679 P.formatLine("Stream {0}, {1} bytes", StreamIdx, 680 getPdb().getStreamByteSize(StreamIdx)); 681 682 printModuleDetailStats<SymbolKind>(P, "Symbols", SS); 683 printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS); 684 } 685 }); 686 687 if (SymStats.Totals.Count > 0) { 688 P.printLine(" Summary |"); 689 AutoIndent Indent(P, 4); 690 printModuleDetailStats<SymbolKind>(P, "Symbols", SymStats); 691 printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", ChunkStats); 692 } 693 694 return Error::success(); 695 } 696 697 Error DumpOutputStyle::dumpTypeStats() { 698 printHeader(P, "Type Record Stats"); 699 700 // Iterate the types, categorize by kind, accumulate size stats. 701 StatCollection TypeStats; 702 LazyRandomTypeCollection &Types = File.types(); 703 for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) { 704 CVType Type = Types.getType(*TI); 705 TypeStats.update(uint32_t(Type.kind()), Type.length()); 706 } 707 708 P.NewLine(); 709 P.formatLine(" Types"); 710 AutoIndent Indent(P); 711 P.formatLine("{0,14}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", "Total", 712 TypeStats.Totals.Count, TypeStats.Totals.Size, 713 (double)TypeStats.Totals.Size / TypeStats.Totals.Count); 714 P.formatLine("{0}", fmt_repeat('-', 74)); 715 716 for (const auto &K : TypeStats.getStatsSortedBySize()) { 717 P.formatLine("{0,14}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", 718 formatTypeLeafKind(TypeLeafKind(K.first)), K.second.Count, 719 K.second.Size, (double)K.second.Size / K.second.Count); 720 } 721 722 723 return Error::success(); 724 } 725 726 static bool isValidNamespaceIdentifier(StringRef S) { 727 if (S.empty()) 728 return false; 729 730 if (std::isdigit(S[0])) 731 return false; 732 733 return llvm::all_of(S, [](char C) { return std::isalnum(C); }); 734 } 735 736 namespace { 737 constexpr uint32_t kNoneUdtKind = 0; 738 constexpr uint32_t kSimpleUdtKind = 1; 739 constexpr uint32_t kUnknownUdtKind = 2; 740 const StringRef NoneLabel("<none type>"); 741 const StringRef SimpleLabel("<simple type>"); 742 const StringRef UnknownLabel("<unknown type>"); 743 744 } // namespace 745 746 static StringRef getUdtStatLabel(uint32_t Kind) { 747 if (Kind == kNoneUdtKind) 748 return NoneLabel; 749 750 if (Kind == kSimpleUdtKind) 751 return SimpleLabel; 752 753 if (Kind == kUnknownUdtKind) 754 return UnknownLabel; 755 756 return formatTypeLeafKind(static_cast<TypeLeafKind>(Kind)); 757 } 758 759 static uint32_t getLongestTypeLeafName(const StatCollection &Stats) { 760 size_t L = 0; 761 for (const auto &Stat : Stats.Individual) { 762 StringRef Label = getUdtStatLabel(Stat.first); 763 L = std::max(L, Label.size()); 764 } 765 return static_cast<uint32_t>(L); 766 } 767 768 Error DumpOutputStyle::dumpUdtStats() { 769 printHeader(P, "S_UDT Record Stats"); 770 771 if (File.isPdb() && !getPdb().hasPDBGlobalsStream()) { 772 printStreamNotPresent("Globals"); 773 return Error::success(); 774 } 775 776 StatCollection UdtStats; 777 StatCollection UdtTargetStats; 778 AutoIndent Indent(P, 4); 779 780 auto &TpiTypes = File.types(); 781 782 StringMap<StatCollection::Stat> NamespacedStats; 783 784 size_t LongestNamespace = 0; 785 auto HandleOneSymbol = [&](const CVSymbol &Sym) { 786 if (Sym.kind() != SymbolKind::S_UDT) 787 return; 788 UdtStats.update(SymbolKind::S_UDT, Sym.length()); 789 790 UDTSym UDT = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(Sym)); 791 792 uint32_t Kind = 0; 793 uint32_t RecordSize = 0; 794 795 if (UDT.Type.isNoneType()) 796 Kind = kNoneUdtKind; 797 else if (UDT.Type.isSimple()) 798 Kind = kSimpleUdtKind; 799 else if (Optional<CVType> T = TpiTypes.tryGetType(UDT.Type)) { 800 Kind = T->kind(); 801 RecordSize = T->length(); 802 } else 803 Kind = kUnknownUdtKind; 804 805 UdtTargetStats.update(Kind, RecordSize); 806 807 size_t Pos = UDT.Name.find("::"); 808 if (Pos == StringRef::npos) 809 return; 810 811 StringRef Scope = UDT.Name.take_front(Pos); 812 if (Scope.empty() || !isValidNamespaceIdentifier(Scope)) 813 return; 814 815 LongestNamespace = std::max(LongestNamespace, Scope.size()); 816 NamespacedStats[Scope].update(RecordSize); 817 }; 818 819 P.NewLine(); 820 821 if (File.isPdb()) { 822 auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream()); 823 auto ExpGlobals = getPdb().getPDBGlobalsStream(); 824 if (!ExpGlobals) 825 return ExpGlobals.takeError(); 826 827 for (uint32_t PubSymOff : ExpGlobals->getGlobalsTable()) { 828 CVSymbol Sym = SymbolRecords.readRecord(PubSymOff); 829 HandleOneSymbol(Sym); 830 } 831 } else { 832 for (const auto &Sec : File.symbol_groups()) { 833 for (const auto &SS : Sec.getDebugSubsections()) { 834 if (SS.kind() != DebugSubsectionKind::Symbols) 835 continue; 836 837 DebugSymbolsSubsectionRef Symbols; 838 BinaryStreamReader Reader(SS.getRecordData()); 839 cantFail(Symbols.initialize(Reader)); 840 for (const auto &S : Symbols) 841 HandleOneSymbol(S); 842 } 843 } 844 } 845 846 LongestNamespace += StringRef(" namespace ''").size(); 847 size_t LongestTypeLeafKind = getLongestTypeLeafName(UdtTargetStats); 848 size_t FieldWidth = std::max(LongestNamespace, LongestTypeLeafKind); 849 850 // Compute the max number of digits for count and size fields, including comma 851 // separators. 852 StringRef CountHeader("Count"); 853 StringRef SizeHeader("Size"); 854 size_t CD = NumDigits(UdtStats.Totals.Count); 855 CD += (CD - 1) / 3; 856 CD = std::max(CD, CountHeader.size()); 857 858 size_t SD = NumDigits(UdtStats.Totals.Size); 859 SD += (SD - 1) / 3; 860 SD = std::max(SD, SizeHeader.size()); 861 862 uint32_t TableWidth = FieldWidth + 3 + CD + 2 + SD + 1; 863 864 P.formatLine("{0} | {1} {2}", 865 fmt_align("Record Kind", AlignStyle::Right, FieldWidth), 866 fmt_align(CountHeader, AlignStyle::Right, CD), 867 fmt_align(SizeHeader, AlignStyle::Right, SD)); 868 869 P.formatLine("{0}", fmt_repeat('-', TableWidth)); 870 for (const auto &Stat : UdtTargetStats.getStatsSortedBySize()) { 871 StringRef Label = getUdtStatLabel(Stat.first); 872 P.formatLine("{0} | {1:N} {2:N}", 873 fmt_align(Label, AlignStyle::Right, FieldWidth), 874 fmt_align(Stat.second.Count, AlignStyle::Right, CD), 875 fmt_align(Stat.second.Size, AlignStyle::Right, SD)); 876 } 877 P.formatLine("{0}", fmt_repeat('-', TableWidth)); 878 P.formatLine("{0} | {1:N} {2:N}", 879 fmt_align("Total (S_UDT)", AlignStyle::Right, FieldWidth), 880 fmt_align(UdtStats.Totals.Count, AlignStyle::Right, CD), 881 fmt_align(UdtStats.Totals.Size, AlignStyle::Right, SD)); 882 P.formatLine("{0}", fmt_repeat('-', TableWidth)); 883 struct StrAndStat { 884 StringRef Key; 885 StatCollection::Stat Stat; 886 }; 887 888 // Print namespace stats in descending order of size. 889 std::vector<StrAndStat> NamespacedStatsSorted; 890 for (const auto &Stat : NamespacedStats) 891 NamespacedStatsSorted.push_back({Stat.getKey(), Stat.second}); 892 std::stable_sort(NamespacedStatsSorted.begin(), NamespacedStatsSorted.end(), 893 [](const StrAndStat &L, const StrAndStat &R) { 894 return L.Stat.Size > R.Stat.Size; 895 }); 896 for (const auto &Stat : NamespacedStatsSorted) { 897 std::string Label = formatv("namespace '{0}'", Stat.Key); 898 P.formatLine("{0} | {1:N} {2:N}", 899 fmt_align(Label, AlignStyle::Right, FieldWidth), 900 fmt_align(Stat.Stat.Count, AlignStyle::Right, CD), 901 fmt_align(Stat.Stat.Size, AlignStyle::Right, SD)); 902 } 903 return Error::success(); 904 } 905 906 static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start, 907 const LineColumnEntry &E) { 908 const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number 909 uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5; 910 911 // Let's try to keep it under 100 characters 912 constexpr uint32_t kMaxRowLength = 100; 913 // At least 3 spaces between columns. 914 uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3); 915 uint32_t ItemsLeft = E.LineNumbers.size(); 916 auto LineIter = E.LineNumbers.begin(); 917 while (ItemsLeft != 0) { 918 uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow); 919 for (uint32_t I = 0; I < RowColumns; ++I) { 920 LineInfo Line(LineIter->Flags); 921 std::string LineStr; 922 if (Line.isAlwaysStepInto()) 923 LineStr = "ASI"; 924 else if (Line.isNeverStepInto()) 925 LineStr = "NSI"; 926 else 927 LineStr = utostr(Line.getStartLine()); 928 char Statement = Line.isStatement() ? ' ' : '!'; 929 P.format("{0} {1:X-} {2} ", 930 fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber), 931 fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'), 932 Statement); 933 ++LineIter; 934 --ItemsLeft; 935 } 936 P.NewLine(); 937 } 938 } 939 940 Error DumpOutputStyle::dumpLines() { 941 printHeader(P, "Lines"); 942 943 if (File.isPdb() && !getPdb().hasPDBDbiStream()) { 944 printStreamNotPresent("DBI"); 945 return Error::success(); 946 } 947 948 uint32_t LastModi = UINT32_MAX; 949 uint32_t LastNameIndex = UINT32_MAX; 950 iterateModuleSubsections<DebugLinesSubsectionRef>( 951 File, PrintScope{P, 4}, 952 [this, &LastModi, &LastNameIndex](uint32_t Modi, 953 const SymbolGroup &Strings, 954 DebugLinesSubsectionRef &Lines) { 955 uint16_t Segment = Lines.header()->RelocSegment; 956 uint32_t Begin = Lines.header()->RelocOffset; 957 uint32_t End = Begin + Lines.header()->CodeSize; 958 for (const auto &Block : Lines) { 959 if (LastModi != Modi || LastNameIndex != Block.NameIndex) { 960 LastModi = Modi; 961 LastNameIndex = Block.NameIndex; 962 Strings.formatFromChecksumsOffset(P, Block.NameIndex); 963 } 964 965 AutoIndent Indent(P, 2); 966 P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End); 967 uint32_t Count = Block.LineNumbers.size(); 968 if (Lines.hasColumnInfo()) 969 P.format("line/column/addr entries = {0}", Count); 970 else 971 P.format("line/addr entries = {0}", Count); 972 973 P.NewLine(); 974 typesetLinesAndColumns(P, Begin, Block); 975 } 976 }); 977 978 return Error::success(); 979 } 980 981 Error DumpOutputStyle::dumpInlineeLines() { 982 printHeader(P, "Inlinee Lines"); 983 984 if (File.isPdb() && !getPdb().hasPDBDbiStream()) { 985 printStreamNotPresent("DBI"); 986 return Error::success(); 987 } 988 989 iterateModuleSubsections<DebugInlineeLinesSubsectionRef>( 990 File, PrintScope{P, 2}, 991 [this](uint32_t Modi, const SymbolGroup &Strings, 992 DebugInlineeLinesSubsectionRef &Lines) { 993 P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File"); 994 for (const auto &Entry : Lines) { 995 P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee, 996 fmtle(Entry.Header->SourceLineNum)); 997 Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true); 998 } 999 P.NewLine(); 1000 }); 1001 1002 return Error::success(); 1003 } 1004 1005 Error DumpOutputStyle::dumpXmi() { 1006 printHeader(P, "Cross Module Imports"); 1007 1008 if (File.isPdb() && !getPdb().hasPDBDbiStream()) { 1009 printStreamNotPresent("DBI"); 1010 return Error::success(); 1011 } 1012 1013 iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>( 1014 File, PrintScope{P, 2}, 1015 [this](uint32_t Modi, const SymbolGroup &Strings, 1016 DebugCrossModuleImportsSubsectionRef &Imports) { 1017 P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs"); 1018 1019 for (const auto &Xmi : Imports) { 1020 auto ExpectedModule = 1021 Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset); 1022 StringRef Module; 1023 SmallString<32> ModuleStorage; 1024 if (!ExpectedModule) { 1025 Module = "(unknown module)"; 1026 consumeError(ExpectedModule.takeError()); 1027 } else 1028 Module = *ExpectedModule; 1029 if (Module.size() > 32) { 1030 ModuleStorage = "..."; 1031 ModuleStorage += Module.take_back(32 - 3); 1032 Module = ModuleStorage; 1033 } 1034 std::vector<std::string> TIs; 1035 for (const auto I : Xmi.Imports) 1036 TIs.push_back(formatv("{0,+10:X+}", fmtle(I))); 1037 std::string Result = 1038 typesetItemList(TIs, P.getIndentLevel() + 35, 12, " "); 1039 P.formatLine("{0,+32} | {1}", Module, Result); 1040 } 1041 }); 1042 1043 return Error::success(); 1044 } 1045 1046 Error DumpOutputStyle::dumpXme() { 1047 printHeader(P, "Cross Module Exports"); 1048 1049 if (File.isPdb() && !getPdb().hasPDBDbiStream()) { 1050 printStreamNotPresent("DBI"); 1051 return Error::success(); 1052 } 1053 1054 iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>( 1055 File, PrintScope{P, 2}, 1056 [this](uint32_t Modi, const SymbolGroup &Strings, 1057 DebugCrossModuleExportsSubsectionRef &Exports) { 1058 P.formatLine("{0,-10} | {1}", "Local ID", "Global ID"); 1059 for (const auto &Export : Exports) { 1060 P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local), 1061 TypeIndex(Export.Global)); 1062 } 1063 }); 1064 1065 return Error::success(); 1066 } 1067 1068 std::string formatFrameType(object::frame_type FT) { 1069 switch (FT) { 1070 case object::frame_type::Fpo: 1071 return "FPO"; 1072 case object::frame_type::NonFpo: 1073 return "Non-FPO"; 1074 case object::frame_type::Trap: 1075 return "Trap"; 1076 case object::frame_type::Tss: 1077 return "TSS"; 1078 } 1079 return "<unknown>"; 1080 } 1081 1082 Error DumpOutputStyle::dumpOldFpo(PDBFile &File) { 1083 printHeader(P, "Old FPO Data"); 1084 1085 ExitOnError Err("Error dumping old fpo data:"); 1086 auto &Dbi = Err(File.getPDBDbiStream()); 1087 1088 if (!Dbi.hasOldFpoRecords()) { 1089 printStreamNotPresent("FPO"); 1090 return Error::success(); 1091 } 1092 1093 const FixedStreamArray<object::FpoData>& Records = Dbi.getOldFpoRecords(); 1094 1095 P.printLine(" RVA | Code | Locals | Params | Prolog | Saved Regs | Use " 1096 "BP | Has SEH | Frame Type"); 1097 1098 for (const object::FpoData &FD : Records) { 1099 P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,6} | {5,10} | {6,6} | " 1100 "{7,7} | {8,9}", 1101 uint32_t(FD.Offset), uint32_t(FD.Size), uint32_t(FD.NumLocals), 1102 uint32_t(FD.NumParams), FD.getPrologSize(), 1103 FD.getNumSavedRegs(), FD.useBP(), FD.hasSEH(), 1104 formatFrameType(FD.getFP())); 1105 } 1106 return Error::success(); 1107 } 1108 1109 Error DumpOutputStyle::dumpNewFpo(PDBFile &File) { 1110 printHeader(P, "New FPO Data"); 1111 1112 ExitOnError Err("Error dumping new fpo data:"); 1113 auto &Dbi = Err(File.getPDBDbiStream()); 1114 1115 if (!Dbi.hasNewFpoRecords()) { 1116 printStreamNotPresent("New FPO"); 1117 return Error::success(); 1118 } 1119 1120 const DebugFrameDataSubsectionRef& FDS = Dbi.getNewFpoRecords(); 1121 1122 P.printLine(" RVA | Code | Locals | Params | Stack | Prolog | Saved Regs " 1123 "| Has SEH | Has C++EH | Start | Program"); 1124 for (const FrameData &FD : FDS) { 1125 bool IsFuncStart = FD.Flags & FrameData::IsFunctionStart; 1126 bool HasEH = FD.Flags & FrameData::HasEH; 1127 bool HasSEH = FD.Flags & FrameData::HasSEH; 1128 1129 auto &StringTable = Err(File.getStringTable()); 1130 1131 auto Program = Err(StringTable.getStringForID(FD.FrameFunc)); 1132 P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,5} | {5,6} | {6,10} | " 1133 "{7,7} | {8,9} | {9,5} | {10}", 1134 uint32_t(FD.RvaStart), uint32_t(FD.CodeSize), 1135 uint32_t(FD.LocalSize), uint32_t(FD.ParamsSize), 1136 uint32_t(FD.MaxStackSize), uint16_t(FD.PrologSize), 1137 uint16_t(FD.SavedRegsSize), HasSEH, HasEH, IsFuncStart, 1138 Program); 1139 } 1140 return Error::success(); 1141 } 1142 1143 Error DumpOutputStyle::dumpFpo() { 1144 if (!File.isPdb()) { 1145 printStreamNotValidForObj(); 1146 return Error::success(); 1147 } 1148 1149 PDBFile &File = getPdb(); 1150 if (!File.hasPDBDbiStream()) { 1151 printStreamNotPresent("DBI"); 1152 return Error::success(); 1153 } 1154 1155 if (auto EC = dumpOldFpo(File)) 1156 return EC; 1157 if (auto EC = dumpNewFpo(File)) 1158 return EC; 1159 return Error::success(); 1160 } 1161 1162 Error DumpOutputStyle::dumpStringTableFromPdb() { 1163 AutoIndent Indent(P); 1164 auto IS = getPdb().getStringTable(); 1165 if (!IS) { 1166 P.formatLine("Not present in file"); 1167 consumeError(IS.takeError()); 1168 return Error::success(); 1169 } 1170 1171 if (opts::dump::DumpStringTable) { 1172 if (IS->name_ids().empty()) 1173 P.formatLine("Empty"); 1174 else { 1175 auto MaxID = 1176 std::max_element(IS->name_ids().begin(), IS->name_ids().end()); 1177 uint32_t Digits = NumDigits(*MaxID); 1178 1179 P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits), 1180 "String"); 1181 1182 std::vector<uint32_t> SortedIDs(IS->name_ids().begin(), 1183 IS->name_ids().end()); 1184 llvm::sort(SortedIDs); 1185 for (uint32_t I : SortedIDs) { 1186 auto ES = IS->getStringForID(I); 1187 llvm::SmallString<32> Str; 1188 if (!ES) { 1189 consumeError(ES.takeError()); 1190 Str = "Error reading string"; 1191 } else if (!ES->empty()) { 1192 Str.append("'"); 1193 Str.append(*ES); 1194 Str.append("'"); 1195 } 1196 1197 if (!Str.empty()) 1198 P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), 1199 Str); 1200 } 1201 } 1202 } 1203 1204 if (opts::dump::DumpStringTableDetails) { 1205 P.NewLine(); 1206 { 1207 P.printLine("String Table Header:"); 1208 AutoIndent Indent(P); 1209 P.formatLine("Signature: {0}", IS->getSignature()); 1210 P.formatLine("Hash Version: {0}", IS->getHashVersion()); 1211 P.formatLine("Name Buffer Size: {0}", IS->getByteSize()); 1212 P.NewLine(); 1213 } 1214 1215 BinaryStreamRef NameBuffer = IS->getStringTable().getBuffer(); 1216 ArrayRef<uint8_t> Contents; 1217 cantFail(NameBuffer.readBytes(0, NameBuffer.getLength(), Contents)); 1218 P.formatBinary("Name Buffer", Contents, 0); 1219 P.NewLine(); 1220 { 1221 P.printLine("Hash Table:"); 1222 AutoIndent Indent(P); 1223 P.formatLine("Bucket Count: {0}", IS->name_ids().size()); 1224 for (const auto &Entry : enumerate(IS->name_ids())) 1225 P.formatLine("Bucket[{0}] : {1}", Entry.index(), 1226 uint32_t(Entry.value())); 1227 P.formatLine("Name Count: {0}", IS->getNameCount()); 1228 } 1229 } 1230 return Error::success(); 1231 } 1232 1233 Error DumpOutputStyle::dumpStringTableFromObj() { 1234 iterateModuleSubsections<DebugStringTableSubsectionRef>( 1235 File, PrintScope{P, 4}, 1236 [&](uint32_t Modi, const SymbolGroup &Strings, 1237 DebugStringTableSubsectionRef &Strings2) { 1238 BinaryStreamRef StringTableBuffer = Strings2.getBuffer(); 1239 BinaryStreamReader Reader(StringTableBuffer); 1240 while (Reader.bytesRemaining() > 0) { 1241 StringRef Str; 1242 uint32_t Offset = Reader.getOffset(); 1243 cantFail(Reader.readCString(Str)); 1244 if (Str.empty()) 1245 continue; 1246 1247 P.formatLine("{0} | {1}", fmt_align(Offset, AlignStyle::Right, 4), 1248 Str); 1249 } 1250 }); 1251 return Error::success(); 1252 } 1253 1254 Error DumpOutputStyle::dumpNamedStreams() { 1255 printHeader(P, "Named Streams"); 1256 1257 if (File.isObj()) { 1258 printStreamNotValidForObj(); 1259 return Error::success(); 1260 } 1261 1262 AutoIndent Indent(P); 1263 ExitOnError Err("Invalid PDB File: "); 1264 1265 auto &IS = Err(File.pdb().getPDBInfoStream()); 1266 const NamedStreamMap &NS = IS.getNamedStreams(); 1267 for (const auto &Entry : NS.entries()) { 1268 P.printLine(Entry.getKey()); 1269 AutoIndent Indent2(P, 2); 1270 P.formatLine("Index: {0}", Entry.getValue()); 1271 P.formatLine("Size in bytes: {0}", 1272 File.pdb().getStreamByteSize(Entry.getValue())); 1273 } 1274 1275 return Error::success(); 1276 } 1277 1278 Error DumpOutputStyle::dumpStringTable() { 1279 printHeader(P, "String Table"); 1280 1281 if (File.isPdb()) 1282 return dumpStringTableFromPdb(); 1283 1284 return dumpStringTableFromObj(); 1285 } 1286 1287 static void buildDepSet(LazyRandomTypeCollection &Types, 1288 ArrayRef<TypeIndex> Indices, 1289 std::map<TypeIndex, CVType> &DepSet) { 1290 SmallVector<TypeIndex, 4> DepList; 1291 for (const auto &I : Indices) { 1292 TypeIndex TI(I); 1293 if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType()) 1294 continue; 1295 1296 CVType Type = Types.getType(TI); 1297 DepSet[TI] = Type; 1298 codeview::discoverTypeIndices(Type, DepList); 1299 buildDepSet(Types, DepList, DepSet); 1300 } 1301 } 1302 1303 static void 1304 dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, 1305 TypeReferenceTracker *RefTracker, uint32_t NumTypeRecords, 1306 uint32_t NumHashBuckets, 1307 FixedStreamArray<support::ulittle32_t> HashValues, 1308 TpiStream *Stream, bool Bytes, bool Extras) { 1309 1310 Printer.formatLine("Showing {0:N} records", NumTypeRecords); 1311 uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords); 1312 1313 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker, 1314 NumHashBuckets, HashValues, Stream); 1315 1316 if (auto EC = codeview::visitTypeStream(Types, V)) { 1317 Printer.formatLine("An error occurred dumping type records: {0}", 1318 toString(std::move(EC))); 1319 } 1320 } 1321 1322 static void dumpPartialTypeStream(LinePrinter &Printer, 1323 LazyRandomTypeCollection &Types, 1324 TypeReferenceTracker *RefTracker, 1325 TpiStream &Stream, ArrayRef<TypeIndex> TiList, 1326 bool Bytes, bool Extras, bool Deps) { 1327 uint32_t Width = 1328 NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); 1329 1330 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker, 1331 Stream.getNumHashBuckets(), Stream.getHashValues(), 1332 &Stream); 1333 1334 if (opts::dump::DumpTypeDependents) { 1335 // If we need to dump all dependents, then iterate each index and find 1336 // all dependents, adding them to a map ordered by TypeIndex. 1337 std::map<TypeIndex, CVType> DepSet; 1338 buildDepSet(Types, TiList, DepSet); 1339 1340 Printer.formatLine( 1341 "Showing {0:N} records and their dependents ({1:N} records total)", 1342 TiList.size(), DepSet.size()); 1343 1344 for (auto &Dep : DepSet) { 1345 if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V)) 1346 Printer.formatLine("An error occurred dumping type record {0}: {1}", 1347 Dep.first, toString(std::move(EC))); 1348 } 1349 } else { 1350 Printer.formatLine("Showing {0:N} records.", TiList.size()); 1351 1352 for (const auto &I : TiList) { 1353 TypeIndex TI(I); 1354 CVType Type = Types.getType(TI); 1355 if (auto EC = codeview::visitTypeRecord(Type, TI, V)) 1356 Printer.formatLine("An error occurred dumping type record {0}: {1}", TI, 1357 toString(std::move(EC))); 1358 } 1359 } 1360 } 1361 1362 Error DumpOutputStyle::dumpTypesFromObjectFile() { 1363 LazyRandomTypeCollection Types(100); 1364 1365 for (const auto &S : getObj().sections()) { 1366 StringRef SectionName; 1367 if (auto EC = S.getName(SectionName)) 1368 return errorCodeToError(EC); 1369 1370 // .debug$T is a standard CodeView type section, while .debug$P is the same 1371 // format but used for MSVC precompiled header object files. 1372 if (SectionName == ".debug$T") 1373 printHeader(P, "Types (.debug$T)"); 1374 else if (SectionName == ".debug$P") 1375 printHeader(P, "Precompiled Types (.debug$P)"); 1376 else 1377 continue; 1378 1379 StringRef Contents; 1380 if (auto EC = S.getContents(Contents)) 1381 return errorCodeToError(EC); 1382 1383 uint32_t Magic; 1384 BinaryStreamReader Reader(Contents, llvm::support::little); 1385 if (auto EC = Reader.readInteger(Magic)) 1386 return EC; 1387 if (Magic != COFF::DEBUG_SECTION_MAGIC) 1388 return make_error<StringError>("Invalid CodeView debug section.", 1389 inconvertibleErrorCode()); 1390 1391 Types.reset(Reader, 100); 1392 1393 if (opts::dump::DumpTypes) { 1394 dumpFullTypeStream(P, Types, RefTracker.get(), 0, 0, {}, nullptr, 1395 opts::dump::DumpTypeData, false); 1396 } else if (opts::dump::DumpTypeExtras) { 1397 auto LocalHashes = LocallyHashedType::hashTypeCollection(Types); 1398 auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types); 1399 assert(LocalHashes.size() == GlobalHashes.size()); 1400 1401 P.formatLine("Local / Global hashes:"); 1402 TypeIndex TI(TypeIndex::FirstNonSimpleIndex); 1403 for (const auto &H : zip(LocalHashes, GlobalHashes)) { 1404 AutoIndent Indent2(P); 1405 LocallyHashedType &L = std::get<0>(H); 1406 GloballyHashedType &G = std::get<1>(H); 1407 1408 P.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI, L, G); 1409 1410 ++TI; 1411 } 1412 P.NewLine(); 1413 } 1414 } 1415 1416 return Error::success(); 1417 } 1418 1419 Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { 1420 assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); 1421 1422 if (StreamIdx == StreamTPI) { 1423 printHeader(P, "Types (TPI Stream)"); 1424 } else if (StreamIdx == StreamIPI) { 1425 printHeader(P, "Types (IPI Stream)"); 1426 } 1427 1428 assert(!File.isObj()); 1429 1430 bool Present = false; 1431 bool DumpTypes = false; 1432 bool DumpBytes = false; 1433 bool DumpExtras = false; 1434 std::vector<uint32_t> Indices; 1435 if (StreamIdx == StreamTPI) { 1436 Present = getPdb().hasPDBTpiStream(); 1437 DumpTypes = opts::dump::DumpTypes; 1438 DumpBytes = opts::dump::DumpTypeData; 1439 DumpExtras = opts::dump::DumpTypeExtras; 1440 Indices.assign(opts::dump::DumpTypeIndex.begin(), 1441 opts::dump::DumpTypeIndex.end()); 1442 } else if (StreamIdx == StreamIPI) { 1443 Present = getPdb().hasPDBIpiStream(); 1444 DumpTypes = opts::dump::DumpIds; 1445 DumpBytes = opts::dump::DumpIdData; 1446 DumpExtras = opts::dump::DumpIdExtras; 1447 Indices.assign(opts::dump::DumpIdIndex.begin(), 1448 opts::dump::DumpIdIndex.end()); 1449 } 1450 1451 if (!Present) { 1452 printStreamNotPresent(StreamIdx == StreamTPI ? "TPI" : "IPI"); 1453 return Error::success(); 1454 } 1455 1456 AutoIndent Indent(P); 1457 ExitOnError Err("Unexpected error processing types: "); 1458 1459 auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream() 1460 : getPdb().getPDBIpiStream()); 1461 1462 auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids(); 1463 1464 // Only emit notes about referenced/unreferenced for types. 1465 TypeReferenceTracker *MaybeTracker = 1466 (StreamIdx == StreamTPI) ? RefTracker.get() : nullptr; 1467 1468 // Enable resolving forward decls. 1469 Stream.buildHashMap(); 1470 1471 if (DumpTypes || !Indices.empty()) { 1472 if (Indices.empty()) 1473 dumpFullTypeStream(P, Types, MaybeTracker, Stream.getNumTypeRecords(), 1474 Stream.getNumHashBuckets(), Stream.getHashValues(), 1475 &Stream, DumpBytes, DumpExtras); 1476 else { 1477 std::vector<TypeIndex> TiList(Indices.begin(), Indices.end()); 1478 dumpPartialTypeStream(P, Types, MaybeTracker, Stream, TiList, DumpBytes, 1479 DumpExtras, opts::dump::DumpTypeDependents); 1480 } 1481 } 1482 1483 if (DumpExtras) { 1484 P.NewLine(); 1485 1486 P.formatLine("Header Version: {0}", 1487 static_cast<uint32_t>(Stream.getTpiVersion())); 1488 P.formatLine("Hash Stream Index: {0}", Stream.getTypeHashStreamIndex()); 1489 P.formatLine("Aux Hash Stream Index: {0}", 1490 Stream.getTypeHashStreamAuxIndex()); 1491 P.formatLine("Hash Key Size: {0}", Stream.getHashKeySize()); 1492 P.formatLine("Num Hash Buckets: {0}", Stream.getNumHashBuckets()); 1493 1494 auto IndexOffsets = Stream.getTypeIndexOffsets(); 1495 P.formatLine("Type Index Offsets:"); 1496 for (const auto &IO : IndexOffsets) { 1497 AutoIndent Indent2(P); 1498 P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset)); 1499 } 1500 1501 if (getPdb().hasPDBStringTable()) { 1502 P.NewLine(); 1503 P.formatLine("Hash Adjusters:"); 1504 auto &Adjusters = Stream.getHashAdjusters(); 1505 auto &Strings = Err(getPdb().getStringTable()); 1506 for (const auto &A : Adjusters) { 1507 AutoIndent Indent2(P); 1508 auto ExpectedStr = Strings.getStringForID(A.first); 1509 TypeIndex TI(A.second); 1510 if (ExpectedStr) 1511 P.formatLine("`{0}` -> {1}", *ExpectedStr, TI); 1512 else { 1513 P.formatLine("unknown str id ({0}) -> {1}", A.first, TI); 1514 consumeError(ExpectedStr.takeError()); 1515 } 1516 } 1517 } 1518 } 1519 return Error::success(); 1520 } 1521 1522 Error DumpOutputStyle::dumpModuleSymsForObj() { 1523 printHeader(P, "Symbols"); 1524 1525 AutoIndent Indent(P); 1526 1527 ExitOnError Err("Unexpected error processing symbols: "); 1528 1529 auto &Types = File.types(); 1530 1531 SymbolVisitorCallbackPipeline Pipeline; 1532 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::ObjectFile); 1533 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types, Types); 1534 1535 Pipeline.addCallbackToPipeline(Deserializer); 1536 Pipeline.addCallbackToPipeline(Dumper); 1537 CVSymbolVisitor Visitor(Pipeline); 1538 1539 std::unique_ptr<llvm::Error> SymbolError; 1540 1541 iterateModuleSubsections<DebugSymbolsSubsectionRef>( 1542 File, PrintScope{P, 2}, 1543 [&](uint32_t Modi, const SymbolGroup &Strings, 1544 DebugSymbolsSubsectionRef &Symbols) { 1545 Dumper.setSymbolGroup(&Strings); 1546 for (auto Symbol : Symbols) { 1547 if (auto EC = Visitor.visitSymbolRecord(Symbol)) { 1548 SymbolError = llvm::make_unique<Error>(std::move(EC)); 1549 return; 1550 } 1551 } 1552 }); 1553 1554 if (SymbolError) 1555 return std::move(*SymbolError); 1556 1557 return Error::success(); 1558 } 1559 1560 Error DumpOutputStyle::dumpModuleSymsForPdb() { 1561 printHeader(P, "Symbols"); 1562 1563 if (File.isPdb() && !getPdb().hasPDBDbiStream()) { 1564 printStreamNotPresent("DBI"); 1565 return Error::success(); 1566 } 1567 1568 AutoIndent Indent(P); 1569 ExitOnError Err("Unexpected error processing symbols: "); 1570 1571 auto &Ids = File.ids(); 1572 auto &Types = File.types(); 1573 1574 iterateSymbolGroups( 1575 File, PrintScope{P, 2}, [&](uint32_t I, const SymbolGroup &Strings) { 1576 auto ExpectedModS = getModuleDebugStream(File.pdb(), I); 1577 if (!ExpectedModS) { 1578 P.formatLine("Error loading module stream {0}. {1}", I, 1579 toString(ExpectedModS.takeError())); 1580 return; 1581 } 1582 1583 ModuleDebugStreamRef &ModS = *ExpectedModS; 1584 1585 SymbolVisitorCallbackPipeline Pipeline; 1586 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); 1587 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Strings, 1588 Ids, Types); 1589 1590 Pipeline.addCallbackToPipeline(Deserializer); 1591 Pipeline.addCallbackToPipeline(Dumper); 1592 CVSymbolVisitor Visitor(Pipeline); 1593 auto SS = ModS.getSymbolsSubstream(); 1594 if (auto EC = 1595 Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) { 1596 P.formatLine("Error while processing symbol records. {0}", 1597 toString(std::move(EC))); 1598 return; 1599 } 1600 }); 1601 return Error::success(); 1602 } 1603 1604 Error DumpOutputStyle::dumpTypeRefStats() { 1605 printHeader(P, "Type Reference Statistics"); 1606 AutoIndent Indent(P); 1607 1608 // Sum the byte size of all type records, and the size and count of all 1609 // referenced records. 1610 size_t TotalRecs = File.types().size(); 1611 size_t RefRecs = 0; 1612 size_t TotalBytes = 0; 1613 size_t RefBytes = 0; 1614 auto &Types = File.types(); 1615 for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) { 1616 CVType Type = File.types().getType(*TI); 1617 TotalBytes += Type.length(); 1618 if (RefTracker->isTypeReferenced(*TI)) { 1619 ++RefRecs; 1620 RefBytes += Type.length(); 1621 } 1622 } 1623 1624 P.formatLine("Records referenced: {0:N} / {1:N} {2:P}", RefRecs, TotalRecs, 1625 (double)RefRecs / TotalRecs); 1626 P.formatLine("Bytes referenced: {0:N} / {1:N} {2:P}", RefBytes, TotalBytes, 1627 (double)RefBytes / TotalBytes); 1628 1629 return Error::success(); 1630 } 1631 1632 Error DumpOutputStyle::dumpGSIRecords() { 1633 printHeader(P, "GSI Records"); 1634 1635 if (File.isObj()) { 1636 printStreamNotValidForObj(); 1637 return Error::success(); 1638 } 1639 1640 if (!getPdb().hasPDBSymbolStream()) { 1641 printStreamNotPresent("GSI Common Symbol"); 1642 return Error::success(); 1643 } 1644 1645 AutoIndent Indent(P); 1646 1647 auto &Records = cantFail(getPdb().getPDBSymbolStream()); 1648 auto &Types = File.types(); 1649 auto &Ids = File.ids(); 1650 1651 P.printLine("Records"); 1652 SymbolVisitorCallbackPipeline Pipeline; 1653 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); 1654 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); 1655 1656 Pipeline.addCallbackToPipeline(Deserializer); 1657 Pipeline.addCallbackToPipeline(Dumper); 1658 CVSymbolVisitor Visitor(Pipeline); 1659 1660 BinaryStreamRef SymStream = Records.getSymbolArray().getUnderlyingStream(); 1661 if (auto E = Visitor.visitSymbolStream(Records.getSymbolArray(), 0)) 1662 return E; 1663 return Error::success(); 1664 } 1665 1666 Error DumpOutputStyle::dumpGlobals() { 1667 printHeader(P, "Global Symbols"); 1668 1669 if (File.isObj()) { 1670 printStreamNotValidForObj(); 1671 return Error::success(); 1672 } 1673 1674 if (!getPdb().hasPDBGlobalsStream()) { 1675 printStreamNotPresent("Globals"); 1676 return Error::success(); 1677 } 1678 1679 AutoIndent Indent(P); 1680 ExitOnError Err("Error dumping globals stream: "); 1681 auto &Globals = Err(getPdb().getPDBGlobalsStream()); 1682 1683 if (opts::dump::DumpGlobalNames.empty()) { 1684 const GSIHashTable &Table = Globals.getGlobalsTable(); 1685 Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras)); 1686 } else { 1687 SymbolStream &SymRecords = cantFail(getPdb().getPDBSymbolStream()); 1688 auto &Types = File.types(); 1689 auto &Ids = File.ids(); 1690 1691 SymbolVisitorCallbackPipeline Pipeline; 1692 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); 1693 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); 1694 1695 Pipeline.addCallbackToPipeline(Deserializer); 1696 Pipeline.addCallbackToPipeline(Dumper); 1697 CVSymbolVisitor Visitor(Pipeline); 1698 1699 using ResultEntryType = std::pair<uint32_t, CVSymbol>; 1700 for (StringRef Name : opts::dump::DumpGlobalNames) { 1701 AutoIndent Indent(P); 1702 P.formatLine("Global Name `{0}`", Name); 1703 std::vector<ResultEntryType> Results = 1704 Globals.findRecordsByName(Name, SymRecords); 1705 if (Results.empty()) { 1706 AutoIndent Indent(P); 1707 P.printLine("(no matching records found)"); 1708 continue; 1709 } 1710 1711 for (ResultEntryType Result : Results) { 1712 if (auto E = Visitor.visitSymbolRecord(Result.second, Result.first)) 1713 return E; 1714 } 1715 } 1716 } 1717 return Error::success(); 1718 } 1719 1720 Error DumpOutputStyle::dumpPublics() { 1721 printHeader(P, "Public Symbols"); 1722 1723 if (File.isObj()) { 1724 printStreamNotValidForObj(); 1725 return Error::success(); 1726 } 1727 1728 if (!getPdb().hasPDBPublicsStream()) { 1729 printStreamNotPresent("Publics"); 1730 return Error::success(); 1731 } 1732 1733 AutoIndent Indent(P); 1734 ExitOnError Err("Error dumping publics stream: "); 1735 auto &Publics = Err(getPdb().getPDBPublicsStream()); 1736 1737 const GSIHashTable &PublicsTable = Publics.getPublicsTable(); 1738 if (opts::dump::DumpPublicExtras) { 1739 P.printLine("Publics Header"); 1740 AutoIndent Indent(P); 1741 P.formatLine("sym hash = {0}, thunk table addr = {1}", Publics.getSymHash(), 1742 formatSegmentOffset(Publics.getThunkTableSection(), 1743 Publics.getThunkTableOffset())); 1744 } 1745 Err(dumpSymbolsFromGSI(PublicsTable, opts::dump::DumpPublicExtras)); 1746 1747 // Skip the rest if we aren't dumping extras. 1748 if (!opts::dump::DumpPublicExtras) 1749 return Error::success(); 1750 1751 P.formatLine("Address Map"); 1752 { 1753 // These are offsets into the publics stream sorted by secidx:secrel. 1754 AutoIndent Indent2(P); 1755 for (uint32_t Addr : Publics.getAddressMap()) 1756 P.formatLine("off = {0}", Addr); 1757 } 1758 1759 // The thunk map is optional debug info used for ILT thunks. 1760 if (!Publics.getThunkMap().empty()) { 1761 P.formatLine("Thunk Map"); 1762 AutoIndent Indent2(P); 1763 for (uint32_t Addr : Publics.getThunkMap()) 1764 P.formatLine("{0:x8}", Addr); 1765 } 1766 1767 // The section offsets table appears to be empty when incremental linking 1768 // isn't in use. 1769 if (!Publics.getSectionOffsets().empty()) { 1770 P.formatLine("Section Offsets"); 1771 AutoIndent Indent2(P); 1772 for (const SectionOffset &SO : Publics.getSectionOffsets()) 1773 P.formatLine("{0:x4}:{1:x8}", uint16_t(SO.Isect), uint32_t(SO.Off)); 1774 } 1775 1776 return Error::success(); 1777 } 1778 1779 Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table, 1780 bool HashExtras) { 1781 auto ExpectedSyms = getPdb().getPDBSymbolStream(); 1782 if (!ExpectedSyms) 1783 return ExpectedSyms.takeError(); 1784 auto &Types = File.types(); 1785 auto &Ids = File.ids(); 1786 1787 if (HashExtras) { 1788 P.printLine("GSI Header"); 1789 AutoIndent Indent(P); 1790 P.formatLine("sig = {0:X}, hdr = {1:X}, hr size = {2}, num buckets = {3}", 1791 Table.getVerSignature(), Table.getVerHeader(), 1792 Table.getHashRecordSize(), Table.getNumBuckets()); 1793 } 1794 1795 { 1796 P.printLine("Records"); 1797 SymbolVisitorCallbackPipeline Pipeline; 1798 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); 1799 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); 1800 1801 Pipeline.addCallbackToPipeline(Deserializer); 1802 Pipeline.addCallbackToPipeline(Dumper); 1803 CVSymbolVisitor Visitor(Pipeline); 1804 1805 1806 BinaryStreamRef SymStream = 1807 ExpectedSyms->getSymbolArray().getUnderlyingStream(); 1808 for (uint32_t PubSymOff : Table) { 1809 Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff); 1810 if (!Sym) 1811 return Sym.takeError(); 1812 if (auto E = Visitor.visitSymbolRecord(*Sym, PubSymOff)) 1813 return E; 1814 } 1815 } 1816 1817 // Return early if we aren't dumping public hash table and address map info. 1818 if (HashExtras) { 1819 P.formatLine("Hash Entries"); 1820 { 1821 AutoIndent Indent2(P); 1822 for (const PSHashRecord &HR : Table.HashRecords) 1823 P.formatLine("off = {0}, refcnt = {1}", uint32_t(HR.Off), 1824 uint32_t(HR.CRef)); 1825 } 1826 1827 P.formatLine("Hash Buckets"); 1828 { 1829 AutoIndent Indent2(P); 1830 for (uint32_t Hash : Table.HashBuckets) 1831 P.formatLine("{0:x8}", Hash); 1832 } 1833 } 1834 1835 return Error::success(); 1836 } 1837 1838 static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel, 1839 OMFSegDescFlags Flags) { 1840 std::vector<std::string> Opts; 1841 if (Flags == OMFSegDescFlags::None) 1842 return "none"; 1843 1844 PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read"); 1845 PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write"); 1846 PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute"); 1847 PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr"); 1848 PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector"); 1849 PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr"); 1850 PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group"); 1851 return typesetItemList(Opts, IndentLevel, 4, " | "); 1852 } 1853 1854 Error DumpOutputStyle::dumpSectionHeaders() { 1855 dumpSectionHeaders("Section Headers", DbgHeaderType::SectionHdr); 1856 dumpSectionHeaders("Original Section Headers", DbgHeaderType::SectionHdrOrig); 1857 return Error::success(); 1858 } 1859 1860 void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) { 1861 printHeader(P, Label); 1862 1863 if (File.isObj()) { 1864 printStreamNotValidForObj(); 1865 return; 1866 } 1867 1868 if (!getPdb().hasPDBDbiStream()) { 1869 printStreamNotPresent("DBI"); 1870 return; 1871 } 1872 1873 AutoIndent Indent(P); 1874 ExitOnError Err("Error dumping section headers: "); 1875 std::unique_ptr<MappedBlockStream> Stream; 1876 ArrayRef<object::coff_section> Headers; 1877 auto ExpectedHeaders = loadSectionHeaders(getPdb(), Type); 1878 if (!ExpectedHeaders) { 1879 P.printLine(toString(ExpectedHeaders.takeError())); 1880 return; 1881 } 1882 std::tie(Stream, Headers) = std::move(*ExpectedHeaders); 1883 1884 uint32_t I = 1; 1885 for (const auto &Header : Headers) { 1886 P.NewLine(); 1887 P.formatLine("SECTION HEADER #{0}", I); 1888 P.formatLine("{0,8} name", Header.Name); 1889 P.formatLine("{0,8:X-} virtual size", uint32_t(Header.VirtualSize)); 1890 P.formatLine("{0,8:X-} virtual address", uint32_t(Header.VirtualAddress)); 1891 P.formatLine("{0,8:X-} size of raw data", uint32_t(Header.SizeOfRawData)); 1892 P.formatLine("{0,8:X-} file pointer to raw data", 1893 uint32_t(Header.PointerToRawData)); 1894 P.formatLine("{0,8:X-} file pointer to relocation table", 1895 uint32_t(Header.PointerToRelocations)); 1896 P.formatLine("{0,8:X-} file pointer to line numbers", 1897 uint32_t(Header.PointerToLinenumbers)); 1898 P.formatLine("{0,8:X-} number of relocations", 1899 uint32_t(Header.NumberOfRelocations)); 1900 P.formatLine("{0,8:X-} number of line numbers", 1901 uint32_t(Header.NumberOfLinenumbers)); 1902 P.formatLine("{0,8:X-} flags", uint32_t(Header.Characteristics)); 1903 AutoIndent IndentMore(P, 9); 1904 P.formatLine("{0}", formatSectionCharacteristics( 1905 P.getIndentLevel(), Header.Characteristics, 1, "")); 1906 ++I; 1907 } 1908 return; 1909 } 1910 1911 Error DumpOutputStyle::dumpSectionContribs() { 1912 printHeader(P, "Section Contributions"); 1913 1914 if (File.isObj()) { 1915 printStreamNotValidForObj(); 1916 return Error::success(); 1917 } 1918 1919 if (!getPdb().hasPDBDbiStream()) { 1920 printStreamNotPresent("DBI"); 1921 return Error::success(); 1922 } 1923 1924 AutoIndent Indent(P); 1925 ExitOnError Err("Error dumping section contributions: "); 1926 1927 auto &Dbi = Err(getPdb().getPDBDbiStream()); 1928 1929 class Visitor : public ISectionContribVisitor { 1930 public: 1931 Visitor(LinePrinter &P, ArrayRef<std::string> Names) : P(P), Names(Names) { 1932 auto Max = std::max_element( 1933 Names.begin(), Names.end(), 1934 [](StringRef S1, StringRef S2) { return S1.size() < S2.size(); }); 1935 MaxNameLen = (Max == Names.end() ? 0 : Max->size()); 1936 } 1937 void visit(const SectionContrib &SC) override { 1938 dumpSectionContrib(P, SC, Names, MaxNameLen); 1939 } 1940 void visit(const SectionContrib2 &SC) override { 1941 dumpSectionContrib(P, SC, Names, MaxNameLen); 1942 } 1943 1944 private: 1945 LinePrinter &P; 1946 uint32_t MaxNameLen; 1947 ArrayRef<std::string> Names; 1948 }; 1949 1950 std::vector<std::string> Names = getSectionNames(getPdb()); 1951 Visitor V(P, makeArrayRef(Names)); 1952 Dbi.visitSectionContributions(V); 1953 return Error::success(); 1954 } 1955 1956 Error DumpOutputStyle::dumpSectionMap() { 1957 printHeader(P, "Section Map"); 1958 1959 if (File.isObj()) { 1960 printStreamNotValidForObj(); 1961 return Error::success(); 1962 } 1963 1964 if (!getPdb().hasPDBDbiStream()) { 1965 printStreamNotPresent("DBI"); 1966 return Error::success(); 1967 } 1968 1969 AutoIndent Indent(P); 1970 ExitOnError Err("Error dumping section map: "); 1971 1972 auto &Dbi = Err(getPdb().getPDBDbiStream()); 1973 1974 uint32_t I = 0; 1975 for (auto &M : Dbi.getSectionMap()) { 1976 P.formatLine( 1977 "Section {0:4} | ovl = {1}, group = {2}, frame = {3}, name = {4}", I, 1978 fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName)); 1979 P.formatLine(" class = {0}, offset = {1}, size = {2}", 1980 fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength)); 1981 P.formatLine(" flags = {0}", 1982 formatSegMapDescriptorFlag( 1983 P.getIndentLevel() + 13, 1984 static_cast<OMFSegDescFlags>(uint16_t(M.Flags)))); 1985 ++I; 1986 } 1987 return Error::success(); 1988 } 1989