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