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