1 //===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "DumpOutputStyle.h" 11 12 #include "FormatUtil.h" 13 #include "InputFile.h" 14 #include "MinimalSymbolDumper.h" 15 #include "MinimalTypeDumper.h" 16 #include "StreamUtil.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/DebugInlineeLinesSubsection.h" 26 #include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" 27 #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" 28 #include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" 29 #include "llvm/DebugInfo/CodeView/Formatters.h" 30 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" 31 #include "llvm/DebugInfo/CodeView/Line.h" 32 #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" 33 #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" 34 #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" 35 #include "llvm/DebugInfo/CodeView/TypeHashing.h" 36 #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" 37 #include "llvm/DebugInfo/MSF/MappedBlockStream.h" 38 #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" 39 #include "llvm/DebugInfo/PDB/Native/DbiStream.h" 40 #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" 41 #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" 42 #include "llvm/DebugInfo/PDB/Native/InfoStream.h" 43 #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" 44 #include "llvm/DebugInfo/PDB/Native/PDBFile.h" 45 #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" 46 #include "llvm/DebugInfo/PDB/Native/RawError.h" 47 #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" 48 #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" 49 #include "llvm/DebugInfo/PDB/Native/TpiStream.h" 50 #include "llvm/Object/COFF.h" 51 #include "llvm/Support/BinaryStreamReader.h" 52 #include "llvm/Support/FormatAdapters.h" 53 #include "llvm/Support/FormatVariadic.h" 54 55 #include <cctype> 56 57 using namespace llvm; 58 using namespace llvm::codeview; 59 using namespace llvm::msf; 60 using namespace llvm::pdb; 61 62 DumpOutputStyle::DumpOutputStyle(InputFile &File) 63 : File(File), P(2, false, outs()) {} 64 65 PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); } 66 object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); } 67 68 Error DumpOutputStyle::dump() { 69 if (opts::dump::DumpSummary) { 70 if (auto EC = dumpFileSummary()) 71 return EC; 72 P.NewLine(); 73 } 74 75 if (opts::dump::DumpStreams) { 76 if (auto EC = dumpStreamSummary()) 77 return EC; 78 P.NewLine(); 79 } 80 81 if (opts::dump::DumpSymbolStats) { 82 if (auto EC = dumpSymbolStats()) 83 return EC; 84 P.NewLine(); 85 } 86 87 if (opts::dump::DumpUdtStats) { 88 if (auto EC = dumpUdtStats()) 89 return EC; 90 P.NewLine(); 91 } 92 93 if (opts::dump::DumpStringTable) { 94 if (auto EC = dumpStringTable()) 95 return EC; 96 P.NewLine(); 97 } 98 99 if (opts::dump::DumpModules) { 100 if (auto EC = dumpModules()) 101 return EC; 102 } 103 104 if (opts::dump::DumpModuleFiles) { 105 if (auto EC = dumpModuleFiles()) 106 return EC; 107 } 108 109 if (opts::dump::DumpLines) { 110 if (auto EC = dumpLines()) 111 return EC; 112 } 113 114 if (opts::dump::DumpInlineeLines) { 115 if (auto EC = dumpInlineeLines()) 116 return EC; 117 } 118 119 if (opts::dump::DumpXmi) { 120 if (auto EC = dumpXmi()) 121 return EC; 122 } 123 124 if (opts::dump::DumpXme) { 125 if (auto EC = dumpXme()) 126 return EC; 127 } 128 129 if (File.isObj()) { 130 if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || 131 opts::dump::DumpTypeExtras) 132 if (auto EC = dumpTypesFromObjectFile()) 133 return EC; 134 } else { 135 if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || 136 opts::dump::DumpTypeExtras) { 137 if (auto EC = dumpTpiStream(StreamTPI)) 138 return EC; 139 } 140 141 if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() || 142 opts::dump::DumpIdExtras) { 143 if (auto EC = dumpTpiStream(StreamIPI)) 144 return EC; 145 } 146 } 147 148 if (opts::dump::DumpGlobals) { 149 if (auto EC = dumpGlobals()) 150 return EC; 151 } 152 153 if (opts::dump::DumpPublics) { 154 if (auto EC = dumpPublics()) 155 return EC; 156 } 157 158 if (opts::dump::DumpSymbols) { 159 auto EC = File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj(); 160 if (EC) 161 return EC; 162 } 163 164 if (opts::dump::DumpSectionHeaders) { 165 if (auto EC = dumpSectionHeaders()) 166 return EC; 167 } 168 169 if (opts::dump::DumpSectionContribs) { 170 if (auto EC = dumpSectionContribs()) 171 return EC; 172 } 173 174 if (opts::dump::DumpSectionMap) { 175 if (auto EC = dumpSectionMap()) 176 return EC; 177 } 178 179 return Error::success(); 180 } 181 182 static void printHeader(LinePrinter &P, const Twine &S) { 183 P.NewLine(); 184 P.formatLine("{0,=60}", S); 185 P.formatLine("{0}", fmt_repeat('=', 60)); 186 } 187 188 Error DumpOutputStyle::dumpFileSummary() { 189 printHeader(P, "Summary"); 190 191 ExitOnError Err("Invalid PDB Format: "); 192 193 AutoIndent Indent(P); 194 if (File.isObj()) { 195 P.formatLine("Dumping File summary is not valid for object files"); 196 return Error::success(); 197 } 198 199 P.formatLine("Block Size: {0}", getPdb().getBlockSize()); 200 P.formatLine("Number of blocks: {0}", getPdb().getBlockCount()); 201 P.formatLine("Number of streams: {0}", getPdb().getNumStreams()); 202 203 auto &PS = Err(getPdb().getPDBInfoStream()); 204 P.formatLine("Signature: {0}", PS.getSignature()); 205 P.formatLine("Age: {0}", PS.getAge()); 206 P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid)); 207 P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures())); 208 P.formatLine("Has Debug Info: {0}", getPdb().hasPDBDbiStream()); 209 P.formatLine("Has Types: {0}", getPdb().hasPDBTpiStream()); 210 P.formatLine("Has IDs: {0}", getPdb().hasPDBIpiStream()); 211 P.formatLine("Has Globals: {0}", getPdb().hasPDBGlobalsStream()); 212 P.formatLine("Has Publics: {0}", getPdb().hasPDBPublicsStream()); 213 if (getPdb().hasPDBDbiStream()) { 214 auto &DBI = Err(getPdb().getPDBDbiStream()); 215 P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked()); 216 P.formatLine("Has conflicting types: {0}", DBI.hasCTypes()); 217 P.formatLine("Is stripped: {0}", DBI.isStripped()); 218 } 219 220 return Error::success(); 221 } 222 223 static StatCollection getSymbolStats(const SymbolGroup &SG, 224 StatCollection &CumulativeStats) { 225 StatCollection Stats; 226 if (SG.getFile().isPdb()) { 227 // For PDB files, all symbols are packed into one stream. 228 for (const auto &S : SG.getPdbModuleStream().symbols(nullptr)) { 229 Stats.update(S.kind(), S.length()); 230 CumulativeStats.update(S.kind(), S.length()); 231 } 232 return Stats; 233 } 234 235 for (const auto &SS : SG.getDebugSubsections()) { 236 // For object files, all symbols are spread across multiple Symbol 237 // subsections of a given .debug$S section. 238 if (SS.kind() != DebugSubsectionKind::Symbols) 239 continue; 240 DebugSymbolsSubsectionRef Symbols; 241 BinaryStreamReader Reader(SS.getRecordData()); 242 cantFail(Symbols.initialize(Reader)); 243 for (const auto &S : Symbols) { 244 Stats.update(S.kind(), S.length()); 245 CumulativeStats.update(S.kind(), S.length()); 246 } 247 } 248 return Stats; 249 } 250 251 static StatCollection getChunkStats(const SymbolGroup &SG, 252 StatCollection &CumulativeStats) { 253 StatCollection Stats; 254 for (const auto &Chunk : SG.getDebugSubsections()) { 255 Stats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength()); 256 CumulativeStats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength()); 257 } 258 return Stats; 259 } 260 261 static inline std::string formatModuleDetailKind(DebugSubsectionKind K) { 262 return formatChunkKind(K, false); 263 } 264 265 static inline std::string formatModuleDetailKind(SymbolKind K) { 266 return formatSymbolKind(K); 267 } 268 269 template <typename Kind> 270 static void printModuleDetailStats(LinePrinter &P, StringRef Label, 271 const StatCollection &Stats) { 272 P.NewLine(); 273 P.formatLine(" {0}", Label); 274 AutoIndent Indent(P); 275 P.formatLine("{0,40}: {1,7} entries ({2,8} bytes)", "Total", 276 Stats.Totals.Count, Stats.Totals.Size); 277 P.formatLine("{0}", fmt_repeat('-', 74)); 278 for (const auto &K : Stats.Individual) { 279 std::string KindName = formatModuleDetailKind(Kind(K.first)); 280 P.formatLine("{0,40}: {1,7} entries ({2,8} bytes)", KindName, 281 K.second.Count, K.second.Size); 282 } 283 } 284 285 static bool isMyCode(const SymbolGroup &Group) { 286 if (Group.getFile().isObj()) 287 return true; 288 289 StringRef Name = Group.name(); 290 if (Name.startswith("Import:")) 291 return false; 292 if (Name.endswith_lower(".dll")) 293 return false; 294 if (Name.equals_lower("* linker *")) 295 return false; 296 if (Name.startswith_lower("f:\\binaries\\Intermediate\\vctools")) 297 return false; 298 if (Name.startswith_lower("f:\\dd\\vctools\\crt")) 299 return false; 300 return true; 301 } 302 303 static bool shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group) { 304 if (opts::dump::JustMyCode && !isMyCode(Group)) 305 return false; 306 307 // If the arg was not specified on the command line, always dump all modules. 308 if (opts::dump::DumpModi.getNumOccurrences() == 0) 309 return true; 310 311 // Otherwise, only dump if this is the same module specified. 312 return (opts::dump::DumpModi == Idx); 313 } 314 315 Error DumpOutputStyle::dumpStreamSummary() { 316 printHeader(P, "Streams"); 317 318 AutoIndent Indent(P); 319 if (File.isObj()) { 320 P.formatLine("Dumping streams is not valid for object files"); 321 return Error::success(); 322 } 323 324 if (StreamPurposes.empty()) 325 discoverStreamPurposes(getPdb(), StreamPurposes); 326 327 uint32_t StreamCount = getPdb().getNumStreams(); 328 uint32_t MaxStreamSize = getPdb().getMaxStreamSize(); 329 330 for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { 331 P.formatLine( 332 "Stream {0} ({1} bytes): [{2}]", 333 fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)), 334 fmt_align(getPdb().getStreamByteSize(StreamIdx), AlignStyle::Right, 335 NumDigits(MaxStreamSize)), 336 StreamPurposes[StreamIdx].getLongName()); 337 338 if (opts::dump::DumpStreamBlocks) { 339 auto Blocks = getPdb().getStreamBlockList(StreamIdx); 340 std::vector<uint32_t> BV(Blocks.begin(), Blocks.end()); 341 P.formatLine(" {0} Blocks: [{1}]", 342 fmt_repeat(' ', NumDigits(StreamCount)), 343 make_range(BV.begin(), BV.end())); 344 } 345 } 346 347 return Error::success(); 348 } 349 350 static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File, 351 uint32_t Index) { 352 ExitOnError Err("Unexpected error: "); 353 354 auto &Dbi = Err(File.getPDBDbiStream()); 355 const auto &Modules = Dbi.modules(); 356 auto Modi = Modules.getModuleDescriptor(Index); 357 358 uint16_t ModiStream = Modi.getModuleStreamIndex(); 359 if (ModiStream == kInvalidStreamIndex) 360 return make_error<RawError>(raw_error_code::no_stream, 361 "Module stream not present"); 362 363 auto ModStreamData = File.createIndexedStream(ModiStream); 364 365 ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); 366 if (auto EC = ModS.reload()) 367 return make_error<RawError>(raw_error_code::corrupt_file, 368 "Invalid module stream"); 369 370 return std::move(ModS); 371 } 372 373 template <typename CallbackT> 374 static void 375 iterateOneModule(InputFile &File, const Optional<PrintScope> &HeaderScope, 376 const SymbolGroup &SG, uint32_t Modi, CallbackT Callback) { 377 if (HeaderScope) { 378 HeaderScope->P.formatLine( 379 "Mod {0:4} | `{1}`: ", 380 fmt_align(Modi, AlignStyle::Right, HeaderScope->LabelWidth), SG.name()); 381 } 382 383 AutoIndent Indent(HeaderScope); 384 Callback(Modi, SG); 385 } 386 387 template <typename CallbackT> 388 static void iterateSymbolGroups(InputFile &Input, 389 const Optional<PrintScope> &HeaderScope, 390 CallbackT Callback) { 391 AutoIndent Indent(HeaderScope); 392 393 ExitOnError Err("Unexpected error processing modules: "); 394 395 if (opts::dump::DumpModi.getNumOccurrences() > 0) { 396 assert(opts::dump::DumpModi.getNumOccurrences() == 1); 397 uint32_t Modi = opts::dump::DumpModi; 398 SymbolGroup SG(&Input, Modi); 399 iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(Modi)), SG, 400 Modi, Callback); 401 return; 402 } 403 404 uint32_t I = 0; 405 406 for (const auto &SG : Input.symbol_groups()) { 407 if (shouldDumpSymbolGroup(I, SG)) 408 iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(I)), SG, I, 409 Callback); 410 411 ++I; 412 } 413 } 414 415 template <typename SubsectionT> 416 static void iterateModuleSubsections( 417 InputFile &File, const Optional<PrintScope> &HeaderScope, 418 llvm::function_ref<void(uint32_t, const SymbolGroup &, SubsectionT &)> 419 Callback) { 420 421 iterateSymbolGroups(File, HeaderScope, 422 [&](uint32_t Modi, const SymbolGroup &SG) { 423 for (const auto &SS : SG.getDebugSubsections()) { 424 SubsectionT Subsection; 425 426 if (SS.kind() != Subsection.kind()) 427 continue; 428 429 BinaryStreamReader Reader(SS.getRecordData()); 430 if (auto EC = Subsection.initialize(Reader)) 431 continue; 432 Callback(Modi, SG, Subsection); 433 } 434 }); 435 } 436 437 Error DumpOutputStyle::dumpModules() { 438 printHeader(P, "Modules"); 439 AutoIndent Indent(P); 440 441 if (File.isObj()) { 442 P.formatLine("Dumping modules is not supported for object files"); 443 return Error::success(); 444 } 445 446 if (!getPdb().hasPDBDbiStream()) { 447 P.formatLine("DBI Stream not present"); 448 return Error::success(); 449 } 450 451 ExitOnError Err("Unexpected error processing modules: "); 452 453 auto &Stream = Err(getPdb().getPDBDbiStream()); 454 455 const DbiModuleList &Modules = Stream.modules(); 456 iterateSymbolGroups( 457 File, PrintScope{P, 11}, [&](uint32_t Modi, const SymbolGroup &Strings) { 458 auto Desc = Modules.getModuleDescriptor(Modi); 459 P.formatLine("Obj: `{0}`: ", Desc.getObjFileName()); 460 P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}", 461 Desc.getModuleStreamIndex(), Desc.getNumberOfFiles(), 462 Desc.hasECInfo()); 463 StringRef PdbFilePath = 464 Err(Stream.getECName(Desc.getPdbFilePathNameIndex())); 465 StringRef SrcFilePath = 466 Err(Stream.getECName(Desc.getSourceFileNameIndex())); 467 P.formatLine("pdb file ni: {0} `{1}`, src file ni: {2} `{3}`", 468 Desc.getPdbFilePathNameIndex(), PdbFilePath, 469 Desc.getSourceFileNameIndex(), SrcFilePath); 470 }); 471 return Error::success(); 472 } 473 474 Error DumpOutputStyle::dumpModuleFiles() { 475 printHeader(P, "Files"); 476 477 if (File.isObj()) { 478 P.formatLine("Dumping files is not valid for object files"); 479 return Error::success(); 480 } 481 482 ExitOnError Err("Unexpected error processing modules: "); 483 484 iterateSymbolGroups(File, PrintScope{P, 11}, 485 [this, &Err](uint32_t Modi, const SymbolGroup &Strings) { 486 auto &Stream = Err(getPdb().getPDBDbiStream()); 487 488 const DbiModuleList &Modules = Stream.modules(); 489 for (const auto &F : Modules.source_files(Modi)) { 490 Strings.formatFromFileName(P, F); 491 } 492 }); 493 return Error::success(); 494 } 495 496 Error DumpOutputStyle::dumpSymbolStats() { 497 printHeader(P, "Module Stats"); 498 499 ExitOnError Err("Unexpected error processing modules: "); 500 501 StatCollection SymStats; 502 StatCollection ChunkStats; 503 504 Optional<PrintScope> Scope; 505 if (File.isPdb()) 506 Scope.emplace(P, 2); 507 508 iterateSymbolGroups(File, Scope, [&](uint32_t Modi, const SymbolGroup &SG) { 509 StatCollection SS = getSymbolStats(SG, SymStats); 510 StatCollection CS = getChunkStats(SG, ChunkStats); 511 512 if (SG.getFile().isPdb()) { 513 AutoIndent Indent(P); 514 auto Modules = cantFail(File.pdb().getPDBDbiStream()).modules(); 515 uint32_t ModCount = Modules.getModuleCount(); 516 DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi); 517 uint32_t StreamIdx = Desc.getModuleStreamIndex(); 518 519 if (StreamIdx == kInvalidStreamIndex) { 520 P.formatLine("Mod {0} (debug info not present): [{1}]", 521 fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)), 522 Desc.getModuleName()); 523 return; 524 } 525 P.formatLine("Stream {0}, {1} bytes", StreamIdx, 526 getPdb().getStreamByteSize(StreamIdx)); 527 528 printModuleDetailStats<SymbolKind>(P, "Symbols", SS); 529 printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS); 530 } 531 }); 532 533 P.printLine(" Summary |"); 534 AutoIndent Indent(P, 4); 535 if (SymStats.Totals.Count > 0) { 536 printModuleDetailStats<SymbolKind>(P, "Symbols", SymStats); 537 printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", ChunkStats); 538 } 539 540 return Error::success(); 541 } 542 543 static bool isValidNamespaceIdentifier(StringRef S) { 544 if (S.empty()) 545 return false; 546 547 if (std::isdigit(S[0])) 548 return false; 549 550 return llvm::all_of(S, [](char C) { return std::isalnum(C); }); 551 } 552 553 namespace { 554 constexpr uint32_t kNoneUdtKind = 0; 555 constexpr uint32_t kSimpleUdtKind = 1; 556 constexpr uint32_t kUnknownUdtKind = 2; 557 const StringRef NoneLabel("<none type>"); 558 const StringRef SimpleLabel("<simple type>"); 559 const StringRef UnknownLabel("<unknown type>"); 560 561 } // namespace 562 563 static StringRef getUdtStatLabel(uint32_t Kind) { 564 if (Kind == kNoneUdtKind) 565 return NoneLabel; 566 567 if (Kind == kSimpleUdtKind) 568 return SimpleLabel; 569 570 if (Kind == kUnknownUdtKind) 571 return UnknownLabel; 572 573 return formatTypeLeafKind(static_cast<TypeLeafKind>(Kind)); 574 } 575 576 static uint32_t getLongestTypeLeafName(const StatCollection &Stats) { 577 size_t L = 0; 578 for (const auto &Stat : Stats.Individual) { 579 StringRef Label = getUdtStatLabel(Stat.first); 580 L = std::max(L, Label.size()); 581 } 582 return static_cast<uint32_t>(L); 583 } 584 585 Error DumpOutputStyle::dumpUdtStats() { 586 printHeader(P, "S_UDT Record Stats"); 587 588 StatCollection UdtStats; 589 StatCollection UdtTargetStats; 590 AutoIndent Indent(P, 4); 591 592 auto &TpiTypes = File.types(); 593 594 StringMap<StatCollection::Stat> NamespacedStats; 595 596 size_t LongestNamespace = 0; 597 auto HandleOneSymbol = [&](const CVSymbol &Sym) { 598 if (Sym.kind() != SymbolKind::S_UDT) 599 return; 600 UdtStats.update(SymbolKind::S_UDT, Sym.length()); 601 602 UDTSym UDT = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(Sym)); 603 604 uint32_t Kind = 0; 605 uint32_t RecordSize = 0; 606 607 if (UDT.Type.isNoneType()) 608 Kind = kNoneUdtKind; 609 else if (UDT.Type.isSimple()) 610 Kind = kSimpleUdtKind; 611 else if (Optional<CVType> T = TpiTypes.tryGetType(UDT.Type)) { 612 Kind = T->kind(); 613 RecordSize = T->length(); 614 } else 615 Kind = kUnknownUdtKind; 616 617 UdtTargetStats.update(Kind, RecordSize); 618 619 size_t Pos = UDT.Name.find("::"); 620 if (Pos == StringRef::npos) 621 return; 622 623 StringRef Scope = UDT.Name.take_front(Pos); 624 if (Scope.empty() || !isValidNamespaceIdentifier(Scope)) 625 return; 626 627 LongestNamespace = std::max(LongestNamespace, Scope.size()); 628 NamespacedStats[Scope].update(RecordSize); 629 }; 630 631 P.NewLine(); 632 633 if (File.isPdb()) { 634 if (!getPdb().hasPDBGlobalsStream()) { 635 P.printLine("- Error: globals stream not present"); 636 return Error::success(); 637 } 638 639 auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream()); 640 auto ExpGlobals = getPdb().getPDBGlobalsStream(); 641 if (!ExpGlobals) 642 return ExpGlobals.takeError(); 643 644 for (uint32_t PubSymOff : ExpGlobals->getGlobalsTable()) { 645 CVSymbol Sym = SymbolRecords.readRecord(PubSymOff); 646 HandleOneSymbol(Sym); 647 } 648 } else { 649 for (const auto &Sec : File.symbol_groups()) { 650 for (const auto &SS : Sec.getDebugSubsections()) { 651 if (SS.kind() != DebugSubsectionKind::Symbols) 652 continue; 653 654 DebugSymbolsSubsectionRef Symbols; 655 BinaryStreamReader Reader(SS.getRecordData()); 656 cantFail(Symbols.initialize(Reader)); 657 for (const auto &S : Symbols) 658 HandleOneSymbol(S); 659 } 660 } 661 } 662 663 LongestNamespace += StringRef(" namespace ''").size(); 664 size_t LongestTypeLeafKind = getLongestTypeLeafName(UdtTargetStats); 665 size_t FieldWidth = std::max(LongestNamespace, LongestTypeLeafKind); 666 667 // Compute the max number of digits for count and size fields, including comma 668 // separators. 669 StringRef CountHeader("Count"); 670 StringRef SizeHeader("Size"); 671 size_t CD = NumDigits(UdtStats.Totals.Count); 672 CD += (CD - 1) / 3; 673 CD = std::max(CD, CountHeader.size()); 674 675 size_t SD = NumDigits(UdtStats.Totals.Size); 676 SD += (SD - 1) / 3; 677 SD = std::max(SD, SizeHeader.size()); 678 679 uint32_t TableWidth = FieldWidth + 3 + CD + 2 + SD + 1; 680 681 P.formatLine("{0} | {1} {2}", 682 fmt_align("Record Kind", AlignStyle::Right, FieldWidth), 683 fmt_align(CountHeader, AlignStyle::Right, CD), 684 fmt_align(SizeHeader, AlignStyle::Right, SD)); 685 686 P.formatLine("{0}", fmt_repeat('-', TableWidth)); 687 for (const auto &Stat : UdtTargetStats.Individual) { 688 StringRef Label = getUdtStatLabel(Stat.first); 689 P.formatLine("{0} | {1:N} {2:N}", 690 fmt_align(Label, AlignStyle::Right, FieldWidth), 691 fmt_align(Stat.second.Count, AlignStyle::Right, CD), 692 fmt_align(Stat.second.Size, AlignStyle::Right, SD)); 693 } 694 P.formatLine("{0}", fmt_repeat('-', TableWidth)); 695 P.formatLine("{0} | {1:N} {2:N}", 696 fmt_align("Total (S_UDT)", AlignStyle::Right, FieldWidth), 697 fmt_align(UdtStats.Totals.Count, AlignStyle::Right, CD), 698 fmt_align(UdtStats.Totals.Size, AlignStyle::Right, SD)); 699 P.formatLine("{0}", fmt_repeat('-', TableWidth)); 700 for (const auto &Stat : NamespacedStats) { 701 std::string Label = formatv("namespace '{0}'", Stat.getKey()); 702 P.formatLine("{0} | {1:N} {2:N}", 703 fmt_align(Label, AlignStyle::Right, FieldWidth), 704 fmt_align(Stat.second.Count, AlignStyle::Right, CD), 705 fmt_align(Stat.second.Size, AlignStyle::Right, SD)); 706 } 707 return Error::success(); 708 } 709 710 static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start, 711 const LineColumnEntry &E) { 712 const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number 713 uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5; 714 715 // Let's try to keep it under 100 characters 716 constexpr uint32_t kMaxRowLength = 100; 717 // At least 3 spaces between columns. 718 uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3); 719 uint32_t ItemsLeft = E.LineNumbers.size(); 720 auto LineIter = E.LineNumbers.begin(); 721 while (ItemsLeft != 0) { 722 uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow); 723 for (uint32_t I = 0; I < RowColumns; ++I) { 724 LineInfo Line(LineIter->Flags); 725 std::string LineStr; 726 if (Line.isAlwaysStepInto()) 727 LineStr = "ASI"; 728 else if (Line.isNeverStepInto()) 729 LineStr = "NSI"; 730 else 731 LineStr = utostr(Line.getStartLine()); 732 char Statement = Line.isStatement() ? ' ' : '!'; 733 P.format("{0} {1:X-} {2} ", 734 fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber), 735 fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'), 736 Statement); 737 ++LineIter; 738 --ItemsLeft; 739 } 740 P.NewLine(); 741 } 742 } 743 744 Error DumpOutputStyle::dumpLines() { 745 printHeader(P, "Lines"); 746 747 uint32_t LastModi = UINT32_MAX; 748 uint32_t LastNameIndex = UINT32_MAX; 749 iterateModuleSubsections<DebugLinesSubsectionRef>( 750 File, PrintScope{P, 4}, 751 [this, &LastModi, &LastNameIndex](uint32_t Modi, 752 const SymbolGroup &Strings, 753 DebugLinesSubsectionRef &Lines) { 754 uint16_t Segment = Lines.header()->RelocSegment; 755 uint32_t Begin = Lines.header()->RelocOffset; 756 uint32_t End = Begin + Lines.header()->CodeSize; 757 for (const auto &Block : Lines) { 758 if (LastModi != Modi || LastNameIndex != Block.NameIndex) { 759 LastModi = Modi; 760 LastNameIndex = Block.NameIndex; 761 Strings.formatFromChecksumsOffset(P, Block.NameIndex); 762 } 763 764 AutoIndent Indent(P, 2); 765 P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End); 766 uint32_t Count = Block.LineNumbers.size(); 767 if (Lines.hasColumnInfo()) 768 P.format("line/column/addr entries = {0}", Count); 769 else 770 P.format("line/addr entries = {0}", Count); 771 772 P.NewLine(); 773 typesetLinesAndColumns(P, Begin, Block); 774 } 775 }); 776 777 return Error::success(); 778 } 779 780 Error DumpOutputStyle::dumpInlineeLines() { 781 printHeader(P, "Inlinee Lines"); 782 783 iterateModuleSubsections<DebugInlineeLinesSubsectionRef>( 784 File, PrintScope{P, 2}, 785 [this](uint32_t Modi, const SymbolGroup &Strings, 786 DebugInlineeLinesSubsectionRef &Lines) { 787 P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File"); 788 for (const auto &Entry : Lines) { 789 P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee, 790 fmtle(Entry.Header->SourceLineNum)); 791 Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true); 792 } 793 P.NewLine(); 794 }); 795 796 return Error::success(); 797 } 798 799 Error DumpOutputStyle::dumpXmi() { 800 printHeader(P, "Cross Module Imports"); 801 iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>( 802 File, PrintScope{P, 2}, 803 [this](uint32_t Modi, const SymbolGroup &Strings, 804 DebugCrossModuleImportsSubsectionRef &Imports) { 805 P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs"); 806 807 for (const auto &Xmi : Imports) { 808 auto ExpectedModule = 809 Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset); 810 StringRef Module; 811 SmallString<32> ModuleStorage; 812 if (!ExpectedModule) { 813 Module = "(unknown module)"; 814 consumeError(ExpectedModule.takeError()); 815 } else 816 Module = *ExpectedModule; 817 if (Module.size() > 32) { 818 ModuleStorage = "..."; 819 ModuleStorage += Module.take_back(32 - 3); 820 Module = ModuleStorage; 821 } 822 std::vector<std::string> TIs; 823 for (const auto I : Xmi.Imports) 824 TIs.push_back(formatv("{0,+10:X+}", fmtle(I))); 825 std::string Result = 826 typesetItemList(TIs, P.getIndentLevel() + 35, 12, " "); 827 P.formatLine("{0,+32} | {1}", Module, Result); 828 } 829 }); 830 831 return Error::success(); 832 } 833 834 Error DumpOutputStyle::dumpXme() { 835 printHeader(P, "Cross Module Exports"); 836 837 iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>( 838 File, PrintScope{P, 2}, 839 [this](uint32_t Modi, const SymbolGroup &Strings, 840 DebugCrossModuleExportsSubsectionRef &Exports) { 841 P.formatLine("{0,-10} | {1}", "Local ID", "Global ID"); 842 for (const auto &Export : Exports) { 843 P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local), 844 TypeIndex(Export.Global)); 845 } 846 }); 847 848 return Error::success(); 849 } 850 851 Error DumpOutputStyle::dumpStringTable() { 852 printHeader(P, "String Table"); 853 854 if (File.isObj()) { 855 P.formatLine("Dumping string table is not supported for object files"); 856 return Error::success(); 857 } 858 859 AutoIndent Indent(P); 860 auto IS = getPdb().getStringTable(); 861 if (!IS) { 862 P.formatLine("Not present in file"); 863 consumeError(IS.takeError()); 864 return Error::success(); 865 } 866 867 if (IS->name_ids().empty()) { 868 P.formatLine("Empty"); 869 return Error::success(); 870 } 871 872 auto MaxID = std::max_element(IS->name_ids().begin(), IS->name_ids().end()); 873 uint32_t Digits = NumDigits(*MaxID); 874 875 P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits), 876 "String"); 877 878 std::vector<uint32_t> SortedIDs(IS->name_ids().begin(), IS->name_ids().end()); 879 std::sort(SortedIDs.begin(), SortedIDs.end()); 880 for (uint32_t I : SortedIDs) { 881 auto ES = IS->getStringForID(I); 882 llvm::SmallString<32> Str; 883 if (!ES) { 884 consumeError(ES.takeError()); 885 Str = "Error reading string"; 886 } else if (!ES->empty()) { 887 Str.append("'"); 888 Str.append(*ES); 889 Str.append("'"); 890 } 891 892 if (!Str.empty()) 893 P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), Str); 894 } 895 return Error::success(); 896 } 897 898 static void buildDepSet(LazyRandomTypeCollection &Types, 899 ArrayRef<TypeIndex> Indices, 900 std::map<TypeIndex, CVType> &DepSet) { 901 SmallVector<TypeIndex, 4> DepList; 902 for (const auto &I : Indices) { 903 TypeIndex TI(I); 904 if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType()) 905 continue; 906 907 CVType Type = Types.getType(TI); 908 DepSet[TI] = Type; 909 codeview::discoverTypeIndices(Type, DepList); 910 buildDepSet(Types, DepList, DepSet); 911 } 912 } 913 914 static void 915 dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, 916 uint32_t NumTypeRecords, uint32_t NumHashBuckets, 917 FixedStreamArray<support::ulittle32_t> HashValues, 918 bool Bytes, bool Extras) { 919 920 Printer.formatLine("Showing {0:N} records", NumTypeRecords); 921 uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords); 922 923 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, 924 NumHashBuckets, HashValues); 925 926 if (auto EC = codeview::visitTypeStream(Types, V)) { 927 Printer.formatLine("An error occurred dumping type records: {0}", 928 toString(std::move(EC))); 929 } 930 } 931 932 static void dumpPartialTypeStream(LinePrinter &Printer, 933 LazyRandomTypeCollection &Types, 934 TpiStream &Stream, ArrayRef<TypeIndex> TiList, 935 bool Bytes, bool Extras, bool Deps) { 936 uint32_t Width = 937 NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); 938 939 MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, 940 Stream.getNumHashBuckets(), Stream.getHashValues()); 941 942 if (opts::dump::DumpTypeDependents) { 943 // If we need to dump all dependents, then iterate each index and find 944 // all dependents, adding them to a map ordered by TypeIndex. 945 std::map<TypeIndex, CVType> DepSet; 946 buildDepSet(Types, TiList, DepSet); 947 948 Printer.formatLine( 949 "Showing {0:N} records and their dependents ({1:N} records total)", 950 TiList.size(), DepSet.size()); 951 952 for (auto &Dep : DepSet) { 953 if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V)) 954 Printer.formatLine("An error occurred dumping type record {0}: {1}", 955 Dep.first, toString(std::move(EC))); 956 } 957 } else { 958 Printer.formatLine("Showing {0:N} records.", TiList.size()); 959 960 for (const auto &I : TiList) { 961 TypeIndex TI(I); 962 CVType Type = Types.getType(TI); 963 if (auto EC = codeview::visitTypeRecord(Type, TI, V)) 964 Printer.formatLine("An error occurred dumping type record {0}: {1}", TI, 965 toString(std::move(EC))); 966 } 967 } 968 } 969 970 Error DumpOutputStyle::dumpTypesFromObjectFile() { 971 LazyRandomTypeCollection Types(100); 972 973 for (const auto &S : getObj().sections()) { 974 StringRef SectionName; 975 if (auto EC = S.getName(SectionName)) 976 return errorCodeToError(EC); 977 978 if (SectionName != ".debug$T") 979 continue; 980 StringRef Contents; 981 if (auto EC = S.getContents(Contents)) 982 return errorCodeToError(EC); 983 984 uint32_t Magic; 985 BinaryStreamReader Reader(Contents, llvm::support::little); 986 if (auto EC = Reader.readInteger(Magic)) 987 return EC; 988 if (Magic != COFF::DEBUG_SECTION_MAGIC) 989 return make_error<StringError>("Invalid CodeView debug section.", 990 inconvertibleErrorCode()); 991 992 Types.reset(Reader, 100); 993 994 if (opts::dump::DumpTypes) { 995 dumpFullTypeStream(P, Types, 0, 0, {}, opts::dump::DumpTypeData, false); 996 } else if (opts::dump::DumpTypeExtras) { 997 auto LocalHashes = LocallyHashedType::hashTypeCollection(Types); 998 auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types); 999 assert(LocalHashes.size() == GlobalHashes.size()); 1000 1001 P.formatLine("Local / Global hashes:"); 1002 TypeIndex TI(TypeIndex::FirstNonSimpleIndex); 1003 for (const auto &H : zip(LocalHashes, GlobalHashes)) { 1004 AutoIndent Indent2(P); 1005 LocallyHashedType &L = std::get<0>(H); 1006 GloballyHashedType &G = std::get<1>(H); 1007 1008 P.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI, L, G); 1009 1010 ++TI; 1011 } 1012 P.NewLine(); 1013 } 1014 } 1015 1016 return Error::success(); 1017 } 1018 1019 Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { 1020 assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); 1021 1022 if (StreamIdx == StreamTPI) { 1023 printHeader(P, "Types (TPI Stream)"); 1024 } else if (StreamIdx == StreamIPI) { 1025 printHeader(P, "Types (IPI Stream)"); 1026 } 1027 1028 AutoIndent Indent(P); 1029 assert(!File.isObj()); 1030 1031 bool Present = false; 1032 bool DumpTypes = false; 1033 bool DumpBytes = false; 1034 bool DumpExtras = false; 1035 std::vector<uint32_t> Indices; 1036 if (StreamIdx == StreamTPI) { 1037 Present = getPdb().hasPDBTpiStream(); 1038 DumpTypes = opts::dump::DumpTypes; 1039 DumpBytes = opts::dump::DumpTypeData; 1040 DumpExtras = opts::dump::DumpTypeExtras; 1041 Indices.assign(opts::dump::DumpTypeIndex.begin(), 1042 opts::dump::DumpTypeIndex.end()); 1043 } else if (StreamIdx == StreamIPI) { 1044 Present = getPdb().hasPDBIpiStream(); 1045 DumpTypes = opts::dump::DumpIds; 1046 DumpBytes = opts::dump::DumpIdData; 1047 DumpExtras = opts::dump::DumpIdExtras; 1048 Indices.assign(opts::dump::DumpIdIndex.begin(), 1049 opts::dump::DumpIdIndex.end()); 1050 } 1051 1052 if (!Present) { 1053 P.formatLine("Stream not present"); 1054 return Error::success(); 1055 } 1056 1057 ExitOnError Err("Unexpected error processing types: "); 1058 1059 auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream() 1060 : getPdb().getPDBIpiStream()); 1061 1062 auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids(); 1063 1064 if (DumpTypes || !Indices.empty()) { 1065 if (Indices.empty()) 1066 dumpFullTypeStream(P, Types, Stream.getNumTypeRecords(), 1067 Stream.getNumHashBuckets(), Stream.getHashValues(), 1068 DumpBytes, DumpExtras); 1069 else { 1070 std::vector<TypeIndex> TiList(Indices.begin(), Indices.end()); 1071 dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras, 1072 opts::dump::DumpTypeDependents); 1073 } 1074 } 1075 1076 if (DumpExtras) { 1077 P.NewLine(); 1078 auto IndexOffsets = Stream.getTypeIndexOffsets(); 1079 P.formatLine("Type Index Offsets:"); 1080 for (const auto &IO : IndexOffsets) { 1081 AutoIndent Indent2(P); 1082 P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset)); 1083 } 1084 1085 P.NewLine(); 1086 P.formatLine("Hash Adjusters:"); 1087 auto &Adjusters = Stream.getHashAdjusters(); 1088 auto &Strings = Err(getPdb().getStringTable()); 1089 for (const auto &A : Adjusters) { 1090 AutoIndent Indent2(P); 1091 auto ExpectedStr = Strings.getStringForID(A.first); 1092 TypeIndex TI(A.second); 1093 if (ExpectedStr) 1094 P.formatLine("`{0}` -> {1}", *ExpectedStr, TI); 1095 else { 1096 P.formatLine("unknown str id ({0}) -> {1}", A.first, TI); 1097 consumeError(ExpectedStr.takeError()); 1098 } 1099 } 1100 } 1101 return Error::success(); 1102 } 1103 1104 Error DumpOutputStyle::dumpModuleSymsForObj() { 1105 printHeader(P, "Symbols"); 1106 1107 AutoIndent Indent(P); 1108 1109 ExitOnError Err("Unexpected error processing symbols: "); 1110 1111 auto &Types = File.types(); 1112 1113 SymbolVisitorCallbackPipeline Pipeline; 1114 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::ObjectFile); 1115 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types, Types); 1116 1117 Pipeline.addCallbackToPipeline(Deserializer); 1118 Pipeline.addCallbackToPipeline(Dumper); 1119 CVSymbolVisitor Visitor(Pipeline); 1120 1121 std::unique_ptr<llvm::Error> SymbolError; 1122 1123 iterateModuleSubsections<DebugSymbolsSubsectionRef>( 1124 File, PrintScope{P, 2}, 1125 [&](uint32_t Modi, const SymbolGroup &Strings, 1126 DebugSymbolsSubsectionRef &Symbols) { 1127 for (auto Symbol : Symbols) { 1128 if (auto EC = Visitor.visitSymbolRecord(Symbol)) { 1129 SymbolError = llvm::make_unique<Error>(std::move(EC)); 1130 return; 1131 } 1132 } 1133 }); 1134 1135 if (SymbolError) 1136 return std::move(*SymbolError); 1137 1138 return Error::success(); 1139 } 1140 1141 Error DumpOutputStyle::dumpModuleSymsForPdb() { 1142 printHeader(P, "Symbols"); 1143 1144 AutoIndent Indent(P); 1145 if (!getPdb().hasPDBDbiStream()) { 1146 P.formatLine("DBI Stream not present"); 1147 return Error::success(); 1148 } 1149 1150 ExitOnError Err("Unexpected error processing symbols: "); 1151 1152 auto &Ids = File.ids(); 1153 auto &Types = File.types(); 1154 1155 iterateSymbolGroups( 1156 File, PrintScope{P, 2}, [&](uint32_t I, const SymbolGroup &Strings) { 1157 auto ExpectedModS = getModuleDebugStream(File.pdb(), I); 1158 if (!ExpectedModS) { 1159 P.formatLine("Error loading module stream {0}. {1}", I, 1160 toString(ExpectedModS.takeError())); 1161 return; 1162 } 1163 1164 ModuleDebugStreamRef &ModS = *ExpectedModS; 1165 1166 SymbolVisitorCallbackPipeline Pipeline; 1167 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); 1168 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, 1169 Types); 1170 1171 Pipeline.addCallbackToPipeline(Deserializer); 1172 Pipeline.addCallbackToPipeline(Dumper); 1173 CVSymbolVisitor Visitor(Pipeline); 1174 auto SS = ModS.getSymbolsSubstream(); 1175 if (auto EC = 1176 Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) { 1177 P.formatLine("Error while processing symbol records. {0}", 1178 toString(std::move(EC))); 1179 return; 1180 } 1181 }); 1182 return Error::success(); 1183 } 1184 1185 Error DumpOutputStyle::dumpGlobals() { 1186 printHeader(P, "Global Symbols"); 1187 AutoIndent Indent(P); 1188 1189 if (File.isObj()) { 1190 P.formatLine("Dumping Globals is not supported for object files"); 1191 return Error::success(); 1192 } 1193 1194 if (!getPdb().hasPDBGlobalsStream()) { 1195 P.formatLine("Globals stream not present"); 1196 return Error::success(); 1197 } 1198 ExitOnError Err("Error dumping globals stream: "); 1199 auto &Globals = Err(getPdb().getPDBGlobalsStream()); 1200 1201 const GSIHashTable &Table = Globals.getGlobalsTable(); 1202 Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras)); 1203 return Error::success(); 1204 } 1205 1206 Error DumpOutputStyle::dumpPublics() { 1207 printHeader(P, "Public Symbols"); 1208 AutoIndent Indent(P); 1209 1210 if (File.isObj()) { 1211 P.formatLine("Dumping Globals is not supported for object files"); 1212 return Error::success(); 1213 } 1214 1215 if (!getPdb().hasPDBPublicsStream()) { 1216 P.formatLine("Publics stream not present"); 1217 return Error::success(); 1218 } 1219 ExitOnError Err("Error dumping publics stream: "); 1220 auto &Publics = Err(getPdb().getPDBPublicsStream()); 1221 1222 const GSIHashTable &PublicsTable = Publics.getPublicsTable(); 1223 if (opts::dump::DumpPublicExtras) { 1224 P.printLine("Publics Header"); 1225 AutoIndent Indent(P); 1226 P.formatLine("sym hash = {0}, thunk table addr = {1}", Publics.getSymHash(), 1227 formatSegmentOffset(Publics.getThunkTableSection(), 1228 Publics.getThunkTableOffset())); 1229 } 1230 Err(dumpSymbolsFromGSI(PublicsTable, opts::dump::DumpPublicExtras)); 1231 1232 // Skip the rest if we aren't dumping extras. 1233 if (!opts::dump::DumpPublicExtras) 1234 return Error::success(); 1235 1236 P.formatLine("Address Map"); 1237 { 1238 // These are offsets into the publics stream sorted by secidx:secrel. 1239 AutoIndent Indent2(P); 1240 for (uint32_t Addr : Publics.getAddressMap()) 1241 P.formatLine("off = {0}", Addr); 1242 } 1243 1244 // The thunk map is optional debug info used for ILT thunks. 1245 if (!Publics.getThunkMap().empty()) { 1246 P.formatLine("Thunk Map"); 1247 AutoIndent Indent2(P); 1248 for (uint32_t Addr : Publics.getThunkMap()) 1249 P.formatLine("{0:x8}", Addr); 1250 } 1251 1252 // The section offsets table appears to be empty when incremental linking 1253 // isn't in use. 1254 if (!Publics.getSectionOffsets().empty()) { 1255 P.formatLine("Section Offsets"); 1256 AutoIndent Indent2(P); 1257 for (const SectionOffset &SO : Publics.getSectionOffsets()) 1258 P.formatLine("{0:x4}:{1:x8}", uint16_t(SO.Isect), uint32_t(SO.Off)); 1259 } 1260 1261 return Error::success(); 1262 } 1263 1264 Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table, 1265 bool HashExtras) { 1266 auto ExpectedSyms = getPdb().getPDBSymbolStream(); 1267 if (!ExpectedSyms) 1268 return ExpectedSyms.takeError(); 1269 auto &Types = File.types(); 1270 auto &Ids = File.ids(); 1271 1272 if (HashExtras) { 1273 P.printLine("GSI Header"); 1274 AutoIndent Indent(P); 1275 P.formatLine("sig = {0:X}, hdr = {1:X}, hr size = {2}, num buckets = {3}", 1276 Table.getVerSignature(), Table.getVerHeader(), 1277 Table.getHashRecordSize(), Table.getNumBuckets()); 1278 } 1279 1280 { 1281 P.printLine("Records"); 1282 SymbolVisitorCallbackPipeline Pipeline; 1283 SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); 1284 MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); 1285 1286 Pipeline.addCallbackToPipeline(Deserializer); 1287 Pipeline.addCallbackToPipeline(Dumper); 1288 CVSymbolVisitor Visitor(Pipeline); 1289 1290 BinaryStreamRef SymStream = 1291 ExpectedSyms->getSymbolArray().getUnderlyingStream(); 1292 for (uint32_t PubSymOff : Table) { 1293 Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff); 1294 if (!Sym) 1295 return Sym.takeError(); 1296 if (auto E = Visitor.visitSymbolRecord(*Sym, PubSymOff)) 1297 return E; 1298 } 1299 } 1300 1301 // Return early if we aren't dumping public hash table and address map info. 1302 if (!HashExtras) 1303 return Error::success(); 1304 1305 P.formatLine("Hash Entries"); 1306 { 1307 AutoIndent Indent2(P); 1308 for (const PSHashRecord &HR : Table.HashRecords) 1309 P.formatLine("off = {0}, refcnt = {1}", uint32_t(HR.Off), 1310 uint32_t(HR.CRef)); 1311 } 1312 1313 // FIXME: Dump the bitmap. 1314 1315 P.formatLine("Hash Buckets"); 1316 { 1317 AutoIndent Indent2(P); 1318 for (uint32_t Hash : Table.HashBuckets) 1319 P.formatLine("{0:x8}", Hash); 1320 } 1321 1322 return Error::success(); 1323 } 1324 1325 static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel, 1326 OMFSegDescFlags Flags) { 1327 std::vector<std::string> Opts; 1328 if (Flags == OMFSegDescFlags::None) 1329 return "none"; 1330 1331 PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read"); 1332 PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write"); 1333 PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute"); 1334 PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr"); 1335 PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector"); 1336 PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr"); 1337 PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group"); 1338 return typesetItemList(Opts, IndentLevel, 4, " | "); 1339 } 1340 1341 Error DumpOutputStyle::dumpSectionHeaders() { 1342 dumpSectionHeaders("Section Headers", DbgHeaderType::SectionHdr); 1343 dumpSectionHeaders("Original Section Headers", DbgHeaderType::SectionHdrOrig); 1344 return Error::success(); 1345 } 1346 1347 static Expected<std::pair<std::unique_ptr<MappedBlockStream>, 1348 ArrayRef<llvm::object::coff_section>>> 1349 loadSectionHeaders(PDBFile &File, DbgHeaderType Type) { 1350 if (!File.hasPDBDbiStream()) 1351 return make_error<StringError>( 1352 "Section headers require a DBI Stream, which could not be loaded", 1353 inconvertibleErrorCode()); 1354 1355 auto &Dbi = cantFail(File.getPDBDbiStream()); 1356 uint32_t SI = Dbi.getDebugStreamIndex(Type); 1357 1358 if (SI == kInvalidStreamIndex) 1359 return make_error<StringError>( 1360 "PDB does not contain the requested image section header type", 1361 inconvertibleErrorCode()); 1362 1363 auto Stream = File.createIndexedStream(SI); 1364 if (!Stream) 1365 return make_error<StringError>("Could not load the required stream data", 1366 inconvertibleErrorCode()); 1367 1368 ArrayRef<object::coff_section> Headers; 1369 if (Stream->getLength() % sizeof(object::coff_section) != 0) 1370 return make_error<StringError>( 1371 "Section header array size is not a multiple of section header size", 1372 inconvertibleErrorCode()); 1373 1374 uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section); 1375 BinaryStreamReader Reader(*Stream); 1376 cantFail(Reader.readArray(Headers, NumHeaders)); 1377 return std::make_pair(std::move(Stream), Headers); 1378 } 1379 1380 void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) { 1381 printHeader(P, Label); 1382 1383 AutoIndent Indent(P); 1384 if (File.isObj()) { 1385 P.formatLine("Dumping Section Headers is not supported for object files"); 1386 return; 1387 } 1388 1389 ExitOnError Err("Error dumping section headers: "); 1390 std::unique_ptr<MappedBlockStream> Stream; 1391 ArrayRef<object::coff_section> Headers; 1392 auto ExpectedHeaders = loadSectionHeaders(getPdb(), Type); 1393 if (!ExpectedHeaders) { 1394 P.printLine(toString(ExpectedHeaders.takeError())); 1395 return; 1396 } 1397 std::tie(Stream, Headers) = std::move(*ExpectedHeaders); 1398 1399 uint32_t I = 1; 1400 for (const auto &Header : Headers) { 1401 P.NewLine(); 1402 P.formatLine("SECTION HEADER #{0}", I); 1403 P.formatLine("{0,8} name", Header.Name); 1404 P.formatLine("{0,8:X-} virtual size", uint32_t(Header.VirtualSize)); 1405 P.formatLine("{0,8:X-} virtual address", uint32_t(Header.VirtualAddress)); 1406 P.formatLine("{0,8:X-} size of raw data", uint32_t(Header.SizeOfRawData)); 1407 P.formatLine("{0,8:X-} file pointer to raw data", 1408 uint32_t(Header.PointerToRawData)); 1409 P.formatLine("{0,8:X-} file pointer to relocation table", 1410 uint32_t(Header.PointerToRelocations)); 1411 P.formatLine("{0,8:X-} file pointer to line numbers", 1412 uint32_t(Header.PointerToLinenumbers)); 1413 P.formatLine("{0,8:X-} number of relocations", 1414 uint32_t(Header.NumberOfRelocations)); 1415 P.formatLine("{0,8:X-} number of line numbers", 1416 uint32_t(Header.NumberOfLinenumbers)); 1417 P.formatLine("{0,8:X-} flags", uint32_t(Header.Characteristics)); 1418 AutoIndent IndentMore(P, 9); 1419 P.formatLine("{0}", formatSectionCharacteristics( 1420 P.getIndentLevel(), Header.Characteristics, 1, "")); 1421 ++I; 1422 } 1423 return; 1424 } 1425 1426 std::vector<std::string> getSectionNames(PDBFile &File) { 1427 auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr); 1428 if (!ExpectedHeaders) 1429 return {}; 1430 1431 std::unique_ptr<MappedBlockStream> Stream; 1432 ArrayRef<object::coff_section> Headers; 1433 std::tie(Stream, Headers) = std::move(*ExpectedHeaders); 1434 std::vector<std::string> Names; 1435 for (const auto &H : Headers) 1436 Names.push_back(H.Name); 1437 return Names; 1438 } 1439 1440 Error DumpOutputStyle::dumpSectionContribs() { 1441 printHeader(P, "Section Contributions"); 1442 1443 AutoIndent Indent(P); 1444 if (File.isObj()) { 1445 P.formatLine( 1446 "Dumping section contributions is not supported for object files"); 1447 return Error::success(); 1448 } 1449 1450 ExitOnError Err("Error dumping section contributions: "); 1451 if (!getPdb().hasPDBDbiStream()) { 1452 P.formatLine( 1453 "Section contribs require a DBI Stream, which could not be loaded"); 1454 return Error::success(); 1455 } 1456 1457 auto &Dbi = Err(getPdb().getPDBDbiStream()); 1458 1459 class Visitor : public ISectionContribVisitor { 1460 public: 1461 Visitor(LinePrinter &P, ArrayRef<std::string> Names) : P(P), Names(Names) { 1462 auto Max = std::max_element( 1463 Names.begin(), Names.end(), 1464 [](StringRef S1, StringRef S2) { return S1.size() < S2.size(); }); 1465 MaxNameLen = (Max == Names.end() ? 0 : Max->size()); 1466 } 1467 void visit(const SectionContrib &SC) override { 1468 assert(SC.ISect > 0); 1469 std::string NameInsert; 1470 if (SC.ISect < Names.size()) { 1471 StringRef SectionName = Names[SC.ISect - 1]; 1472 NameInsert = formatv("[{0}]", SectionName).str(); 1473 } else 1474 NameInsert = "[???]"; 1475 P.formatLine("SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " 1476 "crc = {4}", 1477 formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size), 1478 fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc), 1479 fmt_align(NameInsert, AlignStyle::Left, MaxNameLen + 2)); 1480 AutoIndent Indent(P, MaxNameLen + 2); 1481 P.formatLine(" {0}", 1482 formatSectionCharacteristics(P.getIndentLevel() + 6, 1483 SC.Characteristics, 3, " | ")); 1484 } 1485 void visit(const SectionContrib2 &SC) override { 1486 P.formatLine( 1487 "SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " 1488 "crc = {4}, coff section = {5}", 1489 formatSegmentOffset(SC.Base.ISect, SC.Base.Off), fmtle(SC.Base.Size), 1490 fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc), fmtle(SC.Base.RelocCrc), 1491 fmtle(SC.ISectCoff)); 1492 P.formatLine(" {0}", formatSectionCharacteristics( 1493 P.getIndentLevel() + 6, 1494 SC.Base.Characteristics, 3, " | ")); 1495 } 1496 1497 private: 1498 LinePrinter &P; 1499 uint32_t MaxNameLen; 1500 ArrayRef<std::string> Names; 1501 }; 1502 1503 std::vector<std::string> Names = getSectionNames(getPdb()); 1504 Visitor V(P, makeArrayRef(Names)); 1505 Dbi.visitSectionContributions(V); 1506 return Error::success(); 1507 } 1508 1509 Error DumpOutputStyle::dumpSectionMap() { 1510 printHeader(P, "Section Map"); 1511 AutoIndent Indent(P); 1512 1513 if (File.isObj()) { 1514 P.formatLine("Dumping section map is not supported for object files"); 1515 return Error::success(); 1516 } 1517 1518 ExitOnError Err("Error dumping section map: "); 1519 1520 if (!getPdb().hasPDBDbiStream()) { 1521 P.formatLine("Dumping the section map requires a DBI Stream, which could " 1522 "not be loaded"); 1523 return Error::success(); 1524 } 1525 1526 auto &Dbi = Err(getPdb().getPDBDbiStream()); 1527 1528 uint32_t I = 0; 1529 for (auto &M : Dbi.getSectionMap()) { 1530 P.formatLine( 1531 "Section {0:4} | ovl = {1}, group = {2}, frame = {3}, name = {4}", I, 1532 fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName)); 1533 P.formatLine(" class = {0}, offset = {1}, size = {2}", 1534 fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength)); 1535 P.formatLine(" flags = {0}", 1536 formatSegMapDescriptorFlag( 1537 P.getIndentLevel() + 13, 1538 static_cast<OMFSegDescFlags>(uint16_t(M.Flags)))); 1539 ++I; 1540 } 1541 return Error::success(); 1542 } 1543