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