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