summaryrefslogtreecommitdiff
path: root/tools/llvm-pdbdump
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-04-16 16:01:22 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-04-16 16:01:22 +0000
commit71d5a2540a98c81f5bcaeb48805e0e2881f530ef (patch)
tree5343938942df402b49ec7300a1c25a2d4ccd5821 /tools/llvm-pdbdump
parent31bbf64f3a4974a2d6c8b3b27ad2f519caf74057 (diff)
Notes
Diffstat (limited to 'tools/llvm-pdbdump')
-rw-r--r--tools/llvm-pdbdump/Analyze.cpp164
-rw-r--r--tools/llvm-pdbdump/Analyze.h30
-rw-r--r--tools/llvm-pdbdump/CMakeLists.txt6
-rw-r--r--tools/llvm-pdbdump/CompactTypeDumpVisitor.cpp57
-rw-r--r--tools/llvm-pdbdump/CompactTypeDumpVisitor.h47
-rw-r--r--tools/llvm-pdbdump/Diff.cpp523
-rw-r--r--tools/llvm-pdbdump/Diff.h45
-rw-r--r--tools/llvm-pdbdump/LLVMOutputStyle.cpp337
-rw-r--r--tools/llvm-pdbdump/LLVMOutputStyle.h9
-rw-r--r--tools/llvm-pdbdump/LinePrinter.cpp36
-rw-r--r--tools/llvm-pdbdump/LinePrinter.h12
-rw-r--r--tools/llvm-pdbdump/PdbYaml.cpp117
-rw-r--r--tools/llvm-pdbdump/PdbYaml.h55
-rw-r--r--tools/llvm-pdbdump/PrettyBuiltinDumper.cpp4
-rw-r--r--tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp229
-rw-r--r--tools/llvm-pdbdump/PrettyClassDefinitionDumper.h38
-rw-r--r--tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp151
-rw-r--r--tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h47
-rw-r--r--tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp119
-rw-r--r--tools/llvm-pdbdump/PrettyClassLayoutTextDumper.h44
-rw-r--r--tools/llvm-pdbdump/PrettyFunctionDumper.cpp10
-rw-r--r--tools/llvm-pdbdump/PrettyTypeDumper.cpp199
-rw-r--r--tools/llvm-pdbdump/PrettyTypeDumper.h4
-rw-r--r--tools/llvm-pdbdump/PrettyTypedefDumper.cpp7
-rw-r--r--tools/llvm-pdbdump/PrettyVariableDumper.cpp163
-rw-r--r--tools/llvm-pdbdump/PrettyVariableDumper.h9
-rw-r--r--tools/llvm-pdbdump/StreamUtil.cpp136
-rw-r--r--tools/llvm-pdbdump/StreamUtil.h25
-rw-r--r--tools/llvm-pdbdump/YAMLOutputStyle.cpp44
-rw-r--r--tools/llvm-pdbdump/YAMLOutputStyle.h1
-rw-r--r--tools/llvm-pdbdump/YamlSymbolDumper.cpp1
-rw-r--r--tools/llvm-pdbdump/YamlTypeDumper.cpp23
-rw-r--r--tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp11
-rw-r--r--tools/llvm-pdbdump/llvm-pdbdump.cpp327
-rw-r--r--tools/llvm-pdbdump/llvm-pdbdump.h21
35 files changed, 2375 insertions, 676 deletions
diff --git a/tools/llvm-pdbdump/Analyze.cpp b/tools/llvm-pdbdump/Analyze.cpp
new file mode 100644
index 0000000000000..b65dd40d25ff1
--- /dev/null
+++ b/tools/llvm-pdbdump/Analyze.cpp
@@ -0,0 +1,164 @@
+//===- Analyze.cpp - PDB analysis functions ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Analyze.h"
+
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
+#include "llvm/DebugInfo/CodeView/TypeDatabaseVisitor.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/RawError.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <list>
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static StringRef getLeafTypeName(TypeLeafKind LT) {
+ switch (LT) {
+#define TYPE_RECORD(ename, value, name) \
+ case ename: \
+ return #name;
+#include "llvm/DebugInfo/CodeView/TypeRecords.def"
+ default:
+ break;
+ }
+ return "UnknownLeaf";
+}
+
+namespace {
+struct HashLookupVisitor : public TypeVisitorCallbacks {
+ struct Entry {
+ TypeIndex TI;
+ CVType Record;
+ };
+
+ explicit HashLookupVisitor(TpiStream &Tpi) : Tpi(Tpi) {}
+
+ Error visitTypeBegin(CVType &Record) override {
+ uint32_t H = Tpi.getHashValues()[I];
+ Record.Hash = H;
+ TypeIndex TI(I + TypeIndex::FirstNonSimpleIndex);
+ Lookup[H].push_back(Entry{TI, Record});
+ ++I;
+ return Error::success();
+ }
+
+ uint32_t I = 0;
+ DenseMap<uint32_t, std::list<Entry>> Lookup;
+ TpiStream &Tpi;
+};
+}
+
+AnalysisStyle::AnalysisStyle(PDBFile &File) : File(File) {}
+
+Error AnalysisStyle::dump() {
+ auto Tpi = File.getPDBTpiStream();
+ if (!Tpi)
+ return Tpi.takeError();
+
+ TypeDatabase TypeDB;
+ TypeDatabaseVisitor DBV(TypeDB);
+ TypeDeserializer Deserializer;
+ TypeVisitorCallbackPipeline Pipeline;
+ HashLookupVisitor Hasher(*Tpi);
+ // Deserialize the types
+ Pipeline.addCallbackToPipeline(Deserializer);
+ // Add them to the database
+ Pipeline.addCallbackToPipeline(DBV);
+ // Store their hash values
+ Pipeline.addCallbackToPipeline(Hasher);
+
+ CVTypeVisitor Visitor(Pipeline);
+
+ bool Error = false;
+ for (auto Item : Tpi->types(&Error)) {
+ if (auto EC = Visitor.visitTypeRecord(Item))
+ return EC;
+ }
+ if (Error)
+ return make_error<RawError>(raw_error_code::corrupt_file,
+ "TPI stream contained corrupt record");
+
+ auto &Adjusters = Tpi->getHashAdjusters();
+ DenseSet<uint32_t> AdjusterSet;
+ for (const auto &Adj : Adjusters) {
+ assert(AdjusterSet.find(Adj.second) == AdjusterSet.end());
+ AdjusterSet.insert(Adj.second);
+ }
+
+ uint32_t Count = 0;
+ outs() << "Searching for hash collisions\n";
+ for (const auto &H : Hasher.Lookup) {
+ if (H.second.size() <= 1)
+ continue;
+ ++Count;
+ outs() << formatv("Hash: {0}, Count: {1} records\n", H.first,
+ H.second.size());
+ for (const auto &R : H.second) {
+ auto Iter = AdjusterSet.find(R.TI.getIndex());
+ StringRef Prefix;
+ if (Iter != AdjusterSet.end()) {
+ Prefix = "[HEAD]";
+ AdjusterSet.erase(Iter);
+ }
+ StringRef LeafName = getLeafTypeName(R.Record.Type);
+ uint32_t TI = R.TI.getIndex();
+ StringRef TypeName = TypeDB.getTypeName(R.TI);
+ outs() << formatv("{0,-6} {1} ({2:x}) {3}\n", Prefix, LeafName, TI,
+ TypeName);
+ }
+ }
+
+ outs() << "\n";
+ outs() << "Dumping hash adjustment chains\n";
+ for (const auto &A : Tpi->getHashAdjusters()) {
+ TypeIndex TI(A.second);
+ StringRef TypeName = TypeDB.getTypeName(TI);
+ const CVType &HeadRecord = TypeDB.getTypeRecord(TI);
+ assert(HeadRecord.Hash.hasValue());
+
+ auto CollisionsIter = Hasher.Lookup.find(*HeadRecord.Hash);
+ if (CollisionsIter == Hasher.Lookup.end())
+ continue;
+
+ const auto &Collisions = CollisionsIter->second;
+ outs() << TypeName << "\n";
+ outs() << formatv(" [HEAD] {0:x} {1} {2}\n", A.second,
+ getLeafTypeName(HeadRecord.Type), TypeName);
+ for (const auto &Chain : Collisions) {
+ if (Chain.TI == TI)
+ continue;
+ const CVType &TailRecord = TypeDB.getTypeRecord(Chain.TI);
+ outs() << formatv(" {0:x} {1} {2}\n", Chain.TI.getIndex(),
+ getLeafTypeName(TailRecord.Type),
+ TypeDB.getTypeName(Chain.TI));
+ }
+ }
+ outs() << formatv("There are {0} orphaned hash adjusters\n",
+ AdjusterSet.size());
+ for (const auto &Adj : AdjusterSet) {
+ outs() << formatv(" {0}\n", Adj);
+ }
+
+ uint32_t DistinctHashValues = Hasher.Lookup.size();
+ outs() << formatv("{0}/{1} hash collisions", Count, DistinctHashValues);
+ return Error::success();
+}
diff --git a/tools/llvm-pdbdump/Analyze.h b/tools/llvm-pdbdump/Analyze.h
new file mode 100644
index 0000000000000..7230ae45b0c8c
--- /dev/null
+++ b/tools/llvm-pdbdump/Analyze.h
@@ -0,0 +1,30 @@
+//===- Analyze.h - PDB analysis functions -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_ANALYSIS_H
+#define LLVM_TOOLS_LLVMPDBDUMP_ANALYSIS_H
+
+#include "OutputStyle.h"
+
+namespace llvm {
+namespace pdb {
+class PDBFile;
+class AnalysisStyle : public OutputStyle {
+public:
+ explicit AnalysisStyle(PDBFile &File);
+
+ Error dump() override;
+
+private:
+ PDBFile &File;
+};
+}
+}
+
+#endif
diff --git a/tools/llvm-pdbdump/CMakeLists.txt b/tools/llvm-pdbdump/CMakeLists.txt
index 7c46171941f7d..37c76ab697b46 100644
--- a/tools/llvm-pdbdump/CMakeLists.txt
+++ b/tools/llvm-pdbdump/CMakeLists.txt
@@ -7,6 +7,9 @@ set(LLVM_LINK_COMPONENTS
)
add_llvm_tool(llvm-pdbdump
+ Analyze.cpp
+ Diff.cpp
+ CompactTypeDumpVisitor.cpp
llvm-pdbdump.cpp
YamlSymbolDumper.cpp
YamlTypeDumper.cpp
@@ -15,6 +18,8 @@ add_llvm_tool(llvm-pdbdump
PdbYaml.cpp
PrettyBuiltinDumper.cpp
PrettyClassDefinitionDumper.cpp
+ PrettyClassLayoutTextDumper.cpp
+ PrettyClassLayoutGraphicalDumper.cpp
PrettyCompilandDumper.cpp
PrettyEnumDumper.cpp
PrettyExternalSymbolDumper.cpp
@@ -22,6 +27,7 @@ add_llvm_tool(llvm-pdbdump
PrettyTypeDumper.cpp
PrettyTypedefDumper.cpp
PrettyVariableDumper.cpp
+ StreamUtil.cpp
YAMLOutputStyle.cpp
)
diff --git a/tools/llvm-pdbdump/CompactTypeDumpVisitor.cpp b/tools/llvm-pdbdump/CompactTypeDumpVisitor.cpp
new file mode 100644
index 0000000000000..1fc8dd5d51f0a
--- /dev/null
+++ b/tools/llvm-pdbdump/CompactTypeDumpVisitor.cpp
@@ -0,0 +1,57 @@
+//===-- CompactTypeDumpVisitor.cpp - CodeView type info dumper --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CompactTypeDumpVisitor.h"
+#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::pdb;
+
+static const EnumEntry<TypeLeafKind> LeafTypeNames[] = {
+#define CV_TYPE(enum, val) {#enum, enum},
+#include "llvm/DebugInfo/CodeView/TypeRecords.def"
+};
+
+static StringRef getLeafName(TypeLeafKind K) {
+ for (const auto &E : LeafTypeNames) {
+ if (E.Value == K)
+ return E.Name;
+ }
+ return StringRef();
+}
+
+CompactTypeDumpVisitor::CompactTypeDumpVisitor(TypeDatabase &TypeDB,
+ ScopedPrinter *W)
+ : W(W), TI(TypeIndex::None()), Offset(0), TypeDB(TypeDB) {}
+
+Error CompactTypeDumpVisitor::visitTypeBegin(CVType &Record) {
+ if (TI == TypeIndex::None())
+ TI.setIndex(TypeIndex::FirstNonSimpleIndex);
+ else
+ TI.setIndex(TI.getIndex() + 1);
+
+ return Error::success();
+}
+
+Error CompactTypeDumpVisitor::visitTypeEnd(CVType &Record) {
+ uint32_t I = TI.getIndex();
+ StringRef Leaf = getLeafName(Record.Type);
+ StringRef Name = TypeDB.getTypeName(TI);
+ W->printString(
+ llvm::formatv("Index: {0:x} ({1:N} bytes, offset {2:N}) {3} \"{4}\"", I,
+ Record.length(), Offset, Leaf, Name)
+ .str());
+
+ Offset += Record.length();
+
+ return Error::success();
+}
diff --git a/tools/llvm-pdbdump/CompactTypeDumpVisitor.h b/tools/llvm-pdbdump/CompactTypeDumpVisitor.h
new file mode 100644
index 0000000000000..180eea7b8d6a1
--- /dev/null
+++ b/tools/llvm-pdbdump/CompactTypeDumpVisitor.h
@@ -0,0 +1,47 @@
+//===-- CompactTypeDumpVisitor.h - CodeView type info dumper ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DEBUGINFO_CODEVIEW_COMPACTTYPEDUMPVISITOR_H
+#define LLVM_DEBUGINFO_CODEVIEW_COMPACTTYPEDUMPVISITOR_H
+
+#include "llvm/DebugInfo/CodeView/TypeIndex.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
+
+namespace llvm {
+class ScopedPrinter;
+namespace codeview {
+class TypeDatabase;
+}
+
+namespace pdb {
+
+/// Dumper for CodeView type streams found in COFF object files and PDB files.
+/// Dumps records on a single line, and ignores member records.
+class CompactTypeDumpVisitor : public codeview::TypeVisitorCallbacks {
+public:
+ CompactTypeDumpVisitor(codeview::TypeDatabase &TypeDB, ScopedPrinter *W);
+
+ /// Paired begin/end actions for all types. Receives all record data,
+ /// including the fixed-length record prefix.
+ Error visitTypeBegin(codeview::CVType &Record) override;
+ Error visitTypeEnd(codeview::CVType &Record) override;
+
+private:
+ ScopedPrinter *W;
+
+ codeview::TypeIndex TI;
+ uint32_t Offset;
+ codeview::TypeDatabase &TypeDB;
+};
+
+} // end namespace pdb
+} // end namespace llvm
+
+#endif
diff --git a/tools/llvm-pdbdump/Diff.cpp b/tools/llvm-pdbdump/Diff.cpp
new file mode 100644
index 0000000000000..8c02d36044d82
--- /dev/null
+++ b/tools/llvm-pdbdump/Diff.cpp
@@ -0,0 +1,523 @@
+//===- Diff.cpp - PDB diff utility ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Diff.h"
+
+#include "StreamUtil.h"
+#include "llvm-pdbdump.h"
+
+#include "llvm/DebugInfo/PDB/Native/Formatters.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
+#include "llvm/DebugInfo/PDB/Native/StringTable.h"
+
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatProviders.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+namespace llvm {
+template <> struct format_provider<PdbRaw_FeatureSig> {
+ static void format(const PdbRaw_FeatureSig &Sig, raw_ostream &Stream,
+ StringRef Style) {
+ switch (Sig) {
+ case PdbRaw_FeatureSig::MinimalDebugInfo:
+ Stream << "MinimalDebugInfo";
+ break;
+ case PdbRaw_FeatureSig::NoTypeMerge:
+ Stream << "NoTypeMerge";
+ break;
+ case PdbRaw_FeatureSig::VC110:
+ Stream << "VC110";
+ break;
+ case PdbRaw_FeatureSig::VC140:
+ Stream << "VC140";
+ break;
+ }
+ }
+};
+}
+
+template <typename R> using ValueOfRange = llvm::detail::ValueOfRange<R>;
+
+template <typename Range, typename Comp>
+static void set_differences(Range &&R1, Range &&R2,
+ SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft,
+ SmallVectorImpl<ValueOfRange<Range>> *OnlyRight,
+ SmallVectorImpl<ValueOfRange<Range>> *Intersection,
+ Comp Comparator) {
+
+ std::sort(R1.begin(), R1.end(), Comparator);
+ std::sort(R2.begin(), R2.end(), Comparator);
+
+ if (OnlyLeft) {
+ OnlyLeft->reserve(R1.size());
+ auto End = std::set_difference(R1.begin(), R1.end(), R2.begin(), R2.end(),
+ OnlyLeft->begin(), Comparator);
+ OnlyLeft->set_size(std::distance(OnlyLeft->begin(), End));
+ }
+ if (OnlyRight) {
+ OnlyLeft->reserve(R2.size());
+ auto End = std::set_difference(R2.begin(), R2.end(), R1.begin(), R1.end(),
+ OnlyRight->begin(), Comparator);
+ OnlyRight->set_size(std::distance(OnlyRight->begin(), End));
+ }
+ if (Intersection) {
+ Intersection->reserve(std::min(R1.size(), R2.size()));
+ auto End = std::set_intersection(R1.begin(), R1.end(), R2.begin(), R2.end(),
+ Intersection->begin(), Comparator);
+ Intersection->set_size(std::distance(Intersection->begin(), End));
+ }
+}
+
+template <typename Range>
+static void
+set_differences(Range &&R1, Range &&R2,
+ SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft,
+ SmallVectorImpl<ValueOfRange<Range>> *OnlyRight,
+ SmallVectorImpl<ValueOfRange<Range>> *Intersection = nullptr) {
+ std::less<ValueOfRange<Range>> Comp;
+ set_differences(std::forward<Range>(R1), std::forward<Range>(R2), OnlyLeft,
+ OnlyRight, Intersection, Comp);
+}
+
+DiffStyle::DiffStyle(PDBFile &File1, PDBFile &File2)
+ : File1(File1), File2(File2) {}
+
+Error DiffStyle::dump() {
+ if (auto EC = diffSuperBlock())
+ return EC;
+
+ if (auto EC = diffFreePageMap())
+ return EC;
+
+ if (auto EC = diffStreamDirectory())
+ return EC;
+
+ if (auto EC = diffStringTable())
+ return EC;
+
+ if (auto EC = diffInfoStream())
+ return EC;
+
+ if (auto EC = diffDbiStream())
+ return EC;
+
+ if (auto EC = diffSectionContribs())
+ return EC;
+
+ if (auto EC = diffSectionMap())
+ return EC;
+
+ if (auto EC = diffFpoStream())
+ return EC;
+
+ if (auto EC = diffTpiStream(StreamTPI))
+ return EC;
+
+ if (auto EC = diffTpiStream(StreamIPI))
+ return EC;
+
+ if (auto EC = diffPublics())
+ return EC;
+
+ if (auto EC = diffGlobals())
+ return EC;
+
+ return Error::success();
+}
+
+template <typename T>
+static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, T V1,
+ T V2) {
+ if (V1 == V2) {
+ outs() << formatv(" {0}: No differences detected!\n", Label);
+ return false;
+ }
+
+ outs().indent(2) << Label << "\n";
+ outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), V1);
+ outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), V2);
+ return true;
+}
+
+template <typename T>
+static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2,
+ ArrayRef<T> V1, ArrayRef<T> V2) {
+ if (V1 == V2) {
+ outs() << formatv(" {0}: No differences detected!\n", Label);
+ return false;
+ }
+
+ outs().indent(2) << Label << "\n";
+ outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(),
+ make_range(V1.begin(), V1.end()));
+ outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(),
+ make_range(V2.begin(), V2.end()));
+ return true;
+}
+
+template <typename T>
+static bool printSymmetricDifferences(PDBFile &File1, PDBFile &File2,
+ T &&OnlyRange1, T &&OnlyRange2,
+ StringRef Label) {
+ bool HasDiff = false;
+ if (!OnlyRange1.empty()) {
+ HasDiff = true;
+ outs() << formatv(" {0} {1}(s) only in ({2})\n", OnlyRange1.size(), Label,
+ File1.getFilePath());
+ for (const auto &Item : OnlyRange1)
+ outs() << formatv(" {0}\n", Label, Item);
+ }
+ if (!OnlyRange2.empty()) {
+ HasDiff = true;
+ outs() << formatv(" {0} {1}(s) only in ({2})\n", OnlyRange2.size(),
+ File2.getFilePath());
+ for (const auto &Item : OnlyRange2)
+ outs() << formatv(" {0}\n", Item);
+ }
+ return HasDiff;
+}
+
+Error DiffStyle::diffSuperBlock() {
+ outs() << "MSF Super Block: Searching for differences...\n";
+ bool Diffs = false;
+
+ Diffs |= diffAndPrint("Block Size", File1, File2, File1.getBlockSize(),
+ File2.getBlockSize());
+ Diffs |= diffAndPrint("Block Count", File1, File2, File1.getBlockCount(),
+ File2.getBlockCount());
+ Diffs |= diffAndPrint("Unknown 1", File1, File2, File1.getUnknown1(),
+ File2.getUnknown1());
+
+ if (opts::diff::Pedantic) {
+ Diffs |= diffAndPrint("Free Block Map", File1, File2,
+ File1.getFreeBlockMapBlock(),
+ File2.getFreeBlockMapBlock());
+ Diffs |= diffAndPrint("Directory Size", File1, File2,
+ File1.getNumDirectoryBytes(),
+ File2.getNumDirectoryBytes());
+ Diffs |= diffAndPrint("Block Map Addr", File1, File2,
+ File1.getBlockMapOffset(), File2.getBlockMapOffset());
+ }
+ if (!Diffs)
+ outs() << "MSF Super Block: No differences detected...\n";
+ return Error::success();
+}
+
+Error DiffStyle::diffStreamDirectory() {
+ SmallVector<std::string, 32> P;
+ SmallVector<std::string, 32> Q;
+ discoverStreamPurposes(File1, P);
+ discoverStreamPurposes(File2, Q);
+ outs() << "Stream Directory: Searching for differences...\n";
+
+ bool HasDifferences = false;
+ if (opts::diff::Pedantic) {
+ size_t Min = std::min(P.size(), Q.size());
+ for (size_t I = 0; I < Min; ++I) {
+ StringRef Names[] = {P[I], Q[I]};
+ uint32_t Sizes[] = {File1.getStreamByteSize(I),
+ File2.getStreamByteSize(I)};
+ bool NamesDiffer = Names[0] != Names[1];
+ bool SizesDiffer = Sizes[0] != Sizes[1];
+ if (NamesDiffer) {
+ HasDifferences = true;
+ outs().indent(2) << formatv("Stream {0} - {1}: {2}, {3}: {4}\n", I,
+ File1.getFilePath(), Names[0],
+ File2.getFilePath(), Names[1]);
+ continue;
+ }
+ if (SizesDiffer) {
+ HasDifferences = true;
+ outs().indent(2) << formatv(
+ "Stream {0} ({1}): {2}: {3} bytes, {4}: {5} bytes\n", I, Names[0],
+ File1.getFilePath(), Sizes[0], File2.getFilePath(), Sizes[1]);
+ continue;
+ }
+ }
+
+ ArrayRef<std::string> MaxNames = (P.size() > Q.size() ? P : Q);
+ size_t Max = std::max(P.size(), Q.size());
+ PDBFile &MaxFile = (P.size() > Q.size() ? File1 : File2);
+ StringRef MinFileName =
+ (P.size() < Q.size() ? File1.getFilePath() : File2.getFilePath());
+ for (size_t I = Min; I < Max; ++I) {
+ HasDifferences = true;
+ StringRef StreamName = MaxNames[I];
+
+ outs().indent(2) << formatv(
+ "Stream {0} - {1}: <not present>, {2}: Index {3}, {4} bytes\n",
+ StreamName, MinFileName, MaxFile.getFilePath(), I,
+ MaxFile.getStreamByteSize(I));
+ }
+ if (!HasDifferences)
+ outs() << "Stream Directory: No differences detected...\n";
+ } else {
+ auto PI = to_vector<32>(enumerate(P));
+ auto QI = to_vector<32>(enumerate(Q));
+
+ typedef decltype(PI) ContainerType;
+ typedef typename ContainerType::value_type value_type;
+
+ auto Comparator = [](const value_type &I1, const value_type &I2) {
+ return I1.value() < I2.value();
+ };
+
+ decltype(PI) OnlyP;
+ decltype(QI) OnlyQ;
+ decltype(PI) Common;
+
+ set_differences(PI, QI, &OnlyP, &OnlyQ, &Common, Comparator);
+
+ if (!OnlyP.empty()) {
+ HasDifferences = true;
+ outs().indent(2) << formatv("{0} Stream(s) only in ({1})\n", OnlyP.size(),
+ File1.getFilePath());
+ for (auto &Item : OnlyP) {
+ outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(),
+ Item.value());
+ }
+ }
+
+ if (!OnlyQ.empty()) {
+ HasDifferences = true;
+ outs().indent(2) << formatv("{0} Streams(s) only in ({1})\n",
+ OnlyQ.size(), File2.getFilePath());
+ for (auto &Item : OnlyQ) {
+ outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(),
+ Item.value());
+ }
+ }
+ if (!Common.empty()) {
+ outs().indent(2) << formatv("Found {0} common streams. Searching for "
+ "intra-stream differences.\n",
+ Common.size());
+ bool HasCommonDifferences = false;
+ for (const auto &Left : Common) {
+ // Left was copied from the first range so its index refers to a stream
+ // index in the first file. Find the corresponding stream index in the
+ // second file.
+ auto Range =
+ std::equal_range(QI.begin(), QI.end(), Left,
+ [](const value_type &L, const value_type &R) {
+ return L.value() < R.value();
+ });
+ const auto &Right = *Range.first;
+ assert(Left.value() == Right.value());
+ uint32_t LeftSize = File1.getStreamByteSize(Left.index());
+ uint32_t RightSize = File2.getStreamByteSize(Right.index());
+ if (LeftSize != RightSize) {
+ HasDifferences = true;
+ HasCommonDifferences = true;
+ outs().indent(4) << formatv("{0} ({1}: {2} bytes, {3}: {4} bytes)\n",
+ Left.value(), File1.getFilePath(),
+ LeftSize, File2.getFilePath(), RightSize);
+ }
+ }
+ if (!HasCommonDifferences)
+ outs().indent(2) << "Common Streams: No differences detected!\n";
+ }
+ if (!HasDifferences)
+ outs() << "Stream Directory: No differences detected!\n";
+ }
+
+ return Error::success();
+}
+
+Error DiffStyle::diffStringTable() {
+ auto ExpectedST1 = File1.getStringTable();
+ auto ExpectedST2 = File2.getStringTable();
+ outs() << "String Table: Searching for differences...\n";
+ bool Has1 = !!ExpectedST1;
+ bool Has2 = !!ExpectedST2;
+ if (!(Has1 && Has2)) {
+ // If one has a string table and the other doesn't, we can print less
+ // output.
+ if (Has1 != Has2) {
+ if (Has1) {
+ outs() << formatv(" {0}: ({1} strings)\n", File1.getFilePath(),
+ ExpectedST1->getNameCount());
+ outs() << formatv(" {0}: (string table not present)\n",
+ File2.getFilePath());
+ } else {
+ outs() << formatv(" {0}: (string table not present)\n",
+ File1.getFilePath());
+ outs() << formatv(" {0}: ({1})\n", File2.getFilePath(),
+ ExpectedST2->getNameCount());
+ }
+ }
+ consumeError(ExpectedST1.takeError());
+ consumeError(ExpectedST2.takeError());
+ return Error::success();
+ }
+
+ bool HasDiff = false;
+ auto &ST1 = *ExpectedST1;
+ auto &ST2 = *ExpectedST2;
+
+ if (ST1.getByteSize() != ST2.getByteSize()) {
+ outs() << " Stream Size\n";
+ outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(),
+ ST1.getByteSize());
+ outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(),
+ ST2.getByteSize());
+ outs() << formatv(" Difference: {0} bytes\n",
+ AbsoluteDifference(ST1.getByteSize(), ST2.getByteSize()));
+ HasDiff = true;
+ }
+ HasDiff |= diffAndPrint("Hash Version", File1, File2, ST1.getHashVersion(),
+ ST1.getHashVersion());
+ HasDiff |= diffAndPrint("Signature", File1, File2, ST1.getSignature(),
+ ST1.getSignature());
+
+ // Both have a valid string table, dive in and compare individual strings.
+
+ auto IdList1 = ST1.name_ids();
+ auto IdList2 = ST2.name_ids();
+ if (opts::diff::Pedantic) {
+ // In pedantic mode, we compare index by index (i.e. the strings are in the
+ // same order
+ // in both tables.
+ uint32_t Max = std::max(IdList1.size(), IdList2.size());
+ for (uint32_t I = 0; I < Max; ++I) {
+ Optional<uint32_t> Id1, Id2;
+ StringRef S1, S2;
+ if (I < IdList1.size()) {
+ Id1 = IdList1[I];
+ S1 = ST1.getStringForID(*Id1);
+ }
+ if (I < IdList2.size()) {
+ Id2 = IdList2[I];
+ S2 = ST2.getStringForID(*Id2);
+ }
+ if (Id1 == Id2 && S1 == S2)
+ continue;
+
+ std::string OutId1 =
+ Id1 ? formatv("{0}", *Id1).str() : "(index not present)";
+ std::string OutId2 =
+ Id2 ? formatv("{0}", *Id2).str() : "(index not present)";
+ outs() << formatv(" String {0}\n", I);
+ outs() << formatv(" {0}: Hash - {1}, Value - {2}\n",
+ File1.getFilePath(), OutId1, S1);
+ outs() << formatv(" {0}: Hash - {1}, Value - {2}\n",
+ File2.getFilePath(), OutId2, S2);
+ HasDiff = true;
+ }
+ } else {
+ std::vector<StringRef> Strings1, Strings2;
+ Strings1.reserve(IdList1.size());
+ Strings2.reserve(IdList2.size());
+ for (auto ID : IdList1)
+ Strings1.push_back(ST1.getStringForID(ID));
+ for (auto ID : IdList2)
+ Strings2.push_back(ST2.getStringForID(ID));
+
+ SmallVector<StringRef, 64> OnlyP;
+ SmallVector<StringRef, 64> OnlyQ;
+ auto End1 = std::remove(Strings1.begin(), Strings1.end(), "");
+ auto End2 = std::remove(Strings2.begin(), Strings2.end(), "");
+ uint32_t Empty1 = std::distance(End1, Strings1.end());
+ uint32_t Empty2 = std::distance(End2, Strings2.end());
+ Strings1.erase(End1, Strings1.end());
+ Strings2.erase(End2, Strings2.end());
+ set_differences(Strings1, Strings2, &OnlyP, &OnlyQ);
+ printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "String");
+
+ if (Empty1 != Empty2) {
+ PDBFile &MoreF = (Empty1 > Empty2) ? File1 : File2;
+ PDBFile &LessF = (Empty1 < Empty2) ? File1 : File2;
+ uint32_t Difference = AbsoluteDifference(Empty1, Empty2);
+ outs() << formatv(" {0} had {1} more empty strings than {2}\n",
+ MoreF.getFilePath(), Difference, LessF.getFilePath());
+ }
+ }
+ if (!HasDiff)
+ outs() << "String Table: No differences detected!\n";
+ return Error::success();
+}
+
+Error DiffStyle::diffFreePageMap() { return Error::success(); }
+
+Error DiffStyle::diffInfoStream() {
+ auto ExpectedInfo1 = File1.getPDBInfoStream();
+ auto ExpectedInfo2 = File2.getPDBInfoStream();
+
+ outs() << "PDB Stream: Searching for differences...\n";
+ bool Has1 = !!ExpectedInfo1;
+ bool Has2 = !!ExpectedInfo2;
+ if (!(Has1 && Has2)) {
+ if (Has1 != Has2)
+ outs() << formatv("{0} does not have a PDB Stream!\n",
+ Has1 ? File1.getFilePath() : File2.getFilePath());
+ consumeError(ExpectedInfo2.takeError());
+ consumeError(ExpectedInfo2.takeError());
+ return Error::success();
+ }
+
+ bool HasDiff = false;
+ auto &IS1 = *ExpectedInfo1;
+ auto &IS2 = *ExpectedInfo2;
+ if (IS1.getStreamSize() != IS2.getStreamSize()) {
+ outs() << " Stream Size\n";
+ outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(),
+ IS1.getStreamSize());
+ outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(),
+ IS2.getStreamSize());
+ outs() << formatv(
+ " Difference: {0} bytes\n",
+ AbsoluteDifference(IS1.getStreamSize(), IS2.getStreamSize()));
+ HasDiff = true;
+ }
+ HasDiff |= diffAndPrint("Age", File1, File2, IS1.getAge(), IS2.getAge());
+ HasDiff |= diffAndPrint("Guid", File1, File2, IS1.getGuid(), IS2.getGuid());
+ HasDiff |= diffAndPrint("Signature", File1, File2, IS1.getSignature(),
+ IS2.getSignature());
+ HasDiff |=
+ diffAndPrint("Version", File1, File2, IS1.getVersion(), IS2.getVersion());
+ HasDiff |= diffAndPrint("Features", File1, File2, IS1.getFeatureSignatures(),
+ IS2.getFeatureSignatures());
+ HasDiff |= diffAndPrint("Named Stream Byte Size", File1, File2,
+ IS1.getNamedStreamMapByteSize(),
+ IS2.getNamedStreamMapByteSize());
+ SmallVector<StringRef, 4> NS1;
+ SmallVector<StringRef, 4> NS2;
+ for (const auto &X : IS1.getNamedStreams().entries())
+ NS1.push_back(X.getKey());
+ for (const auto &X : IS2.getNamedStreams().entries())
+ NS2.push_back(X.getKey());
+ SmallVector<StringRef, 4> OnlyP;
+ SmallVector<StringRef, 4> OnlyQ;
+ set_differences(NS1, NS2, &OnlyP, &OnlyQ);
+ printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "Named Streams");
+ if (!HasDiff)
+ outs() << "PDB Stream: No differences detected!\n";
+
+ return Error::success();
+}
+
+Error DiffStyle::diffDbiStream() { return Error::success(); }
+
+Error DiffStyle::diffSectionContribs() { return Error::success(); }
+
+Error DiffStyle::diffSectionMap() { return Error::success(); }
+
+Error DiffStyle::diffFpoStream() { return Error::success(); }
+
+Error DiffStyle::diffTpiStream(int Index) { return Error::success(); }
+
+Error DiffStyle::diffModuleInfoStream(int Index) { return Error::success(); }
+
+Error DiffStyle::diffPublics() { return Error::success(); }
+
+Error DiffStyle::diffGlobals() { return Error::success(); }
diff --git a/tools/llvm-pdbdump/Diff.h b/tools/llvm-pdbdump/Diff.h
new file mode 100644
index 0000000000000..6037576e21bb6
--- /dev/null
+++ b/tools/llvm-pdbdump/Diff.h
@@ -0,0 +1,45 @@
+//===- Diff.h - PDB diff utility --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_DIFF_H
+#define LLVM_TOOLS_LLVMPDBDUMP_DIFF_H
+
+#include "OutputStyle.h"
+
+namespace llvm {
+namespace pdb {
+class PDBFile;
+class DiffStyle : public OutputStyle {
+public:
+ explicit DiffStyle(PDBFile &File1, PDBFile &File2);
+
+ Error dump() override;
+
+private:
+ Error diffSuperBlock();
+ Error diffStreamDirectory();
+ Error diffStringTable();
+ Error diffFreePageMap();
+ Error diffInfoStream();
+ Error diffDbiStream();
+ Error diffSectionContribs();
+ Error diffSectionMap();
+ Error diffFpoStream();
+ Error diffTpiStream(int Index);
+ Error diffModuleInfoStream(int Index);
+ Error diffPublics();
+ Error diffGlobals();
+
+ PDBFile &File1;
+ PDBFile &File2;
+};
+}
+}
+
+#endif
diff --git a/tools/llvm-pdbdump/LLVMOutputStyle.cpp b/tools/llvm-pdbdump/LLVMOutputStyle.cpp
index 629ba40b113c5..8348751703f14 100644
--- a/tools/llvm-pdbdump/LLVMOutputStyle.cpp
+++ b/tools/llvm-pdbdump/LLVMOutputStyle.cpp
@@ -9,7 +9,10 @@
#include "LLVMOutputStyle.h"
+#include "CompactTypeDumpVisitor.h"
+#include "StreamUtil.h"
#include "llvm-pdbdump.h"
+
#include "llvm/DebugInfo/CodeView/CVTypeDumper.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/EnumTables.h"
@@ -20,20 +23,21 @@
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
-#include "llvm/DebugInfo/MSF/StreamReader.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/EnumTables.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/ModInfo.h"
+#include "llvm/DebugInfo/PDB/Native/ModStream.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
+#include "llvm/DebugInfo/PDB/Native/RawError.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/DebugInfo/PDB/PDBExtras.h"
-#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
-#include "llvm/DebugInfo/PDB/Raw/EnumTables.h"
-#include "llvm/DebugInfo/PDB/Raw/GlobalsStream.h"
-#include "llvm/DebugInfo/PDB/Raw/ISectionContribVisitor.h"
-#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
-#include "llvm/DebugInfo/PDB/Raw/ModInfo.h"
-#include "llvm/DebugInfo/PDB/Raw/ModStream.h"
-#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
-#include "llvm/DebugInfo/PDB/Raw/PublicsStream.h"
-#include "llvm/DebugInfo/PDB/Raw/RawError.h"
-#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
#include "llvm/Object/COFF.h"
+#include "llvm/Support/BinaryStreamReader.h"
+#include "llvm/Support/FormatVariadic.h"
#include <unordered_map>
@@ -110,6 +114,9 @@ Error LLVMOutputStyle::dump() {
if (auto EC = dumpStreamBytes())
return EC;
+ if (auto EC = dumpStringTable())
+ return EC;
+
if (auto EC = dumpInfoStream())
return EC;
@@ -166,124 +173,12 @@ Error LLVMOutputStyle::dumpFileHeaders() {
return Error::success();
}
-void LLVMOutputStyle::discoverStreamPurposes() {
- if (!StreamPurposes.empty())
- return;
-
- // It's OK if we fail to load some of these streams, we still attempt to print
- // what we can.
- auto Dbi = File.getPDBDbiStream();
- auto Tpi = File.getPDBTpiStream();
- auto Ipi = File.getPDBIpiStream();
- auto Info = File.getPDBInfoStream();
-
- uint32_t StreamCount = File.getNumStreams();
- std::unordered_map<uint16_t, const ModuleInfoEx *> ModStreams;
- std::unordered_map<uint16_t, std::string> NamedStreams;
-
- if (Dbi) {
- for (auto &ModI : Dbi->modules()) {
- uint16_t SN = ModI.Info.getModuleStreamIndex();
- ModStreams[SN] = &ModI;
- }
- }
- if (Info) {
- for (auto &NSE : Info->named_streams()) {
- NamedStreams[NSE.second] = NSE.first();
- }
- }
-
- StreamPurposes.resize(StreamCount);
- for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
- std::string Value;
- if (StreamIdx == OldMSFDirectory)
- Value = "Old MSF Directory";
- else if (StreamIdx == StreamPDB)
- Value = "PDB Stream";
- else if (StreamIdx == StreamDBI)
- Value = "DBI Stream";
- else if (StreamIdx == StreamTPI)
- Value = "TPI Stream";
- else if (StreamIdx == StreamIPI)
- Value = "IPI Stream";
- else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex())
- Value = "Global Symbol Hash";
- else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex())
- Value = "Public Symbol Hash";
- else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex())
- Value = "Public Symbol Records";
- else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex())
- Value = "TPI Hash";
- else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex())
- Value = "TPI Aux Hash";
- else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex())
- Value = "IPI Hash";
- else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex())
- Value = "IPI Aux Hash";
- else if (Dbi &&
- StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception))
- Value = "Exception Data";
- else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup))
- Value = "Fixup Data";
- else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO))
- Value = "FPO Data";
- else if (Dbi &&
- StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO))
- Value = "New FPO Data";
- else if (Dbi &&
- StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc))
- Value = "Omap From Source Data";
- else if (Dbi &&
- StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc))
- Value = "Omap To Source Data";
- else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata))
- Value = "Pdata";
- else if (Dbi &&
- StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr))
- Value = "Section Header Data";
- else if (Dbi &&
- StreamIdx ==
- Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig))
- Value = "Section Header Original Data";
- else if (Dbi &&
- StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap))
- Value = "Token Rid Data";
- else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata))
- Value = "Xdata";
- else {
- auto ModIter = ModStreams.find(StreamIdx);
- auto NSIter = NamedStreams.find(StreamIdx);
- if (ModIter != ModStreams.end()) {
- Value = "Module \"";
- Value += ModIter->second->Info.getModuleName().str();
- Value += "\"";
- } else if (NSIter != NamedStreams.end()) {
- Value = "Named Stream \"";
- Value += NSIter->second;
- Value += "\"";
- } else {
- Value = "???";
- }
- }
- StreamPurposes[StreamIdx] = Value;
- }
-
- // Consume errors from missing streams.
- if (!Dbi)
- consumeError(Dbi.takeError());
- if (!Tpi)
- consumeError(Tpi.takeError());
- if (!Ipi)
- consumeError(Ipi.takeError());
- if (!Info)
- consumeError(Info.takeError());
-}
-
Error LLVMOutputStyle::dumpStreamSummary() {
if (!opts::raw::DumpStreamSummary)
return Error::success();
- discoverStreamPurposes();
+ if (StreamPurposes.empty())
+ discoverStreamPurposes(File, StreamPurposes);
uint32_t StreamCount = File.getNumStreams();
@@ -425,7 +320,8 @@ Error LLVMOutputStyle::dumpStreamBytes() {
if (opts::raw::DumpStreamData.empty())
return Error::success();
- discoverStreamPurposes();
+ if (StreamPurposes.empty())
+ discoverStreamPurposes(File, StreamPurposes);
DictScope D(P, "Stream Data");
for (uint32_t SI : opts::raw::DumpStreamData) {
@@ -444,7 +340,7 @@ Error LLVMOutputStyle::dumpStreamBytes() {
auto Blocks = File.getMsfLayout().StreamMap[SI];
P.printList("Blocks", Blocks);
- StreamReader R(*S);
+ BinaryStreamReader R(*S);
ArrayRef<uint8_t> StreamData;
if (auto EC = R.readBytes(StreamData, S->getLength()))
return EC;
@@ -453,6 +349,28 @@ Error LLVMOutputStyle::dumpStreamBytes() {
return Error::success();
}
+Error LLVMOutputStyle::dumpStringTable() {
+ if (!opts::raw::DumpStringTable)
+ return Error::success();
+
+ auto IS = File.getStringTable();
+ if (!IS)
+ return IS.takeError();
+
+ DictScope D(P, "String Table");
+ for (uint32_t I : IS->name_ids()) {
+ StringRef S = IS->getStringForID(I);
+ if (!S.empty()) {
+ llvm::SmallString<32> Str;
+ Str.append("'");
+ Str.append(S);
+ Str.append("'");
+ P.printString(Str);
+ }
+ }
+ return Error::success();
+}
+
Error LLVMOutputStyle::dumpInfoStream() {
if (!opts::raw::DumpHeaders)
return Error::success();
@@ -469,25 +387,28 @@ Error LLVMOutputStyle::dumpInfoStream() {
P.printHex("Signature", IS->getSignature());
P.printNumber("Age", IS->getAge());
P.printObject("Guid", IS->getGuid());
+ P.printHex("Features", IS->getFeatures());
+ {
+ DictScope DD(P, "Named Streams");
+ for (const auto &S : IS->getNamedStreams().entries())
+ P.printObject(S.getKey(), S.getValue());
+ }
return Error::success();
}
-static void printTypeIndexOffset(raw_ostream &OS,
- const TypeIndexOffset &TIOff) {
- OS << "{" << TIOff.Type.getIndex() << ", " << TIOff.Offset << "}";
-}
+namespace {
+class RecordBytesVisitor : public TypeVisitorCallbacks {
+public:
+ explicit RecordBytesVisitor(ScopedPrinter &P) : P(P) {}
-static void dumpTpiHash(ScopedPrinter &P, TpiStream &Tpi) {
- if (!opts::raw::DumpTpiHash)
- return;
- DictScope DD(P, "Hash");
- P.printNumber("Number of Hash Buckets", Tpi.NumHashBuckets());
- P.printNumber("Hash Key Size", Tpi.getHashKeySize());
- P.printList("Values", Tpi.getHashValues());
- P.printList("Type Index Offsets", Tpi.getTypeIndexOffsets(),
- printTypeIndexOffset);
- P.printList("Hash Adjustments", Tpi.getHashAdjustments(),
- printTypeIndexOffset);
+ Error visitTypeEnd(CVType &Record) override {
+ P.printBinaryBlock("Bytes", Record.content());
+ return Error::success();
+ }
+
+private:
+ ScopedPrinter &P;
+};
}
Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
@@ -495,6 +416,7 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
bool DumpRecordBytes = false;
bool DumpRecords = false;
+ bool DumpTpiHash = false;
StringRef Label;
StringRef VerLabel;
if (StreamIdx == StreamTPI) {
@@ -504,6 +426,7 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
}
DumpRecordBytes = opts::raw::DumpTpiRecordBytes;
DumpRecords = opts::raw::DumpTpiRecords;
+ DumpTpiHash = opts::raw::DumpTpiHash;
Label = "Type Info Stream (TPI)";
VerLabel = "TPI Version";
} else if (StreamIdx == StreamIPI) {
@@ -516,64 +439,102 @@ Error LLVMOutputStyle::dumpTpiStream(uint32_t StreamIdx) {
Label = "Type Info Stream (IPI)";
VerLabel = "IPI Version";
}
- if (!DumpRecordBytes && !DumpRecords && !opts::raw::DumpModuleSyms)
+ if (!DumpRecordBytes && !DumpRecords && !DumpTpiHash &&
+ !opts::raw::DumpModuleSyms)
return Error::success();
+ bool IsSilentDatabaseBuild = !DumpRecordBytes && !DumpRecords && !DumpTpiHash;
+
auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream()
: File.getPDBIpiStream();
if (!Tpi)
return Tpi.takeError();
- CVTypeDumper Dumper(TypeDB);
- if (DumpRecords || DumpRecordBytes) {
- DictScope D(P, Label);
+ std::unique_ptr<DictScope> StreamScope;
+ std::unique_ptr<ListScope> RecordScope;
+ if (!IsSilentDatabaseBuild) {
+ StreamScope = llvm::make_unique<DictScope>(P, Label);
P.printNumber(VerLabel, Tpi->getTpiVersion());
P.printNumber("Record count", Tpi->NumTypeRecords());
+ }
- ListScope L(P, "Records");
+ TypeDatabase &StreamDB = (StreamIdx == StreamTPI) ? TypeDB : ItemDB;
+
+ TypeDatabaseVisitor DBV(StreamDB);
+ CompactTypeDumpVisitor CTDV(StreamDB, &P);
+ TypeDumpVisitor TDV(TypeDB, &P, false);
+ if (StreamIdx == StreamIPI)
+ TDV.setItemDB(ItemDB);
+ RecordBytesVisitor RBV(P);
+ TypeDeserializer Deserializer;
+
+ // We always need to deserialize and add it to the type database. This is
+ // true if even if we're not dumping anything, because we could need the
+ // type database for the purposes of dumping symbols.
+ TypeVisitorCallbackPipeline Pipeline;
+ Pipeline.addCallbackToPipeline(Deserializer);
+ Pipeline.addCallbackToPipeline(DBV);
+
+ // If we're in dump mode, add a dumper with the appropriate detail level.
+ if (DumpRecords) {
+ if (opts::raw::CompactRecords)
+ Pipeline.addCallbackToPipeline(CTDV);
+ else
+ Pipeline.addCallbackToPipeline(TDV);
+ }
+ if (DumpRecordBytes)
+ Pipeline.addCallbackToPipeline(RBV);
- bool HadError = false;
- for (auto &Type : Tpi->types(&HadError)) {
- DictScope DD(P, "");
+ CVTypeVisitor Visitor(Pipeline);
- if (DumpRecords) {
- TypeDumpVisitor TDV(TypeDB, &P, false);
- if (auto EC = Dumper.dump(Type, TDV))
- return EC;
- }
+ if (DumpRecords || DumpRecordBytes)
+ RecordScope = llvm::make_unique<ListScope>(P, "Records");
- if (DumpRecordBytes)
- P.printBinaryBlock("Bytes", Type.content());
- }
- dumpTpiHash(P, *Tpi);
- if (HadError)
- return make_error<RawError>(raw_error_code::corrupt_file,
- "TPI stream contained corrupt record");
- } else if (opts::raw::DumpModuleSyms) {
- // Even if the user doesn't want to dump type records, we still need to
- // iterate them in order to build the type database. So when they want to
- // dump symbols but not types, don't stick a dumper on the end, just build
- // the type database.
- TypeDatabaseVisitor DBV(TypeDB);
- TypeDeserializer Deserializer;
- TypeVisitorCallbackPipeline Pipeline;
- Pipeline.addCallbackToPipeline(Deserializer);
- Pipeline.addCallbackToPipeline(DBV);
-
- CVTypeVisitor Visitor(Pipeline);
-
- bool HadError = false;
- for (auto Type : Tpi->types(&HadError)) {
- if (auto EC = Visitor.visitTypeRecord(Type))
- return EC;
+ bool HadError = false;
+
+ TypeIndex T(TypeIndex::FirstNonSimpleIndex);
+ for (auto Type : Tpi->types(&HadError)) {
+ std::unique_ptr<DictScope> OneRecordScope;
+
+ if ((DumpRecords || DumpRecordBytes) && !opts::raw::CompactRecords)
+ OneRecordScope = llvm::make_unique<DictScope>(P, "");
+
+ if (auto EC = Visitor.visitTypeRecord(Type))
+ return EC;
+ }
+ if (HadError)
+ return make_error<RawError>(raw_error_code::corrupt_file,
+ "TPI stream contained corrupt record");
+
+ if (DumpTpiHash) {
+ DictScope DD(P, "Hash");
+ P.printNumber("Number of Hash Buckets", Tpi->NumHashBuckets());
+ P.printNumber("Hash Key Size", Tpi->getHashKeySize());
+ P.printList("Values", Tpi->getHashValues());
+
+ ListScope LHA(P, "Adjusters");
+ auto ExpectedST = File.getStringTable();
+ if (!ExpectedST)
+ return ExpectedST.takeError();
+ const auto &ST = *ExpectedST;
+ for (const auto &E : Tpi->getHashAdjusters()) {
+ DictScope DHA(P);
+ StringRef Name = ST.getStringForID(E.first);
+ P.printString("Type", Name);
+ P.printHex("TI", E.second);
}
+ }
- dumpTpiHash(P, *Tpi);
- if (HadError)
- return make_error<RawError>(raw_error_code::corrupt_file,
- "TPI stream contained corrupt record");
+ if (!IsSilentDatabaseBuild) {
+ ListScope L(P, "TypeIndexOffsets");
+ for (const auto &IO : Tpi->getTypeIndexOffsets()) {
+ P.printString(formatv("Index: {0:x}, Offset: {1:N}", IO.Type.getIndex(),
+ (uint32_t)IO.Offset)
+ .str());
+ }
}
+
P.flush();
return Error::success();
}
@@ -679,10 +640,10 @@ Error LLVMOutputStyle::dumpDbiStream() {
public:
RecordVisitor(ScopedPrinter &P, PDBFile &F) : P(P), F(F) {}
Error visitUnknown(ModuleSubstreamKind Kind,
- ReadableStreamRef Stream) override {
+ BinaryStreamRef Stream) override {
DictScope DD(P, "Unknown");
ArrayRef<uint8_t> Data;
- StreamReader R(Stream);
+ BinaryStreamReader R(Stream);
if (auto EC = R.readBytes(Data, R.bytesRemaining())) {
return make_error<RawError>(
raw_error_code::corrupt_file,
@@ -692,7 +653,7 @@ Error LLVMOutputStyle::dumpDbiStream() {
return Error::success();
}
Error
- visitFileChecksums(ReadableStreamRef Data,
+ visitFileChecksums(BinaryStreamRef Data,
const FileChecksumArray &Checksums) override {
DictScope DD(P, "FileChecksums");
for (const auto &C : Checksums) {
@@ -708,7 +669,7 @@ Error LLVMOutputStyle::dumpDbiStream() {
return Error::success();
}
- Error visitLines(ReadableStreamRef Data,
+ Error visitLines(BinaryStreamRef Data,
const LineSubstreamHeader *Header,
const LineInfoArray &Lines) override {
DictScope DD(P, "Lines");
diff --git a/tools/llvm-pdbdump/LLVMOutputStyle.h b/tools/llvm-pdbdump/LLVMOutputStyle.h
index 816d591f08f83..bfff3b8308db9 100644
--- a/tools/llvm-pdbdump/LLVMOutputStyle.h
+++ b/tools/llvm-pdbdump/LLVMOutputStyle.h
@@ -12,9 +12,12 @@
#include "OutputStyle.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/DebugInfo/CodeView/TypeDatabase.h"
#include "llvm/Support/ScopedPrinter.h"
+#include <string>
+
namespace llvm {
class BitVector;
namespace pdb {
@@ -25,8 +28,6 @@ public:
Error dump() override;
private:
- void discoverStreamPurposes();
-
Error dumpFileHeaders();
Error dumpStreamSummary();
Error dumpFreePageMap();
@@ -34,6 +35,7 @@ private:
Error dumpGlobalsStream();
Error dumpStreamBytes();
Error dumpStreamBlocks();
+ Error dumpStringTable();
Error dumpInfoStream();
Error dumpTpiStream(uint32_t StreamIdx);
Error dumpDbiStream();
@@ -50,7 +52,8 @@ private:
PDBFile &File;
ScopedPrinter P;
codeview::TypeDatabase TypeDB;
- std::vector<std::string> StreamPurposes;
+ codeview::TypeDatabase ItemDB;
+ SmallVector<std::string, 32> StreamPurposes;
};
}
}
diff --git a/tools/llvm-pdbdump/LinePrinter.cpp b/tools/llvm-pdbdump/LinePrinter.cpp
index 47c7d3e3c0e74..d4a5a8d859e5c 100644
--- a/tools/llvm-pdbdump/LinePrinter.cpp
+++ b/tools/llvm-pdbdump/LinePrinter.cpp
@@ -12,6 +12,7 @@
#include "llvm-pdbdump.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/DebugInfo/PDB/UDTLayout.h"
#include "llvm/Support/Regex.h"
#include <algorithm>
@@ -42,8 +43,8 @@ bool IsItemExcluded(llvm::StringRef Item,
using namespace llvm;
-LinePrinter::LinePrinter(int Indent, llvm::raw_ostream &Stream)
- : OS(Stream), IndentSpaces(Indent), CurrentIndent(0) {
+LinePrinter::LinePrinter(int Indent, bool UseColor, llvm::raw_ostream &Stream)
+ : OS(Stream), IndentSpaces(Indent), CurrentIndent(0), UseColor(UseColor) {
SetFilters(ExcludeTypeFilters, opts::pretty::ExcludeTypes.begin(),
opts::pretty::ExcludeTypes.end());
SetFilters(ExcludeSymbolFilters, opts::pretty::ExcludeSymbols.begin(),
@@ -70,8 +71,20 @@ void LinePrinter::NewLine() {
OS.indent(CurrentIndent);
}
-bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName) {
- return IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters);
+bool LinePrinter::IsClassExcluded(const ClassLayout &Class) {
+ if (IsTypeExcluded(Class.getUDTName(), Class.getClassSize()))
+ return true;
+ if (Class.deepPaddingSize() < opts::pretty::PaddingThreshold)
+ return true;
+ return false;
+}
+
+bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size) {
+ if (IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters))
+ return true;
+ if (Size < opts::pretty::SizeThreshold)
+ return true;
+ return false;
}
bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) {
@@ -83,17 +96,25 @@ bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) {
ExcludeCompilandFilters);
}
-WithColor::WithColor(LinePrinter &P, PDB_ColorItem C) : OS(P.OS) {
- applyColor(C);
+WithColor::WithColor(LinePrinter &P, PDB_ColorItem C)
+ : OS(P.OS), UseColor(P.hasColor()) {
+ if (UseColor)
+ applyColor(C);
}
-WithColor::~WithColor() { OS.resetColor(); }
+WithColor::~WithColor() {
+ if (UseColor)
+ OS.resetColor();
+}
void WithColor::applyColor(PDB_ColorItem C) {
switch (C) {
case PDB_ColorItem::None:
OS.resetColor();
return;
+ case PDB_ColorItem::Comment:
+ OS.changeColor(raw_ostream::GREEN, false);
+ return;
case PDB_ColorItem::Address:
OS.changeColor(raw_ostream::YELLOW, /*bold=*/true);
return;
@@ -113,6 +134,7 @@ void WithColor::applyColor(PDB_ColorItem C) {
case PDB_ColorItem::Path:
OS.changeColor(raw_ostream::CYAN, false);
return;
+ case PDB_ColorItem::Padding:
case PDB_ColorItem::SectionHeader:
OS.changeColor(raw_ostream::RED, true);
return;
diff --git a/tools/llvm-pdbdump/LinePrinter.h b/tools/llvm-pdbdump/LinePrinter.h
index a4401f8af9552..1a922feb1e622 100644
--- a/tools/llvm-pdbdump/LinePrinter.h
+++ b/tools/llvm-pdbdump/LinePrinter.h
@@ -20,20 +20,24 @@
namespace llvm {
namespace pdb {
+class ClassLayout;
+
class LinePrinter {
friend class WithColor;
public:
- LinePrinter(int Indent, raw_ostream &Stream);
+ LinePrinter(int Indent, bool UseColor, raw_ostream &Stream);
void Indent();
void Unindent();
void NewLine();
+ bool hasColor() const { return UseColor; }
raw_ostream &getStream() { return OS; }
int getIndentLevel() const { return CurrentIndent; }
- bool IsTypeExcluded(llvm::StringRef TypeName);
+ bool IsClassExcluded(const ClassLayout &Class);
+ bool IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size);
bool IsSymbolExcluded(llvm::StringRef SymbolName);
bool IsCompilandExcluded(llvm::StringRef CompilandName);
@@ -48,6 +52,7 @@ private:
raw_ostream &OS;
int IndentSpaces;
int CurrentIndent;
+ bool UseColor;
std::list<Regex> ExcludeCompilandFilters;
std::list<Regex> ExcludeTypeFilters;
@@ -68,6 +73,8 @@ enum class PDB_ColorItem {
None,
Address,
Type,
+ Comment,
+ Padding,
Keyword,
Offset,
Identifier,
@@ -87,6 +94,7 @@ public:
private:
void applyColor(PDB_ColorItem C);
raw_ostream &OS;
+ bool UseColor;
};
}
}
diff --git a/tools/llvm-pdbdump/PdbYaml.cpp b/tools/llvm-pdbdump/PdbYaml.cpp
index 34e0611e14643..e2c4ee967ed36 100644
--- a/tools/llvm-pdbdump/PdbYaml.cpp
+++ b/tools/llvm-pdbdump/PdbYaml.cpp
@@ -16,14 +16,15 @@
#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h"
#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
#include "llvm/DebugInfo/PDB/PDBExtras.h"
#include "llvm/DebugInfo/PDB/PDBTypes.h"
-#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
-#include "llvm/DebugInfo/PDB/Raw/TpiHashing.h"
using namespace llvm;
using namespace llvm::pdb;
@@ -37,6 +38,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbDbiModuleInfo)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbSymbolRecord)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbTpiRecord)
LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::StreamBlockList)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::pdb::PdbRaw_FeatureSig)
namespace llvm {
namespace yaml {
@@ -133,25 +135,45 @@ template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_TpiVer> {
io.enumCase(Value, "VC80", llvm::pdb::PdbRaw_TpiVer::PdbTpiV80);
}
};
+
+template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_FeatureSig> {
+ static void enumeration(IO &io, PdbRaw_FeatureSig &Features) {
+ io.enumCase(Features, "MinimalDebugInfo",
+ PdbRaw_FeatureSig::MinimalDebugInfo);
+ io.enumCase(Features, "NoTypeMerge", PdbRaw_FeatureSig::NoTypeMerge);
+ io.enumCase(Features, "VC110", PdbRaw_FeatureSig::VC110);
+ io.enumCase(Features, "VC140", PdbRaw_FeatureSig::VC140);
+ }
+};
}
}
void MappingTraits<PdbObject>::mapping(IO &IO, PdbObject &Obj) {
+ // Create a single serialization context that will be passed through the
+ // entire process of serializing / deserializing a Tpi Stream. This is
+ // especially important when we are going from Pdb -> Yaml because we need
+ // to maintain state in a TypeTableBuilder across mappings, and at the end of
+ // the entire process, we need to have one TypeTableBuilder that has every
+ // record.
+ pdb::yaml::SerializationContext Context(IO, Obj.Allocator);
+
+
IO.mapOptional("MSF", Obj.Headers);
IO.mapOptional("StreamSizes", Obj.StreamSizes);
IO.mapOptional("StreamMap", Obj.StreamMap);
+ IO.mapOptional("StringTable", Obj.StringTable);
IO.mapOptional("PdbStream", Obj.PdbStream);
- IO.mapOptional("DbiStream", Obj.DbiStream);
- IO.mapOptionalWithContext("TpiStream", Obj.TpiStream, Obj.Allocator);
- IO.mapOptionalWithContext("IpiStream", Obj.IpiStream, Obj.Allocator);
+ IO.mapOptionalWithContext("DbiStream", Obj.DbiStream, Context);
+ IO.mapOptionalWithContext("TpiStream", Obj.TpiStream, Context);
+ IO.mapOptionalWithContext("IpiStream", Obj.IpiStream, Context);
}
void MappingTraits<MSFHeaders>::mapping(IO &IO, MSFHeaders &Obj) {
- IO.mapRequired("SuperBlock", Obj.SuperBlock);
- IO.mapRequired("NumDirectoryBlocks", Obj.NumDirectoryBlocks);
- IO.mapRequired("DirectoryBlocks", Obj.DirectoryBlocks);
- IO.mapRequired("NumStreams", Obj.NumStreams);
- IO.mapRequired("FileSize", Obj.FileSize);
+ IO.mapOptional("SuperBlock", Obj.SuperBlock);
+ IO.mapOptional("NumDirectoryBlocks", Obj.NumDirectoryBlocks);
+ IO.mapOptional("DirectoryBlocks", Obj.DirectoryBlocks);
+ IO.mapOptional("NumStreams", Obj.NumStreams);
+ IO.mapOptional("FileSize", Obj.FileSize);
}
void MappingTraits<msf::SuperBlock>::mapping(IO &IO, msf::SuperBlock &SB) {
@@ -159,12 +181,13 @@ void MappingTraits<msf::SuperBlock>::mapping(IO &IO, msf::SuperBlock &SB) {
::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic));
}
- IO.mapRequired("BlockSize", SB.BlockSize);
- IO.mapRequired("FreeBlockMap", SB.FreeBlockMapBlock);
- IO.mapRequired("NumBlocks", SB.NumBlocks);
- IO.mapRequired("NumDirectoryBytes", SB.NumDirectoryBytes);
- IO.mapRequired("Unknown1", SB.Unknown1);
- IO.mapRequired("BlockMapAddr", SB.BlockMapAddr);
+ using u32 = support::ulittle32_t;
+ IO.mapOptional("BlockSize", SB.BlockSize, u32(4096U));
+ IO.mapOptional("FreeBlockMap", SB.FreeBlockMapBlock, u32(0U));
+ IO.mapOptional("NumBlocks", SB.NumBlocks, u32(0U));
+ IO.mapOptional("NumDirectoryBytes", SB.NumDirectoryBytes, u32(0U));
+ IO.mapOptional("Unknown1", SB.Unknown1, u32(0U));
+ IO.mapOptional("BlockMapAddr", SB.BlockMapAddr, u32(0U));
}
void MappingTraits<StreamBlockList>::mapping(IO &IO, StreamBlockList &SB) {
@@ -172,35 +195,27 @@ void MappingTraits<StreamBlockList>::mapping(IO &IO, StreamBlockList &SB) {
}
void MappingTraits<PdbInfoStream>::mapping(IO &IO, PdbInfoStream &Obj) {
- IO.mapRequired("Age", Obj.Age);
- IO.mapRequired("Guid", Obj.Guid);
- IO.mapRequired("Signature", Obj.Signature);
- IO.mapRequired("Version", Obj.Version);
- IO.mapRequired("NamedStreams", Obj.NamedStreams);
+ IO.mapOptional("Age", Obj.Age, 1U);
+ IO.mapOptional("Guid", Obj.Guid);
+ IO.mapOptional("Signature", Obj.Signature, 0U);
+ IO.mapOptional("Features", Obj.Features);
+ IO.mapOptional("Version", Obj.Version, PdbImplVC70);
}
-void MappingTraits<PdbDbiStream>::mapping(IO &IO, PdbDbiStream &Obj) {
- IO.mapRequired("VerHeader", Obj.VerHeader);
- IO.mapRequired("Age", Obj.Age);
- IO.mapRequired("BuildNumber", Obj.BuildNumber);
- IO.mapRequired("PdbDllVersion", Obj.PdbDllVersion);
- IO.mapRequired("PdbDllRbld", Obj.PdbDllRbld);
- IO.mapRequired("Flags", Obj.Flags);
- IO.mapRequired("MachineType", Obj.MachineType);
- IO.mapOptional("Modules", Obj.ModInfos);
+void MappingContextTraits<PdbDbiStream, pdb::yaml::SerializationContext>::mapping(IO &IO, PdbDbiStream &Obj, pdb::yaml::SerializationContext &Context) {
+ IO.mapOptional("VerHeader", Obj.VerHeader, PdbDbiV70);
+ IO.mapOptional("Age", Obj.Age, 1U);
+ IO.mapOptional("BuildNumber", Obj.BuildNumber, uint16_t(0U));
+ IO.mapOptional("PdbDllVersion", Obj.PdbDllVersion, 0U);
+ IO.mapOptional("PdbDllRbld", Obj.PdbDllRbld, uint16_t(0U));
+ IO.mapOptional("Flags", Obj.Flags, uint16_t(1U));
+ IO.mapOptional("MachineType", Obj.MachineType, PDB_Machine::x86);
+ IO.mapOptionalWithContext("Modules", Obj.ModInfos, Context);
}
-void MappingContextTraits<PdbTpiStream, BumpPtrAllocator>::mapping(
- IO &IO, pdb::yaml::PdbTpiStream &Obj, BumpPtrAllocator &Allocator) {
- // Create a single serialization context that will be passed through the
- // entire process of serializing / deserializing a Tpi Stream. This is
- // especially important when we are going from Pdb -> Yaml because we need
- // to maintain state in a TypeTableBuilder across mappings, and at the end of
- // the entire process, we need to have one TypeTableBuilder that has every
- // record.
- pdb::yaml::SerializationContext Context(IO, Allocator);
-
- IO.mapRequired("Version", Obj.Version);
+void MappingContextTraits<PdbTpiStream, pdb::yaml::SerializationContext>::mapping(
+ IO &IO, pdb::yaml::PdbTpiStream &Obj, pdb::yaml::SerializationContext &Context) {
+ IO.mapOptional("Version", Obj.Version, PdbTpiV80);
IO.mapRequired("Records", Obj.Records, Context);
}
@@ -210,8 +225,9 @@ void MappingTraits<NamedStreamMapping>::mapping(IO &IO,
IO.mapRequired("StreamNum", Obj.StreamNumber);
}
-void MappingTraits<PdbSymbolRecord>::mapping(IO &IO, PdbSymbolRecord &Obj) {
+void MappingContextTraits<PdbSymbolRecord, pdb::yaml::SerializationContext>::mapping(IO &IO, PdbSymbolRecord &Obj, pdb::yaml::SerializationContext &Context) {
codeview::SymbolVisitorCallbackPipeline Pipeline;
+ codeview::SymbolSerializer Serializer(Context.Allocator);
codeview::SymbolDeserializer Deserializer(nullptr);
codeview::yaml::YamlSymbolDumper Dumper(IO);
@@ -220,23 +236,26 @@ void MappingTraits<PdbSymbolRecord>::mapping(IO &IO, PdbSymbolRecord &Obj) {
Pipeline.addCallbackToPipeline(Deserializer);
Pipeline.addCallbackToPipeline(Dumper);
} else {
- return;
+ // For the other way around, dump it into a concrete structure, and then
+ // serialize it into the CVRecord.
+ Pipeline.addCallbackToPipeline(Dumper);
+ Pipeline.addCallbackToPipeline(Serializer);
}
codeview::CVSymbolVisitor Visitor(Pipeline);
consumeError(Visitor.visitSymbolRecord(Obj.Record));
}
-void MappingTraits<PdbModiStream>::mapping(IO &IO, PdbModiStream &Obj) {
- IO.mapRequired("Signature", Obj.Signature);
- IO.mapRequired("Records", Obj.Symbols);
+void MappingContextTraits<PdbModiStream, pdb::yaml::SerializationContext>::mapping(IO &IO, PdbModiStream &Obj, pdb::yaml::SerializationContext &Context) {
+ IO.mapOptional("Signature", Obj.Signature, 4U);
+ IO.mapRequired("Records", Obj.Symbols, Context);
}
-void MappingTraits<PdbDbiModuleInfo>::mapping(IO &IO, PdbDbiModuleInfo &Obj) {
+void MappingContextTraits<PdbDbiModuleInfo, pdb::yaml::SerializationContext>::mapping(IO &IO, PdbDbiModuleInfo &Obj, pdb::yaml::SerializationContext &Context) {
IO.mapRequired("Module", Obj.Mod);
- IO.mapRequired("ObjFile", Obj.Obj);
+ IO.mapOptional("ObjFile", Obj.Obj, Obj.Mod);
IO.mapOptional("SourceFiles", Obj.SourceFiles);
- IO.mapOptional("Modi", Obj.Modi);
+ IO.mapOptionalWithContext("Modi", Obj.Modi, Context);
}
void MappingContextTraits<PdbTpiRecord, pdb::yaml::SerializationContext>::
diff --git a/tools/llvm-pdbdump/PdbYaml.h b/tools/llvm-pdbdump/PdbYaml.h
index 398186f16d723..2c4cd237f8d7f 100644
--- a/tools/llvm-pdbdump/PdbYaml.h
+++ b/tools/llvm-pdbdump/PdbYaml.h
@@ -16,9 +16,9 @@
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/MSF/MSFCommon.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
#include "llvm/DebugInfo/PDB/PDBTypes.h"
-#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
-#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/YAMLTraits.h"
@@ -32,10 +32,10 @@ struct SerializationContext;
struct MSFHeaders {
msf::SuperBlock SuperBlock;
- uint32_t NumDirectoryBlocks;
+ uint32_t NumDirectoryBlocks = 0;
std::vector<uint32_t> DirectoryBlocks;
- uint32_t NumStreams;
- uint32_t FileSize;
+ uint32_t NumStreams = 0;
+ uint32_t FileSize = 0;
};
struct StreamBlockList {
@@ -48,10 +48,11 @@ struct NamedStreamMapping {
};
struct PdbInfoStream {
- PdbRaw_ImplVer Version;
- uint32_t Signature;
- uint32_t Age;
+ PdbRaw_ImplVer Version = PdbImplVC70;
+ uint32_t Signature = 0;
+ uint32_t Age = 1;
PDB_UniqueId Guid;
+ std::vector<PdbRaw_FeatureSig> Features;
std::vector<NamedStreamMapping> NamedStreams;
};
@@ -72,13 +73,13 @@ struct PdbDbiModuleInfo {
};
struct PdbDbiStream {
- PdbRaw_DbiVer VerHeader;
- uint32_t Age;
- uint16_t BuildNumber;
- uint32_t PdbDllVersion;
- uint16_t PdbDllRbld;
- uint16_t Flags;
- PDB_Machine MachineType;
+ PdbRaw_DbiVer VerHeader = PdbDbiV70;
+ uint32_t Age = 1;
+ uint16_t BuildNumber = 0;
+ uint32_t PdbDllVersion = 0;
+ uint16_t PdbDllRbld = 0;
+ uint16_t Flags = 1;
+ PDB_Machine MachineType = PDB_Machine::x86;
std::vector<PdbDbiModuleInfo> ModInfos;
};
@@ -92,7 +93,7 @@ struct PdbTpiFieldListRecord {
};
struct PdbTpiStream {
- PdbRaw_TpiVer Version;
+ PdbRaw_TpiVer Version = PdbTpiV80;
std::vector<PdbTpiRecord> Records;
};
@@ -107,6 +108,8 @@ struct PdbObject {
Optional<PdbTpiStream> TpiStream;
Optional<PdbTpiStream> IpiStream;
+ Optional<std::vector<StringRef>> StringTable;
+
BumpPtrAllocator &Allocator;
};
}
@@ -136,30 +139,30 @@ template <> struct MappingTraits<pdb::yaml::PdbInfoStream> {
static void mapping(IO &IO, pdb::yaml::PdbInfoStream &Obj);
};
-template <> struct MappingTraits<pdb::yaml::PdbDbiStream> {
- static void mapping(IO &IO, pdb::yaml::PdbDbiStream &Obj);
+template <> struct MappingContextTraits<pdb::yaml::PdbDbiStream, pdb::yaml::SerializationContext> {
+ static void mapping(IO &IO, pdb::yaml::PdbDbiStream &Obj, pdb::yaml::SerializationContext &Context);
};
template <>
-struct MappingContextTraits<pdb::yaml::PdbTpiStream, llvm::BumpPtrAllocator> {
+struct MappingContextTraits<pdb::yaml::PdbTpiStream, pdb::yaml::SerializationContext> {
static void mapping(IO &IO, pdb::yaml::PdbTpiStream &Obj,
- llvm::BumpPtrAllocator &Allocator);
+ pdb::yaml::SerializationContext &Context);
};
template <> struct MappingTraits<pdb::yaml::NamedStreamMapping> {
static void mapping(IO &IO, pdb::yaml::NamedStreamMapping &Obj);
};
-template <> struct MappingTraits<pdb::yaml::PdbSymbolRecord> {
- static void mapping(IO &IO, pdb::yaml::PdbSymbolRecord &Obj);
+template <> struct MappingContextTraits<pdb::yaml::PdbSymbolRecord, pdb::yaml::SerializationContext> {
+ static void mapping(IO &IO, pdb::yaml::PdbSymbolRecord &Obj, pdb::yaml::SerializationContext &Context);
};
-template <> struct MappingTraits<pdb::yaml::PdbModiStream> {
- static void mapping(IO &IO, pdb::yaml::PdbModiStream &Obj);
+template <> struct MappingContextTraits<pdb::yaml::PdbModiStream, pdb::yaml::SerializationContext> {
+ static void mapping(IO &IO, pdb::yaml::PdbModiStream &Obj, pdb::yaml::SerializationContext &Context);
};
-template <> struct MappingTraits<pdb::yaml::PdbDbiModuleInfo> {
- static void mapping(IO &IO, pdb::yaml::PdbDbiModuleInfo &Obj);
+template <> struct MappingContextTraits<pdb::yaml::PdbDbiModuleInfo, pdb::yaml::SerializationContext> {
+ static void mapping(IO &IO, pdb::yaml::PdbDbiModuleInfo &Obj, pdb::yaml::SerializationContext &Context);
};
template <>
diff --git a/tools/llvm-pdbdump/PrettyBuiltinDumper.cpp b/tools/llvm-pdbdump/PrettyBuiltinDumper.cpp
index f866132aa8866..591d5e70cfd6e 100644
--- a/tools/llvm-pdbdump/PrettyBuiltinDumper.cpp
+++ b/tools/llvm-pdbdump/PrettyBuiltinDumper.cpp
@@ -20,6 +20,10 @@ BuiltinDumper::BuiltinDumper(LinePrinter &P)
: PDBSymDumper(false), Printer(P) {}
void BuiltinDumper::start(const PDBSymbolTypeBuiltin &Symbol) {
+ if (Symbol.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
+ if (Symbol.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
WithColor(Printer, PDB_ColorItem::Type).get() << getTypeName(Symbol);
}
diff --git a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp
index b0c534f7c5b17..9f213a4b4d960 100644
--- a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp
+++ b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp
@@ -10,22 +10,16 @@
#include "PrettyClassDefinitionDumper.h"
#include "LinePrinter.h"
-#include "PrettyEnumDumper.h"
-#include "PrettyFunctionDumper.h"
-#include "PrettyTypedefDumper.h"
-#include "PrettyVariableDumper.h"
+#include "PrettyClassLayoutGraphicalDumper.h"
+#include "PrettyClassLayoutTextDumper.h"
#include "llvm-pdbdump.h"
-#include "llvm/DebugInfo/PDB/IPDBSession.h"
-#include "llvm/DebugInfo/PDB/PDBExtras.h"
-#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
-#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h"
-#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
-#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"
-#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
-#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h"
+#include "llvm/DebugInfo/PDB/UDTLayout.h"
+
#include "llvm/Support/Format.h"
using namespace llvm;
@@ -35,158 +29,97 @@ ClassDefinitionDumper::ClassDefinitionDumper(LinePrinter &P)
: PDBSymDumper(true), Printer(P) {}
void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) {
- std::string Name = Class.getName();
- WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " ";
- WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName();
-
- auto Bases = Class.findAllChildren<PDBSymbolTypeBaseClass>();
- if (Bases->getChildCount() > 0) {
- Printer.Indent();
- Printer.NewLine();
- Printer << ":";
- uint32_t BaseIndex = 0;
- while (auto Base = Bases->getNext()) {
- Printer << " ";
- WithColor(Printer, PDB_ColorItem::Keyword).get() << Base->getAccess();
- if (Base->isVirtualBaseClass())
- WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual";
- WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base->getName();
- if (++BaseIndex < Bases->getChildCount()) {
- Printer.NewLine();
- Printer << ",";
- }
- }
- Printer.Unindent();
- }
-
- Printer << " {";
- auto Children = Class.findAllChildren();
- if (Children->getChildCount() == 0) {
- Printer << "}";
- return;
- }
+ assert(opts::pretty::ClassFormat !=
+ opts::pretty::ClassDefinitionFormat::None);
- // Try to dump symbols organized by member access level. Public members
- // first, then protected, then private. This might be slow, so it's worth
- // reconsidering the value of this if performance of large PDBs is a problem.
- // NOTE: Access level of nested types is not recorded in the PDB, so we have
- // a special case for them.
- SymbolGroupByAccess Groups;
- Groups.insert(std::make_pair(0, SymbolGroup()));
- Groups.insert(std::make_pair((int)PDB_MemberAccess::Private, SymbolGroup()));
- Groups.insert(
- std::make_pair((int)PDB_MemberAccess::Protected, SymbolGroup()));
- Groups.insert(std::make_pair((int)PDB_MemberAccess::Public, SymbolGroup()));
-
- while (auto Child = Children->getNext()) {
- PDB_MemberAccess Access = Child->getRawSymbol().getAccess();
- if (isa<PDBSymbolTypeBaseClass>(*Child))
- continue;
-
- auto &AccessGroup = Groups.find((int)Access)->second;
-
- if (auto Func = dyn_cast<PDBSymbolFunc>(Child.get())) {
- if (Func->isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
- continue;
- if (Func->getLength() == 0 && !Func->isPureVirtual() &&
- !Func->isIntroVirtualFunction())
- continue;
- Child.release();
- AccessGroup.Functions.push_back(std::unique_ptr<PDBSymbolFunc>(Func));
- } else if (auto Data = dyn_cast<PDBSymbolData>(Child.get())) {
- Child.release();
- AccessGroup.Data.push_back(std::unique_ptr<PDBSymbolData>(Data));
- } else {
- AccessGroup.Unknown.push_back(std::move(Child));
- }
- }
-
- int Count = 0;
- Count += dumpAccessGroup((PDB_MemberAccess)0, Groups[0]);
- Count += dumpAccessGroup(PDB_MemberAccess::Public,
- Groups[(int)PDB_MemberAccess::Public]);
- Count += dumpAccessGroup(PDB_MemberAccess::Protected,
- Groups[(int)PDB_MemberAccess::Protected]);
- Count += dumpAccessGroup(PDB_MemberAccess::Private,
- Groups[(int)PDB_MemberAccess::Private]);
- if (Count > 0)
- Printer.NewLine();
- Printer << "}";
+ ClassLayout Layout(Class);
+ start(Layout);
}
-int ClassDefinitionDumper::dumpAccessGroup(PDB_MemberAccess Access,
- const SymbolGroup &Group) {
- if (Group.Functions.empty() && Group.Data.empty() && Group.Unknown.empty())
- return 0;
+void ClassDefinitionDumper::start(const ClassLayout &Layout) {
+ prettyPrintClassIntro(Layout);
- int Count = 0;
- if (Access == PDB_MemberAccess::Private) {
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Keyword).get() << "private";
- Printer << ":";
- } else if (Access == PDB_MemberAccess::Protected) {
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Keyword).get() << "protected";
- Printer << ":";
- } else if (Access == PDB_MemberAccess::Public) {
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Keyword).get() << "public";
- Printer << ":";
+ switch (opts::pretty::ClassFormat) {
+ case opts::pretty::ClassDefinitionFormat::Graphical: {
+ PrettyClassLayoutGraphicalDumper Dumper(Printer, 0);
+ DumpedAnything = Dumper.start(Layout);
+ break;
}
- Printer.Indent();
- for (auto iter = Group.Functions.begin(), end = Group.Functions.end();
- iter != end; ++iter) {
- ++Count;
- (*iter)->dump(*this);
- }
- for (auto iter = Group.Data.begin(), end = Group.Data.end(); iter != end;
- ++iter) {
- ++Count;
- (*iter)->dump(*this);
+ case opts::pretty::ClassDefinitionFormat::Standard:
+ case opts::pretty::ClassDefinitionFormat::Layout: {
+ PrettyClassLayoutTextDumper Dumper(Printer);
+ DumpedAnything |= Dumper.start(Layout);
+ break;
}
- for (auto iter = Group.Unknown.begin(), end = Group.Unknown.end();
- iter != end; ++iter) {
- ++Count;
- (*iter)->dump(*this);
+ default:
+ llvm_unreachable("Unreachable!");
}
- Printer.Unindent();
- return Count;
-}
-
-void ClassDefinitionDumper::dump(const PDBSymbolTypeBaseClass &Symbol) {}
-void ClassDefinitionDumper::dump(const PDBSymbolData &Symbol) {
- VariableDumper Dumper(Printer);
- Dumper.start(Symbol);
+ prettyPrintClassOutro(Layout);
}
-void ClassDefinitionDumper::dump(const PDBSymbolFunc &Symbol) {
- if (Printer.IsSymbolExcluded(Symbol.getName()))
- return;
+static void printBase(LinePrinter &Printer, const PDBSymbolTypeBaseClass &Base,
+ uint32_t &CurIndex, uint32_t TotalBaseCount,
+ bool IsVirtual) {
+ Printer << " ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << Base.getAccess();
+ if (IsVirtual)
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual";
+ WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base.getName();
+ if (++CurIndex < TotalBaseCount) {
+ Printer.NewLine();
+ Printer << ",";
+ }
+}
+void ClassDefinitionDumper::prettyPrintClassIntro(const ClassLayout &Layout) {
+ DumpedAnything = false;
Printer.NewLine();
- FunctionDumper Dumper(Printer);
- Dumper.start(Symbol, FunctionDumper::PointerType::None);
-}
-void ClassDefinitionDumper::dump(const PDBSymbolTypeVTable &Symbol) {}
+ uint32_t Size = Layout.getClassSize();
+ const PDBSymbolTypeUDT &Class = Layout.getClass();
-void ClassDefinitionDumper::dump(const PDBSymbolTypeEnum &Symbol) {
- if (Printer.IsTypeExcluded(Symbol.getName()))
- return;
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " ";
+ WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName();
+ WithColor(Printer, PDB_ColorItem::Comment).get() << " [sizeof = " << Size
+ << "]";
+ uint32_t BaseCount = Layout.bases().size();
+ uint32_t VBaseCount = Layout.vbases().size();
+ uint32_t TotalBaseCount = BaseCount + VBaseCount;
+ if (TotalBaseCount > 0) {
+ Printer.Indent();
+ Printer.NewLine();
+ Printer << ":";
+ uint32_t BaseIndex = 0;
+ for (auto BC : Layout.bases()) {
+ const auto &Base = BC->getBase();
+ printBase(Printer, Base, BaseIndex, TotalBaseCount, false);
+ }
+ for (auto &BC : Layout.vbases()) {
+ printBase(Printer, *BC, BaseIndex, TotalBaseCount, true);
+ }
- Printer.NewLine();
- EnumDumper Dumper(Printer);
- Dumper.start(Symbol);
-}
+ Printer.Unindent();
+ }
-void ClassDefinitionDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
- if (Printer.IsTypeExcluded(Symbol.getName()))
- return;
+ Printer << " {";
+ Printer.Indent();
+}
+void ClassDefinitionDumper::prettyPrintClassOutro(const ClassLayout &Layout) {
+ Printer.Unindent();
+ if (DumpedAnything)
+ Printer.NewLine();
+ Printer << "}";
Printer.NewLine();
- TypedefDumper Dumper(Printer);
- Dumper.start(Symbol);
+ if (Layout.deepPaddingSize() > 0) {
+ APFloat Pct(100.0 * (double)Layout.deepPaddingSize() /
+ (double)Layout.getClassSize());
+ SmallString<8> PctStr;
+ Pct.toString(PctStr, 4);
+ WithColor(Printer, PDB_ColorItem::Padding).get()
+ << "Total padding " << Layout.deepPaddingSize() << " bytes (" << PctStr
+ << "% of class size)";
+ Printer.NewLine();
+ }
}
-
-void ClassDefinitionDumper::dump(const PDBSymbolTypeUDT &Symbol) {}
diff --git a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h
index 0831f47557ed6..0e27733b3ccb9 100644
--- a/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h
+++ b/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h
@@ -10,6 +10,8 @@
#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSDEFINITIONDUMPER_H
#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSDEFINITIONDUMPER_H
+#include "llvm/ADT/BitVector.h"
+
#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
@@ -19,44 +21,26 @@
#include <unordered_map>
namespace llvm {
+class BitVector;
+
namespace pdb {
+class ClassLayout;
class LinePrinter;
class ClassDefinitionDumper : public PDBSymDumper {
public:
ClassDefinitionDumper(LinePrinter &P);
- void start(const PDBSymbolTypeUDT &Exe);
-
- void dump(const PDBSymbolTypeBaseClass &Symbol) override;
- void dump(const PDBSymbolData &Symbol) override;
- void dump(const PDBSymbolTypeEnum &Symbol) override;
- void dump(const PDBSymbolFunc &Symbol) override;
- void dump(const PDBSymbolTypeTypedef &Symbol) override;
- void dump(const PDBSymbolTypeUDT &Symbol) override;
- void dump(const PDBSymbolTypeVTable &Symbol) override;
+ void start(const PDBSymbolTypeUDT &Class);
+ void start(const ClassLayout &Class);
private:
- LinePrinter &Printer;
+ void prettyPrintClassIntro(const ClassLayout &Class);
+ void prettyPrintClassOutro(const ClassLayout &Class);
- struct SymbolGroup {
- SymbolGroup() {}
- SymbolGroup(SymbolGroup &&Other) {
- Functions = std::move(Other.Functions);
- Data = std::move(Other.Data);
- Unknown = std::move(Other.Unknown);
- }
-
- std::list<std::unique_ptr<PDBSymbolFunc>> Functions;
- std::list<std::unique_ptr<PDBSymbolData>> Data;
- std::list<std::unique_ptr<PDBSymbol>> Unknown;
- SymbolGroup(const SymbolGroup &other) = delete;
- SymbolGroup &operator=(const SymbolGroup &other) = delete;
- };
- typedef std::unordered_map<int, SymbolGroup> SymbolGroupByAccess;
-
- int dumpAccessGroup(PDB_MemberAccess Access, const SymbolGroup &Group);
+ bool DumpedAnything = false;
+ LinePrinter &Printer;
};
}
}
diff --git a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp
new file mode 100644
index 0000000000000..d146ca9d47121
--- /dev/null
+++ b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.cpp
@@ -0,0 +1,151 @@
+//===- PrettyClassLayoutGraphicalDumper.h -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyClassLayoutGraphicalDumper.h"
+
+#include "LinePrinter.h"
+#include "PrettyClassDefinitionDumper.h"
+#include "PrettyVariableDumper.h"
+
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
+#include "llvm/DebugInfo/PDB/UDTLayout.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+PrettyClassLayoutGraphicalDumper::PrettyClassLayoutGraphicalDumper(
+ LinePrinter &P, uint32_t InitialOffset)
+ : PDBSymDumper(true), Printer(P), ClassOffsetZero(InitialOffset),
+ CurrentAbsoluteOffset(InitialOffset) {}
+
+bool PrettyClassLayoutGraphicalDumper::start(const UDTLayoutBase &Layout) {
+ const BitVector &UseMap = Layout.usedBytes();
+ int NextPaddingByte = UseMap.find_first_unset();
+
+ for (auto &Item : Layout.layout_items()) {
+ // Calculate the absolute offset of the first byte of the next field.
+ uint32_t RelativeOffset = Item->getOffsetInParent();
+ CurrentAbsoluteOffset = ClassOffsetZero + RelativeOffset;
+
+ // Since there is storage there, it should be set! However, this might
+ // be an empty base, in which case it could extend outside the bounds of
+ // the parent class.
+ if (RelativeOffset < UseMap.size() && (Item->getSize() > 0)) {
+ assert(UseMap.test(RelativeOffset));
+
+ // If there is any remaining padding in this class, and the offset of the
+ // new item is after the padding, then we must have just jumped over some
+ // padding. Print a padding row and then look for where the next block
+ // of padding begins.
+ if ((NextPaddingByte >= 0) &&
+ (RelativeOffset > uint32_t(NextPaddingByte))) {
+ printPaddingRow(RelativeOffset - NextPaddingByte);
+ NextPaddingByte = UseMap.find_next_unset(RelativeOffset);
+ }
+ }
+
+ CurrentItem = Item.get();
+ Item->getSymbol().dump(*this);
+ }
+
+ if (NextPaddingByte >= 0 && Layout.getClassSize() > 1) {
+ uint32_t Amount = Layout.getClassSize() - NextPaddingByte;
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << Amount
+ << " bytes)";
+ DumpedAnything = true;
+ }
+
+ return DumpedAnything;
+}
+
+void PrettyClassLayoutGraphicalDumper::printPaddingRow(uint32_t Amount) {
+ if (Amount == 0)
+ return;
+
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << Amount
+ << " bytes)";
+ DumpedAnything = true;
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(
+ const PDBSymbolTypeBaseClass &Symbol) {
+ assert(CurrentItem != nullptr);
+
+ Printer.NewLine();
+ BaseClassLayout &Layout = static_cast<BaseClassLayout &>(*CurrentItem);
+
+ std::string Label = Layout.isVirtualBase() ? "vbase" : "base";
+ Printer << Label << " ";
+
+ WithColor(Printer, PDB_ColorItem::Offset).get()
+ << "+" << format_hex(CurrentAbsoluteOffset, 4)
+ << " [sizeof=" << Layout.getSize() << "] ";
+
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Layout.getName();
+
+ Printer.Indent();
+ uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent();
+ PrettyClassLayoutGraphicalDumper BaseDumper(Printer, ChildOffsetZero);
+ BaseDumper.start(Layout);
+ Printer.Unindent();
+
+ DumpedAnything = true;
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolData &Symbol) {
+ assert(CurrentItem != nullptr);
+
+ DataMemberLayoutItem &Layout =
+ static_cast<DataMemberLayoutItem &>(*CurrentItem);
+
+ VariableDumper VarDumper(Printer);
+ VarDumper.start(Symbol, ClassOffsetZero);
+
+ if (Layout.hasUDTLayout()) {
+ Printer.Indent();
+ PrettyClassLayoutGraphicalDumper TypeDumper(Printer, ClassOffsetZero);
+ TypeDumper.start(Layout.getUDTLayout());
+ Printer.Unindent();
+ }
+
+ DumpedAnything = true;
+}
+
+void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeVTable &Symbol) {
+ assert(CurrentItem != nullptr);
+
+ VTableLayoutItem &Layout = static_cast<VTableLayoutItem &>(*CurrentItem);
+
+ VariableDumper VarDumper(Printer);
+ VarDumper.start(Symbol, ClassOffsetZero);
+
+ Printer.Indent();
+ uint32_t Index = 0;
+ for (auto &Func : Layout.funcs()) {
+ Printer.NewLine();
+ std::string Name = Func->getName();
+ auto ParentClass =
+ unique_dyn_cast<PDBSymbolTypeUDT>(Func->getClassParent());
+ assert(ParentClass);
+ WithColor(Printer, PDB_ColorItem::Address).get() << " [" << Index << "] ";
+ WithColor(Printer, PDB_ColorItem::Identifier).get()
+ << "&" << ParentClass->getName();
+ Printer << "::";
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Name;
+ ++Index;
+ }
+ Printer.Unindent();
+
+ DumpedAnything = true;
+}
diff --git a/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h
new file mode 100644
index 0000000000000..7dfb74c4e14b4
--- /dev/null
+++ b/tools/llvm-pdbdump/PrettyClassLayoutGraphicalDumper.h
@@ -0,0 +1,47 @@
+//===- PrettyClassLayoutGraphicalDumper.h -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTGRAPHICALDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTGRAPHICALDUMPER_H
+
+#include "llvm/ADT/BitVector.h"
+
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+
+namespace llvm {
+
+namespace pdb {
+
+class UDTLayoutBase;
+class StorageItemBase;
+class LinePrinter;
+
+class PrettyClassLayoutGraphicalDumper : public PDBSymDumper {
+public:
+ PrettyClassLayoutGraphicalDumper(LinePrinter &P, uint32_t InitialOffset);
+
+ bool start(const UDTLayoutBase &Layout);
+
+ void dump(const PDBSymbolTypeBaseClass &Symbol) override;
+ void dump(const PDBSymbolData &Symbol) override;
+ void dump(const PDBSymbolTypeVTable &Symbol) override;
+
+private:
+ void printPaddingRow(uint32_t Amount);
+
+ LinePrinter &Printer;
+
+ StorageItemBase *CurrentItem = nullptr;
+ uint32_t ClassOffsetZero = 0;
+ uint32_t CurrentAbsoluteOffset = 0;
+ bool DumpedAnything = false;
+};
+}
+}
+#endif
diff --git a/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp
new file mode 100644
index 0000000000000..02f31108b0dfe
--- /dev/null
+++ b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.cpp
@@ -0,0 +1,119 @@
+//===- PrettyClassLayoutTextDumper.cpp --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrettyClassLayoutTextDumper.h"
+
+#include "LinePrinter.h"
+#include "PrettyEnumDumper.h"
+#include "PrettyFunctionDumper.h"
+#include "PrettyTypedefDumper.h"
+#include "PrettyVariableDumper.h"
+#include "llvm-pdbdump.h"
+
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/PDBExtras.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h"
+#include "llvm/DebugInfo/PDB/UDTLayout.h"
+
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+PrettyClassLayoutTextDumper::PrettyClassLayoutTextDumper(LinePrinter &P)
+ : PDBSymDumper(true), Printer(P) {}
+
+bool PrettyClassLayoutTextDumper::start(const ClassLayout &Layout) {
+ if (opts::pretty::ClassFormat ==
+ opts::pretty::ClassDefinitionFormat::Standard) {
+ for (auto &Other : Layout.other_items())
+ Other->dump(*this);
+ for (auto &Func : Layout.funcs())
+ Func->dump(*this);
+ }
+
+ const BitVector &UseMap = Layout.usedBytes();
+ int NextUnusedByte = Layout.usedBytes().find_first_unset();
+ // Next dump items which affect class layout.
+ for (auto &LayoutItem : Layout.layout_items()) {
+ if (NextUnusedByte >= 0) {
+ // If there are padding bytes remaining, see if this field is the first to
+ // cross a padding boundary, and print a padding field indicator if so.
+ int Off = LayoutItem->getOffsetInParent();
+ if (Off > NextUnusedByte) {
+ uint32_t Amount = Off - NextUnusedByte;
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> ("
+ << Amount << " bytes)";
+ assert(UseMap.find_next(NextUnusedByte) == Off);
+ NextUnusedByte = UseMap.find_next_unset(Off);
+ }
+ }
+ LayoutItem->getSymbol().dump(*this);
+ }
+
+ if (NextUnusedByte >= 0 && Layout.getClassSize() > 1) {
+ uint32_t Amount = Layout.getClassSize() - NextUnusedByte;
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << Amount
+ << " bytes)";
+ DumpedAnything = true;
+ }
+
+ return DumpedAnything;
+}
+
+void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeBaseClass &Symbol) {}
+
+void PrettyClassLayoutTextDumper::dump(const PDBSymbolData &Symbol) {
+ VariableDumper Dumper(Printer);
+ Dumper.start(Symbol);
+ DumpedAnything = true;
+}
+
+void PrettyClassLayoutTextDumper::dump(const PDBSymbolFunc &Symbol) {
+ if (Printer.IsSymbolExcluded(Symbol.getName()))
+ return;
+ if (Symbol.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
+ return;
+ if (Symbol.getLength() == 0 && !Symbol.isPureVirtual() &&
+ !Symbol.isIntroVirtualFunction())
+ return;
+
+ DumpedAnything = true;
+ Printer.NewLine();
+ FunctionDumper Dumper(Printer);
+ Dumper.start(Symbol, FunctionDumper::PointerType::None);
+}
+
+void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeVTable &Symbol) {
+ VariableDumper Dumper(Printer);
+ Dumper.start(Symbol);
+ DumpedAnything = true;
+}
+
+void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeEnum &Symbol) {
+ DumpedAnything = true;
+ Printer.NewLine();
+ EnumDumper Dumper(Printer);
+ Dumper.start(Symbol);
+}
+
+void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
+ DumpedAnything = true;
+ Printer.NewLine();
+ TypedefDumper Dumper(Printer);
+ Dumper.start(Symbol);
+}
+
+void PrettyClassLayoutTextDumper::dump(const PDBSymbolTypeUDT &Symbol) {}
diff --git a/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.h b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.h
new file mode 100644
index 0000000000000..56c20f0e84336
--- /dev/null
+++ b/tools/llvm-pdbdump/PrettyClassLayoutTextDumper.h
@@ -0,0 +1,44 @@
+//===- PrettyClassLayoutTextDumper.h ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTTEXTDUMPER_H
+#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSLAYOUTTEXTDUMPER_H
+
+#include "llvm/ADT/BitVector.h"
+
+#include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+
+namespace llvm {
+
+namespace pdb {
+
+class ClassLayout;
+class LinePrinter;
+
+class PrettyClassLayoutTextDumper : public PDBSymDumper {
+public:
+ PrettyClassLayoutTextDumper(LinePrinter &P);
+
+ bool start(const ClassLayout &Layout);
+
+ void dump(const PDBSymbolTypeBaseClass &Symbol) override;
+ void dump(const PDBSymbolData &Symbol) override;
+ void dump(const PDBSymbolTypeEnum &Symbol) override;
+ void dump(const PDBSymbolFunc &Symbol) override;
+ void dump(const PDBSymbolTypeTypedef &Symbol) override;
+ void dump(const PDBSymbolTypeUDT &Symbol) override;
+ void dump(const PDBSymbolTypeVTable &Symbol) override;
+
+private:
+ bool DumpedAnything = false;
+ LinePrinter &Printer;
+};
+}
+}
+#endif
diff --git a/tools/llvm-pdbdump/PrettyFunctionDumper.cpp b/tools/llvm-pdbdump/PrettyFunctionDumper.cpp
index 2f6ca894fadf3..b0be33c157ce6 100644
--- a/tools/llvm-pdbdump/PrettyFunctionDumper.cpp
+++ b/tools/llvm-pdbdump/PrettyFunctionDumper.cpp
@@ -195,10 +195,7 @@ void FunctionDumper::start(const PDBSymbolFunc &Symbol, PointerType Pointer) {
}
void FunctionDumper::dump(const PDBSymbolTypeArray &Symbol) {
- uint32_t ElementTypeId = Symbol.getTypeId();
- auto ElementType = Symbol.getSession().getSymbolById(ElementTypeId);
- if (!ElementType)
- return;
+ auto ElementType = Symbol.getElementType();
ElementType->dump(*this);
Printer << "[";
@@ -232,12 +229,11 @@ void FunctionDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
}
void FunctionDumper::dump(const PDBSymbolTypePointer &Symbol) {
- uint32_t PointeeId = Symbol.getTypeId();
- auto PointeeType = Symbol.getSession().getSymbolById(PointeeId);
+ auto PointeeType = Symbol.getPointeeType();
if (!PointeeType)
return;
- if (auto FuncSig = dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType.get())) {
+ if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) {
FunctionDumper NestedDumper(Printer);
PointerType Pointer =
Symbol.isReference() ? PointerType::Reference : PointerType::Pointer;
diff --git a/tools/llvm-pdbdump/PrettyTypeDumper.cpp b/tools/llvm-pdbdump/PrettyTypeDumper.cpp
index 4f70c8047337a..ffeef72150d26 100644
--- a/tools/llvm-pdbdump/PrettyTypeDumper.cpp
+++ b/tools/llvm-pdbdump/PrettyTypeDumper.cpp
@@ -22,45 +22,166 @@
#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
+#include "llvm/DebugInfo/PDB/UDTLayout.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/FormatVariadic.h"
using namespace llvm;
using namespace llvm::pdb;
+using LayoutPtr = std::unique_ptr<ClassLayout>;
+
+typedef bool (*CompareFunc)(const LayoutPtr &S1, const LayoutPtr &S2);
+
+static bool CompareNames(const LayoutPtr &S1, const LayoutPtr &S2) {
+ return S1->getUDTName() < S2->getUDTName();
+}
+
+static bool CompareSizes(const LayoutPtr &S1, const LayoutPtr &S2) {
+ return S1->getClassSize() < S2->getClassSize();
+}
+
+static bool ComparePadding(const LayoutPtr &S1, const LayoutPtr &S2) {
+ return S1->deepPaddingSize() < S2->deepPaddingSize();
+}
+
+static CompareFunc getComparisonFunc(opts::pretty::ClassSortMode Mode) {
+ switch (Mode) {
+ case opts::pretty::ClassSortMode::Name:
+ return CompareNames;
+ case opts::pretty::ClassSortMode::Size:
+ return CompareSizes;
+ case opts::pretty::ClassSortMode::Padding:
+ return ComparePadding;
+ default:
+ return nullptr;
+ }
+}
+
+template <typename Enumerator>
+static std::vector<std::unique_ptr<ClassLayout>>
+filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E,
+ uint32_t UnfilteredCount) {
+ std::vector<std::unique_ptr<ClassLayout>> Filtered;
+
+ Filtered.reserve(UnfilteredCount);
+ CompareFunc Comp = getComparisonFunc(opts::pretty::ClassOrder);
+
+ uint32_t Examined = 0;
+ uint32_t Discarded = 0;
+ while (auto Class = E.getNext()) {
+ ++Examined;
+ if (Examined % 10000 == 0) {
+ outs() << formatv("Examined {0}/{1} items. {2} items discarded\n",
+ Examined, UnfilteredCount, Discarded);
+ outs().flush();
+ }
+
+ if (Class->getUnmodifiedTypeId() != 0) {
+ ++Discarded;
+ continue;
+ }
+
+ if (Printer.IsTypeExcluded(Class->getName(), Class->getLength())) {
+ ++Discarded;
+ continue;
+ }
+
+ auto Layout = llvm::make_unique<ClassLayout>(std::move(Class));
+ if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) {
+ ++Discarded;
+ continue;
+ }
+
+ Filtered.push_back(std::move(Layout));
+ }
+
+ if (Comp)
+ std::sort(Filtered.begin(), Filtered.end(), Comp);
+ return Filtered;
+}
+
TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {}
void TypeDumper::start(const PDBSymbolExe &Exe) {
- auto Enums = Exe.findAllChildren<PDBSymbolTypeEnum>();
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Identifier).get() << "Enums";
- Printer << ": (" << Enums->getChildCount() << " items)";
- Printer.Indent();
- while (auto Enum = Enums->getNext())
- Enum->dump(*this);
- Printer.Unindent();
-
- auto Typedefs = Exe.findAllChildren<PDBSymbolTypeTypedef>();
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Identifier).get() << "Typedefs";
- Printer << ": (" << Typedefs->getChildCount() << " items)";
- Printer.Indent();
- while (auto Typedef = Typedefs->getNext())
- Typedef->dump(*this);
- Printer.Unindent();
-
- auto Classes = Exe.findAllChildren<PDBSymbolTypeUDT>();
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Identifier).get() << "Classes";
- Printer << ": (" << Classes->getChildCount() << " items)";
- Printer.Indent();
- while (auto Class = Classes->getNext())
- Class->dump(*this);
- Printer.Unindent();
+ if (opts::pretty::Enums) {
+ auto Enums = Exe.findAllChildren<PDBSymbolTypeEnum>();
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << "Enums";
+ Printer << ": (" << Enums->getChildCount() << " items)";
+ Printer.Indent();
+ while (auto Enum = Enums->getNext())
+ Enum->dump(*this);
+ Printer.Unindent();
+ }
+
+ if (opts::pretty::Typedefs) {
+ auto Typedefs = Exe.findAllChildren<PDBSymbolTypeTypedef>();
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << "Typedefs";
+ Printer << ": (" << Typedefs->getChildCount() << " items)";
+ Printer.Indent();
+ while (auto Typedef = Typedefs->getNext())
+ Typedef->dump(*this);
+ Printer.Unindent();
+ }
+
+ if (opts::pretty::Classes) {
+ auto Classes = Exe.findAllChildren<PDBSymbolTypeUDT>();
+ uint32_t All = Classes->getChildCount();
+
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << "Classes";
+
+ bool Precompute = false;
+ Precompute =
+ (opts::pretty::ClassOrder != opts::pretty::ClassSortMode::None);
+
+ // If we're using no sort mode, then we can start getting immediate output
+ // from the tool by just filtering as we go, rather than processing
+ // everything up front so that we can sort it. This makes the tool more
+ // responsive. So only precompute the filtered/sorted set of classes if
+ // necessary due to the specified options.
+ std::vector<LayoutPtr> Filtered;
+ uint32_t Shown = All;
+ if (Precompute) {
+ Filtered = filterAndSortClassDefs(Printer, *Classes, All);
+
+ Shown = Filtered.size();
+ }
+
+ Printer << ": (Showing " << Shown << " items";
+ if (Shown < All)
+ Printer << ", " << (All - Shown) << " filtered";
+ Printer << ")";
+ Printer.Indent();
+
+ // If we pre-computed, iterate the filtered/sorted list, otherwise iterate
+ // the DIA enumerator and filter on the fly.
+ if (Precompute) {
+ for (auto &Class : Filtered)
+ dumpClassLayout(*Class);
+ } else {
+ while (auto Class = Classes->getNext()) {
+ if (Printer.IsTypeExcluded(Class->getName(), Class->getLength()))
+ continue;
+
+ auto Layout = llvm::make_unique<ClassLayout>(std::move(Class));
+ if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold)
+ continue;
+
+ dumpClassLayout(*Layout);
+ }
+ }
+
+ Printer.Unindent();
+ }
}
void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) {
- if (Symbol.getUnmodifiedTypeId() != 0)
- return;
- if (Printer.IsTypeExcluded(Symbol.getName()))
+ assert(opts::pretty::Enums);
+
+ if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength()))
return;
// Dump member enums when dumping their class definition.
if (nullptr != Symbol.getClassParent())
@@ -72,7 +193,9 @@ void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) {
}
void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
- if (Printer.IsTypeExcluded(Symbol.getName()))
+ assert(opts::pretty::Typedefs);
+
+ if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength()))
return;
Printer.NewLine();
@@ -80,19 +203,15 @@ void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
Dumper.start(Symbol);
}
-void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) {
- if (Symbol.getUnmodifiedTypeId() != 0)
- return;
- if (Printer.IsTypeExcluded(Symbol.getName()))
- return;
-
- Printer.NewLine();
+void TypeDumper::dumpClassLayout(const ClassLayout &Class) {
+ assert(opts::pretty::Classes);
- if (opts::pretty::NoClassDefs) {
+ if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) {
+ Printer.NewLine();
WithColor(Printer, PDB_ColorItem::Keyword).get() << "class ";
- WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << Class.getUDTName();
} else {
ClassDefinitionDumper Dumper(Printer);
- Dumper.start(Symbol);
+ Dumper.start(Class);
}
}
diff --git a/tools/llvm-pdbdump/PrettyTypeDumper.h b/tools/llvm-pdbdump/PrettyTypeDumper.h
index f9d8304c3208e..68a2f0246ebae 100644
--- a/tools/llvm-pdbdump/PrettyTypeDumper.h
+++ b/tools/llvm-pdbdump/PrettyTypeDumper.h
@@ -15,6 +15,7 @@
namespace llvm {
namespace pdb {
class LinePrinter;
+class ClassLayout;
class TypeDumper : public PDBSymDumper {
public:
@@ -24,7 +25,8 @@ public:
void dump(const PDBSymbolTypeEnum &Symbol) override;
void dump(const PDBSymbolTypeTypedef &Symbol) override;
- void dump(const PDBSymbolTypeUDT &Symbol) override;
+
+ void dumpClassLayout(const ClassLayout &Class);
private:
LinePrinter &Printer;
diff --git a/tools/llvm-pdbdump/PrettyTypedefDumper.cpp b/tools/llvm-pdbdump/PrettyTypedefDumper.cpp
index c458755cb7806..2d8e915d76043 100644
--- a/tools/llvm-pdbdump/PrettyTypedefDumper.cpp
+++ b/tools/llvm-pdbdump/PrettyTypedefDumper.cpp
@@ -53,11 +53,8 @@ void TypedefDumper::dump(const PDBSymbolTypePointer &Symbol) {
WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
if (Symbol.isVolatileType())
WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
- uint32_t PointeeId = Symbol.getTypeId();
- auto PointeeType = Symbol.getSession().getSymbolById(PointeeId);
- if (!PointeeType)
- return;
- if (auto FuncSig = dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType.get())) {
+ auto PointeeType = Symbol.getPointeeType();
+ if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) {
FunctionDumper::PointerType Pointer = FunctionDumper::PointerType::Pointer;
if (Symbol.isReference())
Pointer = FunctionDumper::PointerType::Reference;
diff --git a/tools/llvm-pdbdump/PrettyVariableDumper.cpp b/tools/llvm-pdbdump/PrettyVariableDumper.cpp
index e1469186ad8b5..76a0d23bf87a4 100644
--- a/tools/llvm-pdbdump/PrettyVariableDumper.cpp
+++ b/tools/llvm-pdbdump/PrettyVariableDumper.cpp
@@ -14,6 +14,7 @@
#include "PrettyFunctionDumper.h"
#include "llvm-pdbdump.h"
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h"
@@ -23,16 +24,18 @@
#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
+#include "llvm/DebugInfo/PDB/PDBTypes.h"
#include "llvm/Support/Format.h"
using namespace llvm;
+using namespace llvm::codeview;
using namespace llvm::pdb;
VariableDumper::VariableDumper(LinePrinter &P)
: PDBSymDumper(true), Printer(P) {}
-void VariableDumper::start(const PDBSymbolData &Var) {
+void VariableDumper::start(const PDBSymbolData &Var, uint32_t Offset) {
if (Var.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
return;
if (Printer.IsSymbolExcluded(Var.getName()))
@@ -40,13 +43,15 @@ void VariableDumper::start(const PDBSymbolData &Var) {
auto VarType = Var.getType();
+ uint64_t Length = VarType->getRawSymbol().getLength();
+
switch (auto LocType = Var.getLocationType()) {
case PDB_LocType::Static:
Printer.NewLine();
Printer << "data [";
WithColor(Printer, PDB_ColorItem::Address).get()
<< format_hex(Var.getVirtualAddress(), 10);
- Printer << "] ";
+ Printer << ", sizeof=" << Length << "] ";
WithColor(Printer, PDB_ColorItem::Keyword).get() << "static ";
dumpSymbolTypeAndName(*VarType, Var.getName());
break;
@@ -54,8 +59,7 @@ void VariableDumper::start(const PDBSymbolData &Var) {
if (isa<PDBSymbolTypeEnum>(*VarType))
break;
Printer.NewLine();
- Printer << "data ";
- WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
+ Printer << "data [sizeof=" << Length << "] ";
dumpSymbolTypeAndName(*VarType, Var.getName());
Printer << " = ";
WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getValue();
@@ -64,27 +68,57 @@ void VariableDumper::start(const PDBSymbolData &Var) {
Printer.NewLine();
Printer << "data ";
WithColor(Printer, PDB_ColorItem::Offset).get()
- << "+" << format_hex(Var.getOffset(), 4) << " ";
+ << "+" << format_hex(Offset + Var.getOffset(), 4)
+ << " [sizeof=" << Length << "] ";
dumpSymbolTypeAndName(*VarType, Var.getName());
break;
case PDB_LocType::BitField:
Printer.NewLine();
Printer << "data ";
WithColor(Printer, PDB_ColorItem::Offset).get()
- << "+" << format_hex(Var.getOffset(), 4) << " ";
+ << "+" << format_hex(Offset + Var.getOffset(), 4)
+ << " [sizeof=" << Length << "] ";
dumpSymbolTypeAndName(*VarType, Var.getName());
Printer << " : ";
WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getLength();
break;
default:
Printer.NewLine();
- Printer << "data ";
+ Printer << "data [sizeof=" << Length << "] ";
Printer << "unknown(" << LocType << ") ";
WithColor(Printer, PDB_ColorItem::Identifier).get() << Var.getName();
break;
}
}
+void VariableDumper::start(const PDBSymbolTypeVTable &Var, uint32_t Offset) {
+ Printer.NewLine();
+ Printer << "vfptr ";
+ auto VTableType = cast<PDBSymbolTypePointer>(Var.getType());
+ uint32_t PointerSize = VTableType->getLength();
+
+ WithColor(Printer, PDB_ColorItem::Offset).get()
+ << "+" << format_hex(Offset + Var.getOffset(), 4)
+ << " [sizeof=" << PointerSize << "] ";
+}
+
+void VariableDumper::dump(const PDBSymbolTypeArray &Symbol) {
+ auto ElementType = Symbol.getElementType();
+ assert(ElementType);
+ if (!ElementType)
+ return;
+ ElementType->dump(*this);
+}
+
+void VariableDumper::dumpRight(const PDBSymbolTypeArray &Symbol) {
+ auto ElementType = Symbol.getElementType();
+ assert(ElementType);
+ if (!ElementType)
+ return;
+ Printer << '[' << Symbol.getCount() << ']';
+ ElementType->dumpRight(*this);
+}
+
void VariableDumper::dump(const PDBSymbolTypeBuiltin &Symbol) {
BuiltinDumper Dumper(Printer);
Dumper.start(Symbol);
@@ -94,27 +128,71 @@ void VariableDumper::dump(const PDBSymbolTypeEnum &Symbol) {
WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName();
}
-void VariableDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) {}
+void VariableDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) {
+ auto ReturnType = Symbol.getReturnType();
+ ReturnType->dump(*this);
+ Printer << " ";
+
+ uint32_t ClassParentId = Symbol.getClassParentId();
+ auto ClassParent =
+ Symbol.getSession().getConcreteSymbolById<PDBSymbolTypeUDT>(
+ ClassParentId);
+
+ if (ClassParent) {
+ WithColor(Printer, PDB_ColorItem::Identifier).get()
+ << ClassParent->getName();
+ Printer << "::";
+ }
+}
+
+void VariableDumper::dumpRight(const PDBSymbolTypeFunctionSig &Symbol) {
+ Printer << "(";
+ if (auto Arguments = Symbol.getArguments()) {
+ uint32_t Index = 0;
+ while (auto Arg = Arguments->getNext()) {
+ Arg->dump(*this);
+ if (++Index < Arguments->getChildCount())
+ Printer << ", ";
+ }
+ }
+ Printer << ")";
+
+ if (Symbol.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " const";
+ if (Symbol.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile";
+}
void VariableDumper::dump(const PDBSymbolTypePointer &Symbol) {
auto PointeeType = Symbol.getPointeeType();
if (!PointeeType)
return;
+ PointeeType->dump(*this);
+ if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) {
+ // A hack to get the calling convention in the right spot.
+ Printer << " (";
+ PDB_CallingConv CC = FuncSig->getCallingConvention();
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " ";
+ } else if (isa<PDBSymbolTypeArray>(PointeeType)) {
+ Printer << " (";
+ }
+ Printer << (Symbol.isReference() ? "&" : "*");
+ if (Symbol.isConstType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " const ";
+ if (Symbol.isVolatileType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile ";
+}
- if (auto Func = dyn_cast<PDBSymbolFunc>(PointeeType.get())) {
- FunctionDumper NestedDumper(Printer);
- FunctionDumper::PointerType Pointer =
- Symbol.isReference() ? FunctionDumper::PointerType::Reference
- : FunctionDumper::PointerType::Pointer;
- NestedDumper.start(*Func, Pointer);
- } else {
- if (Symbol.isConstType())
- WithColor(Printer, PDB_ColorItem::Keyword).get() << "const ";
- if (Symbol.isVolatileType())
- WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
- PointeeType->dump(*this);
- Printer << (Symbol.isReference() ? "&" : "*");
+void VariableDumper::dumpRight(const PDBSymbolTypePointer &Symbol) {
+ auto PointeeType = Symbol.getPointeeType();
+ assert(PointeeType);
+ if (!PointeeType)
+ return;
+ if (isa<PDBSymbolTypeFunctionSig>(PointeeType) ||
+ isa<PDBSymbolTypeArray>(PointeeType)) {
+ Printer << ")";
}
+ PointeeType->dumpRight(*this);
}
void VariableDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
@@ -128,44 +206,7 @@ void VariableDumper::dump(const PDBSymbolTypeUDT &Symbol) {
void VariableDumper::dumpSymbolTypeAndName(const PDBSymbol &Type,
StringRef Name) {
- if (auto *ArrayType = dyn_cast<PDBSymbolTypeArray>(&Type)) {
- std::string IndexSpec;
- raw_string_ostream IndexStream(IndexSpec);
- std::unique_ptr<PDBSymbol> ElementType = ArrayType->getElementType();
- while (auto NestedArray = dyn_cast<PDBSymbolTypeArray>(ElementType.get())) {
- IndexStream << "[";
- IndexStream << NestedArray->getCount();
- IndexStream << "]";
- ElementType = NestedArray->getElementType();
- }
- IndexStream << "[" << ArrayType->getCount() << "]";
- ElementType->dump(*this);
- WithColor(Printer, PDB_ColorItem::Identifier).get() << " " << Name;
- Printer << IndexStream.str();
- } else {
- if (!tryDumpFunctionPointer(Type, Name)) {
- Type.dump(*this);
- WithColor(Printer, PDB_ColorItem::Identifier).get() << " " << Name;
- }
- }
-}
-
-bool VariableDumper::tryDumpFunctionPointer(const PDBSymbol &Type,
- StringRef Name) {
- // Function pointers come across as pointers to function signatures. But the
- // signature carries no name, so we have to handle this case separately.
- if (auto *PointerType = dyn_cast<PDBSymbolTypePointer>(&Type)) {
- auto PointeeType = PointerType->getPointeeType();
- if (auto *FunctionSig =
- dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType.get())) {
- FunctionDumper Dumper(Printer);
- FunctionDumper::PointerType PT = FunctionDumper::PointerType::Pointer;
- if (PointerType->isReference())
- PT = FunctionDumper::PointerType::Reference;
- std::string NameStr(Name.begin(), Name.end());
- Dumper.start(*FunctionSig, NameStr.c_str(), PT);
- return true;
- }
- }
- return false;
+ Type.dump(*this);
+ WithColor(Printer, PDB_ColorItem::Identifier).get() << " " << Name;
+ Type.dumpRight(*this);
}
diff --git a/tools/llvm-pdbdump/PrettyVariableDumper.h b/tools/llvm-pdbdump/PrettyVariableDumper.h
index a122bb86058cf..4ba3bc97d85b6 100644
--- a/tools/llvm-pdbdump/PrettyVariableDumper.h
+++ b/tools/llvm-pdbdump/PrettyVariableDumper.h
@@ -24,8 +24,10 @@ class VariableDumper : public PDBSymDumper {
public:
VariableDumper(LinePrinter &P);
- void start(const PDBSymbolData &Var);
+ void start(const PDBSymbolData &Var, uint32_t Offset = 0);
+ void start(const PDBSymbolTypeVTable &Var, uint32_t Offset = 0);
+ void dump(const PDBSymbolTypeArray &Symbol) override;
void dump(const PDBSymbolTypeBuiltin &Symbol) override;
void dump(const PDBSymbolTypeEnum &Symbol) override;
void dump(const PDBSymbolTypeFunctionSig &Symbol) override;
@@ -33,9 +35,12 @@ public:
void dump(const PDBSymbolTypeTypedef &Symbol) override;
void dump(const PDBSymbolTypeUDT &Symbol) override;
+ void dumpRight(const PDBSymbolTypeArray &Symbol) override;
+ void dumpRight(const PDBSymbolTypeFunctionSig &Symbol) override;
+ void dumpRight(const PDBSymbolTypePointer &Symbol) override;
+
private:
void dumpSymbolTypeAndName(const PDBSymbol &Type, StringRef Name);
- bool tryDumpFunctionPointer(const PDBSymbol &Type, StringRef Name);
LinePrinter &Printer;
};
diff --git a/tools/llvm-pdbdump/StreamUtil.cpp b/tools/llvm-pdbdump/StreamUtil.cpp
new file mode 100644
index 0000000000000..db1e01aa01543
--- /dev/null
+++ b/tools/llvm-pdbdump/StreamUtil.cpp
@@ -0,0 +1,136 @@
+//===- StreamUtil.cpp - PDB stream utilities --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StreamUtil.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/ModInfo.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+
+namespace llvm {
+namespace pdb {
+void discoverStreamPurposes(PDBFile &File,
+ SmallVectorImpl<std::string> &Purposes) {
+
+ // It's OK if we fail to load some of these streams, we still attempt to print
+ // what we can.
+ auto Dbi = File.getPDBDbiStream();
+ auto Tpi = File.getPDBTpiStream();
+ auto Ipi = File.getPDBIpiStream();
+ auto Info = File.getPDBInfoStream();
+
+ uint32_t StreamCount = File.getNumStreams();
+ DenseMap<uint16_t, const ModuleInfoEx *> ModStreams;
+ DenseMap<uint16_t, std::string> NamedStreams;
+
+ if (Dbi) {
+ for (auto &ModI : Dbi->modules()) {
+ uint16_t SN = ModI.Info.getModuleStreamIndex();
+ if (SN != kInvalidStreamIndex)
+ ModStreams[SN] = &ModI;
+ }
+ }
+ if (Info) {
+ for (auto &NSE : Info->named_streams()) {
+ if (NSE.second != kInvalidStreamIndex)
+ NamedStreams[NSE.second] = NSE.first();
+ }
+ }
+
+ Purposes.resize(StreamCount);
+ for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
+ std::string Value;
+ if (StreamIdx == OldMSFDirectory)
+ Value = "Old MSF Directory";
+ else if (StreamIdx == StreamPDB)
+ Value = "PDB Stream";
+ else if (StreamIdx == StreamDBI)
+ Value = "DBI Stream";
+ else if (StreamIdx == StreamTPI)
+ Value = "TPI Stream";
+ else if (StreamIdx == StreamIPI)
+ Value = "IPI Stream";
+ else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex())
+ Value = "Global Symbol Hash";
+ else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex())
+ Value = "Public Symbol Hash";
+ else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex())
+ Value = "Public Symbol Records";
+ else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex())
+ Value = "TPI Hash";
+ else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex())
+ Value = "TPI Aux Hash";
+ else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex())
+ Value = "IPI Hash";
+ else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex())
+ Value = "IPI Aux Hash";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception))
+ Value = "Exception Data";
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup))
+ Value = "Fixup Data";
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO))
+ Value = "FPO Data";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO))
+ Value = "New FPO Data";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc))
+ Value = "Omap From Source Data";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc))
+ Value = "Omap To Source Data";
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata))
+ Value = "Pdata";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr))
+ Value = "Section Header Data";
+ else if (Dbi &&
+ StreamIdx ==
+ Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig))
+ Value = "Section Header Original Data";
+ else if (Dbi &&
+ StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap))
+ Value = "Token Rid Data";
+ else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata))
+ Value = "Xdata";
+ else {
+ auto ModIter = ModStreams.find(StreamIdx);
+ auto NSIter = NamedStreams.find(StreamIdx);
+ if (ModIter != ModStreams.end()) {
+ Value = "Module \"";
+ Value += ModIter->second->Info.getModuleName().str();
+ Value += "\"";
+ } else if (NSIter != NamedStreams.end()) {
+ Value = "Named Stream \"";
+ Value += NSIter->second;
+ Value += "\"";
+ } else {
+ Value = "???";
+ }
+ }
+ Purposes[StreamIdx] = Value;
+ }
+
+ // Consume errors from missing streams.
+ if (!Dbi)
+ consumeError(Dbi.takeError());
+ if (!Tpi)
+ consumeError(Tpi.takeError());
+ if (!Ipi)
+ consumeError(Ipi.takeError());
+ if (!Info)
+ consumeError(Info.takeError());
+}
+}
+}
diff --git a/tools/llvm-pdbdump/StreamUtil.h b/tools/llvm-pdbdump/StreamUtil.h
new file mode 100644
index 0000000000000..b5c0beba44fed
--- /dev/null
+++ b/tools/llvm-pdbdump/StreamUtil.h
@@ -0,0 +1,25 @@
+//===- Streamutil.h - PDB stream utilities ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_STREAMUTIL_H
+#define LLVM_TOOLS_LLVMPDBDUMP_STREAMUTIL_H
+
+#include "llvm/ADT/SmallVector.h"
+
+#include <string>
+
+namespace llvm {
+namespace pdb {
+class PDBFile;
+void discoverStreamPurposes(PDBFile &File,
+ SmallVectorImpl<std::string> &Purposes);
+}
+}
+
+#endif
diff --git a/tools/llvm-pdbdump/YAMLOutputStyle.cpp b/tools/llvm-pdbdump/YAMLOutputStyle.cpp
index 3f2733d701a85..5b53d2137166a 100644
--- a/tools/llvm-pdbdump/YAMLOutputStyle.cpp
+++ b/tools/llvm-pdbdump/YAMLOutputStyle.cpp
@@ -13,18 +13,20 @@
#include "llvm-pdbdump.h"
#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
-#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
-#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
-#include "llvm/DebugInfo/PDB/Raw/ModStream.h"
-#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
-#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
-#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/ModStream.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
using namespace llvm;
using namespace llvm::pdb;
YAMLOutputStyle::YAMLOutputStyle(PDBFile &File)
- : File(File), Out(outs()), Obj(File.getAllocator()) {}
+ : File(File), Out(outs()), Obj(File.getAllocator()) {
+ Out.setWriteDefaultValues(!opts::pdb2yaml::Minimal);
+}
Error YAMLOutputStyle::dump() {
if (opts::pdb2yaml::StreamDirectory)
@@ -45,6 +47,9 @@ Error YAMLOutputStyle::dump() {
if (auto EC = dumpStreamDirectory())
return EC;
+ if (auto EC = dumpStringTable())
+ return EC;
+
if (auto EC = dumpPDBStream())
return EC;
@@ -83,6 +88,24 @@ Error YAMLOutputStyle::dumpFileHeaders() {
return Error::success();
}
+Error YAMLOutputStyle::dumpStringTable() {
+ if (!opts::pdb2yaml::StringTable)
+ return Error::success();
+
+ Obj.StringTable.emplace();
+ auto ExpectedST = File.getStringTable();
+ if (!ExpectedST)
+ return ExpectedST.takeError();
+
+ const auto &ST = ExpectedST.get();
+ for (auto ID : ST.name_ids()) {
+ StringRef S = ST.getStringForID(ID);
+ if (!S.empty())
+ Obj.StringTable->push_back(S);
+ }
+ return Error::success();
+}
+
Error YAMLOutputStyle::dumpStreamMetadata() {
if (!opts::pdb2yaml::StreamMetadata)
return Error::success();
@@ -122,12 +145,7 @@ Error YAMLOutputStyle::dumpPDBStream() {
Obj.PdbStream->Guid = InfoS.getGuid();
Obj.PdbStream->Signature = InfoS.getSignature();
Obj.PdbStream->Version = InfoS.getVersion();
- for (auto &NS : InfoS.named_streams()) {
- yaml::NamedStreamMapping Mapping;
- Mapping.StreamName = NS.getKey();
- Mapping.StreamNumber = NS.getValue();
- Obj.PdbStream->NamedStreams.push_back(Mapping);
- }
+ Obj.PdbStream->Features = InfoS.getFeatureSignatures();
return Error::success();
}
diff --git a/tools/llvm-pdbdump/YAMLOutputStyle.h b/tools/llvm-pdbdump/YAMLOutputStyle.h
index 3cd603a95b6a3..db9868db4a7e1 100644
--- a/tools/llvm-pdbdump/YAMLOutputStyle.h
+++ b/tools/llvm-pdbdump/YAMLOutputStyle.h
@@ -26,6 +26,7 @@ public:
Error dump() override;
private:
+ Error dumpStringTable();
Error dumpFileHeaders();
Error dumpStreamMetadata();
Error dumpStreamDirectory();
diff --git a/tools/llvm-pdbdump/YamlSymbolDumper.cpp b/tools/llvm-pdbdump/YamlSymbolDumper.cpp
index 210260a03b5fc..431bf404fb040 100644
--- a/tools/llvm-pdbdump/YamlSymbolDumper.cpp
+++ b/tools/llvm-pdbdump/YamlSymbolDumper.cpp
@@ -113,6 +113,7 @@ template <> struct ScalarEnumerationTraits<RegisterId> {
for (const auto &E : RegNames) {
io.enumCase(Reg, E.Name.str().c_str(), static_cast<RegisterId>(E.Value));
}
+ io.enumFallback<Hex16>(Reg);
}
};
diff --git a/tools/llvm-pdbdump/YamlTypeDumper.cpp b/tools/llvm-pdbdump/YamlTypeDumper.cpp
index 5c527c71c7e44..b4eb197e866a4 100644
--- a/tools/llvm-pdbdump/YamlTypeDumper.cpp
+++ b/tools/llvm-pdbdump/YamlTypeDumper.cpp
@@ -17,7 +17,7 @@
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/CodeView/TypeSerializer.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h"
-#include "llvm/DebugInfo/PDB/Raw/TpiHashing.h"
+#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
using namespace llvm;
using namespace llvm::codeview;
@@ -194,6 +194,13 @@ template <> struct ScalarEnumerationTraits<WindowsRTClassKind> {
}
};
+template <> struct ScalarEnumerationTraits<LabelType> {
+ static void enumeration(IO &IO, LabelType &Value) {
+ IO.enumCase(Value, "Near", LabelType::Near);
+ IO.enumCase(Value, "Far", LabelType::Far);
+ }
+};
+
template <> struct ScalarBitSetTraits<PointerOptions> {
static void bitset(IO &IO, PointerOptions &Options) {
IO.bitSetCase(Options, "None", PointerOptions::None);
@@ -291,7 +298,11 @@ void MappingTraits<StringIdRecord>::mapping(IO &IO, StringIdRecord &String) {
}
void MappingTraits<ArgListRecord>::mapping(IO &IO, ArgListRecord &Args) {
- IO.mapRequired("ArgIndices", Args.StringIndices);
+ IO.mapRequired("ArgIndices", Args.ArgIndices);
+}
+
+void MappingTraits<StringListRecord>::mapping(IO &IO, StringListRecord &Strings) {
+ IO.mapRequired("StringIndices", Strings.StringIndices);
}
void MappingTraits<ClassRecord>::mapping(IO &IO, ClassRecord &Class) {
@@ -427,6 +438,10 @@ void MappingTraits<BuildInfoRecord>::mapping(IO &IO, BuildInfoRecord &Args) {
IO.mapRequired("ArgIndices", Args.ArgIndices);
}
+void MappingTraits<LabelRecord>::mapping(IO &IO, LabelRecord &R) {
+ IO.mapRequired("Mode", R.Mode);
+}
+
void MappingTraits<NestedTypeRecord>::mapping(IO &IO,
NestedTypeRecord &Nested) {
IO.mapRequired("Type", Nested.Type);
@@ -573,8 +588,8 @@ struct MappingContextTraits<pdb::yaml::PdbTpiFieldListRecord,
assert(IO.outputting());
codeview::TypeVisitorCallbackPipeline Pipeline;
- msf::ByteStream Data(Obj.Record.Data);
- msf::StreamReader FieldReader(Data);
+ BinaryByteStream Data(Obj.Record.Data, llvm::support::little);
+ BinaryStreamReader FieldReader(Data);
codeview::FieldListDeserializer Deserializer(FieldReader);
// For PDB to Yaml, deserialize into a high level record type, then dump
diff --git a/tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp b/tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp
index e818dda32fc02..38eaf16c65b05 100644
--- a/tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp
+++ b/tools/llvm-pdbdump/fuzzer/llvm-pdbdump-fuzzer.cpp
@@ -13,7 +13,7 @@
///
//===----------------------------------------------------------------------===//
#include "llvm/ADT/STLExtras.h"
-#include "llvm/DebugInfo/CodeView/ByteStream.h"
+#include "llvm/DebugInfo/CodeView/BinaryByteStream.h"
#include "llvm/DebugInfo/CodeView/SymbolDumper.h"
#include "llvm/DebugInfo/CodeView/TypeDumper.h"
#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
@@ -28,14 +28,15 @@
using namespace llvm;
namespace {
-// We need a class which behaves like an immutable ByteStream, but whose data
+// We need a class which behaves like an immutable BinaryByteStream, but whose
+// data
// is backed by an llvm::MemoryBuffer. It also needs to own the underlying
// MemoryBuffer, so this simple adapter is a good way to achieve that.
-class InputByteStream : public codeview::ByteStream<false> {
+class InputByteStream : public codeview::BinaryByteStream<false> {
public:
explicit InputByteStream(std::unique_ptr<MemoryBuffer> Buffer)
- : ByteStream(ArrayRef<uint8_t>(Buffer->getBuffer().bytes_begin(),
- Buffer->getBuffer().bytes_end())),
+ : BinaryByteStream(ArrayRef<uint8_t>(Buffer->getBuffer().bytes_begin(),
+ Buffer->getBuffer().bytes_end())),
MemBuffer(std::move(Buffer)) {}
std::unique_ptr<MemoryBuffer> MemBuffer;
diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp
index d3495e524abcf..06c2afc0bc78a 100644
--- a/tools/llvm-pdbdump/llvm-pdbdump.cpp
+++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp
@@ -7,13 +7,14 @@
//
//===----------------------------------------------------------------------===//
//
-// Dumps debug information present in PDB files. This utility makes use of
-// the Microsoft Windows SDK, so will not compile or run on non-Windows
-// platforms.
+// Dumps debug information present in PDB files.
//
//===----------------------------------------------------------------------===//
#include "llvm-pdbdump.h"
+
+#include "Analyze.h"
+#include "Diff.h"
#include "LLVMOutputStyle.h"
#include "LinePrinter.h"
#include "OutputStyle.h"
@@ -29,29 +30,31 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Config/config.h"
-#include "llvm/DebugInfo/MSF/ByteStream.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/PDB/GenericError.h"
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h"
#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/ModInfoBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
+#include "llvm/DebugInfo/PDB/Native/RawError.h"
+#include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
#include "llvm/DebugInfo/PDB/PDB.h"
#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h"
#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h"
-#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
-#include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h"
-#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
-#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h"
-#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
-#include "llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h"
-#include "llvm/DebugInfo/PDB/Raw/RawConstants.h"
-#include "llvm/DebugInfo/PDB/Raw/RawError.h"
-#include "llvm/DebugInfo/PDB/Raw/RawSession.h"
-#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
-#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h"
+#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/COM.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ConvertUTF.h"
@@ -78,6 +81,9 @@ cl::SubCommand RawSubcommand("raw", "Dump raw structure of the PDB file");
cl::SubCommand
PrettySubcommand("pretty",
"Dump semantic information about types and symbols");
+
+cl::SubCommand DiffSubcommand("diff", "Diff the contents of 2 PDB files");
+
cl::SubCommand
YamlToPdbSubcommand("yaml2pdb",
"Generate a PDB file from a YAML description");
@@ -85,8 +91,12 @@ cl::SubCommand
PdbToYamlSubcommand("pdb2yaml",
"Generate a detailed YAML description of a PDB File");
+cl::SubCommand
+ AnalyzeSubcommand("analyze",
+ "Analyze various aspects of a PDB's structure");
+
cl::OptionCategory TypeCategory("Symbol Type Options");
-cl::OptionCategory FilterCategory("Filtering Options");
+cl::OptionCategory FilterCategory("Filtering and Sorting Options");
cl::OptionCategory OtherOptions("Other Options");
namespace pretty {
@@ -102,8 +112,41 @@ cl::opt<bool> Globals("globals", cl::desc("Dump global symbols"),
cl::cat(TypeCategory), cl::sub(PrettySubcommand));
cl::opt<bool> Externals("externals", cl::desc("Dump external symbols"),
cl::cat(TypeCategory), cl::sub(PrettySubcommand));
-cl::opt<bool> Types("types", cl::desc("Display types"), cl::cat(TypeCategory),
- cl::sub(PrettySubcommand));
+cl::opt<bool>
+ Types("types",
+ cl::desc("Display all types (implies -classes, -enums, -typedefs)"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> Classes("classes", cl::desc("Display class types"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> Enums("enums", cl::desc("Display enum types"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> Typedefs("typedefs", cl::desc("Display typedef types"),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<ClassSortMode> ClassOrder(
+ "class-order", cl::desc("Class sort order"), cl::init(ClassSortMode::None),
+ cl::values(clEnumValN(ClassSortMode::None, "none",
+ "Undefined / no particular sort order"),
+ clEnumValN(ClassSortMode::Name, "name", "Sort classes by name"),
+ clEnumValN(ClassSortMode::Size, "size", "Sort classes by size"),
+ clEnumValN(ClassSortMode::Padding, "padding",
+ "Sort classes by amount of padding")),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+
+cl::opt<ClassDefinitionFormat> ClassFormat(
+ "class-definitions", cl::desc("Class definition format"),
+ cl::init(ClassDefinitionFormat::Standard),
+ cl::values(
+ clEnumValN(ClassDefinitionFormat::Standard, "all-members",
+ "Display all class members including data, constants, "
+ "typedefs, functions, etc"),
+ clEnumValN(ClassDefinitionFormat::Layout, "layout-members",
+ "Only display members that contribute to class size."),
+ clEnumValN(ClassDefinitionFormat::Graphical, "graphical",
+ "Display graphical representation of each class's layout."),
+ clEnumValN(ClassDefinitionFormat::None, "none",
+ "Don't display class definitions")),
+ cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+
cl::opt<bool> Lines("lines", cl::desc("Line tables"), cl::cat(TypeCategory),
cl::sub(PrettySubcommand));
cl::opt<bool>
@@ -114,6 +157,12 @@ cl::opt<uint64_t> LoadAddress(
"load-address",
cl::desc("Assume the module is loaded at the specified address"),
cl::cat(OtherOptions), cl::sub(PrettySubcommand));
+cl::opt<bool> Native("native", cl::desc("Use native PDB reader instead of DIA"),
+ cl::cat(OtherOptions), cl::sub(PrettySubcommand));
+cl::opt<cl::boolOrDefault>
+ ColorOutput("color-output",
+ cl::desc("Override use of color (default = isatty)"),
+ cl::cat(OtherOptions), cl::sub(PrettySubcommand));
cl::list<std::string> ExcludeTypes(
"exclude-types", cl::desc("Exclude types by regular expression"),
cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
@@ -136,6 +185,14 @@ cl::list<std::string> IncludeCompilands(
"include-compilands",
cl::desc("Include only compilands those which match a regular expression"),
cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::opt<uint32_t> SizeThreshold(
+ "min-type-size", cl::desc("Displays only those types which are greater "
+ "than or equal to the specified size."),
+ cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::opt<uint32_t> PaddingThreshold(
+ "min-class-padding", cl::desc("Displays only those classes which have at "
+ "least the specified amount of padding."),
+ cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand));
cl::opt<bool> ExcludeCompilerGenerated(
"no-compiler-generated",
@@ -145,14 +202,23 @@ cl::opt<bool>
ExcludeSystemLibraries("no-system-libs",
cl::desc("Don't show symbols from system libraries"),
cl::cat(FilterCategory), cl::sub(PrettySubcommand));
-cl::opt<bool> NoClassDefs("no-class-definitions",
- cl::desc("Don't display full class definitions"),
- cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+
cl::opt<bool> NoEnumDefs("no-enum-definitions",
cl::desc("Don't display full enum definitions"),
cl::cat(FilterCategory), cl::sub(PrettySubcommand));
}
+namespace diff {
+cl::opt<bool> Pedantic("pedantic",
+ cl::desc("Finds all differences (even structural ones "
+ "that produce otherwise identical PDBs)"),
+ cl::sub(DiffSubcommand));
+
+cl::list<std::string> InputFilenames(cl::Positional,
+ cl::desc("<first> <second>"),
+ cl::OneOrMore, cl::sub(DiffSubcommand));
+}
+
namespace raw {
cl::OptionCategory MsfOptions("MSF Container Options");
@@ -187,6 +253,11 @@ cl::list<uint32_t>
// TYPE OPTIONS
cl::opt<bool>
+ CompactRecords("compact-records",
+ cl::desc("Dump type and symbol records with less detail"),
+ cl::cat(TypeOptions), cl::sub(RawSubcommand));
+
+cl::opt<bool>
DumpTpiRecords("tpi-records",
cl::desc("dump CodeView type records from TPI stream"),
cl::cat(TypeOptions), cl::sub(RawSubcommand));
@@ -227,6 +298,9 @@ cl::opt<bool>
cl::cat(SymbolOptions), cl::sub(RawSubcommand));
// MISCELLANEOUS OPTIONS
+cl::opt<bool> DumpStringTable("string-table", cl::desc("dump PDB String Table"),
+ cl::cat(MiscOptions), cl::sub(RawSubcommand));
+
cl::opt<bool> DumpSectionContribs("section-contribs",
cl::desc("dump section contributions"),
cl::cat(MiscOptions), cl::sub(RawSubcommand));
@@ -262,6 +336,9 @@ cl::opt<bool>
cl::desc("Do not dump MSF file headers (you will not be able "
"to generate a fresh PDB from the resulting YAML)"),
cl::sub(PdbToYamlSubcommand), cl::init(false));
+cl::opt<bool> Minimal("minimal",
+ cl::desc("Don't write fields with default values"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
cl::opt<bool> StreamMetadata(
"stream-metadata",
@@ -274,6 +351,10 @@ cl::opt<bool> StreamDirectory(
cl::opt<bool> PdbStream("pdb-stream",
cl::desc("Dump the PDB Stream (Stream 1)"),
cl::sub(PdbToYamlSubcommand), cl::init(false));
+
+cl::opt<bool> StringTable("string-table", cl::desc("Dump the PDB String Table"),
+ cl::sub(PdbToYamlSubcommand), cl::init(false));
+
cl::opt<bool> DbiStream("dbi-stream",
cl::desc("Dump the DBI Stream (Stream 2)"),
cl::sub(PdbToYamlSubcommand), cl::init(false));
@@ -305,6 +386,14 @@ cl::list<std::string> InputFilename(cl::Positional,
cl::desc("<input PDB file>"), cl::Required,
cl::sub(PdbToYamlSubcommand));
}
+
+namespace analyze {
+cl::opt<bool> StringTable("hash-collisions", cl::desc("Find hash collisions"),
+ cl::sub(AnalyzeSubcommand), cl::init(false));
+cl::list<std::string> InputFilename(cl::Positional,
+ cl::desc("<input PDB file>"), cl::Required,
+ cl::sub(AnalyzeSubcommand));
+}
}
static ExitOnError ExitOnErr;
@@ -324,13 +413,13 @@ static void yamlToPdb(StringRef Path) {
llvm::yaml::Input In(Buffer->getBuffer());
pdb::yaml::PdbObject YamlObj(Allocator);
In >> YamlObj;
- if (!YamlObj.Headers.hasValue())
- ExitOnErr(make_error<GenericError>(generic_error_code::unspecified,
- "Yaml does not contain MSF headers"));
PDBFileBuilder Builder(Allocator);
- ExitOnErr(Builder.initialize(YamlObj.Headers->SuperBlock.BlockSize));
+ uint32_t BlockSize = 4096;
+ if (YamlObj.Headers.hasValue())
+ BlockSize = YamlObj.Headers->SuperBlock.BlockSize;
+ ExitOnErr(Builder.initialize(BlockSize));
// Add each of the reserved streams. We ignore stream metadata in the
// yaml, because we will reconstruct our own view of the streams. For
// example, the YAML may say that there were 20 streams in the original
@@ -340,56 +429,74 @@ static void yamlToPdb(StringRef Path) {
for (uint32_t I = 0; I < kSpecialStreamCount; ++I)
ExitOnErr(Builder.getMsfBuilder().addStream(0));
- if (YamlObj.PdbStream.hasValue()) {
- auto &InfoBuilder = Builder.getInfoBuilder();
- InfoBuilder.setAge(YamlObj.PdbStream->Age);
- InfoBuilder.setGuid(YamlObj.PdbStream->Guid);
- InfoBuilder.setSignature(YamlObj.PdbStream->Signature);
- InfoBuilder.setVersion(YamlObj.PdbStream->Version);
- for (auto &NM : YamlObj.PdbStream->NamedStreams)
- InfoBuilder.getNamedStreamsBuilder().addMapping(NM.StreamName,
- NM.StreamNumber);
+ if (YamlObj.StringTable.hasValue()) {
+ auto &Strings = Builder.getStringTableBuilder();
+ for (auto S : *YamlObj.StringTable)
+ Strings.insert(S);
}
- if (YamlObj.DbiStream.hasValue()) {
- auto &DbiBuilder = Builder.getDbiBuilder();
- DbiBuilder.setAge(YamlObj.DbiStream->Age);
- DbiBuilder.setBuildNumber(YamlObj.DbiStream->BuildNumber);
- DbiBuilder.setFlags(YamlObj.DbiStream->Flags);
- DbiBuilder.setMachineType(YamlObj.DbiStream->MachineType);
- DbiBuilder.setPdbDllRbld(YamlObj.DbiStream->PdbDllRbld);
- DbiBuilder.setPdbDllVersion(YamlObj.DbiStream->PdbDllVersion);
- DbiBuilder.setVersionHeader(YamlObj.DbiStream->VerHeader);
- for (const auto &MI : YamlObj.DbiStream->ModInfos) {
- ExitOnErr(DbiBuilder.addModuleInfo(MI.Obj, MI.Mod));
- for (auto S : MI.SourceFiles)
- ExitOnErr(DbiBuilder.addModuleSourceFile(MI.Mod, S));
+ pdb::yaml::PdbInfoStream DefaultInfoStream;
+ pdb::yaml::PdbDbiStream DefaultDbiStream;
+ pdb::yaml::PdbTpiStream DefaultTpiStream;
+
+ const auto &Info = YamlObj.PdbStream.getValueOr(DefaultInfoStream);
+
+ auto &InfoBuilder = Builder.getInfoBuilder();
+ InfoBuilder.setAge(Info.Age);
+ InfoBuilder.setGuid(Info.Guid);
+ InfoBuilder.setSignature(Info.Signature);
+ InfoBuilder.setVersion(Info.Version);
+ for (auto F : Info.Features)
+ InfoBuilder.addFeature(F);
+
+ const auto &Dbi = YamlObj.DbiStream.getValueOr(DefaultDbiStream);
+ auto &DbiBuilder = Builder.getDbiBuilder();
+ DbiBuilder.setAge(Dbi.Age);
+ DbiBuilder.setBuildNumber(Dbi.BuildNumber);
+ DbiBuilder.setFlags(Dbi.Flags);
+ DbiBuilder.setMachineType(Dbi.MachineType);
+ DbiBuilder.setPdbDllRbld(Dbi.PdbDllRbld);
+ DbiBuilder.setPdbDllVersion(Dbi.PdbDllVersion);
+ DbiBuilder.setVersionHeader(Dbi.VerHeader);
+ for (const auto &MI : Dbi.ModInfos) {
+ auto &ModiBuilder = ExitOnErr(DbiBuilder.addModuleInfo(MI.Mod));
+
+ for (auto S : MI.SourceFiles)
+ ExitOnErr(DbiBuilder.addModuleSourceFile(MI.Mod, S));
+ if (MI.Modi.hasValue()) {
+ const auto &ModiStream = *MI.Modi;
+ ModiBuilder.setObjFileName(MI.Obj);
+ for (auto Symbol : ModiStream.Symbols)
+ ModiBuilder.addSymbol(Symbol.Record);
}
}
- if (YamlObj.TpiStream.hasValue()) {
- auto &TpiBuilder = Builder.getTpiBuilder();
- TpiBuilder.setVersionHeader(YamlObj.TpiStream->Version);
- for (const auto &R : YamlObj.TpiStream->Records)
- TpiBuilder.addTypeRecord(R.Record);
- }
+ auto &TpiBuilder = Builder.getTpiBuilder();
+ const auto &Tpi = YamlObj.TpiStream.getValueOr(DefaultTpiStream);
+ TpiBuilder.setVersionHeader(Tpi.Version);
+ for (const auto &R : Tpi.Records)
+ TpiBuilder.addTypeRecord(R.Record.data(), R.Record.Hash);
- if (YamlObj.IpiStream.hasValue()) {
- auto &IpiBuilder = Builder.getIpiBuilder();
- IpiBuilder.setVersionHeader(YamlObj.IpiStream->Version);
- for (const auto &R : YamlObj.IpiStream->Records)
- IpiBuilder.addTypeRecord(R.Record);
- }
+ const auto &Ipi = YamlObj.IpiStream.getValueOr(DefaultTpiStream);
+ auto &IpiBuilder = Builder.getIpiBuilder();
+ IpiBuilder.setVersionHeader(Ipi.Version);
+ for (const auto &R : Ipi.Records)
+ TpiBuilder.addTypeRecord(R.Record.data(), R.Record.Hash);
ExitOnErr(Builder.commit(opts::yaml2pdb::YamlPdbOutputFile));
}
+static PDBFile &loadPDB(StringRef Path, std::unique_ptr<IPDBSession> &Session) {
+ ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session));
+
+ NativeSession *NS = static_cast<NativeSession *>(Session.get());
+ return NS->getPDBFile();
+}
+
static void pdb2Yaml(StringRef Path) {
std::unique_ptr<IPDBSession> Session;
- ExitOnErr(loadDataForPDB(PDB_ReaderType::Raw, Path, Session));
+ auto &File = loadPDB(Path, Session);
- RawSession *RS = static_cast<RawSession *>(Session.get());
- PDBFile &File = RS->getPDBFile();
auto O = llvm::make_unique<YAMLOutputStyle>(File);
O = llvm::make_unique<YAMLOutputStyle>(File);
@@ -398,24 +505,48 @@ static void pdb2Yaml(StringRef Path) {
static void dumpRaw(StringRef Path) {
std::unique_ptr<IPDBSession> Session;
- ExitOnErr(loadDataForPDB(PDB_ReaderType::Raw, Path, Session));
+ auto &File = loadPDB(Path, Session);
- RawSession *RS = static_cast<RawSession *>(Session.get());
- PDBFile &File = RS->getPDBFile();
auto O = llvm::make_unique<LLVMOutputStyle>(File);
ExitOnErr(O->dump());
}
+static void dumpAnalysis(StringRef Path) {
+ std::unique_ptr<IPDBSession> Session;
+ auto &File = loadPDB(Path, Session);
+ auto O = llvm::make_unique<AnalysisStyle>(File);
+
+ ExitOnErr(O->dump());
+}
+
+static void diff(StringRef Path1, StringRef Path2) {
+ std::unique_ptr<IPDBSession> Session1;
+ std::unique_ptr<IPDBSession> Session2;
+
+ auto &File1 = loadPDB(Path1, Session1);
+ auto &File2 = loadPDB(Path2, Session2);
+
+ auto O = llvm::make_unique<DiffStyle>(File1, File2);
+
+ ExitOnErr(O->dump());
+}
+
static void dumpPretty(StringRef Path) {
std::unique_ptr<IPDBSession> Session;
- ExitOnErr(loadDataForPDB(PDB_ReaderType::DIA, Path, Session));
+ const auto ReaderType =
+ opts::pretty::Native ? PDB_ReaderType::Native : PDB_ReaderType::DIA;
+ ExitOnErr(loadDataForPDB(ReaderType, Path, Session));
if (opts::pretty::LoadAddress)
Session->setLoadAddress(opts::pretty::LoadAddress);
- LinePrinter Printer(2, outs());
+ auto &Stream = outs();
+ const bool UseColor = opts::pretty::ColorOutput == cl::BOU_UNSET
+ ? Stream.has_colors()
+ : opts::pretty::ColorOutput == cl::BOU_TRUE;
+ LinePrinter Printer(2, UseColor, Stream);
auto GlobalScope(Session->getGlobalScope());
std::string FileName(GlobalScope->getSymbolsFileName());
@@ -465,7 +596,7 @@ static void dumpPretty(StringRef Path) {
Printer.Unindent();
}
- if (opts::pretty::Types) {
+ if (opts::pretty::Classes || opts::pretty::Enums || opts::pretty::Typedefs) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---TYPES---";
Printer.Indent();
@@ -556,24 +687,34 @@ int main(int argc_, const char *argv_[]) {
}
}
- if (opts::RawSubcommand && opts::raw::RawAll) {
- opts::raw::DumpHeaders = true;
- opts::raw::DumpModules = true;
- opts::raw::DumpModuleFiles = true;
- opts::raw::DumpModuleSyms = true;
- opts::raw::DumpGlobals = true;
- opts::raw::DumpPublics = true;
- opts::raw::DumpSectionHeaders = true;
- opts::raw::DumpStreamSummary = true;
- opts::raw::DumpPageStats = true;
- opts::raw::DumpStreamBlocks = true;
- opts::raw::DumpTpiRecords = true;
- opts::raw::DumpTpiHash = true;
- opts::raw::DumpIpiRecords = true;
- opts::raw::DumpSectionMap = true;
- opts::raw::DumpSectionContribs = true;
- opts::raw::DumpLineInfo = true;
- opts::raw::DumpFpo = true;
+ if (opts::RawSubcommand) {
+ if (opts::raw::RawAll) {
+ opts::raw::DumpHeaders = true;
+ opts::raw::DumpModules = true;
+ opts::raw::DumpModuleFiles = true;
+ opts::raw::DumpModuleSyms = true;
+ opts::raw::DumpGlobals = true;
+ opts::raw::DumpPublics = true;
+ opts::raw::DumpSectionHeaders = true;
+ opts::raw::DumpStreamSummary = true;
+ opts::raw::DumpPageStats = true;
+ opts::raw::DumpStreamBlocks = true;
+ opts::raw::DumpTpiRecords = true;
+ opts::raw::DumpTpiHash = true;
+ opts::raw::DumpIpiRecords = true;
+ opts::raw::DumpSectionMap = true;
+ opts::raw::DumpSectionContribs = true;
+ opts::raw::DumpLineInfo = true;
+ opts::raw::DumpFpo = true;
+ opts::raw::DumpStringTable = true;
+ }
+
+ if (opts::raw::CompactRecords &&
+ (opts::raw::DumpTpiRecordBytes || opts::raw::DumpIpiRecordBytes)) {
+ errs() << "-compact-records is incompatible with -tpi-record-bytes and "
+ "-ipi-record-bytes.\n";
+ exit(1);
+ }
}
llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
@@ -582,6 +723,8 @@ int main(int argc_, const char *argv_[]) {
pdb2Yaml(opts::pdb2yaml::InputFilename.front());
} else if (opts::YamlToPdbSubcommand) {
yamlToPdb(opts::yaml2pdb::InputFilename.front());
+ } else if (opts::AnalyzeSubcommand) {
+ dumpAnalysis(opts::analyze::InputFilename.front());
} else if (opts::PrettySubcommand) {
if (opts::pretty::Lines)
opts::pretty::Compilands = true;
@@ -595,6 +738,12 @@ int main(int argc_, const char *argv_[]) {
opts::pretty::Lines = true;
}
+ if (opts::pretty::Types) {
+ opts::pretty::Classes = true;
+ opts::pretty::Typedefs = true;
+ opts::pretty::Enums = true;
+ }
+
// When adding filters for excluded compilands and types, we need to
// remember that these are regexes. So special characters such as * and \
// need to be escaped in the regex. In the case of a literal \, this means
@@ -616,6 +765,12 @@ int main(int argc_, const char *argv_[]) {
} else if (opts::RawSubcommand) {
std::for_each(opts::raw::InputFilenames.begin(),
opts::raw::InputFilenames.end(), dumpRaw);
+ } else if (opts::DiffSubcommand) {
+ if (opts::diff::InputFilenames.size() != 2) {
+ errs() << "diff subcommand expects exactly 2 arguments.\n";
+ exit(1);
+ }
+ diff(opts::diff::InputFilenames[0], opts::diff::InputFilenames[1]);
}
outs().flush();
diff --git a/tools/llvm-pdbdump/llvm-pdbdump.h b/tools/llvm-pdbdump/llvm-pdbdump.h
index 42d847a23fcc9..a5429a253df40 100644
--- a/tools/llvm-pdbdump/llvm-pdbdump.h
+++ b/tools/llvm-pdbdump/llvm-pdbdump.h
@@ -17,14 +17,19 @@
namespace opts {
namespace pretty {
+
+enum class ClassDefinitionFormat { None, Layout, Graphical, Standard };
+enum class ClassSortMode { None, Name, Size, Padding };
+
extern llvm::cl::opt<bool> Compilands;
extern llvm::cl::opt<bool> Symbols;
extern llvm::cl::opt<bool> Globals;
-extern llvm::cl::opt<bool> Types;
+extern llvm::cl::opt<bool> Classes;
+extern llvm::cl::opt<bool> Enums;
+extern llvm::cl::opt<bool> Typedefs;
extern llvm::cl::opt<bool> All;
extern llvm::cl::opt<bool> ExcludeCompilerGenerated;
-extern llvm::cl::opt<bool> NoClassDefs;
extern llvm::cl::opt<bool> NoEnumDefs;
extern llvm::cl::list<std::string> ExcludeTypes;
extern llvm::cl::list<std::string> ExcludeSymbols;
@@ -32,6 +37,10 @@ extern llvm::cl::list<std::string> ExcludeCompilands;
extern llvm::cl::list<std::string> IncludeTypes;
extern llvm::cl::list<std::string> IncludeSymbols;
extern llvm::cl::list<std::string> IncludeCompilands;
+extern llvm::cl::opt<ClassSortMode> ClassOrder;
+extern llvm::cl::opt<uint32_t> SizeThreshold;
+extern llvm::cl::opt<uint32_t> PaddingThreshold;
+extern llvm::cl::opt<ClassDefinitionFormat> ClassFormat;
}
namespace raw {
@@ -43,6 +52,7 @@ struct BlockRange {
extern llvm::Optional<BlockRange> DumpBlockRange;
extern llvm::cl::list<uint32_t> DumpStreamData;
+extern llvm::cl::opt<bool> CompactRecords;
extern llvm::cl::opt<bool> DumpGlobals;
extern llvm::cl::opt<bool> DumpHeaders;
extern llvm::cl::opt<bool> DumpStreamBlocks;
@@ -63,12 +73,19 @@ extern llvm::cl::opt<bool> DumpSectionMap;
extern llvm::cl::opt<bool> DumpSymRecordBytes;
extern llvm::cl::opt<bool> DumpSectionHeaders;
extern llvm::cl::opt<bool> DumpFpo;
+extern llvm::cl::opt<bool> DumpStringTable;
+}
+
+namespace diff {
+extern llvm::cl::opt<bool> Pedantic;
}
namespace pdb2yaml {
extern llvm::cl::opt<bool> NoFileHeaders;
+extern llvm::cl::opt<bool> Minimal;
extern llvm::cl::opt<bool> StreamMetadata;
extern llvm::cl::opt<bool> StreamDirectory;
+extern llvm::cl::opt<bool> StringTable;
extern llvm::cl::opt<bool> PdbStream;
extern llvm::cl::opt<bool> DbiStream;
extern llvm::cl::opt<bool> DbiModuleInfo;