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