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