2017-06-22 22:57:39 +02:00
|
|
|
//===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===//
|
2017-06-16 00:24:24 +02:00
|
|
|
//
|
2019-01-19 09:50:56 +01:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2017-06-16 00:24:24 +02:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
#include "DumpOutputStyle.h"
|
2017-06-16 00:24:24 +02:00
|
|
|
|
|
|
|
#include "FormatUtil.h"
|
2017-09-01 22:06:56 +02:00
|
|
|
#include "InputFile.h"
|
2017-06-16 00:24:24 +02:00
|
|
|
#include "MinimalSymbolDumper.h"
|
|
|
|
#include "MinimalTypeDumper.h"
|
|
|
|
#include "StreamUtil.h"
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
#include "TypeReferenceTracker.h"
|
2017-06-16 00:24:24 +02:00
|
|
|
#include "llvm-pdbutil.h"
|
|
|
|
|
2017-06-16 01:59:56 +02:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2017-06-16 00:24:24 +02:00
|
|
|
#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h"
|
2018-09-12 00:35:01 +02:00
|
|
|
#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"
|
2017-06-16 00:24:24 +02:00
|
|
|
#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/Formatters.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/Line.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"
|
|
|
|
#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h"
|
2017-12-06 00:58:18 +01:00
|
|
|
#include "llvm/DebugInfo/CodeView/TypeHashing.h"
|
2017-06-30 20:15:47 +02:00
|
|
|
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
|
2017-06-16 00:24:24 +02:00
|
|
|
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h"
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h"
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h"
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/RawError.h"
|
2017-12-06 00:58:18 +01:00
|
|
|
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
|
2017-06-16 00:24:24 +02:00
|
|
|
#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
|
|
|
|
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
|
|
|
#include "llvm/Object/COFF.h"
|
|
|
|
#include "llvm/Support/BinaryStreamReader.h"
|
|
|
|
#include "llvm/Support/FormatAdapters.h"
|
|
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
|
|
|
2017-12-13 23:21:02 +01:00
|
|
|
#include <cctype>
|
2017-06-16 00:24:24 +02:00
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
using namespace llvm::codeview;
|
|
|
|
using namespace llvm::msf;
|
|
|
|
using namespace llvm::pdb;
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
DumpOutputStyle::DumpOutputStyle(InputFile &File)
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
: File(File), P(2, false, outs()) {
|
|
|
|
if (opts::dump::DumpTypeRefStats)
|
|
|
|
RefTracker.reset(new TypeReferenceTracker(File));
|
|
|
|
}
|
|
|
|
|
|
|
|
DumpOutputStyle::~DumpOutputStyle() {}
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); }
|
|
|
|
object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); }
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
void DumpOutputStyle::printStreamNotValidForObj() {
|
|
|
|
AutoIndent Indent(P, 4);
|
|
|
|
P.formatLine("Dumping this stream is not valid for object files");
|
|
|
|
}
|
|
|
|
|
|
|
|
void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) {
|
|
|
|
AutoIndent Indent(P, 4);
|
|
|
|
P.formatLine("{0} stream not present", StreamName);
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dump() {
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
// Walk symbols & globals if we are supposed to mark types referenced.
|
|
|
|
if (opts::dump::DumpTypeRefStats)
|
|
|
|
RefTracker->mark();
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpSummary) {
|
2017-06-16 00:24:24 +02:00
|
|
|
if (auto EC = dumpFileSummary())
|
|
|
|
return EC;
|
|
|
|
P.NewLine();
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpStreams) {
|
2017-06-16 00:24:24 +02:00
|
|
|
if (auto EC = dumpStreamSummary())
|
|
|
|
return EC;
|
|
|
|
P.NewLine();
|
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
if (opts::dump::DumpSymbolStats) {
|
2017-08-31 22:43:22 +02:00
|
|
|
if (auto EC = dumpSymbolStats())
|
|
|
|
return EC;
|
|
|
|
P.NewLine();
|
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
if (opts::dump::DumpUdtStats) {
|
2017-08-31 22:43:22 +02:00
|
|
|
if (auto EC = dumpUdtStats())
|
2017-08-21 16:53:25 +02:00
|
|
|
return EC;
|
|
|
|
P.NewLine();
|
|
|
|
}
|
|
|
|
|
2020-06-04 21:30:45 +02:00
|
|
|
if (opts::dump::DumpTypeStats || opts::dump::DumpIDStats) {
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
if (auto EC = dumpTypeStats())
|
|
|
|
return EC;
|
|
|
|
P.NewLine();
|
|
|
|
}
|
|
|
|
|
2018-03-23 19:43:39 +01:00
|
|
|
if (opts::dump::DumpNamedStreams) {
|
|
|
|
if (auto EC = dumpNamedStreams())
|
|
|
|
return EC;
|
|
|
|
P.NewLine();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts::dump::DumpStringTable || opts::dump::DumpStringTableDetails) {
|
2017-06-16 00:24:24 +02:00
|
|
|
if (auto EC = dumpStringTable())
|
|
|
|
return EC;
|
|
|
|
P.NewLine();
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpModules) {
|
2017-06-16 00:24:24 +02:00
|
|
|
if (auto EC = dumpModules())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpModuleFiles) {
|
2017-06-16 01:12:41 +02:00
|
|
|
if (auto EC = dumpModuleFiles())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpLines) {
|
2017-06-16 01:56:19 +02:00
|
|
|
if (auto EC = dumpLines())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpInlineeLines) {
|
2017-06-16 01:56:19 +02:00
|
|
|
if (auto EC = dumpInlineeLines())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpXmi) {
|
2017-06-16 02:04:24 +02:00
|
|
|
if (auto EC = dumpXmi())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpXme) {
|
2017-06-16 02:04:24 +02:00
|
|
|
if (auto EC = dumpXme())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2018-09-12 00:35:01 +02:00
|
|
|
if (opts::dump::DumpFpo) {
|
|
|
|
if (auto EC = dumpFpo())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-12-06 00:58:18 +01:00
|
|
|
if (File.isObj()) {
|
|
|
|
if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||
|
|
|
|
opts::dump::DumpTypeExtras)
|
|
|
|
if (auto EC = dumpTypesFromObjectFile())
|
|
|
|
return EC;
|
|
|
|
} else {
|
|
|
|
if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() ||
|
|
|
|
opts::dump::DumpTypeExtras) {
|
|
|
|
if (auto EC = dumpTpiStream(StreamTPI))
|
|
|
|
return EC;
|
|
|
|
}
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-12-06 00:58:18 +01:00
|
|
|
if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() ||
|
|
|
|
opts::dump::DumpIdExtras) {
|
|
|
|
if (auto EC = dumpTpiStream(StreamIPI))
|
|
|
|
return EC;
|
|
|
|
}
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
|
|
|
|
2018-07-06 04:59:25 +02:00
|
|
|
if (opts::dump::DumpGSIRecords) {
|
|
|
|
if (auto EC = dumpGSIRecords())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-07-26 02:40:36 +02:00
|
|
|
if (opts::dump::DumpGlobals) {
|
|
|
|
if (auto EC = dumpGlobals())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpPublics) {
|
2017-06-16 00:24:24 +02:00
|
|
|
if (auto EC = dumpPublics())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpSymbols) {
|
2017-09-01 22:06:56 +02:00
|
|
|
auto EC = File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj();
|
|
|
|
if (EC)
|
2017-06-16 00:24:24 +02:00
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
if (opts::dump::DumpTypeRefStats) {
|
|
|
|
if (auto EC = dumpTypeRefStats())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-08-04 22:02:38 +02:00
|
|
|
if (opts::dump::DumpSectionHeaders) {
|
|
|
|
if (auto EC = dumpSectionHeaders())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpSectionContribs) {
|
2017-06-16 00:24:24 +02:00
|
|
|
if (auto EC = dumpSectionContribs())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
if (opts::dump::DumpSectionMap) {
|
2017-06-16 00:24:24 +02:00
|
|
|
if (auto EC = dumpSectionMap())
|
|
|
|
return EC;
|
|
|
|
}
|
|
|
|
|
2019-07-12 20:22:47 +02:00
|
|
|
P.NewLine();
|
|
|
|
|
2017-06-16 00:24:24 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void printHeader(LinePrinter &P, const Twine &S) {
|
|
|
|
P.NewLine();
|
|
|
|
P.formatLine("{0,=60}", S);
|
|
|
|
P.formatLine("{0}", fmt_repeat('=', 60));
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpFileSummary() {
|
2017-06-16 00:24:24 +02:00
|
|
|
printHeader(P, "Summary");
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
if (File.isObj()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotValidForObj();
|
2017-09-01 22:06:56 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
AutoIndent Indent(P);
|
|
|
|
ExitOnError Err("Invalid PDB Format: ");
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
P.formatLine("Block Size: {0}", getPdb().getBlockSize());
|
|
|
|
P.formatLine("Number of blocks: {0}", getPdb().getBlockCount());
|
|
|
|
P.formatLine("Number of streams: {0}", getPdb().getNumStreams());
|
|
|
|
|
|
|
|
auto &PS = Err(getPdb().getPDBInfoStream());
|
2017-06-16 00:24:24 +02:00
|
|
|
P.formatLine("Signature: {0}", PS.getSignature());
|
|
|
|
P.formatLine("Age: {0}", PS.getAge());
|
|
|
|
P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid));
|
|
|
|
P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures()));
|
2017-09-01 22:06:56 +02:00
|
|
|
P.formatLine("Has Debug Info: {0}", getPdb().hasPDBDbiStream());
|
|
|
|
P.formatLine("Has Types: {0}", getPdb().hasPDBTpiStream());
|
|
|
|
P.formatLine("Has IDs: {0}", getPdb().hasPDBIpiStream());
|
|
|
|
P.formatLine("Has Globals: {0}", getPdb().hasPDBGlobalsStream());
|
|
|
|
P.formatLine("Has Publics: {0}", getPdb().hasPDBPublicsStream());
|
|
|
|
if (getPdb().hasPDBDbiStream()) {
|
|
|
|
auto &DBI = Err(getPdb().getPDBDbiStream());
|
2017-06-16 00:24:24 +02:00
|
|
|
P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked());
|
|
|
|
P.formatLine("Has conflicting types: {0}", DBI.hasCTypes());
|
|
|
|
P.formatLine("Is stripped: {0}", DBI.isStripped());
|
|
|
|
}
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
static StatCollection getSymbolStats(const SymbolGroup &SG,
|
2017-08-21 16:53:25 +02:00
|
|
|
StatCollection &CumulativeStats) {
|
|
|
|
StatCollection Stats;
|
2018-11-02 19:00:37 +01:00
|
|
|
if (SG.getFile().isPdb() && SG.hasDebugStream()) {
|
2017-09-01 22:06:56 +02:00
|
|
|
// For PDB files, all symbols are packed into one stream.
|
|
|
|
for (const auto &S : SG.getPdbModuleStream().symbols(nullptr)) {
|
|
|
|
Stats.update(S.kind(), S.length());
|
|
|
|
CumulativeStats.update(S.kind(), S.length());
|
|
|
|
}
|
|
|
|
return Stats;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &SS : SG.getDebugSubsections()) {
|
|
|
|
// For object files, all symbols are spread across multiple Symbol
|
|
|
|
// subsections of a given .debug$S section.
|
|
|
|
if (SS.kind() != DebugSubsectionKind::Symbols)
|
|
|
|
continue;
|
|
|
|
DebugSymbolsSubsectionRef Symbols;
|
|
|
|
BinaryStreamReader Reader(SS.getRecordData());
|
|
|
|
cantFail(Symbols.initialize(Reader));
|
|
|
|
for (const auto &S : Symbols) {
|
|
|
|
Stats.update(S.kind(), S.length());
|
|
|
|
CumulativeStats.update(S.kind(), S.length());
|
|
|
|
}
|
2017-08-21 16:53:25 +02:00
|
|
|
}
|
|
|
|
return Stats;
|
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
static StatCollection getChunkStats(const SymbolGroup &SG,
|
2017-08-21 16:53:25 +02:00
|
|
|
StatCollection &CumulativeStats) {
|
|
|
|
StatCollection Stats;
|
2017-09-01 22:06:56 +02:00
|
|
|
for (const auto &Chunk : SG.getDebugSubsections()) {
|
2017-08-21 16:53:25 +02:00
|
|
|
Stats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());
|
|
|
|
CumulativeStats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength());
|
|
|
|
}
|
|
|
|
return Stats;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline std::string formatModuleDetailKind(DebugSubsectionKind K) {
|
|
|
|
return formatChunkKind(K, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline std::string formatModuleDetailKind(SymbolKind K) {
|
|
|
|
return formatSymbolKind(K);
|
|
|
|
}
|
|
|
|
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
// Get the stats sorted by size, descending.
|
|
|
|
std::vector<StatCollection::KindAndStat>
|
|
|
|
StatCollection::getStatsSortedBySize() const {
|
|
|
|
std::vector<KindAndStat> SortedStats(Individual.begin(), Individual.end());
|
2019-04-22 17:53:43 +02:00
|
|
|
llvm::stable_sort(SortedStats,
|
|
|
|
[](const KindAndStat &LHS, const KindAndStat &RHS) {
|
|
|
|
return LHS.second.Size > RHS.second.Size;
|
|
|
|
});
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
return SortedStats;
|
|
|
|
}
|
|
|
|
|
2017-08-21 16:53:25 +02:00
|
|
|
template <typename Kind>
|
|
|
|
static void printModuleDetailStats(LinePrinter &P, StringRef Label,
|
|
|
|
const StatCollection &Stats) {
|
|
|
|
P.NewLine();
|
|
|
|
P.formatLine(" {0}", Label);
|
|
|
|
AutoIndent Indent(P);
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", "Total",
|
2017-08-21 16:53:25 +02:00
|
|
|
Stats.Totals.Count, Stats.Totals.Size);
|
|
|
|
P.formatLine("{0}", fmt_repeat('-', 74));
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
|
|
|
|
for (const auto &K : Stats.getStatsSortedBySize()) {
|
2017-08-21 16:53:25 +02:00
|
|
|
std::string KindName = formatModuleDetailKind(Kind(K.first));
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", KindName,
|
2017-08-21 16:53:25 +02:00
|
|
|
K.second.Count, K.second.Size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
static bool isMyCode(const SymbolGroup &Group) {
|
|
|
|
if (Group.getFile().isObj())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
StringRef Name = Group.name();
|
2017-08-21 16:53:25 +02:00
|
|
|
if (Name.startswith("Import:"))
|
|
|
|
return false;
|
2021-06-23 13:52:36 +02:00
|
|
|
if (Name.endswith_insensitive(".dll"))
|
2017-08-21 16:53:25 +02:00
|
|
|
return false;
|
2021-06-23 13:52:36 +02:00
|
|
|
if (Name.equals_insensitive("* linker *"))
|
2017-08-21 16:53:25 +02:00
|
|
|
return false;
|
2021-06-23 13:52:36 +02:00
|
|
|
if (Name.startswith_insensitive("f:\\binaries\\Intermediate\\vctools"))
|
2017-08-21 16:53:25 +02:00
|
|
|
return false;
|
2021-06-23 13:52:36 +02:00
|
|
|
if (Name.startswith_insensitive("f:\\dd\\vctools\\crt"))
|
2017-08-21 16:53:25 +02:00
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
static bool shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group) {
|
|
|
|
if (opts::dump::JustMyCode && !isMyCode(Group))
|
2017-08-21 16:53:25 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// If the arg was not specified on the command line, always dump all modules.
|
|
|
|
if (opts::dump::DumpModi.getNumOccurrences() == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Otherwise, only dump if this is the same module specified.
|
2017-09-01 22:06:56 +02:00
|
|
|
return (opts::dump::DumpModi == Idx);
|
2017-08-21 16:53:25 +02:00
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpStreamSummary() {
|
2017-06-16 00:24:24 +02:00
|
|
|
printHeader(P, "Streams");
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
if (File.isObj()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotValidForObj();
|
2017-09-01 22:06:56 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
AutoIndent Indent(P);
|
|
|
|
|
2017-06-16 00:24:24 +02:00
|
|
|
if (StreamPurposes.empty())
|
2017-09-01 22:06:56 +02:00
|
|
|
discoverStreamPurposes(getPdb(), StreamPurposes);
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
uint32_t StreamCount = getPdb().getNumStreams();
|
|
|
|
uint32_t MaxStreamSize = getPdb().getMaxStreamSize();
|
2017-06-16 00:24:24 +02:00
|
|
|
|
|
|
|
for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
|
|
|
|
P.formatLine(
|
2017-08-21 16:53:25 +02:00
|
|
|
"Stream {0} ({1} bytes): [{2}]",
|
2017-06-16 00:24:24 +02:00
|
|
|
fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)),
|
2017-09-01 22:06:56 +02:00
|
|
|
fmt_align(getPdb().getStreamByteSize(StreamIdx), AlignStyle::Right,
|
2017-08-21 16:53:25 +02:00
|
|
|
NumDigits(MaxStreamSize)),
|
|
|
|
StreamPurposes[StreamIdx].getLongName());
|
|
|
|
|
2017-06-23 22:28:14 +02:00
|
|
|
if (opts::dump::DumpStreamBlocks) {
|
2017-09-01 22:06:56 +02:00
|
|
|
auto Blocks = getPdb().getStreamBlockList(StreamIdx);
|
2017-06-23 22:28:14 +02:00
|
|
|
std::vector<uint32_t> BV(Blocks.begin(), Blocks.end());
|
|
|
|
P.formatLine(" {0} Blocks: [{1}]",
|
|
|
|
fmt_repeat(' ', NumDigits(StreamCount)),
|
|
|
|
make_range(BV.begin(), BV.end()));
|
|
|
|
}
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-16 01:12:41 +02:00
|
|
|
static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File,
|
|
|
|
uint32_t Index) {
|
2017-07-28 01:13:18 +02:00
|
|
|
ExitOnError Err("Unexpected error: ");
|
2017-06-16 01:12:41 +02:00
|
|
|
|
|
|
|
auto &Dbi = Err(File.getPDBDbiStream());
|
|
|
|
const auto &Modules = Dbi.modules();
|
|
|
|
auto Modi = Modules.getModuleDescriptor(Index);
|
|
|
|
|
|
|
|
uint16_t ModiStream = Modi.getModuleStreamIndex();
|
|
|
|
if (ModiStream == kInvalidStreamIndex)
|
|
|
|
return make_error<RawError>(raw_error_code::no_stream,
|
|
|
|
"Module stream not present");
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
auto ModStreamData = File.createIndexedStream(ModiStream);
|
2017-06-16 01:12:41 +02:00
|
|
|
|
|
|
|
ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));
|
|
|
|
if (auto EC = ModS.reload())
|
|
|
|
return make_error<RawError>(raw_error_code::corrupt_file,
|
|
|
|
"Invalid module stream");
|
|
|
|
|
2020-02-10 16:06:45 +01:00
|
|
|
return std::move(ModS);
|
2017-06-16 01:12:41 +02:00
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
template <typename CallbackT>
|
|
|
|
static void
|
|
|
|
iterateOneModule(InputFile &File, const Optional<PrintScope> &HeaderScope,
|
|
|
|
const SymbolGroup &SG, uint32_t Modi, CallbackT Callback) {
|
|
|
|
if (HeaderScope) {
|
|
|
|
HeaderScope->P.formatLine(
|
|
|
|
"Mod {0:4} | `{1}`: ",
|
|
|
|
fmt_align(Modi, AlignStyle::Right, HeaderScope->LabelWidth), SG.name());
|
2017-06-16 01:56:19 +02:00
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
AutoIndent Indent(HeaderScope);
|
|
|
|
Callback(Modi, SG);
|
2017-08-04 01:11:52 +02:00
|
|
|
}
|
|
|
|
|
2017-06-16 01:59:56 +02:00
|
|
|
template <typename CallbackT>
|
2017-09-01 22:06:56 +02:00
|
|
|
static void iterateSymbolGroups(InputFile &Input,
|
|
|
|
const Optional<PrintScope> &HeaderScope,
|
|
|
|
CallbackT Callback) {
|
|
|
|
AutoIndent Indent(HeaderScope);
|
2017-06-16 01:59:56 +02:00
|
|
|
|
2017-07-28 01:13:18 +02:00
|
|
|
ExitOnError Err("Unexpected error processing modules: ");
|
2017-06-16 01:59:56 +02:00
|
|
|
|
2017-08-04 01:11:52 +02:00
|
|
|
if (opts::dump::DumpModi.getNumOccurrences() > 0) {
|
|
|
|
assert(opts::dump::DumpModi.getNumOccurrences() == 1);
|
|
|
|
uint32_t Modi = opts::dump::DumpModi;
|
2017-09-01 22:06:56 +02:00
|
|
|
SymbolGroup SG(&Input, Modi);
|
|
|
|
iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(Modi)), SG,
|
|
|
|
Modi, Callback);
|
2017-08-04 01:11:52 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
uint32_t I = 0;
|
|
|
|
|
|
|
|
for (const auto &SG : Input.symbol_groups()) {
|
|
|
|
if (shouldDumpSymbolGroup(I, SG))
|
|
|
|
iterateOneModule(Input, withLabelWidth(HeaderScope, NumDigits(I)), SG, I,
|
|
|
|
Callback);
|
|
|
|
|
|
|
|
++I;
|
2017-06-16 01:59:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename SubsectionT>
|
|
|
|
static void iterateModuleSubsections(
|
2017-09-01 22:06:56 +02:00
|
|
|
InputFile &File, const Optional<PrintScope> &HeaderScope,
|
|
|
|
llvm::function_ref<void(uint32_t, const SymbolGroup &, SubsectionT &)>
|
2017-06-16 01:59:56 +02:00
|
|
|
Callback) {
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
iterateSymbolGroups(File, HeaderScope,
|
|
|
|
[&](uint32_t Modi, const SymbolGroup &SG) {
|
|
|
|
for (const auto &SS : SG.getDebugSubsections()) {
|
|
|
|
SubsectionT Subsection;
|
2017-06-16 01:59:56 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
if (SS.kind() != Subsection.kind())
|
|
|
|
continue;
|
2017-06-16 01:59:56 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
BinaryStreamReader Reader(SS.getRecordData());
|
|
|
|
if (auto EC = Subsection.initialize(Reader))
|
|
|
|
continue;
|
|
|
|
Callback(Modi, SG, Subsection);
|
|
|
|
}
|
|
|
|
});
|
2017-06-16 01:59:56 +02:00
|
|
|
}
|
|
|
|
|
2018-04-17 22:06:43 +02:00
|
|
|
static Expected<std::pair<std::unique_ptr<MappedBlockStream>,
|
|
|
|
ArrayRef<llvm::object::coff_section>>>
|
|
|
|
loadSectionHeaders(PDBFile &File, DbgHeaderType Type) {
|
|
|
|
if (!File.hasPDBDbiStream())
|
|
|
|
return make_error<StringError>(
|
|
|
|
"Section headers require a DBI Stream, which could not be loaded",
|
|
|
|
inconvertibleErrorCode());
|
|
|
|
|
|
|
|
auto &Dbi = cantFail(File.getPDBDbiStream());
|
|
|
|
uint32_t SI = Dbi.getDebugStreamIndex(Type);
|
|
|
|
|
|
|
|
if (SI == kInvalidStreamIndex)
|
|
|
|
return make_error<StringError>(
|
|
|
|
"PDB does not contain the requested image section header type",
|
|
|
|
inconvertibleErrorCode());
|
|
|
|
|
|
|
|
auto Stream = File.createIndexedStream(SI);
|
|
|
|
if (!Stream)
|
|
|
|
return make_error<StringError>("Could not load the required stream data",
|
|
|
|
inconvertibleErrorCode());
|
|
|
|
|
|
|
|
ArrayRef<object::coff_section> Headers;
|
|
|
|
if (Stream->getLength() % sizeof(object::coff_section) != 0)
|
|
|
|
return make_error<StringError>(
|
|
|
|
"Section header array size is not a multiple of section header size",
|
|
|
|
inconvertibleErrorCode());
|
|
|
|
|
|
|
|
uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section);
|
|
|
|
BinaryStreamReader Reader(*Stream);
|
|
|
|
cantFail(Reader.readArray(Headers, NumHeaders));
|
|
|
|
return std::make_pair(std::move(Stream), Headers);
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::vector<std::string> getSectionNames(PDBFile &File) {
|
|
|
|
auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr);
|
|
|
|
if (!ExpectedHeaders)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
std::unique_ptr<MappedBlockStream> Stream;
|
|
|
|
ArrayRef<object::coff_section> Headers;
|
|
|
|
std::tie(Stream, Headers) = std::move(*ExpectedHeaders);
|
|
|
|
std::vector<std::string> Names;
|
|
|
|
for (const auto &H : Headers)
|
|
|
|
Names.push_back(H.Name);
|
|
|
|
return Names;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dumpSectionContrib(LinePrinter &P, const SectionContrib &SC,
|
|
|
|
ArrayRef<std::string> SectionNames,
|
|
|
|
uint32_t FieldWidth) {
|
|
|
|
std::string NameInsert;
|
2018-04-17 23:44:17 +02:00
|
|
|
if (SC.ISect > 0 && SC.ISect <= SectionNames.size()) {
|
2018-04-17 22:06:43 +02:00
|
|
|
StringRef SectionName = SectionNames[SC.ISect - 1];
|
|
|
|
NameInsert = formatv("[{0}]", SectionName).str();
|
|
|
|
} else
|
|
|
|
NameInsert = "[???]";
|
|
|
|
P.formatLine("SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
|
|
|
|
"crc = {4}",
|
|
|
|
formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size),
|
|
|
|
fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc),
|
|
|
|
fmt_align(NameInsert, AlignStyle::Left, FieldWidth + 2));
|
|
|
|
AutoIndent Indent(P, FieldWidth + 2);
|
|
|
|
P.formatLine(" {0}",
|
|
|
|
formatSectionCharacteristics(P.getIndentLevel() + 6,
|
|
|
|
SC.Characteristics, 3, " | "));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dumpSectionContrib(LinePrinter &P, const SectionContrib2 &SC,
|
|
|
|
ArrayRef<std::string> SectionNames,
|
|
|
|
uint32_t FieldWidth) {
|
|
|
|
P.formatLine("SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
|
|
|
|
"crc = {4}, coff section = {5}",
|
|
|
|
formatSegmentOffset(SC.Base.ISect, SC.Base.Off),
|
|
|
|
fmtle(SC.Base.Size), fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc),
|
|
|
|
fmtle(SC.Base.RelocCrc), fmtle(SC.ISectCoff));
|
|
|
|
P.formatLine(" {0}",
|
|
|
|
formatSectionCharacteristics(P.getIndentLevel() + 6,
|
|
|
|
SC.Base.Characteristics, 3, " | "));
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpModules() {
|
2017-06-16 00:24:24 +02:00
|
|
|
printHeader(P, "Modules");
|
2017-09-01 22:06:56 +02:00
|
|
|
|
|
|
|
if (File.isObj()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotValidForObj();
|
2017-09-01 22:06:56 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!getPdb().hasPDBDbiStream()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotPresent("DBI");
|
2017-06-16 00:24:24 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
AutoIndent Indent(P);
|
2017-07-28 01:13:18 +02:00
|
|
|
ExitOnError Err("Unexpected error processing modules: ");
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
auto &Stream = Err(getPdb().getPDBDbiStream());
|
2017-06-16 00:24:24 +02:00
|
|
|
|
|
|
|
const DbiModuleList &Modules = Stream.modules();
|
2017-09-01 22:06:56 +02:00
|
|
|
iterateSymbolGroups(
|
|
|
|
File, PrintScope{P, 11}, [&](uint32_t Modi, const SymbolGroup &Strings) {
|
2017-08-21 16:53:25 +02:00
|
|
|
auto Desc = Modules.getModuleDescriptor(Modi);
|
2018-04-17 22:06:43 +02:00
|
|
|
if (opts::dump::DumpSectionContribs) {
|
|
|
|
std::vector<std::string> Sections = getSectionNames(getPdb());
|
|
|
|
dumpSectionContrib(P, Desc.getSectionContrib(), Sections, 0);
|
|
|
|
}
|
2017-08-21 16:53:25 +02:00
|
|
|
P.formatLine("Obj: `{0}`: ", Desc.getObjFileName());
|
|
|
|
P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}",
|
|
|
|
Desc.getModuleStreamIndex(), Desc.getNumberOfFiles(),
|
|
|
|
Desc.hasECInfo());
|
|
|
|
StringRef PdbFilePath =
|
|
|
|
Err(Stream.getECName(Desc.getPdbFilePathNameIndex()));
|
|
|
|
StringRef SrcFilePath =
|
|
|
|
Err(Stream.getECName(Desc.getSourceFileNameIndex()));
|
|
|
|
P.formatLine("pdb file ni: {0} `{1}`, src file ni: {2} `{3}`",
|
|
|
|
Desc.getPdbFilePathNameIndex(), PdbFilePath,
|
|
|
|
Desc.getSourceFileNameIndex(), SrcFilePath);
|
|
|
|
});
|
2017-06-16 01:12:41 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpModuleFiles() {
|
2017-06-16 01:12:41 +02:00
|
|
|
printHeader(P, "Files");
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
if (File.isObj()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotValidForObj();
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!getPdb().hasPDBDbiStream()) {
|
|
|
|
printStreamNotPresent("DBI");
|
2017-09-01 22:06:56 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-07-28 01:13:18 +02:00
|
|
|
ExitOnError Err("Unexpected error processing modules: ");
|
2017-06-16 01:12:41 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
iterateSymbolGroups(File, PrintScope{P, 11},
|
|
|
|
[this, &Err](uint32_t Modi, const SymbolGroup &Strings) {
|
|
|
|
auto &Stream = Err(getPdb().getPDBDbiStream());
|
2017-06-16 01:12:41 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
const DbiModuleList &Modules = Stream.modules();
|
|
|
|
for (const auto &F : Modules.source_files(Modi)) {
|
|
|
|
Strings.formatFromFileName(P, F);
|
|
|
|
}
|
|
|
|
});
|
2017-06-16 01:56:19 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-08-31 22:43:22 +02:00
|
|
|
Error DumpOutputStyle::dumpSymbolStats() {
|
2017-08-21 16:53:25 +02:00
|
|
|
printHeader(P, "Module Stats");
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
|
|
|
|
printStreamNotPresent("DBI");
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-08-21 16:53:25 +02:00
|
|
|
ExitOnError Err("Unexpected error processing modules: ");
|
|
|
|
|
|
|
|
StatCollection SymStats;
|
|
|
|
StatCollection ChunkStats;
|
|
|
|
|
2017-09-01 22:17:20 +02:00
|
|
|
Optional<PrintScope> Scope;
|
|
|
|
if (File.isPdb())
|
|
|
|
Scope.emplace(P, 2);
|
|
|
|
|
|
|
|
iterateSymbolGroups(File, Scope, [&](uint32_t Modi, const SymbolGroup &SG) {
|
2017-09-01 22:06:56 +02:00
|
|
|
StatCollection SS = getSymbolStats(SG, SymStats);
|
|
|
|
StatCollection CS = getChunkStats(SG, ChunkStats);
|
|
|
|
|
|
|
|
if (SG.getFile().isPdb()) {
|
2017-09-01 22:17:20 +02:00
|
|
|
AutoIndent Indent(P);
|
2017-09-01 22:06:56 +02:00
|
|
|
auto Modules = cantFail(File.pdb().getPDBDbiStream()).modules();
|
|
|
|
uint32_t ModCount = Modules.getModuleCount();
|
|
|
|
DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi);
|
|
|
|
uint32_t StreamIdx = Desc.getModuleStreamIndex();
|
|
|
|
|
|
|
|
if (StreamIdx == kInvalidStreamIndex) {
|
|
|
|
P.formatLine("Mod {0} (debug info not present): [{1}]",
|
|
|
|
fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)),
|
|
|
|
Desc.getModuleName());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
P.formatLine("Stream {0}, {1} bytes", StreamIdx,
|
|
|
|
getPdb().getStreamByteSize(StreamIdx));
|
2017-08-21 16:53:25 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
printModuleDetailStats<SymbolKind>(P, "Symbols", SS);
|
|
|
|
printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS);
|
2017-08-21 16:53:25 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (SymStats.Totals.Count > 0) {
|
2018-08-06 21:35:00 +02:00
|
|
|
P.printLine(" Summary |");
|
|
|
|
AutoIndent Indent(P, 4);
|
2017-08-21 16:53:25 +02:00
|
|
|
printModuleDetailStats<SymbolKind>(P, "Symbols", SymStats);
|
|
|
|
printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", ChunkStats);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
Error DumpOutputStyle::dumpTypeStats() {
|
|
|
|
printHeader(P, "Type Record Stats");
|
|
|
|
|
|
|
|
// Iterate the types, categorize by kind, accumulate size stats.
|
|
|
|
StatCollection TypeStats;
|
2020-06-04 21:30:45 +02:00
|
|
|
LazyRandomTypeCollection &Types =
|
|
|
|
opts::dump::DumpTypeStats ? File.types() : File.ids();
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) {
|
|
|
|
CVType Type = Types.getType(*TI);
|
|
|
|
TypeStats.update(uint32_t(Type.kind()), Type.length());
|
|
|
|
}
|
|
|
|
|
|
|
|
P.NewLine();
|
|
|
|
P.formatLine(" Types");
|
|
|
|
AutoIndent Indent(P);
|
2020-06-04 21:30:45 +02:00
|
|
|
P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", "Total",
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
TypeStats.Totals.Count, TypeStats.Totals.Size,
|
|
|
|
(double)TypeStats.Totals.Size / TypeStats.Totals.Count);
|
|
|
|
P.formatLine("{0}", fmt_repeat('-', 74));
|
|
|
|
|
|
|
|
for (const auto &K : TypeStats.getStatsSortedBySize()) {
|
2020-06-04 21:30:45 +02:00
|
|
|
P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)",
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
formatTypeLeafKind(TypeLeafKind(K.first)), K.second.Count,
|
|
|
|
K.second.Size, (double)K.second.Size / K.second.Count);
|
|
|
|
}
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-08-31 22:43:22 +02:00
|
|
|
static bool isValidNamespaceIdentifier(StringRef S) {
|
|
|
|
if (S.empty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (std::isdigit(S[0]))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return llvm::all_of(S, [](char C) { return std::isalnum(C); });
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
constexpr uint32_t kNoneUdtKind = 0;
|
|
|
|
constexpr uint32_t kSimpleUdtKind = 1;
|
|
|
|
constexpr uint32_t kUnknownUdtKind = 2;
|
|
|
|
} // namespace
|
|
|
|
|
2021-01-07 21:41:47 +01:00
|
|
|
static std::string getUdtStatLabel(uint32_t Kind) {
|
2017-08-31 22:43:22 +02:00
|
|
|
if (Kind == kNoneUdtKind)
|
2021-01-07 21:41:47 +01:00
|
|
|
return "<none type>";
|
2017-08-31 22:43:22 +02:00
|
|
|
|
|
|
|
if (Kind == kSimpleUdtKind)
|
2021-01-07 21:41:47 +01:00
|
|
|
return "<simple type>";
|
2017-08-31 22:43:22 +02:00
|
|
|
|
|
|
|
if (Kind == kUnknownUdtKind)
|
2021-01-07 21:41:47 +01:00
|
|
|
return "<unknown type>";
|
2017-08-31 22:43:22 +02:00
|
|
|
|
|
|
|
return formatTypeLeafKind(static_cast<TypeLeafKind>(Kind));
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t getLongestTypeLeafName(const StatCollection &Stats) {
|
|
|
|
size_t L = 0;
|
|
|
|
for (const auto &Stat : Stats.Individual) {
|
2021-01-07 21:41:47 +01:00
|
|
|
std::string Label = getUdtStatLabel(Stat.first);
|
2017-08-31 22:43:22 +02:00
|
|
|
L = std::max(L, Label.size());
|
|
|
|
}
|
|
|
|
return static_cast<uint32_t>(L);
|
|
|
|
}
|
|
|
|
|
|
|
|
Error DumpOutputStyle::dumpUdtStats() {
|
|
|
|
printHeader(P, "S_UDT Record Stats");
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
if (File.isPdb() && !getPdb().hasPDBGlobalsStream()) {
|
|
|
|
printStreamNotPresent("Globals");
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-08-31 22:43:22 +02:00
|
|
|
StatCollection UdtStats;
|
|
|
|
StatCollection UdtTargetStats;
|
|
|
|
AutoIndent Indent(P, 4);
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
auto &TpiTypes = File.types();
|
2017-08-31 22:43:22 +02:00
|
|
|
|
|
|
|
StringMap<StatCollection::Stat> NamespacedStats;
|
|
|
|
|
|
|
|
size_t LongestNamespace = 0;
|
2017-09-01 22:06:56 +02:00
|
|
|
auto HandleOneSymbol = [&](const CVSymbol &Sym) {
|
2017-08-31 22:43:22 +02:00
|
|
|
if (Sym.kind() != SymbolKind::S_UDT)
|
2017-09-01 22:06:56 +02:00
|
|
|
return;
|
2017-08-31 22:43:22 +02:00
|
|
|
UdtStats.update(SymbolKind::S_UDT, Sym.length());
|
|
|
|
|
|
|
|
UDTSym UDT = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(Sym));
|
|
|
|
|
|
|
|
uint32_t Kind = 0;
|
|
|
|
uint32_t RecordSize = 0;
|
2017-09-01 22:06:56 +02:00
|
|
|
|
|
|
|
if (UDT.Type.isNoneType())
|
|
|
|
Kind = kNoneUdtKind;
|
|
|
|
else if (UDT.Type.isSimple())
|
|
|
|
Kind = kSimpleUdtKind;
|
|
|
|
else if (Optional<CVType> T = TpiTypes.tryGetType(UDT.Type)) {
|
|
|
|
Kind = T->kind();
|
|
|
|
RecordSize = T->length();
|
|
|
|
} else
|
|
|
|
Kind = kUnknownUdtKind;
|
2017-08-31 22:43:22 +02:00
|
|
|
|
|
|
|
UdtTargetStats.update(Kind, RecordSize);
|
|
|
|
|
|
|
|
size_t Pos = UDT.Name.find("::");
|
|
|
|
if (Pos == StringRef::npos)
|
2017-09-01 22:06:56 +02:00
|
|
|
return;
|
2017-08-31 22:43:22 +02:00
|
|
|
|
|
|
|
StringRef Scope = UDT.Name.take_front(Pos);
|
|
|
|
if (Scope.empty() || !isValidNamespaceIdentifier(Scope))
|
2017-09-01 22:06:56 +02:00
|
|
|
return;
|
2017-08-31 22:43:22 +02:00
|
|
|
|
|
|
|
LongestNamespace = std::max(LongestNamespace, Scope.size());
|
|
|
|
NamespacedStats[Scope].update(RecordSize);
|
2017-09-01 22:06:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
P.NewLine();
|
|
|
|
|
|
|
|
if (File.isPdb()) {
|
|
|
|
auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream());
|
2017-10-27 02:45:51 +02:00
|
|
|
auto ExpGlobals = getPdb().getPDBGlobalsStream();
|
|
|
|
if (!ExpGlobals)
|
|
|
|
return ExpGlobals.takeError();
|
2017-09-01 22:06:56 +02:00
|
|
|
|
2017-10-27 02:45:51 +02:00
|
|
|
for (uint32_t PubSymOff : ExpGlobals->getGlobalsTable()) {
|
2017-09-01 22:06:56 +02:00
|
|
|
CVSymbol Sym = SymbolRecords.readRecord(PubSymOff);
|
|
|
|
HandleOneSymbol(Sym);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (const auto &Sec : File.symbol_groups()) {
|
|
|
|
for (const auto &SS : Sec.getDebugSubsections()) {
|
|
|
|
if (SS.kind() != DebugSubsectionKind::Symbols)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
DebugSymbolsSubsectionRef Symbols;
|
|
|
|
BinaryStreamReader Reader(SS.getRecordData());
|
|
|
|
cantFail(Symbols.initialize(Reader));
|
|
|
|
for (const auto &S : Symbols)
|
|
|
|
HandleOneSymbol(S);
|
|
|
|
}
|
|
|
|
}
|
2017-08-31 22:43:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
LongestNamespace += StringRef(" namespace ''").size();
|
2017-08-31 22:50:25 +02:00
|
|
|
size_t LongestTypeLeafKind = getLongestTypeLeafName(UdtTargetStats);
|
|
|
|
size_t FieldWidth = std::max(LongestNamespace, LongestTypeLeafKind);
|
2017-08-31 22:43:22 +02:00
|
|
|
|
|
|
|
// Compute the max number of digits for count and size fields, including comma
|
|
|
|
// separators.
|
|
|
|
StringRef CountHeader("Count");
|
|
|
|
StringRef SizeHeader("Size");
|
2017-08-31 22:50:25 +02:00
|
|
|
size_t CD = NumDigits(UdtStats.Totals.Count);
|
2017-08-31 22:43:22 +02:00
|
|
|
CD += (CD - 1) / 3;
|
|
|
|
CD = std::max(CD, CountHeader.size());
|
|
|
|
|
2017-08-31 22:50:25 +02:00
|
|
|
size_t SD = NumDigits(UdtStats.Totals.Size);
|
2017-08-31 22:43:22 +02:00
|
|
|
SD += (SD - 1) / 3;
|
|
|
|
SD = std::max(SD, SizeHeader.size());
|
|
|
|
|
|
|
|
uint32_t TableWidth = FieldWidth + 3 + CD + 2 + SD + 1;
|
|
|
|
|
|
|
|
P.formatLine("{0} | {1} {2}",
|
|
|
|
fmt_align("Record Kind", AlignStyle::Right, FieldWidth),
|
|
|
|
fmt_align(CountHeader, AlignStyle::Right, CD),
|
|
|
|
fmt_align(SizeHeader, AlignStyle::Right, SD));
|
|
|
|
|
|
|
|
P.formatLine("{0}", fmt_repeat('-', TableWidth));
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
for (const auto &Stat : UdtTargetStats.getStatsSortedBySize()) {
|
2021-01-07 21:41:47 +01:00
|
|
|
std::string Label = getUdtStatLabel(Stat.first);
|
2017-08-31 22:43:22 +02:00
|
|
|
P.formatLine("{0} | {1:N} {2:N}",
|
|
|
|
fmt_align(Label, AlignStyle::Right, FieldWidth),
|
|
|
|
fmt_align(Stat.second.Count, AlignStyle::Right, CD),
|
|
|
|
fmt_align(Stat.second.Size, AlignStyle::Right, SD));
|
|
|
|
}
|
|
|
|
P.formatLine("{0}", fmt_repeat('-', TableWidth));
|
|
|
|
P.formatLine("{0} | {1:N} {2:N}",
|
|
|
|
fmt_align("Total (S_UDT)", AlignStyle::Right, FieldWidth),
|
|
|
|
fmt_align(UdtStats.Totals.Count, AlignStyle::Right, CD),
|
|
|
|
fmt_align(UdtStats.Totals.Size, AlignStyle::Right, SD));
|
|
|
|
P.formatLine("{0}", fmt_repeat('-', TableWidth));
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
struct StrAndStat {
|
|
|
|
StringRef Key;
|
|
|
|
StatCollection::Stat Stat;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Print namespace stats in descending order of size.
|
|
|
|
std::vector<StrAndStat> NamespacedStatsSorted;
|
|
|
|
for (const auto &Stat : NamespacedStats)
|
|
|
|
NamespacedStatsSorted.push_back({Stat.getKey(), Stat.second});
|
2019-04-22 17:53:43 +02:00
|
|
|
llvm::stable_sort(NamespacedStatsSorted,
|
|
|
|
[](const StrAndStat &L, const StrAndStat &R) {
|
|
|
|
return L.Stat.Size > R.Stat.Size;
|
|
|
|
});
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
for (const auto &Stat : NamespacedStatsSorted) {
|
2020-01-28 20:23:46 +01:00
|
|
|
std::string Label = std::string(formatv("namespace '{0}'", Stat.Key));
|
2017-08-31 22:43:22 +02:00
|
|
|
P.formatLine("{0} | {1:N} {2:N}",
|
|
|
|
fmt_align(Label, AlignStyle::Right, FieldWidth),
|
[pdb] Add -type-stats and sort stats by descending size
Summary:
It prints this on chromium browser_tests.exe.pdb:
Types
Total: 5647475 entries ( 371,897,512 bytes, 65.85 avg)
--------------------------------------------------------------------------
LF_CLASS: 397894 entries ( 119,537,780 bytes, 300.43 avg)
LF_STRUCTURE: 236351 entries ( 83,208,084 bytes, 352.05 avg)
LF_FIELDLIST: 291003 entries ( 66,087,920 bytes, 227.10 avg)
LF_MFUNCTION: 1884176 entries ( 52,756,928 bytes, 28.00 avg)
LF_POINTER: 1149030 entries ( 13,877,344 bytes, 12.08 avg)
LF_ARGLIST: 789980 entries ( 12,436,752 bytes, 15.74 avg)
LF_METHODLIST: 361498 entries ( 8,351,008 bytes, 23.10 avg)
LF_ENUM: 16069 entries ( 6,108,340 bytes, 380.13 avg)
LF_PROCEDURE: 269374 entries ( 4,309,984 bytes, 16.00 avg)
LF_MODIFIER: 235602 entries ( 2,827,224 bytes, 12.00 avg)
LF_UNION: 9131 entries ( 2,072,168 bytes, 226.94 avg)
LF_VFTABLE: 323 entries ( 207,784 bytes, 643.29 avg)
LF_ARRAY: 6639 entries ( 106,380 bytes, 16.02 avg)
LF_VTSHAPE: 126 entries ( 6,472 bytes, 51.37 avg)
LF_BITFIELD: 278 entries ( 3,336 bytes, 12.00 avg)
LF_LABEL: 1 entries ( 8 bytes, 8.00 avg)
The PDB is overall 1.9GB, so the LF_CLASS and LF_STRUCTURE declarations
account for about 10% of the overall file size. I was surprised to find
that on average LF_FIELDLIST records are short. Maybe this is because
there are many more types with short member lists than there are
instantiations with lots of members, like std::vector.
Reviewers: aganea, zturner
Subscribers: llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59672
llvm-svn: 356813
2019-03-22 22:22:13 +01:00
|
|
|
fmt_align(Stat.Stat.Count, AlignStyle::Right, CD),
|
|
|
|
fmt_align(Stat.Stat.Size, AlignStyle::Right, SD));
|
2017-08-31 22:43:22 +02:00
|
|
|
}
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start,
|
|
|
|
const LineColumnEntry &E) {
|
2017-06-16 01:56:19 +02:00
|
|
|
const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number
|
|
|
|
uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5;
|
|
|
|
|
|
|
|
// Let's try to keep it under 100 characters
|
|
|
|
constexpr uint32_t kMaxRowLength = 100;
|
|
|
|
// At least 3 spaces between columns.
|
|
|
|
uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3);
|
|
|
|
uint32_t ItemsLeft = E.LineNumbers.size();
|
|
|
|
auto LineIter = E.LineNumbers.begin();
|
|
|
|
while (ItemsLeft != 0) {
|
|
|
|
uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow);
|
|
|
|
for (uint32_t I = 0; I < RowColumns; ++I) {
|
|
|
|
LineInfo Line(LineIter->Flags);
|
|
|
|
std::string LineStr;
|
|
|
|
if (Line.isAlwaysStepInto())
|
|
|
|
LineStr = "ASI";
|
|
|
|
else if (Line.isNeverStepInto())
|
|
|
|
LineStr = "NSI";
|
2017-06-16 01:12:41 +02:00
|
|
|
else
|
2017-06-16 01:56:19 +02:00
|
|
|
LineStr = utostr(Line.getStartLine());
|
|
|
|
char Statement = Line.isStatement() ? ' ' : '!';
|
|
|
|
P.format("{0} {1:X-} {2} ",
|
|
|
|
fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber),
|
|
|
|
fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'),
|
|
|
|
Statement);
|
|
|
|
++LineIter;
|
|
|
|
--ItemsLeft;
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
2017-06-16 01:56:19 +02:00
|
|
|
P.NewLine();
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
2017-06-16 01:56:19 +02:00
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpLines() {
|
2017-06-16 01:56:19 +02:00
|
|
|
printHeader(P, "Lines");
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
|
|
|
|
printStreamNotPresent("DBI");
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-16 01:59:56 +02:00
|
|
|
uint32_t LastModi = UINT32_MAX;
|
|
|
|
uint32_t LastNameIndex = UINT32_MAX;
|
|
|
|
iterateModuleSubsections<DebugLinesSubsectionRef>(
|
2017-09-01 22:06:56 +02:00
|
|
|
File, PrintScope{P, 4},
|
2017-06-16 01:59:56 +02:00
|
|
|
[this, &LastModi, &LastNameIndex](uint32_t Modi,
|
2017-09-01 22:06:56 +02:00
|
|
|
const SymbolGroup &Strings,
|
2017-06-16 01:59:56 +02:00
|
|
|
DebugLinesSubsectionRef &Lines) {
|
|
|
|
uint16_t Segment = Lines.header()->RelocSegment;
|
|
|
|
uint32_t Begin = Lines.header()->RelocOffset;
|
|
|
|
uint32_t End = Begin + Lines.header()->CodeSize;
|
|
|
|
for (const auto &Block : Lines) {
|
|
|
|
if (LastModi != Modi || LastNameIndex != Block.NameIndex) {
|
|
|
|
LastModi = Modi;
|
|
|
|
LastNameIndex = Block.NameIndex;
|
|
|
|
Strings.formatFromChecksumsOffset(P, Block.NameIndex);
|
2017-06-16 01:56:19 +02:00
|
|
|
}
|
|
|
|
|
2017-06-16 01:59:56 +02:00
|
|
|
AutoIndent Indent(P, 2);
|
|
|
|
P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End);
|
|
|
|
uint32_t Count = Block.LineNumbers.size();
|
|
|
|
if (Lines.hasColumnInfo())
|
|
|
|
P.format("line/column/addr entries = {0}", Count);
|
|
|
|
else
|
|
|
|
P.format("line/addr entries = {0}", Count);
|
|
|
|
|
|
|
|
P.NewLine();
|
2017-09-01 22:06:56 +02:00
|
|
|
typesetLinesAndColumns(P, Begin, Block);
|
2017-06-16 01:56:19 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpInlineeLines() {
|
2017-06-16 01:56:19 +02:00
|
|
|
printHeader(P, "Inlinee Lines");
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
|
|
|
|
printStreamNotPresent("DBI");
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-16 01:59:56 +02:00
|
|
|
iterateModuleSubsections<DebugInlineeLinesSubsectionRef>(
|
2017-09-01 22:06:56 +02:00
|
|
|
File, PrintScope{P, 2},
|
|
|
|
[this](uint32_t Modi, const SymbolGroup &Strings,
|
2017-06-16 01:59:56 +02:00
|
|
|
DebugInlineeLinesSubsectionRef &Lines) {
|
|
|
|
P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File");
|
|
|
|
for (const auto &Entry : Lines) {
|
|
|
|
P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee,
|
|
|
|
fmtle(Entry.Header->SourceLineNum));
|
|
|
|
Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true);
|
2019-06-03 20:15:38 +02:00
|
|
|
for (const auto &ExtraFileID : Entry.ExtraFiles) {
|
|
|
|
P.formatLine(" ");
|
|
|
|
Strings.formatFromChecksumsOffset(P, ExtraFileID, true);
|
|
|
|
}
|
2017-06-16 01:56:19 +02:00
|
|
|
}
|
2017-06-16 01:59:56 +02:00
|
|
|
P.NewLine();
|
2017-06-16 01:56:19 +02:00
|
|
|
});
|
|
|
|
|
2017-06-16 00:24:24 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
2017-06-16 01:12:41 +02:00
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpXmi() {
|
2017-06-16 02:04:24 +02:00
|
|
|
printHeader(P, "Cross Module Imports");
|
2018-08-06 21:35:00 +02:00
|
|
|
|
|
|
|
if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
|
|
|
|
printStreamNotPresent("DBI");
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-16 02:04:24 +02:00
|
|
|
iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>(
|
2017-09-01 22:06:56 +02:00
|
|
|
File, PrintScope{P, 2},
|
|
|
|
[this](uint32_t Modi, const SymbolGroup &Strings,
|
2017-06-16 02:04:24 +02:00
|
|
|
DebugCrossModuleImportsSubsectionRef &Imports) {
|
|
|
|
P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs");
|
|
|
|
|
|
|
|
for (const auto &Xmi : Imports) {
|
|
|
|
auto ExpectedModule =
|
|
|
|
Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset);
|
|
|
|
StringRef Module;
|
|
|
|
SmallString<32> ModuleStorage;
|
|
|
|
if (!ExpectedModule) {
|
|
|
|
Module = "(unknown module)";
|
|
|
|
consumeError(ExpectedModule.takeError());
|
|
|
|
} else
|
|
|
|
Module = *ExpectedModule;
|
|
|
|
if (Module.size() > 32) {
|
|
|
|
ModuleStorage = "...";
|
|
|
|
ModuleStorage += Module.take_back(32 - 3);
|
|
|
|
Module = ModuleStorage;
|
|
|
|
}
|
|
|
|
std::vector<std::string> TIs;
|
|
|
|
for (const auto I : Xmi.Imports)
|
2020-01-28 20:23:46 +01:00
|
|
|
TIs.push_back(std::string(formatv("{0,+10:X+}", fmtle(I))));
|
2017-06-16 02:04:24 +02:00
|
|
|
std::string Result =
|
|
|
|
typesetItemList(TIs, P.getIndentLevel() + 35, 12, " ");
|
|
|
|
P.formatLine("{0,+32} | {1}", Module, Result);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpXme() {
|
2017-06-16 02:04:24 +02:00
|
|
|
printHeader(P, "Cross Module Exports");
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
|
|
|
|
printStreamNotPresent("DBI");
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-16 02:04:24 +02:00
|
|
|
iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>(
|
2017-09-01 22:06:56 +02:00
|
|
|
File, PrintScope{P, 2},
|
|
|
|
[this](uint32_t Modi, const SymbolGroup &Strings,
|
2017-06-16 02:04:24 +02:00
|
|
|
DebugCrossModuleExportsSubsectionRef &Exports) {
|
|
|
|
P.formatLine("{0,-10} | {1}", "Local ID", "Global ID");
|
|
|
|
for (const auto &Export : Exports) {
|
|
|
|
P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local),
|
|
|
|
TypeIndex(Export.Global));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-09-12 23:02:01 +02:00
|
|
|
std::string formatFrameType(object::frame_type FT) {
|
|
|
|
switch (FT) {
|
|
|
|
case object::frame_type::Fpo:
|
|
|
|
return "FPO";
|
|
|
|
case object::frame_type::NonFpo:
|
|
|
|
return "Non-FPO";
|
|
|
|
case object::frame_type::Trap:
|
|
|
|
return "Trap";
|
|
|
|
case object::frame_type::Tss:
|
|
|
|
return "TSS";
|
|
|
|
}
|
|
|
|
return "<unknown>";
|
|
|
|
}
|
2018-09-12 00:35:01 +02:00
|
|
|
|
2018-09-12 23:02:01 +02:00
|
|
|
Error DumpOutputStyle::dumpOldFpo(PDBFile &File) {
|
|
|
|
printHeader(P, "Old FPO Data");
|
|
|
|
|
|
|
|
ExitOnError Err("Error dumping old fpo data:");
|
|
|
|
auto &Dbi = Err(File.getPDBDbiStream());
|
|
|
|
|
2019-01-30 11:40:45 +01:00
|
|
|
if (!Dbi.hasOldFpoRecords()) {
|
2018-09-12 23:02:01 +02:00
|
|
|
printStreamNotPresent("FPO");
|
2018-09-12 00:35:01 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2019-01-30 11:40:45 +01:00
|
|
|
const FixedStreamArray<object::FpoData>& Records = Dbi.getOldFpoRecords();
|
2018-09-12 23:02:01 +02:00
|
|
|
|
|
|
|
P.printLine(" RVA | Code | Locals | Params | Prolog | Saved Regs | Use "
|
|
|
|
"BP | Has SEH | Frame Type");
|
|
|
|
|
|
|
|
for (const object::FpoData &FD : Records) {
|
|
|
|
P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,6} | {5,10} | {6,6} | "
|
|
|
|
"{7,7} | {8,9}",
|
|
|
|
uint32_t(FD.Offset), uint32_t(FD.Size), uint32_t(FD.NumLocals),
|
|
|
|
uint32_t(FD.NumParams), FD.getPrologSize(),
|
|
|
|
FD.getNumSavedRegs(), FD.useBP(), FD.hasSEH(),
|
|
|
|
formatFrameType(FD.getFP()));
|
2018-09-12 00:35:01 +02:00
|
|
|
}
|
2018-09-12 23:02:01 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
2018-09-12 00:35:01 +02:00
|
|
|
|
2018-09-12 23:02:01 +02:00
|
|
|
Error DumpOutputStyle::dumpNewFpo(PDBFile &File) {
|
|
|
|
printHeader(P, "New FPO Data");
|
2018-09-12 00:35:01 +02:00
|
|
|
|
2018-09-12 23:02:01 +02:00
|
|
|
ExitOnError Err("Error dumping new fpo data:");
|
2018-09-12 00:35:01 +02:00
|
|
|
auto &Dbi = Err(File.getPDBDbiStream());
|
|
|
|
|
2019-01-30 11:40:45 +01:00
|
|
|
if (!Dbi.hasNewFpoRecords()) {
|
2018-09-12 00:35:01 +02:00
|
|
|
printStreamNotPresent("New FPO");
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2019-01-30 11:40:45 +01:00
|
|
|
const DebugFrameDataSubsectionRef& FDS = Dbi.getNewFpoRecords();
|
2018-09-12 00:35:01 +02:00
|
|
|
|
|
|
|
P.printLine(" RVA | Code | Locals | Params | Stack | Prolog | Saved Regs "
|
|
|
|
"| Has SEH | Has C++EH | Start | Program");
|
|
|
|
for (const FrameData &FD : FDS) {
|
|
|
|
bool IsFuncStart = FD.Flags & FrameData::IsFunctionStart;
|
|
|
|
bool HasEH = FD.Flags & FrameData::HasEH;
|
|
|
|
bool HasSEH = FD.Flags & FrameData::HasSEH;
|
|
|
|
|
|
|
|
auto &StringTable = Err(File.getStringTable());
|
|
|
|
|
|
|
|
auto Program = Err(StringTable.getStringForID(FD.FrameFunc));
|
|
|
|
P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,5} | {5,6} | {6,10} | "
|
|
|
|
"{7,7} | {8,9} | {9,5} | {10}",
|
|
|
|
uint32_t(FD.RvaStart), uint32_t(FD.CodeSize),
|
|
|
|
uint32_t(FD.LocalSize), uint32_t(FD.ParamsSize),
|
|
|
|
uint32_t(FD.MaxStackSize), uint16_t(FD.PrologSize),
|
|
|
|
uint16_t(FD.SavedRegsSize), HasSEH, HasEH, IsFuncStart,
|
|
|
|
Program);
|
|
|
|
}
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-09-12 23:02:01 +02:00
|
|
|
Error DumpOutputStyle::dumpFpo() {
|
|
|
|
if (!File.isPdb()) {
|
|
|
|
printStreamNotValidForObj();
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
PDBFile &File = getPdb();
|
|
|
|
if (!File.hasPDBDbiStream()) {
|
|
|
|
printStreamNotPresent("DBI");
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (auto EC = dumpOldFpo(File))
|
|
|
|
return EC;
|
|
|
|
if (auto EC = dumpNewFpo(File))
|
|
|
|
return EC;
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-01-05 20:12:40 +01:00
|
|
|
Error DumpOutputStyle::dumpStringTableFromPdb() {
|
2017-06-16 00:24:24 +02:00
|
|
|
AutoIndent Indent(P);
|
2017-09-01 22:06:56 +02:00
|
|
|
auto IS = getPdb().getStringTable();
|
2017-06-16 00:24:24 +02:00
|
|
|
if (!IS) {
|
|
|
|
P.formatLine("Not present in file");
|
|
|
|
consumeError(IS.takeError());
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-03-23 19:43:39 +01:00
|
|
|
if (opts::dump::DumpStringTable) {
|
|
|
|
if (IS->name_ids().empty())
|
|
|
|
P.formatLine("Empty");
|
|
|
|
else {
|
|
|
|
auto MaxID =
|
|
|
|
std::max_element(IS->name_ids().begin(), IS->name_ids().end());
|
|
|
|
uint32_t Digits = NumDigits(*MaxID);
|
|
|
|
|
|
|
|
P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits),
|
|
|
|
"String");
|
|
|
|
|
|
|
|
std::vector<uint32_t> SortedIDs(IS->name_ids().begin(),
|
|
|
|
IS->name_ids().end());
|
llvm::sort(C.begin(), C.end(), ...) -> llvm::sort(C, ...)
Summary: The convenience wrapper in STLExtras is available since rL342102.
Reviewers: dblaikie, javed.absar, JDevlieghere, andreadb
Subscribers: MatzeB, sanjoy, arsenm, dschuff, mehdi_amini, sdardis, nemanjai, jvesely, nhaehnle, sbc100, jgravelle-google, eraman, aheejin, kbarton, JDevlieghere, javed.absar, gbedwell, jrtc27, mgrang, atanasyan, steven_wu, george.burgess.iv, dexonsmith, kristina, jsji, llvm-commits
Differential Revision: https://reviews.llvm.org/D52573
llvm-svn: 343163
2018-09-27 04:13:45 +02:00
|
|
|
llvm::sort(SortedIDs);
|
2018-03-23 19:43:39 +01:00
|
|
|
for (uint32_t I : SortedIDs) {
|
|
|
|
auto ES = IS->getStringForID(I);
|
|
|
|
llvm::SmallString<32> Str;
|
|
|
|
if (!ES) {
|
|
|
|
consumeError(ES.takeError());
|
|
|
|
Str = "Error reading string";
|
|
|
|
} else if (!ES->empty()) {
|
|
|
|
Str.append("'");
|
|
|
|
Str.append(*ES);
|
|
|
|
Str.append("'");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Str.empty())
|
|
|
|
P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits),
|
|
|
|
Str);
|
|
|
|
}
|
|
|
|
}
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
|
|
|
|
2018-03-23 19:43:39 +01:00
|
|
|
if (opts::dump::DumpStringTableDetails) {
|
|
|
|
P.NewLine();
|
|
|
|
{
|
|
|
|
P.printLine("String Table Header:");
|
|
|
|
AutoIndent Indent(P);
|
|
|
|
P.formatLine("Signature: {0}", IS->getSignature());
|
|
|
|
P.formatLine("Hash Version: {0}", IS->getHashVersion());
|
|
|
|
P.formatLine("Name Buffer Size: {0}", IS->getByteSize());
|
|
|
|
P.NewLine();
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
|
|
|
|
2018-03-23 19:43:39 +01:00
|
|
|
BinaryStreamRef NameBuffer = IS->getStringTable().getBuffer();
|
|
|
|
ArrayRef<uint8_t> Contents;
|
|
|
|
cantFail(NameBuffer.readBytes(0, NameBuffer.getLength(), Contents));
|
|
|
|
P.formatBinary("Name Buffer", Contents, 0);
|
|
|
|
P.NewLine();
|
|
|
|
{
|
|
|
|
P.printLine("Hash Table:");
|
|
|
|
AutoIndent Indent(P);
|
|
|
|
P.formatLine("Bucket Count: {0}", IS->name_ids().size());
|
|
|
|
for (const auto &Entry : enumerate(IS->name_ids()))
|
|
|
|
P.formatLine("Bucket[{0}] : {1}", Entry.index(),
|
|
|
|
uint32_t(Entry.value()));
|
|
|
|
P.formatLine("Name Count: {0}", IS->getNameCount());
|
|
|
|
}
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-01-05 20:12:40 +01:00
|
|
|
Error DumpOutputStyle::dumpStringTableFromObj() {
|
|
|
|
iterateModuleSubsections<DebugStringTableSubsectionRef>(
|
|
|
|
File, PrintScope{P, 4},
|
|
|
|
[&](uint32_t Modi, const SymbolGroup &Strings,
|
|
|
|
DebugStringTableSubsectionRef &Strings2) {
|
|
|
|
BinaryStreamRef StringTableBuffer = Strings2.getBuffer();
|
|
|
|
BinaryStreamReader Reader(StringTableBuffer);
|
|
|
|
while (Reader.bytesRemaining() > 0) {
|
|
|
|
StringRef Str;
|
|
|
|
uint32_t Offset = Reader.getOffset();
|
|
|
|
cantFail(Reader.readCString(Str));
|
|
|
|
if (Str.empty())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
P.formatLine("{0} | {1}", fmt_align(Offset, AlignStyle::Right, 4),
|
|
|
|
Str);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-03-23 19:43:39 +01:00
|
|
|
Error DumpOutputStyle::dumpNamedStreams() {
|
|
|
|
printHeader(P, "Named Streams");
|
|
|
|
|
|
|
|
if (File.isObj()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotValidForObj();
|
2018-03-23 19:43:39 +01:00
|
|
|
return Error::success();
|
|
|
|
}
|
2018-08-06 21:35:00 +02:00
|
|
|
|
|
|
|
AutoIndent Indent(P);
|
2018-03-23 19:43:39 +01:00
|
|
|
ExitOnError Err("Invalid PDB File: ");
|
|
|
|
|
|
|
|
auto &IS = Err(File.pdb().getPDBInfoStream());
|
|
|
|
const NamedStreamMap &NS = IS.getNamedStreams();
|
|
|
|
for (const auto &Entry : NS.entries()) {
|
|
|
|
P.printLine(Entry.getKey());
|
|
|
|
AutoIndent Indent2(P, 2);
|
|
|
|
P.formatLine("Index: {0}", Entry.getValue());
|
|
|
|
P.formatLine("Size in bytes: {0}",
|
|
|
|
File.pdb().getStreamByteSize(Entry.getValue()));
|
|
|
|
}
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-01-05 20:12:40 +01:00
|
|
|
Error DumpOutputStyle::dumpStringTable() {
|
|
|
|
printHeader(P, "String Table");
|
|
|
|
|
|
|
|
if (File.isPdb())
|
|
|
|
return dumpStringTableFromPdb();
|
|
|
|
|
|
|
|
return dumpStringTableFromObj();
|
|
|
|
}
|
|
|
|
|
2017-06-30 20:15:47 +02:00
|
|
|
static void buildDepSet(LazyRandomTypeCollection &Types,
|
|
|
|
ArrayRef<TypeIndex> Indices,
|
|
|
|
std::map<TypeIndex, CVType> &DepSet) {
|
|
|
|
SmallVector<TypeIndex, 4> DepList;
|
|
|
|
for (const auto &I : Indices) {
|
|
|
|
TypeIndex TI(I);
|
|
|
|
if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
CVType Type = Types.getType(TI);
|
|
|
|
DepSet[TI] = Type;
|
|
|
|
codeview::discoverTypeIndices(Type, DepList);
|
|
|
|
buildDepSet(Types, DepList, DepSet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-06 00:58:18 +01:00
|
|
|
static void
|
|
|
|
dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types,
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
TypeReferenceTracker *RefTracker, uint32_t NumTypeRecords,
|
|
|
|
uint32_t NumHashBuckets,
|
2017-12-06 00:58:18 +01:00
|
|
|
FixedStreamArray<support::ulittle32_t> HashValues,
|
2018-09-20 17:50:13 +02:00
|
|
|
TpiStream *Stream, bool Bytes, bool Extras) {
|
2017-12-06 00:58:18 +01:00
|
|
|
|
2017-12-06 01:26:43 +01:00
|
|
|
Printer.formatLine("Showing {0:N} records", NumTypeRecords);
|
|
|
|
uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords);
|
2017-06-30 20:15:47 +02:00
|
|
|
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker,
|
2018-09-20 17:50:13 +02:00
|
|
|
NumHashBuckets, HashValues, Stream);
|
2017-06-30 20:15:47 +02:00
|
|
|
|
|
|
|
if (auto EC = codeview::visitTypeStream(Types, V)) {
|
|
|
|
Printer.formatLine("An error occurred dumping type records: {0}",
|
|
|
|
toString(std::move(EC)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dumpPartialTypeStream(LinePrinter &Printer,
|
|
|
|
LazyRandomTypeCollection &Types,
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
TypeReferenceTracker *RefTracker,
|
2017-06-30 20:15:47 +02:00
|
|
|
TpiStream &Stream, ArrayRef<TypeIndex> TiList,
|
|
|
|
bool Bytes, bool Extras, bool Deps) {
|
|
|
|
uint32_t Width =
|
|
|
|
NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords());
|
|
|
|
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker,
|
2018-09-20 17:50:13 +02:00
|
|
|
Stream.getNumHashBuckets(), Stream.getHashValues(),
|
|
|
|
&Stream);
|
2017-06-30 20:15:47 +02:00
|
|
|
|
|
|
|
if (opts::dump::DumpTypeDependents) {
|
|
|
|
// If we need to dump all dependents, then iterate each index and find
|
|
|
|
// all dependents, adding them to a map ordered by TypeIndex.
|
|
|
|
std::map<TypeIndex, CVType> DepSet;
|
|
|
|
buildDepSet(Types, TiList, DepSet);
|
|
|
|
|
|
|
|
Printer.formatLine(
|
|
|
|
"Showing {0:N} records and their dependents ({1:N} records total)",
|
|
|
|
TiList.size(), DepSet.size());
|
|
|
|
|
|
|
|
for (auto &Dep : DepSet) {
|
|
|
|
if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V))
|
|
|
|
Printer.formatLine("An error occurred dumping type record {0}: {1}",
|
|
|
|
Dep.first, toString(std::move(EC)));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Printer.formatLine("Showing {0:N} records.", TiList.size());
|
|
|
|
|
|
|
|
for (const auto &I : TiList) {
|
|
|
|
TypeIndex TI(I);
|
|
|
|
CVType Type = Types.getType(TI);
|
|
|
|
if (auto EC = codeview::visitTypeRecord(Type, TI, V))
|
|
|
|
Printer.formatLine("An error occurred dumping type record {0}: {1}", TI,
|
|
|
|
toString(std::move(EC)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-06 00:58:18 +01:00
|
|
|
Error DumpOutputStyle::dumpTypesFromObjectFile() {
|
|
|
|
LazyRandomTypeCollection Types(100);
|
|
|
|
|
|
|
|
for (const auto &S : getObj().sections()) {
|
2019-08-14 13:10:11 +02:00
|
|
|
Expected<StringRef> NameOrErr = S.getName();
|
|
|
|
if (!NameOrErr)
|
|
|
|
return NameOrErr.takeError();
|
|
|
|
StringRef SectionName = *NameOrErr;
|
2017-12-06 00:58:18 +01:00
|
|
|
|
2018-04-05 20:18:12 +02:00
|
|
|
// .debug$T is a standard CodeView type section, while .debug$P is the same
|
|
|
|
// format but used for MSVC precompiled header object files.
|
|
|
|
if (SectionName == ".debug$T")
|
|
|
|
printHeader(P, "Types (.debug$T)");
|
|
|
|
else if (SectionName == ".debug$P")
|
|
|
|
printHeader(P, "Precompiled Types (.debug$P)");
|
|
|
|
else
|
2017-12-06 00:58:18 +01:00
|
|
|
continue;
|
2018-04-05 20:18:12 +02:00
|
|
|
|
2019-05-16 15:24:04 +02:00
|
|
|
Expected<StringRef> ContentsOrErr = S.getContents();
|
|
|
|
if (!ContentsOrErr)
|
|
|
|
return ContentsOrErr.takeError();
|
2017-12-06 00:58:18 +01:00
|
|
|
|
|
|
|
uint32_t Magic;
|
2019-05-16 15:24:04 +02:00
|
|
|
BinaryStreamReader Reader(*ContentsOrErr, llvm::support::little);
|
2017-12-06 00:58:18 +01:00
|
|
|
if (auto EC = Reader.readInteger(Magic))
|
|
|
|
return EC;
|
|
|
|
if (Magic != COFF::DEBUG_SECTION_MAGIC)
|
|
|
|
return make_error<StringError>("Invalid CodeView debug section.",
|
|
|
|
inconvertibleErrorCode());
|
|
|
|
|
|
|
|
Types.reset(Reader, 100);
|
|
|
|
|
|
|
|
if (opts::dump::DumpTypes) {
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
dumpFullTypeStream(P, Types, RefTracker.get(), 0, 0, {}, nullptr,
|
|
|
|
opts::dump::DumpTypeData, false);
|
2017-12-06 00:58:18 +01:00
|
|
|
} else if (opts::dump::DumpTypeExtras) {
|
|
|
|
auto LocalHashes = LocallyHashedType::hashTypeCollection(Types);
|
|
|
|
auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types);
|
|
|
|
assert(LocalHashes.size() == GlobalHashes.size());
|
|
|
|
|
|
|
|
P.formatLine("Local / Global hashes:");
|
|
|
|
TypeIndex TI(TypeIndex::FirstNonSimpleIndex);
|
2019-12-22 19:11:17 +01:00
|
|
|
for (auto H : zip(LocalHashes, GlobalHashes)) {
|
2017-12-06 00:58:18 +01:00
|
|
|
AutoIndent Indent2(P);
|
|
|
|
LocallyHashedType &L = std::get<0>(H);
|
|
|
|
GloballyHashedType &G = std::get<1>(H);
|
|
|
|
|
|
|
|
P.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI, L, G);
|
|
|
|
|
|
|
|
++TI;
|
|
|
|
}
|
|
|
|
P.NewLine();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
|
2017-06-16 00:24:24 +02:00
|
|
|
assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI);
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
if (StreamIdx == StreamTPI) {
|
|
|
|
printHeader(P, "Types (TPI Stream)");
|
|
|
|
} else if (StreamIdx == StreamIPI) {
|
|
|
|
printHeader(P, "Types (IPI Stream)");
|
|
|
|
}
|
|
|
|
|
2017-12-06 00:58:18 +01:00
|
|
|
assert(!File.isObj());
|
2017-09-01 22:06:56 +02:00
|
|
|
|
2017-06-16 00:24:24 +02:00
|
|
|
bool Present = false;
|
2017-06-16 01:04:42 +02:00
|
|
|
bool DumpTypes = false;
|
2017-06-16 00:24:24 +02:00
|
|
|
bool DumpBytes = false;
|
2017-06-16 01:04:42 +02:00
|
|
|
bool DumpExtras = false;
|
2017-06-17 01:42:15 +02:00
|
|
|
std::vector<uint32_t> Indices;
|
2017-06-16 00:24:24 +02:00
|
|
|
if (StreamIdx == StreamTPI) {
|
2017-09-01 22:06:56 +02:00
|
|
|
Present = getPdb().hasPDBTpiStream();
|
2017-06-22 22:57:39 +02:00
|
|
|
DumpTypes = opts::dump::DumpTypes;
|
|
|
|
DumpBytes = opts::dump::DumpTypeData;
|
|
|
|
DumpExtras = opts::dump::DumpTypeExtras;
|
|
|
|
Indices.assign(opts::dump::DumpTypeIndex.begin(),
|
|
|
|
opts::dump::DumpTypeIndex.end());
|
2017-06-16 00:24:24 +02:00
|
|
|
} else if (StreamIdx == StreamIPI) {
|
2017-09-01 22:06:56 +02:00
|
|
|
Present = getPdb().hasPDBIpiStream();
|
2017-06-22 22:57:39 +02:00
|
|
|
DumpTypes = opts::dump::DumpIds;
|
|
|
|
DumpBytes = opts::dump::DumpIdData;
|
|
|
|
DumpExtras = opts::dump::DumpIdExtras;
|
|
|
|
Indices.assign(opts::dump::DumpIdIndex.begin(),
|
|
|
|
opts::dump::DumpIdIndex.end());
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!Present) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotPresent(StreamIdx == StreamTPI ? "TPI" : "IPI");
|
2017-06-16 00:24:24 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
AutoIndent Indent(P);
|
2017-07-28 01:13:18 +02:00
|
|
|
ExitOnError Err("Unexpected error processing types: ");
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream()
|
|
|
|
: getPdb().getPDBIpiStream());
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids();
|
2017-06-16 00:24:24 +02:00
|
|
|
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
// Only emit notes about referenced/unreferenced for types.
|
|
|
|
TypeReferenceTracker *MaybeTracker =
|
|
|
|
(StreamIdx == StreamTPI) ? RefTracker.get() : nullptr;
|
|
|
|
|
2018-09-20 17:50:13 +02:00
|
|
|
// Enable resolving forward decls.
|
|
|
|
Stream.buildHashMap();
|
|
|
|
|
2017-06-30 20:15:47 +02:00
|
|
|
if (DumpTypes || !Indices.empty()) {
|
|
|
|
if (Indices.empty())
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
dumpFullTypeStream(P, Types, MaybeTracker, Stream.getNumTypeRecords(),
|
2017-12-06 01:26:43 +01:00
|
|
|
Stream.getNumHashBuckets(), Stream.getHashValues(),
|
2018-09-20 17:50:13 +02:00
|
|
|
&Stream, DumpBytes, DumpExtras);
|
2017-06-30 20:15:47 +02:00
|
|
|
else {
|
|
|
|
std::vector<TypeIndex> TiList(Indices.begin(), Indices.end());
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
dumpPartialTypeStream(P, Types, MaybeTracker, Stream, TiList, DumpBytes,
|
|
|
|
DumpExtras, opts::dump::DumpTypeDependents);
|
2017-06-16 01:04:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DumpExtras) {
|
|
|
|
P.NewLine();
|
2019-01-24 23:25:55 +01:00
|
|
|
|
|
|
|
P.formatLine("Header Version: {0}",
|
|
|
|
static_cast<uint32_t>(Stream.getTpiVersion()));
|
|
|
|
P.formatLine("Hash Stream Index: {0}", Stream.getTypeHashStreamIndex());
|
|
|
|
P.formatLine("Aux Hash Stream Index: {0}",
|
|
|
|
Stream.getTypeHashStreamAuxIndex());
|
|
|
|
P.formatLine("Hash Key Size: {0}", Stream.getHashKeySize());
|
|
|
|
P.formatLine("Num Hash Buckets: {0}", Stream.getNumHashBuckets());
|
|
|
|
|
2017-06-16 01:04:42 +02:00
|
|
|
auto IndexOffsets = Stream.getTypeIndexOffsets();
|
|
|
|
P.formatLine("Type Index Offsets:");
|
|
|
|
for (const auto &IO : IndexOffsets) {
|
|
|
|
AutoIndent Indent2(P);
|
|
|
|
P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset));
|
|
|
|
}
|
|
|
|
|
2018-11-02 19:00:37 +01:00
|
|
|
if (getPdb().hasPDBStringTable()) {
|
|
|
|
P.NewLine();
|
|
|
|
P.formatLine("Hash Adjusters:");
|
|
|
|
auto &Adjusters = Stream.getHashAdjusters();
|
|
|
|
auto &Strings = Err(getPdb().getStringTable());
|
|
|
|
for (const auto &A : Adjusters) {
|
|
|
|
AutoIndent Indent2(P);
|
|
|
|
auto ExpectedStr = Strings.getStringForID(A.first);
|
|
|
|
TypeIndex TI(A.second);
|
|
|
|
if (ExpectedStr)
|
|
|
|
P.formatLine("`{0}` -> {1}", *ExpectedStr, TI);
|
|
|
|
else {
|
|
|
|
P.formatLine("unknown str id ({0}) -> {1}", A.first, TI);
|
|
|
|
consumeError(ExpectedStr.takeError());
|
|
|
|
}
|
2017-06-16 01:04:42 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
Error DumpOutputStyle::dumpModuleSymsForObj() {
|
|
|
|
printHeader(P, "Symbols");
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
AutoIndent Indent(P);
|
|
|
|
|
|
|
|
ExitOnError Err("Unexpected error processing symbols: ");
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
auto &Types = File.types();
|
|
|
|
|
|
|
|
SymbolVisitorCallbackPipeline Pipeline;
|
|
|
|
SymbolDeserializer Deserializer(nullptr, CodeViewContainer::ObjectFile);
|
|
|
|
MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types, Types);
|
|
|
|
|
|
|
|
Pipeline.addCallbackToPipeline(Deserializer);
|
|
|
|
Pipeline.addCallbackToPipeline(Dumper);
|
|
|
|
CVSymbolVisitor Visitor(Pipeline);
|
|
|
|
|
|
|
|
std::unique_ptr<llvm::Error> SymbolError;
|
|
|
|
|
|
|
|
iterateModuleSubsections<DebugSymbolsSubsectionRef>(
|
|
|
|
File, PrintScope{P, 2},
|
|
|
|
[&](uint32_t Modi, const SymbolGroup &Strings,
|
|
|
|
DebugSymbolsSubsectionRef &Symbols) {
|
2018-01-05 20:12:40 +01:00
|
|
|
Dumper.setSymbolGroup(&Strings);
|
2017-09-01 22:06:56 +02:00
|
|
|
for (auto Symbol : Symbols) {
|
|
|
|
if (auto EC = Visitor.visitSymbolRecord(Symbol)) {
|
2019-08-15 17:54:37 +02:00
|
|
|
SymbolError = std::make_unique<Error>(std::move(EC));
|
2017-09-01 22:06:56 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (SymbolError)
|
|
|
|
return std::move(*SymbolError);
|
|
|
|
|
|
|
|
return Error::success();
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
Error DumpOutputStyle::dumpModuleSymsForPdb() {
|
2017-06-16 00:24:24 +02:00
|
|
|
printHeader(P, "Symbols");
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
if (File.isPdb() && !getPdb().hasPDBDbiStream()) {
|
|
|
|
printStreamNotPresent("DBI");
|
2017-06-16 00:24:24 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
AutoIndent Indent(P);
|
2017-07-28 01:13:18 +02:00
|
|
|
ExitOnError Err("Unexpected error processing symbols: ");
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
auto &Ids = File.ids();
|
|
|
|
auto &Types = File.types();
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
iterateSymbolGroups(
|
|
|
|
File, PrintScope{P, 2}, [&](uint32_t I, const SymbolGroup &Strings) {
|
|
|
|
auto ExpectedModS = getModuleDebugStream(File.pdb(), I);
|
2017-08-04 01:11:52 +02:00
|
|
|
if (!ExpectedModS) {
|
|
|
|
P.formatLine("Error loading module stream {0}. {1}", I,
|
|
|
|
toString(ExpectedModS.takeError()));
|
|
|
|
return;
|
|
|
|
}
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-08-04 01:11:52 +02:00
|
|
|
ModuleDebugStreamRef &ModS = *ExpectedModS;
|
|
|
|
|
|
|
|
SymbolVisitorCallbackPipeline Pipeline;
|
|
|
|
SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
|
2018-01-05 20:12:40 +01:00
|
|
|
MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Strings,
|
|
|
|
Ids, Types);
|
2017-08-04 01:11:52 +02:00
|
|
|
|
|
|
|
Pipeline.addCallbackToPipeline(Deserializer);
|
|
|
|
Pipeline.addCallbackToPipeline(Dumper);
|
|
|
|
CVSymbolVisitor Visitor(Pipeline);
|
|
|
|
auto SS = ModS.getSymbolsSubstream();
|
|
|
|
if (auto EC =
|
|
|
|
Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) {
|
|
|
|
P.formatLine("Error while processing symbol records. {0}",
|
|
|
|
toString(std::move(EC)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
});
|
2017-06-16 00:24:24 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
[llvm-pdbutil] Add -type-ref-stats to help find unused type info
Summary:
This considers module symbol streams and the global symbol stream to be
roots. Most types that this considers "unreferenced" are referenced by
LF_UDT_MOD_SRC_LINE id records, which VC seems to always include.
Essentially, they are types that the user can only find in the debugger
if they call them by name, they cannot be found by traversing a symbol.
In practice, around 80% of type information in a PDB is referenced by a
symbol. That seems like a reasonable number.
I don't really plan to do anything with this tool. It mostly just exists
for informational purposes, and to confirm that we probably don't need
to implement type reference tracking in LLD. We can continue to merge
all types as we do today without wasting space.
Reviewers: zturner, aganea
Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D59620
llvm-svn: 356692
2019-03-21 19:02:34 +01:00
|
|
|
Error DumpOutputStyle::dumpTypeRefStats() {
|
|
|
|
printHeader(P, "Type Reference Statistics");
|
|
|
|
AutoIndent Indent(P);
|
|
|
|
|
|
|
|
// Sum the byte size of all type records, and the size and count of all
|
|
|
|
// referenced records.
|
|
|
|
size_t TotalRecs = File.types().size();
|
|
|
|
size_t RefRecs = 0;
|
|
|
|
size_t TotalBytes = 0;
|
|
|
|
size_t RefBytes = 0;
|
|
|
|
auto &Types = File.types();
|
|
|
|
for (Optional<TypeIndex> TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) {
|
|
|
|
CVType Type = File.types().getType(*TI);
|
|
|
|
TotalBytes += Type.length();
|
|
|
|
if (RefTracker->isTypeReferenced(*TI)) {
|
|
|
|
++RefRecs;
|
|
|
|
RefBytes += Type.length();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
P.formatLine("Records referenced: {0:N} / {1:N} {2:P}", RefRecs, TotalRecs,
|
|
|
|
(double)RefRecs / TotalRecs);
|
|
|
|
P.formatLine("Bytes referenced: {0:N} / {1:N} {2:P}", RefBytes, TotalBytes,
|
|
|
|
(double)RefBytes / TotalBytes);
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-07-06 04:59:25 +02:00
|
|
|
Error DumpOutputStyle::dumpGSIRecords() {
|
|
|
|
printHeader(P, "GSI Records");
|
|
|
|
|
|
|
|
if (File.isObj()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotValidForObj();
|
2018-07-06 04:59:25 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!getPdb().hasPDBSymbolStream()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotPresent("GSI Common Symbol");
|
2018-07-06 04:59:25 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
AutoIndent Indent(P);
|
|
|
|
|
2018-07-06 04:59:25 +02:00
|
|
|
auto &Records = cantFail(getPdb().getPDBSymbolStream());
|
|
|
|
auto &Types = File.types();
|
|
|
|
auto &Ids = File.ids();
|
|
|
|
|
|
|
|
P.printLine("Records");
|
|
|
|
SymbolVisitorCallbackPipeline Pipeline;
|
|
|
|
SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
|
|
|
|
MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
|
|
|
|
|
|
|
|
Pipeline.addCallbackToPipeline(Deserializer);
|
|
|
|
Pipeline.addCallbackToPipeline(Dumper);
|
|
|
|
CVSymbolVisitor Visitor(Pipeline);
|
|
|
|
|
|
|
|
BinaryStreamRef SymStream = Records.getSymbolArray().getUnderlyingStream();
|
|
|
|
if (auto E = Visitor.visitSymbolStream(Records.getSymbolArray(), 0))
|
|
|
|
return E;
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-07-26 02:40:36 +02:00
|
|
|
Error DumpOutputStyle::dumpGlobals() {
|
|
|
|
printHeader(P, "Global Symbols");
|
2017-09-01 22:06:56 +02:00
|
|
|
|
|
|
|
if (File.isObj()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotValidForObj();
|
2017-09-01 22:06:56 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!getPdb().hasPDBGlobalsStream()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotPresent("Globals");
|
2017-07-26 02:40:36 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
2018-08-06 21:35:00 +02:00
|
|
|
|
|
|
|
AutoIndent Indent(P);
|
2017-07-28 01:13:18 +02:00
|
|
|
ExitOnError Err("Error dumping globals stream: ");
|
2017-09-01 22:06:56 +02:00
|
|
|
auto &Globals = Err(getPdb().getPDBGlobalsStream());
|
2017-07-26 02:40:36 +02:00
|
|
|
|
2018-10-08 06:19:16 +02:00
|
|
|
if (opts::dump::DumpGlobalNames.empty()) {
|
|
|
|
const GSIHashTable &Table = Globals.getGlobalsTable();
|
|
|
|
Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras));
|
|
|
|
} else {
|
|
|
|
SymbolStream &SymRecords = cantFail(getPdb().getPDBSymbolStream());
|
|
|
|
auto &Types = File.types();
|
|
|
|
auto &Ids = File.ids();
|
|
|
|
|
|
|
|
SymbolVisitorCallbackPipeline Pipeline;
|
|
|
|
SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
|
|
|
|
MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
|
|
|
|
|
|
|
|
Pipeline.addCallbackToPipeline(Deserializer);
|
|
|
|
Pipeline.addCallbackToPipeline(Dumper);
|
|
|
|
CVSymbolVisitor Visitor(Pipeline);
|
|
|
|
|
|
|
|
using ResultEntryType = std::pair<uint32_t, CVSymbol>;
|
|
|
|
for (StringRef Name : opts::dump::DumpGlobalNames) {
|
|
|
|
AutoIndent Indent(P);
|
|
|
|
P.formatLine("Global Name `{0}`", Name);
|
|
|
|
std::vector<ResultEntryType> Results =
|
|
|
|
Globals.findRecordsByName(Name, SymRecords);
|
|
|
|
if (Results.empty()) {
|
|
|
|
AutoIndent Indent(P);
|
|
|
|
P.printLine("(no matching records found)");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ResultEntryType Result : Results) {
|
|
|
|
if (auto E = Visitor.visitSymbolRecord(Result.second, Result.first))
|
|
|
|
return E;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-07-26 02:40:36 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpPublics() {
|
2017-06-16 00:24:24 +02:00
|
|
|
printHeader(P, "Public Symbols");
|
2017-09-01 22:06:56 +02:00
|
|
|
|
|
|
|
if (File.isObj()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotValidForObj();
|
2017-09-01 22:06:56 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!getPdb().hasPDBPublicsStream()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotPresent("Publics");
|
2017-06-16 00:24:24 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
2018-08-06 21:35:00 +02:00
|
|
|
|
|
|
|
AutoIndent Indent(P);
|
2017-07-28 01:13:18 +02:00
|
|
|
ExitOnError Err("Error dumping publics stream: ");
|
2017-09-01 22:06:56 +02:00
|
|
|
auto &Publics = Err(getPdb().getPDBPublicsStream());
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-07-26 02:40:36 +02:00
|
|
|
const GSIHashTable &PublicsTable = Publics.getPublicsTable();
|
2017-08-09 06:23:59 +02:00
|
|
|
if (opts::dump::DumpPublicExtras) {
|
|
|
|
P.printLine("Publics Header");
|
|
|
|
AutoIndent Indent(P);
|
|
|
|
P.formatLine("sym hash = {0}, thunk table addr = {1}", Publics.getSymHash(),
|
|
|
|
formatSegmentOffset(Publics.getThunkTableSection(),
|
|
|
|
Publics.getThunkTableOffset()));
|
|
|
|
}
|
2017-07-26 02:40:36 +02:00
|
|
|
Err(dumpSymbolsFromGSI(PublicsTable, opts::dump::DumpPublicExtras));
|
2017-06-16 00:24:24 +02:00
|
|
|
|
2017-07-26 02:40:36 +02:00
|
|
|
// Skip the rest if we aren't dumping extras.
|
2017-07-21 20:28:55 +02:00
|
|
|
if (!opts::dump::DumpPublicExtras)
|
|
|
|
return Error::success();
|
|
|
|
|
|
|
|
P.formatLine("Address Map");
|
|
|
|
{
|
|
|
|
// These are offsets into the publics stream sorted by secidx:secrel.
|
|
|
|
AutoIndent Indent2(P);
|
|
|
|
for (uint32_t Addr : Publics.getAddressMap())
|
|
|
|
P.formatLine("off = {0}", Addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The thunk map is optional debug info used for ILT thunks.
|
|
|
|
if (!Publics.getThunkMap().empty()) {
|
|
|
|
P.formatLine("Thunk Map");
|
|
|
|
AutoIndent Indent2(P);
|
|
|
|
for (uint32_t Addr : Publics.getThunkMap())
|
|
|
|
P.formatLine("{0:x8}", Addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The section offsets table appears to be empty when incremental linking
|
|
|
|
// isn't in use.
|
|
|
|
if (!Publics.getSectionOffsets().empty()) {
|
|
|
|
P.formatLine("Section Offsets");
|
|
|
|
AutoIndent Indent2(P);
|
|
|
|
for (const SectionOffset &SO : Publics.getSectionOffsets())
|
|
|
|
P.formatLine("{0:x4}:{1:x8}", uint16_t(SO.Isect), uint32_t(SO.Off));
|
|
|
|
}
|
|
|
|
|
2017-06-16 00:24:24 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-07-26 02:40:36 +02:00
|
|
|
Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table,
|
|
|
|
bool HashExtras) {
|
2017-09-01 22:06:56 +02:00
|
|
|
auto ExpectedSyms = getPdb().getPDBSymbolStream();
|
2017-07-26 02:40:36 +02:00
|
|
|
if (!ExpectedSyms)
|
|
|
|
return ExpectedSyms.takeError();
|
2017-09-01 22:06:56 +02:00
|
|
|
auto &Types = File.types();
|
|
|
|
auto &Ids = File.ids();
|
2017-07-26 02:40:36 +02:00
|
|
|
|
2017-08-09 06:23:59 +02:00
|
|
|
if (HashExtras) {
|
|
|
|
P.printLine("GSI Header");
|
|
|
|
AutoIndent Indent(P);
|
|
|
|
P.formatLine("sig = {0:X}, hdr = {1:X}, hr size = {2}, num buckets = {3}",
|
|
|
|
Table.getVerSignature(), Table.getVerHeader(),
|
|
|
|
Table.getHashRecordSize(), Table.getNumBuckets());
|
|
|
|
}
|
2017-07-26 02:40:36 +02:00
|
|
|
|
2017-08-09 06:23:59 +02:00
|
|
|
{
|
|
|
|
P.printLine("Records");
|
|
|
|
SymbolVisitorCallbackPipeline Pipeline;
|
|
|
|
SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
|
2017-09-01 22:06:56 +02:00
|
|
|
MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
|
2017-08-09 06:23:59 +02:00
|
|
|
|
|
|
|
Pipeline.addCallbackToPipeline(Deserializer);
|
|
|
|
Pipeline.addCallbackToPipeline(Dumper);
|
|
|
|
CVSymbolVisitor Visitor(Pipeline);
|
|
|
|
|
2018-07-06 04:59:25 +02:00
|
|
|
|
2017-08-09 06:23:59 +02:00
|
|
|
BinaryStreamRef SymStream =
|
|
|
|
ExpectedSyms->getSymbolArray().getUnderlyingStream();
|
|
|
|
for (uint32_t PubSymOff : Table) {
|
|
|
|
Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff);
|
|
|
|
if (!Sym)
|
|
|
|
return Sym.takeError();
|
|
|
|
if (auto E = Visitor.visitSymbolRecord(*Sym, PubSymOff))
|
|
|
|
return E;
|
|
|
|
}
|
2017-07-26 02:40:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return early if we aren't dumping public hash table and address map info.
|
2018-07-06 04:59:25 +02:00
|
|
|
if (HashExtras) {
|
|
|
|
P.formatLine("Hash Entries");
|
|
|
|
{
|
|
|
|
AutoIndent Indent2(P);
|
|
|
|
for (const PSHashRecord &HR : Table.HashRecords)
|
|
|
|
P.formatLine("off = {0}, refcnt = {1}", uint32_t(HR.Off),
|
|
|
|
uint32_t(HR.CRef));
|
|
|
|
}
|
2017-07-26 02:40:36 +02:00
|
|
|
|
2018-07-06 04:59:25 +02:00
|
|
|
P.formatLine("Hash Buckets");
|
|
|
|
{
|
|
|
|
AutoIndent Indent2(P);
|
|
|
|
for (uint32_t Hash : Table.HashBuckets)
|
|
|
|
P.formatLine("{0:x8}", Hash);
|
|
|
|
}
|
2017-07-26 02:40:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-16 00:24:24 +02:00
|
|
|
static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel,
|
|
|
|
OMFSegDescFlags Flags) {
|
|
|
|
std::vector<std::string> Opts;
|
|
|
|
if (Flags == OMFSegDescFlags::None)
|
|
|
|
return "none";
|
|
|
|
|
|
|
|
PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read");
|
|
|
|
PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write");
|
|
|
|
PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute");
|
|
|
|
PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr");
|
|
|
|
PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector");
|
|
|
|
PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr");
|
|
|
|
PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group");
|
2017-06-16 02:04:24 +02:00
|
|
|
return typesetItemList(Opts, IndentLevel, 4, " | ");
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
|
|
|
|
2017-08-04 22:02:38 +02:00
|
|
|
Error DumpOutputStyle::dumpSectionHeaders() {
|
|
|
|
dumpSectionHeaders("Section Headers", DbgHeaderType::SectionHdr);
|
|
|
|
dumpSectionHeaders("Original Section Headers", DbgHeaderType::SectionHdrOrig);
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-08-04 23:10:04 +02:00
|
|
|
void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {
|
|
|
|
printHeader(P, Label);
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
if (File.isObj()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotValidForObj();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!getPdb().hasPDBDbiStream()) {
|
|
|
|
printStreamNotPresent("DBI");
|
2017-09-01 22:06:56 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
AutoIndent Indent(P);
|
2017-09-01 22:06:56 +02:00
|
|
|
ExitOnError Err("Error dumping section headers: ");
|
2017-08-04 23:10:04 +02:00
|
|
|
std::unique_ptr<MappedBlockStream> Stream;
|
|
|
|
ArrayRef<object::coff_section> Headers;
|
2017-09-01 22:06:56 +02:00
|
|
|
auto ExpectedHeaders = loadSectionHeaders(getPdb(), Type);
|
2017-08-04 23:10:04 +02:00
|
|
|
if (!ExpectedHeaders) {
|
|
|
|
P.printLine(toString(ExpectedHeaders.takeError()));
|
2017-08-04 22:02:38 +02:00
|
|
|
return;
|
|
|
|
}
|
2017-08-04 23:10:04 +02:00
|
|
|
std::tie(Stream, Headers) = std::move(*ExpectedHeaders);
|
2017-08-04 22:02:38 +02:00
|
|
|
|
|
|
|
uint32_t I = 1;
|
|
|
|
for (const auto &Header : Headers) {
|
|
|
|
P.NewLine();
|
|
|
|
P.formatLine("SECTION HEADER #{0}", I);
|
|
|
|
P.formatLine("{0,8} name", Header.Name);
|
|
|
|
P.formatLine("{0,8:X-} virtual size", uint32_t(Header.VirtualSize));
|
|
|
|
P.formatLine("{0,8:X-} virtual address", uint32_t(Header.VirtualAddress));
|
|
|
|
P.formatLine("{0,8:X-} size of raw data", uint32_t(Header.SizeOfRawData));
|
|
|
|
P.formatLine("{0,8:X-} file pointer to raw data",
|
|
|
|
uint32_t(Header.PointerToRawData));
|
|
|
|
P.formatLine("{0,8:X-} file pointer to relocation table",
|
|
|
|
uint32_t(Header.PointerToRelocations));
|
|
|
|
P.formatLine("{0,8:X-} file pointer to line numbers",
|
|
|
|
uint32_t(Header.PointerToLinenumbers));
|
|
|
|
P.formatLine("{0,8:X-} number of relocations",
|
|
|
|
uint32_t(Header.NumberOfRelocations));
|
|
|
|
P.formatLine("{0,8:X-} number of line numbers",
|
|
|
|
uint32_t(Header.NumberOfLinenumbers));
|
|
|
|
P.formatLine("{0,8:X-} flags", uint32_t(Header.Characteristics));
|
|
|
|
AutoIndent IndentMore(P, 9);
|
|
|
|
P.formatLine("{0}", formatSectionCharacteristics(
|
|
|
|
P.getIndentLevel(), Header.Characteristics, 1, ""));
|
|
|
|
++I;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpSectionContribs() {
|
2017-06-16 00:24:24 +02:00
|
|
|
printHeader(P, "Section Contributions");
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
if (File.isObj()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotValidForObj();
|
2017-09-01 22:06:56 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!getPdb().hasPDBDbiStream()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotPresent("DBI");
|
2017-06-16 00:24:24 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
AutoIndent Indent(P);
|
|
|
|
ExitOnError Err("Error dumping section contributions: ");
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
auto &Dbi = Err(getPdb().getPDBDbiStream());
|
2017-06-16 00:24:24 +02:00
|
|
|
|
|
|
|
class Visitor : public ISectionContribVisitor {
|
|
|
|
public:
|
2017-08-04 23:10:04 +02:00
|
|
|
Visitor(LinePrinter &P, ArrayRef<std::string> Names) : P(P), Names(Names) {
|
|
|
|
auto Max = std::max_element(
|
|
|
|
Names.begin(), Names.end(),
|
|
|
|
[](StringRef S1, StringRef S2) { return S1.size() < S2.size(); });
|
|
|
|
MaxNameLen = (Max == Names.end() ? 0 : Max->size());
|
|
|
|
}
|
2017-06-16 00:24:24 +02:00
|
|
|
void visit(const SectionContrib &SC) override {
|
2018-04-17 22:06:43 +02:00
|
|
|
dumpSectionContrib(P, SC, Names, MaxNameLen);
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
|
|
|
void visit(const SectionContrib2 &SC) override {
|
2018-04-17 22:06:43 +02:00
|
|
|
dumpSectionContrib(P, SC, Names, MaxNameLen);
|
2017-06-16 00:24:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
LinePrinter &P;
|
2017-08-04 23:10:04 +02:00
|
|
|
uint32_t MaxNameLen;
|
|
|
|
ArrayRef<std::string> Names;
|
2017-06-16 00:24:24 +02:00
|
|
|
};
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
std::vector<std::string> Names = getSectionNames(getPdb());
|
2017-08-04 23:10:04 +02:00
|
|
|
Visitor V(P, makeArrayRef(Names));
|
2017-06-16 00:24:24 +02:00
|
|
|
Dbi.visitSectionContributions(V);
|
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2017-06-22 22:57:39 +02:00
|
|
|
Error DumpOutputStyle::dumpSectionMap() {
|
2017-06-16 00:24:24 +02:00
|
|
|
printHeader(P, "Section Map");
|
2017-09-01 22:06:56 +02:00
|
|
|
|
|
|
|
if (File.isObj()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotValidForObj();
|
2017-09-01 22:06:56 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!getPdb().hasPDBDbiStream()) {
|
2018-08-06 21:35:00 +02:00
|
|
|
printStreamNotPresent("DBI");
|
2017-06-16 00:24:24 +02:00
|
|
|
return Error::success();
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:35:00 +02:00
|
|
|
AutoIndent Indent(P);
|
|
|
|
ExitOnError Err("Error dumping section map: ");
|
|
|
|
|
2017-09-01 22:06:56 +02:00
|
|
|
auto &Dbi = Err(getPdb().getPDBDbiStream());
|
2017-06-16 00:24:24 +02:00
|
|
|
|
|
|
|
uint32_t I = 0;
|
|
|
|
for (auto &M : Dbi.getSectionMap()) {
|
|
|
|
P.formatLine(
|
2017-08-04 22:02:38 +02:00
|
|
|
"Section {0:4} | ovl = {1}, group = {2}, frame = {3}, name = {4}", I,
|
2017-06-16 00:24:24 +02:00
|
|
|
fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName));
|
|
|
|
P.formatLine(" class = {0}, offset = {1}, size = {2}",
|
|
|
|
fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength));
|
|
|
|
P.formatLine(" flags = {0}",
|
|
|
|
formatSegMapDescriptorFlag(
|
|
|
|
P.getIndentLevel() + 13,
|
|
|
|
static_cast<OMFSegDescFlags>(uint16_t(M.Flags))));
|
|
|
|
++I;
|
|
|
|
}
|
|
|
|
return Error::success();
|
|
|
|
}
|