diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:01:22 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:01:22 +0000 |
commit | 71d5a2540a98c81f5bcaeb48805e0e2881f530ef (patch) | |
tree | 5343938942df402b49ec7300a1c25a2d4ccd5821 /tools/llvm-pdbdump | |
parent | 31bbf64f3a4974a2d6c8b3b27ad2f519caf74057 (diff) |
Notes
Diffstat (limited to 'tools/llvm-pdbdump')
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; |