diff options
Diffstat (limited to 'contrib/llvm/tools/llvm-pdbutil')
47 files changed, 9155 insertions, 0 deletions
diff --git a/contrib/llvm/tools/llvm-pdbutil/Analyze.cpp b/contrib/llvm/tools/llvm-pdbutil/Analyze.cpp new file mode 100644 index 0000000..6c603dd --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/Analyze.cpp @@ -0,0 +1,148 @@ +//===- 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/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.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/CodeViewTypes.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(); + + HashLookupVisitor Hasher(*Tpi); + + uint32_t RecordCount = Tpi->getNumTypeRecords(); + auto Offsets = Tpi->getTypeIndexOffsets(); + auto Types = llvm::make_unique<LazyRandomTypeCollection>( + Tpi->typeArray(), RecordCount, Offsets); + + if (auto EC = codeview::visitTypeStream(*Types, Hasher)) + return EC; + + 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 = Types->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 = Types->getTypeName(TI); + const CVType &HeadRecord = Types->getType(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 = Types->getType(Chain.TI); + outs() << formatv(" {0:x} {1} {2}\n", Chain.TI.getIndex(), + getLeafTypeName(TailRecord.Type), + Types->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/contrib/llvm/tools/llvm-pdbutil/Analyze.h b/contrib/llvm/tools/llvm-pdbutil/Analyze.h new file mode 100644 index 0000000..7230ae4 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/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/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.cpp b/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.cpp new file mode 100644 index 0000000..9e5a28c --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.cpp @@ -0,0 +1,501 @@ +//===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "BytesOutputStyle.h" + +#include "FormatUtil.h" +#include "StreamUtil.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +namespace { +struct StreamSpec { + uint32_t SI = 0; + uint32_t Begin = 0; + uint32_t Size = 0; +}; +} // namespace + +static Expected<StreamSpec> parseStreamSpec(StringRef Str) { + StreamSpec Result; + if (Str.consumeInteger(0, Result.SI)) + return make_error<RawError>(raw_error_code::invalid_format, + "Invalid Stream Specification"); + if (Str.consume_front(":")) { + if (Str.consumeInteger(0, Result.Begin)) + return make_error<RawError>(raw_error_code::invalid_format, + "Invalid Stream Specification"); + } + if (Str.consume_front("@")) { + if (Str.consumeInteger(0, Result.Size)) + return make_error<RawError>(raw_error_code::invalid_format, + "Invalid Stream Specification"); + } + + if (!Str.empty()) + return make_error<RawError>(raw_error_code::invalid_format, + "Invalid Stream Specification"); + return Result; +} + +static SmallVector<StreamSpec, 2> parseStreamSpecs(LinePrinter &P) { + SmallVector<StreamSpec, 2> Result; + + for (auto &Str : opts::bytes::DumpStreamData) { + auto ESS = parseStreamSpec(Str); + if (!ESS) { + P.formatLine("Error parsing stream spec {0}: {1}", Str, + toString(ESS.takeError())); + continue; + } + Result.push_back(*ESS); + } + return Result; +} + +static void printHeader(LinePrinter &P, const Twine &S) { + P.NewLine(); + P.formatLine("{0,=60}", S); + P.formatLine("{0}", fmt_repeat('=', 60)); +} + +BytesOutputStyle::BytesOutputStyle(PDBFile &File) + : File(File), P(2, false, outs()) {} + +Error BytesOutputStyle::dump() { + + if (opts::bytes::DumpBlockRange.hasValue()) { + auto &R = *opts::bytes::DumpBlockRange; + uint32_t Max = R.Max.getValueOr(R.Min); + + if (Max < R.Min) + return make_error<StringError>( + "Invalid block range specified. Max < Min", + inconvertibleErrorCode()); + if (Max >= File.getBlockCount()) + return make_error<StringError>( + "Invalid block range specified. Requested block out of bounds", + inconvertibleErrorCode()); + + dumpBlockRanges(R.Min, Max); + P.NewLine(); + } + + if (opts::bytes::DumpByteRange.hasValue()) { + auto &R = *opts::bytes::DumpByteRange; + uint32_t Max = R.Max.getValueOr(File.getFileSize()); + + if (Max < R.Min) + return make_error<StringError>("Invalid byte range specified. Max < Min", + inconvertibleErrorCode()); + if (Max >= File.getFileSize()) + return make_error<StringError>( + "Invalid byte range specified. Requested byte larger than file size", + inconvertibleErrorCode()); + + dumpByteRanges(R.Min, Max); + P.NewLine(); + } + + if (!opts::bytes::DumpStreamData.empty()) { + dumpStreamBytes(); + P.NewLine(); + } + + if (opts::bytes::NameMap) { + dumpNameMap(); + P.NewLine(); + } + + if (opts::bytes::SectionContributions) { + dumpSectionContributions(); + P.NewLine(); + } + + if (opts::bytes::SectionMap) { + dumpSectionMap(); + P.NewLine(); + } + + if (opts::bytes::ModuleInfos) { + dumpModuleInfos(); + P.NewLine(); + } + + if (opts::bytes::FileInfo) { + dumpFileInfo(); + P.NewLine(); + } + + if (opts::bytes::TypeServerMap) { + dumpTypeServerMap(); + P.NewLine(); + } + + if (opts::bytes::ECData) { + dumpECData(); + P.NewLine(); + } + + if (!opts::bytes::TypeIndex.empty()) { + dumpTypeIndex(StreamTPI, opts::bytes::TypeIndex); + P.NewLine(); + } + + if (!opts::bytes::IdIndex.empty()) { + dumpTypeIndex(StreamIPI, opts::bytes::IdIndex); + P.NewLine(); + } + + if (opts::bytes::ModuleSyms) { + dumpModuleSyms(); + P.NewLine(); + } + + if (opts::bytes::ModuleC11) { + dumpModuleC11(); + P.NewLine(); + } + + if (opts::bytes::ModuleC13) { + dumpModuleC13(); + P.NewLine(); + } + + return Error::success(); +} + +void BytesOutputStyle::dumpNameMap() { + printHeader(P, "Named Stream Map"); + + AutoIndent Indent(P); + + auto &InfoS = Err(File.getPDBInfoStream()); + BinarySubstreamRef NS = InfoS.getNamedStreamsBuffer(); + auto Layout = File.getStreamLayout(StreamPDB); + P.formatMsfStreamData("Named Stream Map", File, Layout, NS); +} + +void BytesOutputStyle::dumpBlockRanges(uint32_t Min, uint32_t Max) { + printHeader(P, "MSF Blocks"); + + AutoIndent Indent(P); + for (uint32_t I = Min; I <= Max; ++I) { + uint64_t Base = I; + Base *= File.getBlockSize(); + + auto ExpectedData = File.getBlockData(I, File.getBlockSize()); + if (!ExpectedData) { + P.formatLine("Could not get block {0}. Reason = {1}", I, + toString(ExpectedData.takeError())); + continue; + } + std::string Label = formatv("Block {0}", I).str(); + P.formatBinary(Label, *ExpectedData, Base, 0); + } +} + +void BytesOutputStyle::dumpSectionContributions() { + printHeader(P, "Section Contributions"); + + AutoIndent Indent(P); + + auto &DbiS = Err(File.getPDBDbiStream()); + BinarySubstreamRef NS = DbiS.getSectionContributionData(); + auto Layout = File.getStreamLayout(StreamDBI); + P.formatMsfStreamData("Section Contributions", File, Layout, NS); +} + +void BytesOutputStyle::dumpSectionMap() { + printHeader(P, "Section Map"); + + AutoIndent Indent(P); + + auto &DbiS = Err(File.getPDBDbiStream()); + BinarySubstreamRef NS = DbiS.getSecMapSubstreamData(); + auto Layout = File.getStreamLayout(StreamDBI); + P.formatMsfStreamData("Section Map", File, Layout, NS); +} + +void BytesOutputStyle::dumpModuleInfos() { + printHeader(P, "Module Infos"); + + AutoIndent Indent(P); + + auto &DbiS = Err(File.getPDBDbiStream()); + BinarySubstreamRef NS = DbiS.getModiSubstreamData(); + auto Layout = File.getStreamLayout(StreamDBI); + P.formatMsfStreamData("Module Infos", File, Layout, NS); +} + +void BytesOutputStyle::dumpFileInfo() { + printHeader(P, "File Info"); + + AutoIndent Indent(P); + + auto &DbiS = Err(File.getPDBDbiStream()); + BinarySubstreamRef NS = DbiS.getFileInfoSubstreamData(); + auto Layout = File.getStreamLayout(StreamDBI); + P.formatMsfStreamData("File Info", File, Layout, NS); +} + +void BytesOutputStyle::dumpTypeServerMap() { + printHeader(P, "Type Server Map"); + + AutoIndent Indent(P); + + auto &DbiS = Err(File.getPDBDbiStream()); + BinarySubstreamRef NS = DbiS.getTypeServerMapSubstreamData(); + auto Layout = File.getStreamLayout(StreamDBI); + P.formatMsfStreamData("Type Server Map", File, Layout, NS); +} + +void BytesOutputStyle::dumpECData() { + printHeader(P, "Edit and Continue Data"); + + AutoIndent Indent(P); + + auto &DbiS = Err(File.getPDBDbiStream()); + BinarySubstreamRef NS = DbiS.getECSubstreamData(); + auto Layout = File.getStreamLayout(StreamDBI); + P.formatMsfStreamData("Edit and Continue Data", File, Layout, NS); +} + +void BytesOutputStyle::dumpTypeIndex(uint32_t StreamIdx, + ArrayRef<uint32_t> Indices) { + assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); + assert(!Indices.empty()); + + bool IsTpi = (StreamIdx == StreamTPI); + + StringRef Label = IsTpi ? "Type (TPI) Records" : "Index (IPI) Records"; + printHeader(P, Label); + auto &Stream = Err(IsTpi ? File.getPDBTpiStream() : File.getPDBIpiStream()); + + AutoIndent Indent(P); + + auto Substream = Stream.getTypeRecordsSubstream(); + auto &Types = Err(initializeTypes(StreamIdx)); + auto Layout = File.getStreamLayout(StreamIdx); + for (const auto &Id : Indices) { + TypeIndex TI(Id); + if (TI.toArrayIndex() >= Types.capacity()) { + P.formatLine("Error: TypeIndex {0} does not exist", TI); + continue; + } + + auto Type = Types.getType(TI); + uint32_t Offset = Types.getOffsetOfType(TI); + auto OneType = Substream.slice(Offset, Type.length()); + P.formatMsfStreamData(formatv("Type {0}", TI).str(), File, Layout, OneType); + } +} + +template <typename CallbackT> +static void iterateOneModule(PDBFile &File, LinePrinter &P, + const DbiModuleList &Modules, uint32_t I, + uint32_t Digits, uint32_t IndentLevel, + CallbackT Callback) { + if (I >= Modules.getModuleCount()) { + P.formatLine("Mod {0:4} | Invalid module index ", + fmt_align(I, AlignStyle::Right, std::max(Digits, 4U))); + return; + } + + auto Modi = Modules.getModuleDescriptor(I); + P.formatLine("Mod {0:4} | `{1}`: ", + fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)), + Modi.getModuleName()); + + uint16_t ModiStream = Modi.getModuleStreamIndex(); + AutoIndent Indent2(P, IndentLevel); + if (ModiStream == kInvalidStreamIndex) + return; + + auto ModStreamData = MappedBlockStream::createIndexedStream( + File.getMsfLayout(), File.getMsfBuffer(), ModiStream, + File.getAllocator()); + ModuleDebugStreamRef ModStream(Modi, std::move(ModStreamData)); + if (auto EC = ModStream.reload()) { + P.formatLine("Could not parse debug information."); + return; + } + auto Layout = File.getStreamLayout(ModiStream); + Callback(I, ModStream, Layout); +} + +template <typename CallbackT> +static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel, + CallbackT Callback) { + AutoIndent Indent(P); + if (!File.hasPDBDbiStream()) { + P.formatLine("DBI Stream not present"); + return; + } + + ExitOnError Err("Unexpected error processing modules"); + + auto &Stream = Err(File.getPDBDbiStream()); + + const DbiModuleList &Modules = Stream.modules(); + + if (opts::bytes::ModuleIndex.getNumOccurrences() > 0) { + iterateOneModule(File, P, Modules, opts::bytes::ModuleIndex, 1, IndentLevel, + Callback); + } else { + uint32_t Count = Modules.getModuleCount(); + uint32_t Digits = NumDigits(Count); + for (uint32_t I = 0; I < Count; ++I) { + iterateOneModule(File, P, Modules, I, Digits, IndentLevel, Callback); + } + } +} + +void BytesOutputStyle::dumpModuleSyms() { + printHeader(P, "Module Symbols"); + + AutoIndent Indent(P); + + iterateModules(File, P, 2, + [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, + const MSFStreamLayout &Layout) { + auto Symbols = Stream.getSymbolsSubstream(); + P.formatMsfStreamData("Symbols", File, Layout, Symbols); + }); +} + +void BytesOutputStyle::dumpModuleC11() { + printHeader(P, "C11 Debug Chunks"); + + AutoIndent Indent(P); + + iterateModules(File, P, 2, + [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, + const MSFStreamLayout &Layout) { + auto Chunks = Stream.getC11LinesSubstream(); + P.formatMsfStreamData("C11 Debug Chunks", File, Layout, + Chunks); + }); +} + +static std::string formatChunkKind(DebugSubsectionKind Kind) { + switch (Kind) { + RETURN_CASE(DebugSubsectionKind, None, "none"); + RETURN_CASE(DebugSubsectionKind, Symbols, "symbols"); + RETURN_CASE(DebugSubsectionKind, Lines, "lines"); + RETURN_CASE(DebugSubsectionKind, StringTable, "strings"); + RETURN_CASE(DebugSubsectionKind, FileChecksums, "checksums"); + RETURN_CASE(DebugSubsectionKind, FrameData, "frames"); + RETURN_CASE(DebugSubsectionKind, InlineeLines, "inlinee lines"); + RETURN_CASE(DebugSubsectionKind, CrossScopeImports, "xmi"); + RETURN_CASE(DebugSubsectionKind, CrossScopeExports, "xme"); + RETURN_CASE(DebugSubsectionKind, ILLines, "il lines"); + RETURN_CASE(DebugSubsectionKind, FuncMDTokenMap, "func md token map"); + RETURN_CASE(DebugSubsectionKind, TypeMDTokenMap, "type md token map"); + RETURN_CASE(DebugSubsectionKind, MergedAssemblyInput, + "merged assembly input"); + RETURN_CASE(DebugSubsectionKind, CoffSymbolRVA, "coff symbol rva"); + } + return formatUnknownEnum(Kind); +} + +void BytesOutputStyle::dumpModuleC13() { + printHeader(P, "Debug Chunks"); + + AutoIndent Indent(P); + + iterateModules( + File, P, 2, + [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, + const MSFStreamLayout &Layout) { + auto Chunks = Stream.getC13LinesSubstream(); + if (opts::bytes::SplitChunks) { + for (const auto &SS : Stream.subsections()) { + BinarySubstreamRef ThisChunk; + std::tie(ThisChunk, Chunks) = Chunks.split(SS.getRecordLength()); + P.formatMsfStreamData(formatChunkKind(SS.kind()), File, Layout, + ThisChunk); + } + } else { + P.formatMsfStreamData("Debug Chunks", File, Layout, Chunks); + } + }); +} + +void BytesOutputStyle::dumpByteRanges(uint32_t Min, uint32_t Max) { + printHeader(P, "MSF Bytes"); + + AutoIndent Indent(P); + + BinaryStreamReader Reader(File.getMsfBuffer()); + ArrayRef<uint8_t> Data; + consumeError(Reader.skip(Min)); + uint32_t Size = Max - Min + 1; + auto EC = Reader.readBytes(Data, Size); + assert(!EC); + consumeError(std::move(EC)); + P.formatBinary("Bytes", Data, Min); +} + +Expected<codeview::LazyRandomTypeCollection &> +BytesOutputStyle::initializeTypes(uint32_t StreamIdx) { + auto &TypeCollection = (StreamIdx == StreamTPI) ? TpiTypes : IpiTypes; + if (TypeCollection) + return *TypeCollection; + + auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream() + : File.getPDBIpiStream(); + if (!Tpi) + return Tpi.takeError(); + + auto &Types = Tpi->typeArray(); + uint32_t Count = Tpi->getNumTypeRecords(); + auto Offsets = Tpi->getTypeIndexOffsets(); + TypeCollection = + llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets); + + return *TypeCollection; +} + +void BytesOutputStyle::dumpStreamBytes() { + if (StreamPurposes.empty()) + discoverStreamPurposes(File, StreamPurposes); + + printHeader(P, "Stream Data"); + ExitOnError Err("Unexpected error reading stream data"); + + auto Specs = parseStreamSpecs(P); + + for (const auto &Spec : Specs) { + AutoIndent Indent(P); + if (Spec.SI >= StreamPurposes.size()) { + P.formatLine("Stream {0}: Not present", Spec.SI); + continue; + } + P.formatMsfStreamData("Data", File, Spec.SI, StreamPurposes[Spec.SI], + Spec.Begin, Spec.Size); + } +} diff --git a/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.h b/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.h new file mode 100644 index 0000000..c162163 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/BytesOutputStyle.h @@ -0,0 +1,67 @@ +//===- BytesOutputStyle.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_BYTESOUTPUTSTYLE_H +#define LLVM_TOOLS_LLVMPDBDUMP_BYTESOUTPUTSTYLE_H + +#include "LinePrinter.h" +#include "OutputStyle.h" + +#include "llvm/Support/Error.h" + +namespace llvm { + +namespace codeview { +class LazyRandomTypeCollection; +} + +namespace pdb { + +class PDBFile; + +class BytesOutputStyle : public OutputStyle { +public: + BytesOutputStyle(PDBFile &File); + + Error dump() override; + +private: + void dumpNameMap(); + void dumpBlockRanges(uint32_t Min, uint32_t Max); + void dumpByteRanges(uint32_t Min, uint32_t Max); + void dumpStreamBytes(); + + void dumpSectionContributions(); + void dumpSectionMap(); + void dumpModuleInfos(); + void dumpFileInfo(); + void dumpTypeServerMap(); + void dumpECData(); + + void dumpModuleSyms(); + void dumpModuleC11(); + void dumpModuleC13(); + + void dumpTypeIndex(uint32_t StreamIdx, ArrayRef<uint32_t> Indices); + + Expected<codeview::LazyRandomTypeCollection &> + initializeTypes(uint32_t StreamIdx); + + std::unique_ptr<codeview::LazyRandomTypeCollection> TpiTypes; + std::unique_ptr<codeview::LazyRandomTypeCollection> IpiTypes; + + PDBFile &File; + LinePrinter P; + ExitOnError Err; + SmallVector<std::string, 8> StreamPurposes; +}; +} // namespace pdb +} // namespace llvm + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/Diff.cpp b/contrib/llvm/tools/llvm-pdbutil/Diff.cpp new file mode 100644 index 0000000..aad4e1b --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/Diff.cpp @@ -0,0 +1,608 @@ +//===- 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 "DiffPrinter.h" +#include "FormatUtil.h" +#include "StreamUtil.h" +#include "llvm-pdbutil.h" + +#include "llvm/ADT/StringSet.h" + +#include "llvm/DebugInfo/PDB/Native/DbiStream.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/PDBStringTable.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatProviders.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +// Compare and format two stream numbers. Stream numbers are considered +// identical if they contain the same value, equivalent if they are both +// the invalid stream or neither is the invalid stream, and different if +// one is the invalid stream and another isn't. +struct StreamNumberProvider { + static DiffResult compare(uint16_t L, uint16_t R) { + if (L == R) + return DiffResult::IDENTICAL; + bool LP = L != kInvalidStreamIndex; + bool RP = R != kInvalidStreamIndex; + if (LP != RP) + return DiffResult::DIFFERENT; + return DiffResult::EQUIVALENT; + } + + static std::string format(uint16_t SN, bool Right) { + if (SN == kInvalidStreamIndex) + return "(not present)"; + return formatv("{0}", SN).str(); + } +}; + +// Compares and formats two module indices. Modis are considered identical +// if they are identical, equivalent if they either both contain a value or +// both don't contain a value, and different if one contains a value and the +// other doesn't. +struct ModiProvider { + DiffResult compare(Optional<uint32_t> L, Optional<uint32_t> R) { + if (L == R) + return DiffResult::IDENTICAL; + if (L.hasValue() != R.hasValue()) + return DiffResult::DIFFERENT; + return DiffResult::EQUIVALENT; + } + + std::string format(Optional<uint32_t> Modi, bool Right) { + if (!Modi.hasValue()) + return "(not present)"; + return formatv("{0}", *Modi).str(); + } +}; + +// Compares and formats two paths embedded in the PDB, ignoring the beginning +// of the path if the user specified it as a "root path" on the command line. +struct BinaryPathProvider { + explicit BinaryPathProvider(uint32_t MaxLen) : MaxLen(MaxLen) {} + + DiffResult compare(StringRef L, StringRef R) { + if (L == R) + return DiffResult::IDENTICAL; + + SmallString<64> LN = removeRoot(L, false); + SmallString<64> RN = removeRoot(R, true); + + return (LN.equals_lower(RN)) ? DiffResult::EQUIVALENT + : DiffResult::DIFFERENT; + } + + std::string format(StringRef S, bool Right) { + if (S.empty()) + return "(empty)"; + + SmallString<64> Native = removeRoot(S, Right); + return truncateStringFront(Native.str(), MaxLen); + } + + SmallString<64> removeRoot(StringRef Path, bool IsRight) const { + SmallString<64> Native(Path); + auto &RootOpt = IsRight ? opts::diff::RightRoot : opts::diff::LeftRoot; + SmallString<64> Root(static_cast<std::string>(RootOpt)); + // pdb paths always use windows syntax, convert slashes to backslashes. + sys::path::native(Root, sys::path::Style::windows); + if (sys::path::has_stem(Root, sys::path::Style::windows)) + sys::path::append(Root, sys::path::Style::windows, + sys::path::get_separator(sys::path::Style::windows)); + + sys::path::replace_path_prefix(Native, Root, "", sys::path::Style::windows); + return Native; + } + uint32_t MaxLen; +}; + +// Compare and format two stream purposes. For general streams, this just +// compares the description. For module streams it uses the path comparison +// algorithm taking into consideration the binary root, described above. +// Formatting stream purposes just prints the stream purpose, except for +// module streams and named streams, where it prefixes the name / module +// with an identifier. Example: +// +// Named Stream "\names" +// Module Stream "foo.obj" +// +// If a named stream is too long to fit in a column, it is truncated at the +// end, and if a module is too long to fit in a column, it is truncated at the +// beginning. Example: +// +// Named Stream "\Really Long Str..." +// Module Stream "...puts\foo.obj" +// +struct StreamPurposeProvider { + explicit StreamPurposeProvider(uint32_t MaxLen) : MaxLen(MaxLen) {} + + DiffResult compare(const std::pair<StreamPurpose, std::string> &L, + const std::pair<StreamPurpose, std::string> &R) { + if (L.first != R.first) + return DiffResult::DIFFERENT; + if (L.first == StreamPurpose::ModuleStream) { + BinaryPathProvider PathProvider(MaxLen); + return PathProvider.compare(L.second, R.second); + } + return (L.second == R.second) ? DiffResult::IDENTICAL + : DiffResult::DIFFERENT; + } + + std::string format(const std::pair<StreamPurpose, std::string> &P, + bool Right) { + if (P.first == StreamPurpose::Other) + return truncateStringBack(P.second, MaxLen); + if (P.first == StreamPurpose::NamedStream) + return truncateQuotedNameBack("Named Stream", P.second, MaxLen); + + assert(P.first == StreamPurpose::ModuleStream); + uint32_t ExtraChars = strlen("Module \"\""); + BinaryPathProvider PathProvider(MaxLen - ExtraChars); + std::string Result = PathProvider.format(P.second, Right); + return formatv("Module \"{0}\"", Result); + } + + uint32_t MaxLen; +}; +} // namespace + +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>; + +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(); +} + +Error DiffStyle::diffSuperBlock() { + DiffPrinter D(2, "MSF Super Block", 16, 20, opts::diff::PrintResultColumn, + opts::diff::PrintValueColumns, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + truncateStringFront(File1.getFilePath(), 18), + truncateStringFront(File2.getFilePath(), 18)); + D.print("Block Size", File1.getBlockSize(), File2.getBlockSize()); + D.print("Block Count", File1.getBlockCount(), File2.getBlockCount()); + D.print("Unknown 1", File1.getUnknown1(), File2.getUnknown1()); + D.print("Directory Size", File1.getNumDirectoryBytes(), + File2.getNumDirectoryBytes()); + return Error::success(); +} + +Error DiffStyle::diffStreamDirectory() { + DiffPrinter D(2, "Stream Directory", 30, 20, opts::diff::PrintResultColumn, + opts::diff::PrintValueColumns, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + truncateStringFront(File1.getFilePath(), 18), + truncateStringFront(File2.getFilePath(), 18)); + + SmallVector<std::pair<StreamPurpose, std::string>, 32> P; + SmallVector<std::pair<StreamPurpose, std::string>, 32> Q; + discoverStreamPurposes(File1, P); + discoverStreamPurposes(File2, Q); + D.print("Stream Count", File1.getNumStreams(), File2.getNumStreams()); + auto PI = to_vector<32>(enumerate(P)); + auto QI = to_vector<32>(enumerate(Q)); + + // Scan all streams in the left hand side, looking for ones that are also + // in the right. Each time we find one, remove it. When we're done, Q + // should contain all the streams that are in the right but not in the left. + StreamPurposeProvider StreamProvider(28); + for (const auto &P : PI) { + typedef decltype(PI) ContainerType; + typedef typename ContainerType::value_type value_type; + + auto Iter = llvm::find_if(QI, [P, &StreamProvider](const value_type &V) { + DiffResult Result = StreamProvider.compare(P.value(), V.value()); + return Result == DiffResult::EQUIVALENT || + Result == DiffResult::IDENTICAL; + }); + + if (Iter == QI.end()) { + D.printExplicit(StreamProvider.format(P.value(), false), + DiffResult::DIFFERENT, P.index(), "(not present)"); + continue; + } + + D.print<EquivalentDiffProvider>(StreamProvider.format(P.value(), false), + P.index(), Iter->index()); + QI.erase(Iter); + } + + for (const auto &Q : QI) { + D.printExplicit(StreamProvider.format(Q.value(), true), + DiffResult::DIFFERENT, "(not present)", Q.index()); + } + + return Error::success(); +} + +Error DiffStyle::diffStringTable() { + DiffPrinter D(2, "String Table", 30, 20, opts::diff::PrintResultColumn, + opts::diff::PrintValueColumns, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + truncateStringFront(File1.getFilePath(), 18), + truncateStringFront(File2.getFilePath(), 18)); + + auto ExpectedST1 = File1.getStringTable(); + auto ExpectedST2 = File2.getStringTable(); + bool Has1 = !!ExpectedST1; + bool Has2 = !!ExpectedST2; + std::string Count1 = Has1 ? llvm::utostr(ExpectedST1->getNameCount()) + : "(string table not present)"; + std::string Count2 = Has2 ? llvm::utostr(ExpectedST2->getNameCount()) + : "(string table not present)"; + D.print("Number of Strings", Count1, Count2); + + if (!Has1 || !Has2) { + consumeError(ExpectedST1.takeError()); + consumeError(ExpectedST2.takeError()); + return Error::success(); + } + + auto &ST1 = *ExpectedST1; + auto &ST2 = *ExpectedST2; + + D.print("Hash Version", ST1.getHashVersion(), ST2.getHashVersion()); + D.print("Byte Size", ST1.getByteSize(), ST2.getByteSize()); + D.print("Signature", ST1.getSignature(), ST2.getSignature()); + + // Both have a valid string table, dive in and compare individual strings. + + auto IdList1 = ST1.name_ids(); + auto IdList2 = ST2.name_ids(); + StringSet<> LS; + StringSet<> RS; + uint32_t Empty1 = 0; + uint32_t Empty2 = 0; + for (auto ID : IdList1) { + auto S = ST1.getStringForID(ID); + if (!S) + return S.takeError(); + if (S->empty()) + ++Empty1; + else + LS.insert(*S); + } + for (auto ID : IdList2) { + auto S = ST2.getStringForID(ID); + if (!S) + return S.takeError(); + if (S->empty()) + ++Empty2; + else + RS.insert(*S); + } + D.print("Empty Strings", Empty1, Empty2); + + for (const auto &S : LS) { + auto R = RS.find(S.getKey()); + std::string Truncated = truncateStringMiddle(S.getKey(), 28); + uint32_t I = cantFail(ST1.getIDForString(S.getKey())); + if (R == RS.end()) { + D.printExplicit(Truncated, DiffResult::DIFFERENT, I, "(not present)"); + continue; + } + + uint32_t J = cantFail(ST2.getIDForString(R->getKey())); + D.print<EquivalentDiffProvider>(Truncated, I, J); + RS.erase(R); + } + + for (const auto &S : RS) { + auto L = LS.find(S.getKey()); + std::string Truncated = truncateStringMiddle(S.getKey(), 28); + uint32_t J = cantFail(ST2.getIDForString(S.getKey())); + if (L == LS.end()) { + D.printExplicit(Truncated, DiffResult::DIFFERENT, "(not present)", J); + continue; + } + + uint32_t I = cantFail(ST1.getIDForString(L->getKey())); + D.print<EquivalentDiffProvider>(Truncated, I, J); + } + return Error::success(); +} + +Error DiffStyle::diffFreePageMap() { return Error::success(); } + +Error DiffStyle::diffInfoStream() { + DiffPrinter D(2, "PDB Stream", 22, 40, opts::diff::PrintResultColumn, + opts::diff::PrintValueColumns, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + truncateStringFront(File1.getFilePath(), 38), + truncateStringFront(File2.getFilePath(), 38)); + + auto ExpectedInfo1 = File1.getPDBInfoStream(); + auto ExpectedInfo2 = File2.getPDBInfoStream(); + + bool Has1 = !!ExpectedInfo1; + bool Has2 = !!ExpectedInfo2; + if (!(Has1 && Has2)) { + std::string L = Has1 ? "(present)" : "(not present)"; + std::string R = Has2 ? "(present)" : "(not present)"; + D.print("Stream", L, R); + + consumeError(ExpectedInfo1.takeError()); + consumeError(ExpectedInfo2.takeError()); + return Error::success(); + } + + auto &IS1 = *ExpectedInfo1; + auto &IS2 = *ExpectedInfo2; + D.print("Stream Size", IS1.getStreamSize(), IS2.getStreamSize()); + D.print("Age", IS1.getAge(), IS2.getAge()); + D.print("Guid", IS1.getGuid(), IS2.getGuid()); + D.print("Signature", IS1.getSignature(), IS2.getSignature()); + D.print("Version", IS1.getVersion(), IS2.getVersion()); + D.diffUnorderedArray("Feature", IS1.getFeatureSignatures(), + IS2.getFeatureSignatures()); + D.print("Named Stream Size", IS1.getNamedStreamMapByteSize(), + IS2.getNamedStreamMapByteSize()); + StringMap<uint32_t> NSL = IS1.getNamedStreams().getStringMap(); + StringMap<uint32_t> NSR = IS2.getNamedStreams().getStringMap(); + D.diffUnorderedMap<EquivalentDiffProvider>("Named Stream", NSL, NSR); + return Error::success(); +} + +static std::vector<std::pair<uint32_t, DbiModuleDescriptor>> +getModuleDescriptors(const DbiModuleList &ML) { + std::vector<std::pair<uint32_t, DbiModuleDescriptor>> List; + List.reserve(ML.getModuleCount()); + for (uint32_t I = 0; I < ML.getModuleCount(); ++I) + List.emplace_back(I, ML.getModuleDescriptor(I)); + return List; +} + +static void +diffOneModule(DiffPrinter &D, + const std::pair<uint32_t, DbiModuleDescriptor> Item, + std::vector<std::pair<uint32_t, DbiModuleDescriptor>> &Other, + bool ItemIsRight) { + StreamPurposeProvider HeaderProvider(70); + std::pair<StreamPurpose, std::string> Header; + Header.first = StreamPurpose::ModuleStream; + Header.second = Item.second.getModuleName(); + D.printFullRow(HeaderProvider.format(Header, ItemIsRight)); + + const auto *L = &Item; + + BinaryPathProvider PathProvider(28); + auto Iter = llvm::find_if( + Other, [&PathProvider, ItemIsRight, + L](const std::pair<uint32_t, DbiModuleDescriptor> &Other) { + const auto *Left = L; + const auto *Right = &Other; + if (ItemIsRight) + std::swap(Left, Right); + DiffResult Result = PathProvider.compare(Left->second.getModuleName(), + Right->second.getModuleName()); + return Result == DiffResult::EQUIVALENT || + Result == DiffResult::IDENTICAL; + }); + if (Iter == Other.end()) { + // We didn't find this module at all on the other side. Just print one row + // and continue. + D.print<ModiProvider>("- Modi", Item.first, None); + return; + } + + // We did find this module. Go through and compare each field. + const auto *R = &*Iter; + if (ItemIsRight) + std::swap(L, R); + + D.print<ModiProvider>("- Modi", L->first, R->first); + D.print<BinaryPathProvider>("- Obj File Name", L->second.getObjFileName(), + R->second.getObjFileName(), PathProvider); + D.print<StreamNumberProvider>("- Debug Stream", + L->second.getModuleStreamIndex(), + R->second.getModuleStreamIndex()); + D.print("- C11 Byte Size", L->second.getC11LineInfoByteSize(), + R->second.getC11LineInfoByteSize()); + D.print("- C13 Byte Size", L->second.getC13LineInfoByteSize(), + R->second.getC13LineInfoByteSize()); + D.print("- # of files", L->second.getNumberOfFiles(), + R->second.getNumberOfFiles()); + D.print("- Pdb File Path Index", L->second.getPdbFilePathNameIndex(), + R->second.getPdbFilePathNameIndex()); + D.print("- Source File Name Index", L->second.getSourceFileNameIndex(), + R->second.getSourceFileNameIndex()); + D.print("- Symbol Byte Size", L->second.getSymbolDebugInfoByteSize(), + R->second.getSymbolDebugInfoByteSize()); + Other.erase(Iter); +} + +Error DiffStyle::diffDbiStream() { + DiffPrinter D(2, "DBI Stream", 40, 30, opts::diff::PrintResultColumn, + opts::diff::PrintValueColumns, outs()); + D.printExplicit("File", DiffResult::UNSPECIFIED, + truncateStringFront(File1.getFilePath(), 28), + truncateStringFront(File2.getFilePath(), 28)); + + auto ExpectedDbi1 = File1.getPDBDbiStream(); + auto ExpectedDbi2 = File2.getPDBDbiStream(); + + bool Has1 = !!ExpectedDbi1; + bool Has2 = !!ExpectedDbi2; + if (!(Has1 && Has2)) { + std::string L = Has1 ? "(present)" : "(not present)"; + std::string R = Has2 ? "(present)" : "(not present)"; + D.print("Stream", L, R); + + consumeError(ExpectedDbi1.takeError()); + consumeError(ExpectedDbi2.takeError()); + return Error::success(); + } + + auto &DL = *ExpectedDbi1; + auto &DR = *ExpectedDbi2; + + D.print("Dbi Version", (uint32_t)DL.getDbiVersion(), + (uint32_t)DR.getDbiVersion()); + D.print("Age", DL.getAge(), DR.getAge()); + D.print("Machine", (uint16_t)DL.getMachineType(), + (uint16_t)DR.getMachineType()); + D.print("Flags", DL.getFlags(), DR.getFlags()); + D.print("Build Major", DL.getBuildMajorVersion(), DR.getBuildMajorVersion()); + D.print("Build Minor", DL.getBuildMinorVersion(), DR.getBuildMinorVersion()); + D.print("Build Number", DL.getBuildNumber(), DR.getBuildNumber()); + D.print("PDB DLL Version", DL.getPdbDllVersion(), DR.getPdbDllVersion()); + D.print("PDB DLL RBLD", DL.getPdbDllRbld(), DR.getPdbDllRbld()); + D.print<StreamNumberProvider>("DBG (FPO)", + DL.getDebugStreamIndex(DbgHeaderType::FPO), + DR.getDebugStreamIndex(DbgHeaderType::FPO)); + D.print<StreamNumberProvider>( + "DBG (Exception)", DL.getDebugStreamIndex(DbgHeaderType::Exception), + DR.getDebugStreamIndex(DbgHeaderType::Exception)); + D.print<StreamNumberProvider>("DBG (Fixup)", + DL.getDebugStreamIndex(DbgHeaderType::Fixup), + DR.getDebugStreamIndex(DbgHeaderType::Fixup)); + D.print<StreamNumberProvider>( + "DBG (OmapToSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapToSrc), + DR.getDebugStreamIndex(DbgHeaderType::OmapToSrc)); + D.print<StreamNumberProvider>( + "DBG (OmapFromSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapFromSrc), + DR.getDebugStreamIndex(DbgHeaderType::OmapFromSrc)); + D.print<StreamNumberProvider>( + "DBG (SectionHdr)", DL.getDebugStreamIndex(DbgHeaderType::SectionHdr), + DR.getDebugStreamIndex(DbgHeaderType::SectionHdr)); + D.print<StreamNumberProvider>( + "DBG (TokenRidMap)", DL.getDebugStreamIndex(DbgHeaderType::TokenRidMap), + DR.getDebugStreamIndex(DbgHeaderType::TokenRidMap)); + D.print<StreamNumberProvider>("DBG (Xdata)", + DL.getDebugStreamIndex(DbgHeaderType::Xdata), + DR.getDebugStreamIndex(DbgHeaderType::Xdata)); + D.print<StreamNumberProvider>("DBG (Pdata)", + DL.getDebugStreamIndex(DbgHeaderType::Pdata), + DR.getDebugStreamIndex(DbgHeaderType::Pdata)); + D.print<StreamNumberProvider>("DBG (NewFPO)", + DL.getDebugStreamIndex(DbgHeaderType::NewFPO), + DR.getDebugStreamIndex(DbgHeaderType::NewFPO)); + D.print<StreamNumberProvider>( + "DBG (SectionHdrOrig)", + DL.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig), + DR.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)); + D.print<StreamNumberProvider>("Globals Stream", + DL.getGlobalSymbolStreamIndex(), + DR.getGlobalSymbolStreamIndex()); + D.print<StreamNumberProvider>("Publics Stream", + DL.getPublicSymbolStreamIndex(), + DR.getPublicSymbolStreamIndex()); + D.print<StreamNumberProvider>("Symbol Records", DL.getSymRecordStreamIndex(), + DR.getSymRecordStreamIndex()); + D.print("Has CTypes", DL.hasCTypes(), DR.hasCTypes()); + D.print("Is Incrementally Linked", DL.isIncrementallyLinked(), + DR.isIncrementallyLinked()); + D.print("Is Stripped", DL.isStripped(), DR.isStripped()); + const DbiModuleList &ML = DL.modules(); + const DbiModuleList &MR = DR.modules(); + D.print("Module Count", ML.getModuleCount(), MR.getModuleCount()); + D.print("Source File Count", ML.getSourceFileCount(), + MR.getSourceFileCount()); + auto MDL = getModuleDescriptors(ML); + auto MDR = getModuleDescriptors(MR); + // Scan all module descriptors from the left, and look for corresponding + // module descriptors on the right. + for (const auto &L : MDL) + diffOneModule(D, L, MDR, false); + + for (const auto &R : MDR) + diffOneModule(D, R, MDL, true); + + 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/contrib/llvm/tools/llvm-pdbutil/Diff.h b/contrib/llvm/tools/llvm-pdbutil/Diff.h new file mode 100644 index 0000000..6037576 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/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/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.cpp b/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.cpp new file mode 100644 index 0000000..dd61cc1 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.cpp @@ -0,0 +1,147 @@ + +#include "DiffPrinter.h" + +#include "llvm/Support/FormatAdapters.h" + +using namespace llvm; +using namespace llvm::pdb; + +namespace { +struct Colorize { + Colorize(raw_ostream &OS, DiffResult Result) : OS(OS) { + if (!OS.has_colors()) + return; + switch (Result) { + case DiffResult::IDENTICAL: + OS.changeColor(raw_ostream::Colors::GREEN, false); + break; + case DiffResult::EQUIVALENT: + OS.changeColor(raw_ostream::Colors::YELLOW, true); + break; + default: + OS.changeColor(raw_ostream::Colors::RED, false); + break; + } + } + + ~Colorize() { + if (OS.has_colors()) + OS.resetColor(); + } + + raw_ostream &OS; +}; +} + +DiffPrinter::DiffPrinter(uint32_t Indent, StringRef Header, + uint32_t PropertyWidth, uint32_t FieldWidth, + bool Result, bool Fields, raw_ostream &Stream) + : PrintResult(Result), PrintValues(Fields), Indent(Indent), + PropertyWidth(PropertyWidth), FieldWidth(FieldWidth), OS(Stream) { + printHeaderRow(); + printFullRow(Header); +} + +DiffPrinter::~DiffPrinter() {} + +uint32_t DiffPrinter::tableWidth() const { + // `|` + uint32_t W = 1; + + // `<width>|` + W += PropertyWidth + 1; + + if (PrintResult) { + // ` I |` + W += 4; + } + + if (PrintValues) { + // `<width>|<width>|` + W += 2 * (FieldWidth + 1); + } + return W; +} + +void DiffPrinter::printFullRow(StringRef Text) { + newLine(); + printValue(Text, DiffResult::UNSPECIFIED, AlignStyle::Center, + tableWidth() - 2, true); + printSeparatorRow(); +} + +void DiffPrinter::printSeparatorRow() { + newLine(); + OS << formatv("{0}", fmt_repeat('-', PropertyWidth)); + if (PrintResult) { + OS << '+'; + OS << formatv("{0}", fmt_repeat('-', 3)); + } + if (PrintValues) { + OS << '+'; + OS << formatv("{0}", fmt_repeat('-', FieldWidth)); + OS << '+'; + OS << formatv("{0}", fmt_repeat('-', FieldWidth)); + } + OS << '|'; +} + +void DiffPrinter::printHeaderRow() { + newLine('-'); + OS << formatv("{0}", fmt_repeat('-', tableWidth() - 1)); +} + +void DiffPrinter::newLine(char InitialChar) { + OS << "\n"; + OS.indent(Indent) << InitialChar; +} + +void DiffPrinter::printExplicit(StringRef Property, DiffResult C, + StringRef Left, StringRef Right) { + newLine(); + printValue(Property, DiffResult::UNSPECIFIED, AlignStyle::Right, + PropertyWidth, true); + printResult(C); + printValue(Left, C, AlignStyle::Center, FieldWidth, false); + printValue(Right, C, AlignStyle::Center, FieldWidth, false); + printSeparatorRow(); +} + +void DiffPrinter::printResult(DiffResult Result) { + if (!PrintResult) + return; + switch (Result) { + case DiffResult::DIFFERENT: + printValue("D", Result, AlignStyle::Center, 3, true); + break; + case DiffResult::EQUIVALENT: + printValue("E", Result, AlignStyle::Center, 3, true); + break; + case DiffResult::IDENTICAL: + printValue("I", Result, AlignStyle::Center, 3, true); + break; + case DiffResult::UNSPECIFIED: + printValue(" ", Result, AlignStyle::Center, 3, true); + break; + } +} + +void DiffPrinter::printValue(StringRef Value, DiffResult C, AlignStyle Style, + uint32_t Width, bool Force) { + if (!Force && !PrintValues) + return; + + if (Style == AlignStyle::Right) + --Width; + + std::string FormattedItem = + formatv("{0}", fmt_align(Value, Style, Width)).str(); + if (C != DiffResult::UNSPECIFIED) { + Colorize Color(OS, C); + OS << FormattedItem; + } else + OS << FormattedItem; + if (Style == AlignStyle::Right) + OS << ' '; + OS << '|'; +} diff --git a/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.h b/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.h new file mode 100644 index 0000000..475747d --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.h @@ -0,0 +1,172 @@ +//===- DiffPrinter.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_DIFFPRINTER_H +#define LLVM_TOOLS_LLVMPDBDUMP_DIFFPRINTER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +#include <list> +#include <unordered_set> + +namespace std { +template <> struct hash<llvm::pdb::PdbRaw_FeatureSig> { + typedef llvm::pdb::PdbRaw_FeatureSig argument_type; + typedef std::size_t result_type; + result_type operator()(argument_type Item) const { + return std::hash<uint32_t>{}(uint32_t(Item)); + } +}; +} // namespace std + +namespace llvm { +namespace pdb { + +class PDBFile; + +enum class DiffResult { UNSPECIFIED, IDENTICAL, EQUIVALENT, DIFFERENT }; + +struct IdenticalDiffProvider { + template <typename T, typename U> + DiffResult compare(const T &Left, const U &Right) { + return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::DIFFERENT; + } + + template <typename T> std::string format(const T &Item, bool Right) { + return formatv("{0}", Item).str(); + } +}; + +struct EquivalentDiffProvider { + template <typename T, typename U> + DiffResult compare(const T &Left, const U &Right) { + return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::EQUIVALENT; + } + + template <typename T> std::string format(const T &Item, bool Right) { + return formatv("{0}", Item).str(); + } +}; + +class DiffPrinter { +public: + DiffPrinter(uint32_t Indent, StringRef Header, uint32_t PropertyWidth, + uint32_t FieldWidth, bool Result, bool Values, + raw_ostream &Stream); + ~DiffPrinter(); + + template <typename T, typename U> struct Identical {}; + + template <typename Provider = IdenticalDiffProvider, typename T, typename U> + void print(StringRef Property, const T &Left, const U &Right, + Provider P = Provider()) { + std::string L = P.format(Left, false); + std::string R = P.format(Right, true); + + DiffResult Result = P.compare(Left, Right); + printExplicit(Property, Result, L, R); + } + + void printExplicit(StringRef Property, DiffResult C, StringRef Left, + StringRef Right); + + template <typename T, typename U> + void printExplicit(StringRef Property, DiffResult C, const T &Left, + const U &Right) { + std::string L = formatv("{0}", Left).str(); + std::string R = formatv("{0}", Right).str(); + printExplicit(Property, C, StringRef(L), StringRef(R)); + } + + template <typename T, typename U> + void diffUnorderedArray(StringRef Property, ArrayRef<T> Left, + ArrayRef<U> Right) { + std::unordered_set<T> LS(Left.begin(), Left.end()); + std::unordered_set<U> RS(Right.begin(), Right.end()); + std::string Count1 = formatv("{0} element(s)", Left.size()); + std::string Count2 = formatv("{0} element(s)", Right.size()); + print(std::string(Property) + "s (set)", Count1, Count2); + for (const auto &L : LS) { + auto Iter = RS.find(L); + std::string Text = formatv("{0}", L).str(); + if (Iter == RS.end()) { + print(Property, Text, "(not present)"); + continue; + } + print(Property, Text, Text); + RS.erase(Iter); + } + for (const auto &R : RS) { + auto Iter = LS.find(R); + std::string Text = formatv("{0}", R).str(); + if (Iter == LS.end()) { + print(Property, "(not present)", Text); + continue; + } + print(Property, Text, Text); + } + } + + template <typename ValueProvider = IdenticalDiffProvider, typename T, + typename U> + void diffUnorderedMap(StringRef Property, const StringMap<T> &Left, + const StringMap<U> &Right, + ValueProvider P = ValueProvider()) { + StringMap<U> RightCopy(Right); + + std::string Count1 = formatv("{0} element(s)", Left.size()); + std::string Count2 = formatv("{0} element(s)", Right.size()); + print(std::string(Property) + "s (map)", Count1, Count2); + + for (const auto &L : Left) { + auto Iter = RightCopy.find(L.getKey()); + if (Iter == RightCopy.end()) { + printExplicit(L.getKey(), DiffResult::DIFFERENT, L.getValue(), + "(not present)"); + continue; + } + + print(L.getKey(), L.getValue(), Iter->getValue(), P); + RightCopy.erase(Iter); + } + + for (const auto &R : RightCopy) { + printExplicit(R.getKey(), DiffResult::DIFFERENT, "(not present)", + R.getValue()); + } + } + + void printFullRow(StringRef Text); + +private: + uint32_t tableWidth() const; + + void printHeaderRow(); + void printSeparatorRow(); + void newLine(char InitialChar = '|'); + void printValue(StringRef Value, DiffResult C, AlignStyle Style, + uint32_t Width, bool Force); + void printResult(DiffResult Result); + + bool PrintResult; + bool PrintValues; + uint32_t Indent; + uint32_t PropertyWidth; + uint32_t FieldWidth; + raw_ostream &OS; +}; +} // namespace pdb +} // namespace llvm + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp new file mode 100644 index 0000000..01c7481 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -0,0 +1,1043 @@ +//===- DumpOutputStyle.cpp ------------------------------------ *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DumpOutputStyle.h" + +#include "FormatUtil.h" +#include "MinimalSymbolDumper.h" +#include "MinimalTypeDumper.h" +#include "StreamUtil.h" +#include "llvm-pdbutil.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolDumper.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" +#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/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/ModuleDebugStream.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/TpiHashing.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +#include <unordered_map> + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +DumpOutputStyle::DumpOutputStyle(PDBFile &File) + : File(File), P(2, false, outs()) {} + +Error DumpOutputStyle::dump() { + if (opts::dump::DumpSummary) { + if (auto EC = dumpFileSummary()) + return EC; + P.NewLine(); + } + + if (opts::dump::DumpStreams) { + if (auto EC = dumpStreamSummary()) + return EC; + P.NewLine(); + } + + if (opts::dump::DumpStringTable) { + if (auto EC = dumpStringTable()) + return EC; + P.NewLine(); + } + + if (opts::dump::DumpModules) { + if (auto EC = dumpModules()) + return EC; + } + + if (opts::dump::DumpModuleFiles) { + if (auto EC = dumpModuleFiles()) + return EC; + } + + if (opts::dump::DumpLines) { + if (auto EC = dumpLines()) + return EC; + } + + if (opts::dump::DumpInlineeLines) { + if (auto EC = dumpInlineeLines()) + return EC; + } + + if (opts::dump::DumpXmi) { + if (auto EC = dumpXmi()) + return EC; + } + + if (opts::dump::DumpXme) { + if (auto EC = dumpXme()) + return EC; + } + + if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || + opts::dump::DumpTypeExtras) { + if (auto EC = dumpTpiStream(StreamTPI)) + return EC; + } + + if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() || + opts::dump::DumpIdExtras) { + if (auto EC = dumpTpiStream(StreamIPI)) + return EC; + } + + if (opts::dump::DumpPublics) { + if (auto EC = dumpPublics()) + return EC; + } + + if (opts::dump::DumpSymbols) { + if (auto EC = dumpModuleSyms()) + return EC; + } + + if (opts::dump::DumpSectionContribs) { + if (auto EC = dumpSectionContribs()) + return EC; + } + + if (opts::dump::DumpSectionMap) { + if (auto EC = dumpSectionMap()) + return EC; + } + + return Error::success(); +} + +static void printHeader(LinePrinter &P, const Twine &S) { + P.NewLine(); + P.formatLine("{0,=60}", S); + P.formatLine("{0}", fmt_repeat('=', 60)); +} + +Error DumpOutputStyle::dumpFileSummary() { + printHeader(P, "Summary"); + + ExitOnError Err("Invalid PDB Format"); + + AutoIndent Indent(P); + P.formatLine("Block Size: {0}", File.getBlockSize()); + P.formatLine("Number of blocks: {0}", File.getBlockCount()); + P.formatLine("Number of streams: {0}", File.getNumStreams()); + + auto &PS = Err(File.getPDBInfoStream()); + P.formatLine("Signature: {0}", PS.getSignature()); + P.formatLine("Age: {0}", PS.getAge()); + P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid)); + P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures())); + P.formatLine("Has Debug Info: {0}", File.hasPDBDbiStream()); + P.formatLine("Has Types: {0}", File.hasPDBTpiStream()); + P.formatLine("Has IDs: {0}", File.hasPDBIpiStream()); + P.formatLine("Has Globals: {0}", File.hasPDBGlobalsStream()); + P.formatLine("Has Publics: {0}", File.hasPDBPublicsStream()); + if (File.hasPDBDbiStream()) { + auto &DBI = Err(File.getPDBDbiStream()); + P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked()); + P.formatLine("Has conflicting types: {0}", DBI.hasCTypes()); + P.formatLine("Is stripped: {0}", DBI.isStripped()); + } + + return Error::success(); +} + +Error DumpOutputStyle::dumpStreamSummary() { + printHeader(P, "Streams"); + + if (StreamPurposes.empty()) + discoverStreamPurposes(File, StreamPurposes); + + AutoIndent Indent(P); + uint32_t StreamCount = File.getNumStreams(); + + for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { + P.formatLine( + "Stream {0}: [{1}] ({2} bytes)", + fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)), + StreamPurposes[StreamIdx], File.getStreamByteSize(StreamIdx)); + if (opts::dump::DumpStreamBlocks) { + auto Blocks = File.getStreamBlockList(StreamIdx); + std::vector<uint32_t> BV(Blocks.begin(), Blocks.end()); + P.formatLine(" {0} Blocks: [{1}]", + fmt_repeat(' ', NumDigits(StreamCount)), + make_range(BV.begin(), BV.end())); + } + } + + return Error::success(); +} + +static Expected<ModuleDebugStreamRef> getModuleDebugStream(PDBFile &File, + uint32_t Index) { + ExitOnError Err("Unexpected error"); + + auto &Dbi = Err(File.getPDBDbiStream()); + const auto &Modules = Dbi.modules(); + auto Modi = Modules.getModuleDescriptor(Index); + + uint16_t ModiStream = Modi.getModuleStreamIndex(); + if (ModiStream == kInvalidStreamIndex) + return make_error<RawError>(raw_error_code::no_stream, + "Module stream not present"); + + auto ModStreamData = MappedBlockStream::createIndexedStream( + File.getMsfLayout(), File.getMsfBuffer(), ModiStream, + File.getAllocator()); + + ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); + if (auto EC = ModS.reload()) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid module stream"); + + return std::move(ModS); +} + +static std::string formatChecksumKind(FileChecksumKind Kind) { + switch (Kind) { + RETURN_CASE(FileChecksumKind, None, "None"); + RETURN_CASE(FileChecksumKind, MD5, "MD5"); + RETURN_CASE(FileChecksumKind, SHA1, "SHA-1"); + RETURN_CASE(FileChecksumKind, SHA256, "SHA-256"); + } + return formatUnknownEnum(Kind); +} + +namespace { +class StringsAndChecksumsPrinter { + const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) { + ExitOnError Err("Unexpected error processing modules"); + return Err(File.getStringTable()).getStringTable(); + } + + template <typename... Args> + void formatInternal(LinePrinter &Printer, bool Append, + Args &&... args) const { + if (Append) + Printer.format(std::forward<Args>(args)...); + else + Printer.formatLine(std::forward<Args>(args)...); + } + +public: + StringsAndChecksumsPrinter(PDBFile &File, uint32_t Modi) + : Records(extractStringTable(File)) { + auto MDS = getModuleDebugStream(File, Modi); + if (!MDS) { + consumeError(MDS.takeError()); + return; + } + + DebugStream = llvm::make_unique<ModuleDebugStreamRef>(std::move(*MDS)); + Records.initialize(MDS->subsections()); + if (Records.hasChecksums()) { + for (const auto &Entry : Records.checksums()) { + auto S = Records.strings().getString(Entry.FileNameOffset); + if (!S) + continue; + ChecksumsByFile[*S] = Entry; + } + } + } + + Expected<StringRef> getNameFromStringTable(uint32_t Offset) const { + return Records.strings().getString(Offset); + } + + void formatFromFileName(LinePrinter &Printer, StringRef File, + bool Append = false) const { + auto FC = ChecksumsByFile.find(File); + if (FC == ChecksumsByFile.end()) { + formatInternal(Printer, Append, "- (no checksum) {0}", File); + return; + } + + formatInternal(Printer, Append, "- ({0}: {1}) {2}", + formatChecksumKind(FC->getValue().Kind), + toHex(FC->getValue().Checksum), File); + } + + void formatFromChecksumsOffset(LinePrinter &Printer, uint32_t Offset, + bool Append = false) const { + if (!Records.hasChecksums()) { + formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); + return; + } + + auto Iter = Records.checksums().getArray().at(Offset); + if (Iter == Records.checksums().getArray().end()) { + formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); + return; + } + + uint32_t FO = Iter->FileNameOffset; + auto ExpectedFile = getNameFromStringTable(FO); + if (!ExpectedFile) { + formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); + consumeError(ExpectedFile.takeError()); + return; + } + if (Iter->Kind == FileChecksumKind::None) { + formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile); + } else { + formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile, + formatChecksumKind(Iter->Kind), toHex(Iter->Checksum)); + } + } + + std::unique_ptr<ModuleDebugStreamRef> DebugStream; + StringsAndChecksumsRef Records; + StringMap<FileChecksumEntry> ChecksumsByFile; +}; +} // namespace + +template <typename CallbackT> +static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel, + CallbackT Callback) { + AutoIndent Indent(P); + if (!File.hasPDBDbiStream()) { + P.formatLine("DBI Stream not present"); + return; + } + + ExitOnError Err("Unexpected error processing modules"); + + auto &Stream = Err(File.getPDBDbiStream()); + + const DbiModuleList &Modules = Stream.modules(); + uint32_t Count = Modules.getModuleCount(); + uint32_t Digits = NumDigits(Count); + for (uint32_t I = 0; I < Count; ++I) { + auto Modi = Modules.getModuleDescriptor(I); + P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, Digits), + Modi.getModuleName()); + + StringsAndChecksumsPrinter Strings(File, I); + AutoIndent Indent2(P, IndentLevel); + Callback(I, Strings); + } +} + +template <typename SubsectionT> +static void iterateModuleSubsections( + PDBFile &File, LinePrinter &P, uint32_t IndentLevel, + llvm::function_ref<void(uint32_t, StringsAndChecksumsPrinter &, + SubsectionT &)> + Callback) { + + iterateModules( + File, P, IndentLevel, + [&File, &Callback](uint32_t Modi, StringsAndChecksumsPrinter &Strings) { + auto MDS = getModuleDebugStream(File, Modi); + if (!MDS) { + consumeError(MDS.takeError()); + return; + } + + for (const auto &SS : MDS->subsections()) { + SubsectionT Subsection; + + if (SS.kind() != Subsection.kind()) + continue; + + BinaryStreamReader Reader(SS.getRecordData()); + if (auto EC = Subsection.initialize(Reader)) + continue; + Callback(Modi, Strings, Subsection); + } + }); +} + +Error DumpOutputStyle::dumpModules() { + printHeader(P, "Modules"); + + AutoIndent Indent(P); + if (!File.hasPDBDbiStream()) { + P.formatLine("DBI Stream not present"); + return Error::success(); + } + + ExitOnError Err("Unexpected error processing modules"); + + auto &Stream = Err(File.getPDBDbiStream()); + + const DbiModuleList &Modules = Stream.modules(); + uint32_t Count = Modules.getModuleCount(); + uint32_t Digits = NumDigits(Count); + for (uint32_t I = 0; I < Count; ++I) { + auto Modi = Modules.getModuleDescriptor(I); + P.formatLine("Mod {0:4} | Name: `{1}`: ", + fmt_align(I, AlignStyle::Right, Digits), Modi.getModuleName()); + P.formatLine(" Obj: `{0}`: ", Modi.getObjFileName()); + P.formatLine(" debug stream: {0}, # files: {1}, has ec info: {2}", + Modi.getModuleStreamIndex(), Modi.getNumberOfFiles(), + Modi.hasECInfo()); + StringRef PdbFilePath = + Err(Stream.getECName(Modi.getPdbFilePathNameIndex())); + StringRef SrcFilePath = + Err(Stream.getECName(Modi.getSourceFileNameIndex())); + P.formatLine(" pdb file ni: {0} `{1}`, src file ni: {2} `{3}`", + Modi.getPdbFilePathNameIndex(), PdbFilePath, + Modi.getSourceFileNameIndex(), SrcFilePath); + } + return Error::success(); +} + +Error DumpOutputStyle::dumpModuleFiles() { + printHeader(P, "Files"); + + ExitOnError Err("Unexpected error processing modules"); + + iterateModules( + File, P, 11, + [this, &Err](uint32_t Modi, StringsAndChecksumsPrinter &Strings) { + auto &Stream = Err(File.getPDBDbiStream()); + + const DbiModuleList &Modules = Stream.modules(); + for (const auto &F : Modules.source_files(Modi)) { + Strings.formatFromFileName(P, F); + } + }); + return Error::success(); +} + +static void typesetLinesAndColumns(PDBFile &File, LinePrinter &P, + uint32_t Start, const LineColumnEntry &E) { + const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number + uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5; + + // Let's try to keep it under 100 characters + constexpr uint32_t kMaxRowLength = 100; + // At least 3 spaces between columns. + uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3); + uint32_t ItemsLeft = E.LineNumbers.size(); + auto LineIter = E.LineNumbers.begin(); + while (ItemsLeft != 0) { + uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow); + for (uint32_t I = 0; I < RowColumns; ++I) { + LineInfo Line(LineIter->Flags); + std::string LineStr; + if (Line.isAlwaysStepInto()) + LineStr = "ASI"; + else if (Line.isNeverStepInto()) + LineStr = "NSI"; + else + LineStr = utostr(Line.getStartLine()); + char Statement = Line.isStatement() ? ' ' : '!'; + P.format("{0} {1:X-} {2} ", + fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber), + fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'), + Statement); + ++LineIter; + --ItemsLeft; + } + P.NewLine(); + } +} + +Error DumpOutputStyle::dumpLines() { + printHeader(P, "Lines"); + + uint32_t LastModi = UINT32_MAX; + uint32_t LastNameIndex = UINT32_MAX; + iterateModuleSubsections<DebugLinesSubsectionRef>( + File, P, 4, + [this, &LastModi, &LastNameIndex](uint32_t Modi, + StringsAndChecksumsPrinter &Strings, + DebugLinesSubsectionRef &Lines) { + uint16_t Segment = Lines.header()->RelocSegment; + uint32_t Begin = Lines.header()->RelocOffset; + uint32_t End = Begin + Lines.header()->CodeSize; + for (const auto &Block : Lines) { + if (LastModi != Modi || LastNameIndex != Block.NameIndex) { + LastModi = Modi; + LastNameIndex = Block.NameIndex; + Strings.formatFromChecksumsOffset(P, Block.NameIndex); + } + + AutoIndent Indent(P, 2); + P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End); + uint32_t Count = Block.LineNumbers.size(); + if (Lines.hasColumnInfo()) + P.format("line/column/addr entries = {0}", Count); + else + P.format("line/addr entries = {0}", Count); + + P.NewLine(); + typesetLinesAndColumns(File, P, Begin, Block); + } + }); + + return Error::success(); +} + +Error DumpOutputStyle::dumpInlineeLines() { + printHeader(P, "Inlinee Lines"); + + iterateModuleSubsections<DebugInlineeLinesSubsectionRef>( + File, P, 2, + [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, + DebugInlineeLinesSubsectionRef &Lines) { + P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File"); + for (const auto &Entry : Lines) { + P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee, + fmtle(Entry.Header->SourceLineNum)); + Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true); + } + P.NewLine(); + }); + + return Error::success(); +} + +Error DumpOutputStyle::dumpXmi() { + printHeader(P, "Cross Module Imports"); + iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>( + File, P, 2, + [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, + DebugCrossModuleImportsSubsectionRef &Imports) { + P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs"); + + for (const auto &Xmi : Imports) { + auto ExpectedModule = + Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset); + StringRef Module; + SmallString<32> ModuleStorage; + if (!ExpectedModule) { + Module = "(unknown module)"; + consumeError(ExpectedModule.takeError()); + } else + Module = *ExpectedModule; + if (Module.size() > 32) { + ModuleStorage = "..."; + ModuleStorage += Module.take_back(32 - 3); + Module = ModuleStorage; + } + std::vector<std::string> TIs; + for (const auto I : Xmi.Imports) + TIs.push_back(formatv("{0,+10:X+}", fmtle(I))); + std::string Result = + typesetItemList(TIs, P.getIndentLevel() + 35, 12, " "); + P.formatLine("{0,+32} | {1}", Module, Result); + } + }); + + return Error::success(); +} + +Error DumpOutputStyle::dumpXme() { + printHeader(P, "Cross Module Exports"); + + iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>( + File, P, 2, + [this](uint32_t Modi, StringsAndChecksumsPrinter &Strings, + DebugCrossModuleExportsSubsectionRef &Exports) { + P.formatLine("{0,-10} | {1}", "Local ID", "Global ID"); + for (const auto &Export : Exports) { + P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local), + TypeIndex(Export.Global)); + } + }); + + return Error::success(); +} + +Error DumpOutputStyle::dumpStringTable() { + printHeader(P, "String Table"); + + AutoIndent Indent(P); + auto IS = File.getStringTable(); + if (!IS) { + P.formatLine("Not present in file"); + consumeError(IS.takeError()); + return Error::success(); + } + + if (IS->name_ids().empty()) { + P.formatLine("Empty"); + return Error::success(); + } + + auto MaxID = std::max_element(IS->name_ids().begin(), IS->name_ids().end()); + uint32_t Digits = NumDigits(*MaxID); + + P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits), + "String"); + + std::vector<uint32_t> SortedIDs(IS->name_ids().begin(), IS->name_ids().end()); + std::sort(SortedIDs.begin(), SortedIDs.end()); + for (uint32_t I : SortedIDs) { + auto ES = IS->getStringForID(I); + llvm::SmallString<32> Str; + if (!ES) { + consumeError(ES.takeError()); + Str = "Error reading string"; + } else if (!ES->empty()) { + Str.append("'"); + Str.append(*ES); + Str.append("'"); + } + + if (!Str.empty()) + P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), Str); + } + return Error::success(); +} + +static void buildDepSet(LazyRandomTypeCollection &Types, + ArrayRef<TypeIndex> Indices, + std::map<TypeIndex, CVType> &DepSet) { + SmallVector<TypeIndex, 4> DepList; + for (const auto &I : Indices) { + TypeIndex TI(I); + if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType()) + continue; + + CVType Type = Types.getType(TI); + DepSet[TI] = Type; + codeview::discoverTypeIndices(Type, DepList); + buildDepSet(Types, DepList, DepSet); + } +} + +static void dumpFullTypeStream(LinePrinter &Printer, + LazyRandomTypeCollection &Types, + TpiStream &Stream, bool Bytes, bool Extras) { + Printer.formatLine("Showing {0:N} records", Stream.getNumTypeRecords()); + uint32_t Width = + NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); + + MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, + Stream.getNumHashBuckets(), Stream.getHashValues()); + + if (auto EC = codeview::visitTypeStream(Types, V)) { + Printer.formatLine("An error occurred dumping type records: {0}", + toString(std::move(EC))); + } +} + +static void dumpPartialTypeStream(LinePrinter &Printer, + LazyRandomTypeCollection &Types, + TpiStream &Stream, ArrayRef<TypeIndex> TiList, + bool Bytes, bool Extras, bool Deps) { + uint32_t Width = + NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); + + MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, + Stream.getNumHashBuckets(), Stream.getHashValues()); + + if (opts::dump::DumpTypeDependents) { + // If we need to dump all dependents, then iterate each index and find + // all dependents, adding them to a map ordered by TypeIndex. + std::map<TypeIndex, CVType> DepSet; + buildDepSet(Types, TiList, DepSet); + + Printer.formatLine( + "Showing {0:N} records and their dependents ({1:N} records total)", + TiList.size(), DepSet.size()); + + for (auto &Dep : DepSet) { + if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V)) + Printer.formatLine("An error occurred dumping type record {0}: {1}", + Dep.first, toString(std::move(EC))); + } + } else { + Printer.formatLine("Showing {0:N} records.", TiList.size()); + + for (const auto &I : TiList) { + TypeIndex TI(I); + CVType Type = Types.getType(TI); + if (auto EC = codeview::visitTypeRecord(Type, TI, V)) + Printer.formatLine("An error occurred dumping type record {0}: {1}", TI, + toString(std::move(EC))); + } + } +} + +Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { + assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); + + bool Present = false; + bool DumpTypes = false; + bool DumpBytes = false; + bool DumpExtras = false; + std::vector<uint32_t> Indices; + if (StreamIdx == StreamTPI) { + printHeader(P, "Types (TPI Stream)"); + Present = File.hasPDBTpiStream(); + DumpTypes = opts::dump::DumpTypes; + DumpBytes = opts::dump::DumpTypeData; + DumpExtras = opts::dump::DumpTypeExtras; + Indices.assign(opts::dump::DumpTypeIndex.begin(), + opts::dump::DumpTypeIndex.end()); + } else if (StreamIdx == StreamIPI) { + printHeader(P, "Types (IPI Stream)"); + Present = File.hasPDBIpiStream(); + DumpTypes = opts::dump::DumpIds; + DumpBytes = opts::dump::DumpIdData; + DumpExtras = opts::dump::DumpIdExtras; + Indices.assign(opts::dump::DumpIdIndex.begin(), + opts::dump::DumpIdIndex.end()); + } + + AutoIndent Indent(P); + if (!Present) { + P.formatLine("Stream not present"); + return Error::success(); + } + + ExitOnError Err("Unexpected error processing types"); + + auto &Stream = Err((StreamIdx == StreamTPI) ? File.getPDBTpiStream() + : File.getPDBIpiStream()); + + auto &Types = Err(initializeTypes(StreamIdx)); + + if (DumpTypes || !Indices.empty()) { + if (Indices.empty()) + dumpFullTypeStream(P, Types, Stream, DumpBytes, DumpExtras); + else { + std::vector<TypeIndex> TiList(Indices.begin(), Indices.end()); + dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras, + opts::dump::DumpTypeDependents); + } + } + + if (DumpExtras) { + P.NewLine(); + auto IndexOffsets = Stream.getTypeIndexOffsets(); + P.formatLine("Type Index Offsets:"); + for (const auto &IO : IndexOffsets) { + AutoIndent Indent2(P); + P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset)); + } + + P.NewLine(); + P.formatLine("Hash Adjusters:"); + auto &Adjusters = Stream.getHashAdjusters(); + auto &Strings = Err(File.getStringTable()); + for (const auto &A : Adjusters) { + AutoIndent Indent2(P); + auto ExpectedStr = Strings.getStringForID(A.first); + TypeIndex TI(A.second); + if (ExpectedStr) + P.formatLine("`{0}` -> {1}", *ExpectedStr, TI); + else { + P.formatLine("unknown str id ({0}) -> {1}", A.first, TI); + consumeError(ExpectedStr.takeError()); + } + } + } + return Error::success(); +} + +Expected<codeview::LazyRandomTypeCollection &> +DumpOutputStyle::initializeTypes(uint32_t SN) { + auto &TypeCollection = (SN == StreamTPI) ? TpiTypes : IpiTypes; + auto Tpi = + (SN == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream(); + if (!Tpi) + return Tpi.takeError(); + + if (!TypeCollection) { + auto &Types = Tpi->typeArray(); + uint32_t Count = Tpi->getNumTypeRecords(); + auto Offsets = Tpi->getTypeIndexOffsets(); + TypeCollection = + llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets); + } + + return *TypeCollection; +} + +Error DumpOutputStyle::dumpModuleSyms() { + printHeader(P, "Symbols"); + + AutoIndent Indent(P); + if (!File.hasPDBDbiStream()) { + P.formatLine("DBI Stream not present"); + return Error::success(); + } + + ExitOnError Err("Unexpected error processing symbols"); + + auto &Stream = Err(File.getPDBDbiStream()); + + auto &Types = Err(initializeTypes(StreamTPI)); + + const DbiModuleList &Modules = Stream.modules(); + uint32_t Count = Modules.getModuleCount(); + uint32_t Digits = NumDigits(Count); + for (uint32_t I = 0; I < Count; ++I) { + auto Modi = Modules.getModuleDescriptor(I); + P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, Digits), + Modi.getModuleName()); + uint16_t ModiStream = Modi.getModuleStreamIndex(); + if (ModiStream == kInvalidStreamIndex) { + P.formatLine(" <symbols not present>"); + continue; + } + auto ModStreamData = MappedBlockStream::createIndexedStream( + File.getMsfLayout(), File.getMsfBuffer(), ModiStream, + File.getAllocator()); + + ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData)); + if (auto EC = ModS.reload()) { + P.formatLine("Error loading module stream {0}. {1}", I, + toString(std::move(EC))); + continue; + } + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + auto SS = ModS.getSymbolsSubstream(); + if (auto EC = Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) { + P.formatLine("Error while processing symbol records. {0}", + toString(std::move(EC))); + continue; + } + } + return Error::success(); +} + +Error DumpOutputStyle::dumpPublics() { + printHeader(P, "Public Symbols"); + + AutoIndent Indent(P); + if (!File.hasPDBPublicsStream()) { + P.formatLine("Publics stream not present"); + return Error::success(); + } + + ExitOnError Err("Error dumping publics stream"); + + auto &Types = Err(initializeTypes(StreamTPI)); + auto &Publics = Err(File.getPDBPublicsStream()); + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + + auto ExpectedSymbols = Publics.getSymbolArray(); + if (!ExpectedSymbols) { + P.formatLine("Could not read public symbol record stream"); + return Error::success(); + } + + if (auto EC = Visitor.visitSymbolStream(*ExpectedSymbols, 0)) + P.formatLine("Error while processing public symbol records. {0}", + toString(std::move(EC))); + + return Error::success(); +} + +static std::string formatSectionCharacteristics(uint32_t IndentLevel, + uint32_t C) { + using SC = COFF::SectionCharacteristics; + std::vector<std::string> Opts; + if (C == COFF::SC_Invalid) + return "invalid"; + if (C == 0) + return "none"; + + PUSH_FLAG(SC, IMAGE_SCN_TYPE_NOLOAD, C, "IMAGE_SCN_TYPE_NOLOAD"); + PUSH_FLAG(SC, IMAGE_SCN_TYPE_NO_PAD, C, "IMAGE_SCN_TYPE_NO_PAD"); + PUSH_FLAG(SC, IMAGE_SCN_CNT_CODE, C, "IMAGE_SCN_CNT_CODE"); + PUSH_FLAG(SC, IMAGE_SCN_CNT_INITIALIZED_DATA, C, + "IMAGE_SCN_CNT_INITIALIZED_DATA"); + PUSH_FLAG(SC, IMAGE_SCN_CNT_UNINITIALIZED_DATA, C, + "IMAGE_SCN_CNT_UNINITIALIZED_DATA"); + PUSH_FLAG(SC, IMAGE_SCN_LNK_OTHER, C, "IMAGE_SCN_LNK_OTHER"); + PUSH_FLAG(SC, IMAGE_SCN_LNK_INFO, C, "IMAGE_SCN_LNK_INFO"); + PUSH_FLAG(SC, IMAGE_SCN_LNK_REMOVE, C, "IMAGE_SCN_LNK_REMOVE"); + PUSH_FLAG(SC, IMAGE_SCN_LNK_COMDAT, C, "IMAGE_SCN_LNK_COMDAT"); + PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_PURGEABLE, C, "IMAGE_SCN_MEM_PURGEABLE"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_16BIT, C, "IMAGE_SCN_MEM_16BIT"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_LOCKED, C, "IMAGE_SCN_MEM_LOCKED"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_PRELOAD, C, "IMAGE_SCN_MEM_PRELOAD"); + PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); + PUSH_FLAG(SC, IMAGE_SCN_GPREL, C, "IMAGE_SCN_GPREL"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1BYTES, C, + "IMAGE_SCN_ALIGN_1BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2BYTES, C, + "IMAGE_SCN_ALIGN_2BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4BYTES, C, + "IMAGE_SCN_ALIGN_4BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8BYTES, C, + "IMAGE_SCN_ALIGN_8BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_16BYTES, C, + "IMAGE_SCN_ALIGN_16BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_32BYTES, C, + "IMAGE_SCN_ALIGN_32BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_64BYTES, C, + "IMAGE_SCN_ALIGN_64BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_128BYTES, C, + "IMAGE_SCN_ALIGN_128BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_256BYTES, C, + "IMAGE_SCN_ALIGN_256BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_512BYTES, C, + "IMAGE_SCN_ALIGN_512BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_1024BYTES, C, + "IMAGE_SCN_ALIGN_1024BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_2048BYTES, C, + "IMAGE_SCN_ALIGN_2048BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_4096BYTES, C, + "IMAGE_SCN_ALIGN_4096BYTES"); + PUSH_MASKED_FLAG(SC, 0xF00000, IMAGE_SCN_ALIGN_8192BYTES, C, + "IMAGE_SCN_ALIGN_8192BYTES"); + PUSH_FLAG(SC, IMAGE_SCN_LNK_NRELOC_OVFL, C, "IMAGE_SCN_LNK_NRELOC_OVFL"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_DISCARDABLE, C, "IMAGE_SCN_MEM_DISCARDABLE"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_NOT_CACHED, C, "IMAGE_SCN_MEM_NOT_CACHED"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_NOT_PAGED, C, "IMAGE_SCN_MEM_NOT_PAGED"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_SHARED, C, "IMAGE_SCN_MEM_SHARED"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_EXECUTE, C, "IMAGE_SCN_MEM_EXECUTE"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_READ, C, "IMAGE_SCN_MEM_READ"); + PUSH_FLAG(SC, IMAGE_SCN_MEM_WRITE, C, "IMAGE_SCN_MEM_WRITE"); + return typesetItemList(Opts, IndentLevel, 3, " | "); +} + +static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel, + OMFSegDescFlags Flags) { + std::vector<std::string> Opts; + if (Flags == OMFSegDescFlags::None) + return "none"; + + PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read"); + PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write"); + PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute"); + PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr"); + PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector"); + PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr"); + PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group"); + return typesetItemList(Opts, IndentLevel, 4, " | "); +} + +Error DumpOutputStyle::dumpSectionContribs() { + printHeader(P, "Section Contributions"); + ExitOnError Err("Error dumping publics stream"); + + AutoIndent Indent(P); + if (!File.hasPDBDbiStream()) { + P.formatLine( + "Section contribs require a DBI Stream, which could not be loaded"); + return Error::success(); + } + + auto &Dbi = Err(File.getPDBDbiStream()); + + class Visitor : public ISectionContribVisitor { + public: + Visitor(LinePrinter &P) : P(P) {} + void visit(const SectionContrib &SC) override { + P.formatLine( + "SC | mod = {2}, {0}, size = {1}, data crc = {3}, reloc crc = {4}", + formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size), fmtle(SC.Imod), + fmtle(SC.DataCrc), fmtle(SC.RelocCrc)); + P.formatLine(" {0}", + formatSectionCharacteristics(P.getIndentLevel() + 6, + SC.Characteristics)); + } + void visit(const SectionContrib2 &SC) override { + P.formatLine("SC2 | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " + "crc = {4}, coff section = {5}", + formatSegmentOffset(SC.Base.ISect, SC.Base.Off), + fmtle(SC.Base.Size), fmtle(SC.Base.Imod), + fmtle(SC.Base.DataCrc), fmtle(SC.Base.RelocCrc), + fmtle(SC.ISectCoff)); + P.formatLine(" {0}", + formatSectionCharacteristics(P.getIndentLevel() + 6, + SC.Base.Characteristics)); + } + + private: + LinePrinter &P; + }; + + Visitor V(P); + Dbi.visitSectionContributions(V); + return Error::success(); +} + +Error DumpOutputStyle::dumpSectionMap() { + printHeader(P, "Section Map"); + ExitOnError Err("Error dumping section map"); + + AutoIndent Indent(P); + if (!File.hasPDBDbiStream()) { + P.formatLine("Dumping the section map requires a DBI Stream, which could " + "not be loaded"); + return Error::success(); + } + + auto &Dbi = Err(File.getPDBDbiStream()); + + uint32_t I = 0; + for (auto &M : Dbi.getSectionMap()) { + P.formatLine( + "Section {0:4} | ovl = {0}, group = {1}, frame = {2}, name = {3}", I, + fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName)); + P.formatLine(" class = {0}, offset = {1}, size = {2}", + fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength)); + P.formatLine(" flags = {0}", + formatSegMapDescriptorFlag( + P.getIndentLevel() + 13, + static_cast<OMFSegDescFlags>(uint16_t(M.Flags)))); + ++I; + } + return Error::success(); +} diff --git a/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h new file mode 100644 index 0000000..4c52289 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h @@ -0,0 +1,62 @@ +//===- DumpOutputStyle.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_DUMPOUTPUTSTYLE_H +#define LLVM_TOOLS_LLVMPDBDUMP_DUMPOUTPUTSTYLE_H + +#include "LinePrinter.h" +#include "OutputStyle.h" + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" + +#include <string> + +namespace llvm { +class BitVector; + +namespace codeview { +class LazyRandomTypeCollection; +} + +namespace pdb { +class DumpOutputStyle : public OutputStyle { +public: + DumpOutputStyle(PDBFile &File); + + Error dump() override; + +private: + Expected<codeview::LazyRandomTypeCollection &> initializeTypes(uint32_t SN); + + Error dumpFileSummary(); + Error dumpStreamSummary(); + Error dumpStringTable(); + Error dumpLines(); + Error dumpInlineeLines(); + Error dumpXmi(); + Error dumpXme(); + Error dumpTpiStream(uint32_t StreamIdx); + Error dumpModules(); + Error dumpModuleFiles(); + Error dumpModuleSyms(); + Error dumpPublics(); + Error dumpSectionContribs(); + Error dumpSectionMap(); + + PDBFile &File; + LinePrinter P; + std::unique_ptr<codeview::LazyRandomTypeCollection> TpiTypes; + std::unique_ptr<codeview::LazyRandomTypeCollection> IpiTypes; + SmallVector<std::string, 32> StreamPurposes; +}; +} // namespace pdb +} // namespace llvm + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/FormatUtil.cpp b/contrib/llvm/tools/llvm-pdbutil/FormatUtil.cpp new file mode 100644 index 0000000..0203027 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/FormatUtil.cpp @@ -0,0 +1,101 @@ +//===- FormatUtil.cpp ----------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FormatUtil.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::pdb; + +std::string llvm::pdb::truncateStringBack(StringRef S, uint32_t MaxLen) { + if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3) + return S; + + assert(MaxLen >= 3); + uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3); + S = S.take_front(FinalLen); + return std::string(S) + std::string("..."); +} + +std::string llvm::pdb::truncateStringMiddle(StringRef S, uint32_t MaxLen) { + if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3) + return S; + + assert(MaxLen >= 3); + uint32_t FinalLen = std::min<size_t>(S.size(), MaxLen - 3); + StringRef Front = S.take_front(FinalLen / 2); + StringRef Back = S.take_back(Front.size()); + return std::string(Front) + std::string("...") + std::string(Back); +} + +std::string llvm::pdb::truncateStringFront(StringRef S, uint32_t MaxLen) { + if (MaxLen == 0 || S.size() <= MaxLen || S.size() <= 3) + return S; + + assert(MaxLen >= 3); + S = S.take_back(MaxLen - 3); + return std::string("...") + std::string(S); +} + +std::string llvm::pdb::truncateQuotedNameFront(StringRef Label, StringRef Name, + uint32_t MaxLen) { + uint32_t RequiredExtraChars = Label.size() + 1 + 2; + if (MaxLen == 0 || RequiredExtraChars + Name.size() <= MaxLen) + return formatv("{0} \"{1}\"", Label, Name).str(); + + assert(MaxLen >= RequiredExtraChars); + std::string TN = truncateStringFront(Name, MaxLen - RequiredExtraChars); + return formatv("{0} \"{1}\"", Label, TN).str(); +} + +std::string llvm::pdb::truncateQuotedNameBack(StringRef Label, StringRef Name, + uint32_t MaxLen) { + uint32_t RequiredExtraChars = Label.size() + 1 + 2; + if (MaxLen == 0 || RequiredExtraChars + Name.size() <= MaxLen) + return formatv("{0} \"{1}\"", Label, Name).str(); + + assert(MaxLen >= RequiredExtraChars); + std::string TN = truncateStringBack(Name, MaxLen - RequiredExtraChars); + return formatv("{0} \"{1}\"", Label, TN).str(); +} + +std::string llvm::pdb::typesetItemList(ArrayRef<std::string> Opts, + uint32_t IndentLevel, uint32_t GroupSize, + StringRef Sep) { + std::string Result; + while (!Opts.empty()) { + ArrayRef<std::string> ThisGroup; + ThisGroup = Opts.take_front(GroupSize); + Opts = Opts.drop_front(ThisGroup.size()); + Result += join(ThisGroup, Sep); + if (!Opts.empty()) { + Result += Sep; + Result += "\n"; + Result += formatv("{0}", fmt_repeat(' ', IndentLevel)); + } + } + return Result; +} + +std::string llvm::pdb::typesetStringList(uint32_t IndentLevel, + ArrayRef<StringRef> Strings) { + std::string Result = "["; + for (const auto &S : Strings) { + Result += formatv("\n{0}{1}", fmt_repeat(' ', IndentLevel), S); + } + Result += "]"; + return Result; +} + +std::string llvm::pdb::formatSegmentOffset(uint16_t Segment, uint32_t Offset) { + return formatv("{0:4}:{1:4}", Segment, Offset); +} diff --git a/contrib/llvm/tools/llvm-pdbutil/FormatUtil.h b/contrib/llvm/tools/llvm-pdbutil/FormatUtil.h new file mode 100644 index 0000000..df32ed9 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/FormatUtil.h @@ -0,0 +1,128 @@ +//===- FormatUtil.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_LLVMPDBUTIL_FORMAT_UTIL_H +#define LLVM_TOOLS_LLVMPDBUTIL_FORMAT_UTIL_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +#include <string> +#include <type_traits> + +namespace llvm { +namespace pdb { + +std::string truncateStringBack(StringRef S, uint32_t MaxLen); +std::string truncateStringMiddle(StringRef S, uint32_t MaxLen); +std::string truncateStringFront(StringRef S, uint32_t MaxLen); +std::string truncateQuotedNameFront(StringRef Label, StringRef Name, + uint32_t MaxLen); +std::string truncateQuotedNameBack(StringRef Label, StringRef Name, + uint32_t MaxLen); + +#define PUSH_MASKED_FLAG(Enum, Mask, TheOpt, Value, Text) \ + if (Enum::TheOpt == (Value & Mask)) \ + Opts.push_back(Text); + +#define PUSH_FLAG(Enum, TheOpt, Value, Text) \ + PUSH_MASKED_FLAG(Enum, Enum::TheOpt, TheOpt, Value, Text) + +#define RETURN_CASE(Enum, X, Ret) \ + case Enum::X: \ + return Ret; + +template <typename T> std::string formatUnknownEnum(T Value) { + return formatv("unknown ({0})", + static_cast<typename std::underlying_type<T>::type>(Value)) + .str(); +} + +std::string formatSegmentOffset(uint16_t Segment, uint32_t Offset); + +std::string typesetItemList(ArrayRef<std::string> Opts, uint32_t IndentLevel, + uint32_t GroupSize, StringRef Sep); + +std::string typesetStringList(uint32_t IndentLevel, + ArrayRef<StringRef> Strings); + +/// Returns the number of digits in the given integer. +inline int NumDigits(uint64_t N) { + if (N < 10ULL) + return 1; + if (N < 100ULL) + return 2; + if (N < 1000ULL) + return 3; + if (N < 10000ULL) + return 4; + if (N < 100000ULL) + return 5; + if (N < 1000000ULL) + return 6; + if (N < 10000000ULL) + return 7; + if (N < 100000000ULL) + return 8; + if (N < 1000000000ULL) + return 9; + if (N < 10000000000ULL) + return 10; + if (N < 100000000000ULL) + return 11; + if (N < 1000000000000ULL) + return 12; + if (N < 10000000000000ULL) + return 13; + if (N < 100000000000000ULL) + return 14; + if (N < 1000000000000000ULL) + return 15; + if (N < 10000000000000000ULL) + return 16; + if (N < 100000000000000000ULL) + return 17; + if (N < 1000000000000000000ULL) + return 18; + if (N < 10000000000000000000ULL) + return 19; + return 20; +} + +namespace detail { +template <typename T> +struct EndianAdapter final + : public FormatAdapter<support::detail::packed_endian_specific_integral< + T, support::little, support::unaligned>> { + using EndianType = + support::detail::packed_endian_specific_integral<T, support::little, + support::unaligned>; + + explicit EndianAdapter(EndianType &&Item) + : FormatAdapter<EndianType>(std::move(Item)) {} + + void format(llvm::raw_ostream &Stream, StringRef Style) { + format_provider<T>::format(static_cast<T>(this->Item), Stream, Style); + } +}; +} // namespace detail + +template <typename T> +detail::EndianAdapter<T> +fmtle(support::detail::packed_endian_specific_integral<T, support::little, + support::unaligned> + Value) { + return detail::EndianAdapter<T>(std::move(Value)); +} +} +} // namespace llvm +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/LinePrinter.cpp b/contrib/llvm/tools/llvm-pdbutil/LinePrinter.cpp new file mode 100644 index 0000000..6e4d95a --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/LinePrinter.cpp @@ -0,0 +1,312 @@ +//===- LinePrinter.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LinePrinter.h" + +#include "llvm-pdbutil.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/MSF/MSFStreamLayout.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/UDTLayout.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Regex.h" + +#include <algorithm> + +using namespace llvm; +using namespace llvm::msf; +using namespace llvm::pdb; + +namespace { +bool IsItemExcluded(llvm::StringRef Item, + std::list<llvm::Regex> &IncludeFilters, + std::list<llvm::Regex> &ExcludeFilters) { + if (Item.empty()) + return false; + + auto match_pred = [Item](llvm::Regex &R) { return R.match(Item); }; + + // Include takes priority over exclude. If the user specified include + // filters, and none of them include this item, them item is gone. + if (!IncludeFilters.empty() && !any_of(IncludeFilters, match_pred)) + return true; + + if (any_of(ExcludeFilters, match_pred)) + return true; + + return false; +} +} + +using namespace llvm; + +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(), + opts::pretty::ExcludeSymbols.end()); + SetFilters(ExcludeCompilandFilters, opts::pretty::ExcludeCompilands.begin(), + opts::pretty::ExcludeCompilands.end()); + + SetFilters(IncludeTypeFilters, opts::pretty::IncludeTypes.begin(), + opts::pretty::IncludeTypes.end()); + SetFilters(IncludeSymbolFilters, opts::pretty::IncludeSymbols.begin(), + opts::pretty::IncludeSymbols.end()); + SetFilters(IncludeCompilandFilters, opts::pretty::IncludeCompilands.begin(), + opts::pretty::IncludeCompilands.end()); +} + +void LinePrinter::Indent(uint32_t Amount) { + if (Amount == 0) + Amount = IndentSpaces; + CurrentIndent += Amount; +} + +void LinePrinter::Unindent(uint32_t Amount) { + if (Amount == 0) + Amount = IndentSpaces; + CurrentIndent = std::max<int>(0, CurrentIndent - Amount); +} + +void LinePrinter::NewLine() { + OS << "\n"; + OS.indent(CurrentIndent); +} + +void LinePrinter::print(const Twine &T) { OS << T; } + +void LinePrinter::printLine(const Twine &T) { + NewLine(); + OS << T; +} + +bool LinePrinter::IsClassExcluded(const ClassLayout &Class) { + if (IsTypeExcluded(Class.getName(), Class.getSize())) + return true; + if (Class.deepPaddingSize() < opts::pretty::PaddingThreshold) + return true; + return false; +} + +void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, + uint32_t StartOffset) { + NewLine(); + OS << Label << " ("; + if (!Data.empty()) { + OS << "\n"; + OS << format_bytes_with_ascii(Data, StartOffset, 32, 4, + CurrentIndent + IndentSpaces, true); + NewLine(); + } + OS << ")"; +} + +void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, + uint64_t Base, uint32_t StartOffset) { + NewLine(); + OS << Label << " ("; + if (!Data.empty()) { + OS << "\n"; + Base += StartOffset; + OS << format_bytes_with_ascii(Data, Base, 32, 4, + CurrentIndent + IndentSpaces, true); + NewLine(); + } + OS << ")"; +} + +namespace { +struct Run { + Run() = default; + explicit Run(uint32_t Block) : Block(Block) {} + uint32_t Block = 0; + uint32_t ByteLen = 0; +}; +} // namespace + +static std::vector<Run> computeBlockRuns(uint32_t BlockSize, + const msf::MSFStreamLayout &Layout) { + std::vector<Run> Runs; + if (Layout.Length == 0) + return Runs; + + ArrayRef<support::ulittle32_t> Blocks = Layout.Blocks; + assert(!Blocks.empty()); + uint32_t StreamBytesRemaining = Layout.Length; + uint32_t CurrentBlock = Blocks[0]; + Runs.emplace_back(CurrentBlock); + while (!Blocks.empty()) { + Run *CurrentRun = &Runs.back(); + uint32_t NextBlock = Blocks.front(); + if (NextBlock < CurrentBlock || (NextBlock - CurrentBlock > 1)) { + Runs.emplace_back(NextBlock); + CurrentRun = &Runs.back(); + } + uint32_t Used = std::min(BlockSize, StreamBytesRemaining); + CurrentRun->ByteLen += Used; + StreamBytesRemaining -= Used; + CurrentBlock = NextBlock; + Blocks = Blocks.drop_front(); + } + return Runs; +} + +static std::pair<Run, uint32_t> findRun(uint32_t Offset, ArrayRef<Run> Runs) { + for (const auto &R : Runs) { + if (Offset < R.ByteLen) + return std::make_pair(R, Offset); + Offset -= R.ByteLen; + } + llvm_unreachable("Invalid offset!"); +} + +void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File, + uint32_t StreamIdx, + StringRef StreamPurpose, uint32_t Offset, + uint32_t Size) { + if (StreamIdx >= File.getNumStreams()) { + formatLine("Stream {0}: Not present", StreamIdx); + return; + } + if (Size + Offset > File.getStreamByteSize(StreamIdx)) { + formatLine( + "Stream {0}: Invalid offset and size, range out of stream bounds", + StreamIdx); + return; + } + + auto S = MappedBlockStream::createIndexedStream( + File.getMsfLayout(), File.getMsfBuffer(), StreamIdx, File.getAllocator()); + if (!S) { + NewLine(); + formatLine("Stream {0}: Not present", StreamIdx); + return; + } + + uint32_t End = + (Size == 0) ? S->getLength() : std::min(Offset + Size, S->getLength()); + Size = End - Offset; + + formatLine("Stream {0}: {1} (dumping {2:N} / {3:N} bytes)", StreamIdx, + StreamPurpose, Size, S->getLength()); + AutoIndent Indent(*this); + BinaryStreamRef Slice(*S); + BinarySubstreamRef Substream; + Substream.Offset = Offset; + Substream.StreamData = Slice.drop_front(Offset).keep_front(Size); + + auto Layout = File.getStreamLayout(StreamIdx); + formatMsfStreamData(Label, File, Layout, Substream); +} + +void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File, + const msf::MSFStreamLayout &Stream, + BinarySubstreamRef Substream) { + BinaryStreamReader Reader(Substream.StreamData); + + auto Runs = computeBlockRuns(File.getBlockSize(), Stream); + + NewLine(); + OS << Label << " ("; + while (Reader.bytesRemaining() > 0) { + OS << "\n"; + + Run FoundRun; + uint32_t RunOffset; + std::tie(FoundRun, RunOffset) = findRun(Substream.Offset, Runs); + assert(FoundRun.ByteLen >= RunOffset); + uint32_t Len = FoundRun.ByteLen - RunOffset; + Len = std::min(Len, Reader.bytesRemaining()); + uint64_t Base = FoundRun.Block * File.getBlockSize() + RunOffset; + ArrayRef<uint8_t> Data; + consumeError(Reader.readBytes(Data, Len)); + OS << format_bytes_with_ascii(Data, Base, 32, 4, + CurrentIndent + IndentSpaces, true); + if (Reader.bytesRemaining() > 0) { + NewLine(); + OS << formatv(" {0}", + fmt_align("<discontinuity>", AlignStyle::Center, 114, '-')); + } + Substream.Offset += Len; + } + NewLine(); + OS << ")"; +} + +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) { + return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters); +} + +bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) { + return IsItemExcluded(CompilandName, IncludeCompilandFilters, + ExcludeCompilandFilters); +} + +WithColor::WithColor(LinePrinter &P, PDB_ColorItem C) + : OS(P.OS), UseColor(P.hasColor()) { + if (UseColor) + applyColor(C); +} + +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; + case PDB_ColorItem::Keyword: + OS.changeColor(raw_ostream::MAGENTA, true); + return; + case PDB_ColorItem::Register: + case PDB_ColorItem::Offset: + OS.changeColor(raw_ostream::YELLOW, false); + return; + case PDB_ColorItem::Type: + OS.changeColor(raw_ostream::CYAN, true); + return; + case PDB_ColorItem::Identifier: + OS.changeColor(raw_ostream::CYAN, false); + return; + 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; + case PDB_ColorItem::LiteralValue: + OS.changeColor(raw_ostream::GREEN, true); + return; + } +} diff --git a/contrib/llvm/tools/llvm-pdbutil/LinePrinter.h b/contrib/llvm/tools/llvm-pdbutil/LinePrinter.h new file mode 100644 index 0000000..68ce321 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/LinePrinter.h @@ -0,0 +1,142 @@ +//===- LinePrinter.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_LINEPRINTER_H +#define LLVM_TOOLS_LLVMPDBDUMP_LINEPRINTER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/BinaryStreamRef.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/raw_ostream.h" + +#include <list> + +namespace llvm { +class BinaryStreamReader; +namespace msf { +class MSFStreamLayout; +} // namespace msf +namespace pdb { + +class ClassLayout; +class PDBFile; + +class LinePrinter { + friend class WithColor; + +public: + LinePrinter(int Indent, bool UseColor, raw_ostream &Stream); + + void Indent(uint32_t Amount = 0); + void Unindent(uint32_t Amount = 0); + void NewLine(); + + void printLine(const Twine &T); + void print(const Twine &T); + template <typename... Ts> void formatLine(const char *Fmt, Ts &&... Items) { + printLine(formatv(Fmt, std::forward<Ts>(Items)...)); + } + template <typename... Ts> void format(const char *Fmt, Ts &&... Items) { + print(formatv(Fmt, std::forward<Ts>(Items)...)); + } + + void formatBinary(StringRef Label, ArrayRef<uint8_t> Data, + uint32_t StartOffset); + void formatBinary(StringRef Label, ArrayRef<uint8_t> Data, uint64_t BaseAddr, + uint32_t StartOffset); + + void formatMsfStreamData(StringRef Label, PDBFile &File, uint32_t StreamIdx, + StringRef StreamPurpose, uint32_t Offset, + uint32_t Size); + void formatMsfStreamData(StringRef Label, PDBFile &File, + const msf::MSFStreamLayout &Stream, + BinarySubstreamRef Substream); + + bool hasColor() const { return UseColor; } + raw_ostream &getStream() { return OS; } + int getIndentLevel() const { return CurrentIndent; } + + bool IsClassExcluded(const ClassLayout &Class); + bool IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size); + bool IsSymbolExcluded(llvm::StringRef SymbolName); + bool IsCompilandExcluded(llvm::StringRef CompilandName); + +private: + template <typename Iter> + void SetFilters(std::list<Regex> &List, Iter Begin, Iter End) { + List.clear(); + for (; Begin != End; ++Begin) + List.emplace_back(StringRef(*Begin)); + } + + raw_ostream &OS; + int IndentSpaces; + int CurrentIndent; + bool UseColor; + + std::list<Regex> ExcludeCompilandFilters; + std::list<Regex> ExcludeTypeFilters; + std::list<Regex> ExcludeSymbolFilters; + + std::list<Regex> IncludeCompilandFilters; + std::list<Regex> IncludeTypeFilters; + std::list<Regex> IncludeSymbolFilters; +}; + +struct AutoIndent { + explicit AutoIndent(LinePrinter &L, uint32_t Amount = 0) + : L(L), Amount(Amount) { + L.Indent(Amount); + } + ~AutoIndent() { L.Unindent(Amount); } + + LinePrinter &L; + uint32_t Amount = 0; +}; + +template <class T> +inline raw_ostream &operator<<(LinePrinter &Printer, const T &Item) { + Printer.getStream() << Item; + return Printer.getStream(); +} + +enum class PDB_ColorItem { + None, + Address, + Type, + Comment, + Padding, + Keyword, + Offset, + Identifier, + Path, + SectionHeader, + LiteralValue, + Register, +}; + +class WithColor { +public: + WithColor(LinePrinter &P, PDB_ColorItem C); + ~WithColor(); + + raw_ostream &get() { return OS; } + +private: + void applyColor(PDB_ColorItem C); + raw_ostream &OS; + bool UseColor; +}; +} +} + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp new file mode 100644 index 0000000..d938436 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -0,0 +1,772 @@ +//===- MinimalSymbolDumper.cpp -------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MinimalSymbolDumper.h" + +#include "FormatUtil.h" +#include "LinePrinter.h" + +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static StringRef getSymbolKindName(SymbolKind K) { + switch (K) { +#define SYMBOL_RECORD(EnumName, value, name) \ + case EnumName: \ + return #EnumName; +#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" + default: + llvm_unreachable("Unknown symbol kind!"); + } + return ""; +} + +static std::string formatLocalSymFlags(uint32_t IndentLevel, + LocalSymFlags Flags) { + std::vector<std::string> Opts; + if (Flags == LocalSymFlags::None) + return "none"; + + PUSH_FLAG(LocalSymFlags, IsParameter, Flags, "param"); + PUSH_FLAG(LocalSymFlags, IsAddressTaken, Flags, "address is taken"); + PUSH_FLAG(LocalSymFlags, IsCompilerGenerated, Flags, "compiler generated"); + PUSH_FLAG(LocalSymFlags, IsAggregate, Flags, "aggregate"); + PUSH_FLAG(LocalSymFlags, IsAggregated, Flags, "aggregated"); + PUSH_FLAG(LocalSymFlags, IsAliased, Flags, "aliased"); + PUSH_FLAG(LocalSymFlags, IsAlias, Flags, "alias"); + PUSH_FLAG(LocalSymFlags, IsReturnValue, Flags, "return val"); + PUSH_FLAG(LocalSymFlags, IsOptimizedOut, Flags, "optimized away"); + PUSH_FLAG(LocalSymFlags, IsEnregisteredGlobal, Flags, "enreg global"); + PUSH_FLAG(LocalSymFlags, IsEnregisteredStatic, Flags, "enreg static"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatExportFlags(uint32_t IndentLevel, ExportFlags Flags) { + std::vector<std::string> Opts; + if (Flags == ExportFlags::None) + return "none"; + + PUSH_FLAG(ExportFlags, IsConstant, Flags, "constant"); + PUSH_FLAG(ExportFlags, IsData, Flags, "data"); + PUSH_FLAG(ExportFlags, IsPrivate, Flags, "private"); + PUSH_FLAG(ExportFlags, HasNoName, Flags, "no name"); + PUSH_FLAG(ExportFlags, HasExplicitOrdinal, Flags, "explicit ord"); + PUSH_FLAG(ExportFlags, IsForwarder, Flags, "forwarder"); + + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatCompileSym2Flags(uint32_t IndentLevel, + CompileSym2Flags Flags) { + std::vector<std::string> Opts; + Flags &= ~CompileSym2Flags::SourceLanguageMask; + if (Flags == CompileSym2Flags::None) + return "none"; + + PUSH_FLAG(CompileSym2Flags, EC, Flags, "edit and continue"); + PUSH_FLAG(CompileSym2Flags, NoDbgInfo, Flags, "no dbg info"); + PUSH_FLAG(CompileSym2Flags, LTCG, Flags, "ltcg"); + PUSH_FLAG(CompileSym2Flags, NoDataAlign, Flags, "no data align"); + PUSH_FLAG(CompileSym2Flags, ManagedPresent, Flags, "has managed code"); + PUSH_FLAG(CompileSym2Flags, SecurityChecks, Flags, "security checks"); + PUSH_FLAG(CompileSym2Flags, HotPatch, Flags, "hot patchable"); + PUSH_FLAG(CompileSym2Flags, CVTCIL, Flags, "cvtcil"); + PUSH_FLAG(CompileSym2Flags, MSILModule, Flags, "msil module"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatCompileSym3Flags(uint32_t IndentLevel, + CompileSym3Flags Flags) { + std::vector<std::string> Opts; + Flags &= ~CompileSym3Flags::SourceLanguageMask; + + if (Flags == CompileSym3Flags::None) + return "none"; + + PUSH_FLAG(CompileSym3Flags, EC, Flags, "edit and continue"); + PUSH_FLAG(CompileSym3Flags, NoDbgInfo, Flags, "no dbg info"); + PUSH_FLAG(CompileSym3Flags, LTCG, Flags, "ltcg"); + PUSH_FLAG(CompileSym3Flags, NoDataAlign, Flags, "no data align"); + PUSH_FLAG(CompileSym3Flags, ManagedPresent, Flags, "has managed code"); + PUSH_FLAG(CompileSym3Flags, SecurityChecks, Flags, "security checks"); + PUSH_FLAG(CompileSym3Flags, HotPatch, Flags, "hot patchable"); + PUSH_FLAG(CompileSym3Flags, CVTCIL, Flags, "cvtcil"); + PUSH_FLAG(CompileSym3Flags, MSILModule, Flags, "msil module"); + PUSH_FLAG(CompileSym3Flags, Sdl, Flags, "sdl"); + PUSH_FLAG(CompileSym3Flags, PGO, Flags, "pgo"); + PUSH_FLAG(CompileSym3Flags, Exp, Flags, "exp"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatFrameProcedureOptions(uint32_t IndentLevel, + FrameProcedureOptions FPO) { + std::vector<std::string> Opts; + if (FPO == FrameProcedureOptions::None) + return "none"; + + PUSH_FLAG(FrameProcedureOptions, HasAlloca, FPO, "has alloca"); + PUSH_FLAG(FrameProcedureOptions, HasSetJmp, FPO, "has setjmp"); + PUSH_FLAG(FrameProcedureOptions, HasLongJmp, FPO, "has longjmp"); + PUSH_FLAG(FrameProcedureOptions, HasInlineAssembly, FPO, "has inline asm"); + PUSH_FLAG(FrameProcedureOptions, HasExceptionHandling, FPO, "has eh"); + PUSH_FLAG(FrameProcedureOptions, MarkedInline, FPO, "marked inline"); + PUSH_FLAG(FrameProcedureOptions, HasStructuredExceptionHandling, FPO, + "has seh"); + PUSH_FLAG(FrameProcedureOptions, Naked, FPO, "naked"); + PUSH_FLAG(FrameProcedureOptions, SecurityChecks, FPO, "secure checks"); + PUSH_FLAG(FrameProcedureOptions, AsynchronousExceptionHandling, FPO, + "has async eh"); + PUSH_FLAG(FrameProcedureOptions, NoStackOrderingForSecurityChecks, FPO, + "no stack order"); + PUSH_FLAG(FrameProcedureOptions, Inlined, FPO, "inlined"); + PUSH_FLAG(FrameProcedureOptions, StrictSecurityChecks, FPO, + "strict secure checks"); + PUSH_FLAG(FrameProcedureOptions, SafeBuffers, FPO, "safe buffers"); + PUSH_FLAG(FrameProcedureOptions, ProfileGuidedOptimization, FPO, "pgo"); + PUSH_FLAG(FrameProcedureOptions, ValidProfileCounts, FPO, + "has profile counts"); + PUSH_FLAG(FrameProcedureOptions, OptimizedForSpeed, FPO, "opt speed"); + PUSH_FLAG(FrameProcedureOptions, GuardCfg, FPO, "guard cfg"); + PUSH_FLAG(FrameProcedureOptions, GuardCfw, FPO, "guard cfw"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatPublicSymFlags(uint32_t IndentLevel, + PublicSymFlags Flags) { + std::vector<std::string> Opts; + if (Flags == PublicSymFlags::None) + return "none"; + + PUSH_FLAG(PublicSymFlags, Code, Flags, "code"); + PUSH_FLAG(PublicSymFlags, Function, Flags, "function"); + PUSH_FLAG(PublicSymFlags, Managed, Flags, "managed"); + PUSH_FLAG(PublicSymFlags, MSIL, Flags, "msil"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatProcSymFlags(uint32_t IndentLevel, + ProcSymFlags Flags) { + std::vector<std::string> Opts; + if (Flags == ProcSymFlags::None) + return "none"; + + PUSH_FLAG(ProcSymFlags, HasFP, Flags, "has fp"); + PUSH_FLAG(ProcSymFlags, HasIRET, Flags, "has iret"); + PUSH_FLAG(ProcSymFlags, HasFRET, Flags, "has fret"); + PUSH_FLAG(ProcSymFlags, IsNoReturn, Flags, "noreturn"); + PUSH_FLAG(ProcSymFlags, IsUnreachable, Flags, "unreachable"); + PUSH_FLAG(ProcSymFlags, HasCustomCallingConv, Flags, "custom calling conv"); + PUSH_FLAG(ProcSymFlags, IsNoInline, Flags, "noinline"); + PUSH_FLAG(ProcSymFlags, HasOptimizedDebugInfo, Flags, "opt debuginfo"); + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string formatThunkOrdinal(ThunkOrdinal Ordinal) { + switch (Ordinal) { + RETURN_CASE(ThunkOrdinal, Standard, "thunk"); + RETURN_CASE(ThunkOrdinal, ThisAdjustor, "this adjustor"); + RETURN_CASE(ThunkOrdinal, Vcall, "vcall"); + RETURN_CASE(ThunkOrdinal, Pcode, "pcode"); + RETURN_CASE(ThunkOrdinal, UnknownLoad, "unknown load"); + RETURN_CASE(ThunkOrdinal, TrampIncremental, "tramp incremental"); + RETURN_CASE(ThunkOrdinal, BranchIsland, "branch island"); + } + return formatUnknownEnum(Ordinal); +} + +static std::string formatTrampolineType(TrampolineType Tramp) { + switch (Tramp) { + RETURN_CASE(TrampolineType, TrampIncremental, "tramp incremental"); + RETURN_CASE(TrampolineType, BranchIsland, "branch island"); + } + return formatUnknownEnum(Tramp); +} + +static std::string formatSourceLanguage(SourceLanguage Lang) { + switch (Lang) { + RETURN_CASE(SourceLanguage, C, "c"); + RETURN_CASE(SourceLanguage, Cpp, "c++"); + RETURN_CASE(SourceLanguage, Fortran, "fortran"); + RETURN_CASE(SourceLanguage, Masm, "masm"); + RETURN_CASE(SourceLanguage, Pascal, "pascal"); + RETURN_CASE(SourceLanguage, Basic, "basic"); + RETURN_CASE(SourceLanguage, Cobol, "cobol"); + RETURN_CASE(SourceLanguage, Link, "link"); + RETURN_CASE(SourceLanguage, VB, "vb"); + RETURN_CASE(SourceLanguage, Cvtres, "cvtres"); + RETURN_CASE(SourceLanguage, Cvtpgd, "cvtpgd"); + RETURN_CASE(SourceLanguage, CSharp, "c#"); + RETURN_CASE(SourceLanguage, ILAsm, "il asm"); + RETURN_CASE(SourceLanguage, Java, "java"); + RETURN_CASE(SourceLanguage, JScript, "javascript"); + RETURN_CASE(SourceLanguage, MSIL, "msil"); + RETURN_CASE(SourceLanguage, HLSL, "hlsl"); + } + return formatUnknownEnum(Lang); +} + +static std::string formatMachineType(CPUType Cpu) { + switch (Cpu) { + RETURN_CASE(CPUType, Intel8080, "intel 8080"); + RETURN_CASE(CPUType, Intel8086, "intel 8086"); + RETURN_CASE(CPUType, Intel80286, "intel 80286"); + RETURN_CASE(CPUType, Intel80386, "intel 80386"); + RETURN_CASE(CPUType, Intel80486, "intel 80486"); + RETURN_CASE(CPUType, Pentium, "intel pentium"); + RETURN_CASE(CPUType, PentiumPro, "intel pentium pro"); + RETURN_CASE(CPUType, Pentium3, "intel pentium 3"); + RETURN_CASE(CPUType, MIPS, "mips"); + RETURN_CASE(CPUType, MIPS16, "mips-16"); + RETURN_CASE(CPUType, MIPS32, "mips-32"); + RETURN_CASE(CPUType, MIPS64, "mips-64"); + RETURN_CASE(CPUType, MIPSI, "mips i"); + RETURN_CASE(CPUType, MIPSII, "mips ii"); + RETURN_CASE(CPUType, MIPSIII, "mips iii"); + RETURN_CASE(CPUType, MIPSIV, "mips iv"); + RETURN_CASE(CPUType, MIPSV, "mips v"); + RETURN_CASE(CPUType, M68000, "motorola 68000"); + RETURN_CASE(CPUType, M68010, "motorola 68010"); + RETURN_CASE(CPUType, M68020, "motorola 68020"); + RETURN_CASE(CPUType, M68030, "motorola 68030"); + RETURN_CASE(CPUType, M68040, "motorola 68040"); + RETURN_CASE(CPUType, Alpha, "alpha"); + RETURN_CASE(CPUType, Alpha21164, "alpha 21164"); + RETURN_CASE(CPUType, Alpha21164A, "alpha 21164a"); + RETURN_CASE(CPUType, Alpha21264, "alpha 21264"); + RETURN_CASE(CPUType, Alpha21364, "alpha 21364"); + RETURN_CASE(CPUType, PPC601, "powerpc 601"); + RETURN_CASE(CPUType, PPC603, "powerpc 603"); + RETURN_CASE(CPUType, PPC604, "powerpc 604"); + RETURN_CASE(CPUType, PPC620, "powerpc 620"); + RETURN_CASE(CPUType, PPCFP, "powerpc fp"); + RETURN_CASE(CPUType, PPCBE, "powerpc be"); + RETURN_CASE(CPUType, SH3, "sh3"); + RETURN_CASE(CPUType, SH3E, "sh3e"); + RETURN_CASE(CPUType, SH3DSP, "sh3 dsp"); + RETURN_CASE(CPUType, SH4, "sh4"); + RETURN_CASE(CPUType, SHMedia, "shmedia"); + RETURN_CASE(CPUType, ARM3, "arm 3"); + RETURN_CASE(CPUType, ARM4, "arm 4"); + RETURN_CASE(CPUType, ARM4T, "arm 4t"); + RETURN_CASE(CPUType, ARM5, "arm 5"); + RETURN_CASE(CPUType, ARM5T, "arm 5t"); + RETURN_CASE(CPUType, ARM6, "arm 6"); + RETURN_CASE(CPUType, ARM_XMAC, "arm xmac"); + RETURN_CASE(CPUType, ARM_WMMX, "arm wmmx"); + RETURN_CASE(CPUType, ARM7, "arm 7"); + RETURN_CASE(CPUType, Omni, "omni"); + RETURN_CASE(CPUType, Ia64, "intel itanium ia64"); + RETURN_CASE(CPUType, Ia64_2, "intel itanium ia64 2"); + RETURN_CASE(CPUType, CEE, "cee"); + RETURN_CASE(CPUType, AM33, "am33"); + RETURN_CASE(CPUType, M32R, "m32r"); + RETURN_CASE(CPUType, TriCore, "tri-core"); + RETURN_CASE(CPUType, X64, "intel x86-x64"); + RETURN_CASE(CPUType, EBC, "ebc"); + RETURN_CASE(CPUType, Thumb, "thumb"); + RETURN_CASE(CPUType, ARMNT, "arm nt"); + RETURN_CASE(CPUType, D3D11_Shader, "d3d11 shader"); + } + return formatUnknownEnum(Cpu); +} + +static std::string formatCookieKind(FrameCookieKind Kind) { + switch (Kind) { + RETURN_CASE(FrameCookieKind, Copy, "copy"); + RETURN_CASE(FrameCookieKind, XorStackPointer, "xor stack ptr"); + RETURN_CASE(FrameCookieKind, XorFramePointer, "xor frame ptr"); + RETURN_CASE(FrameCookieKind, XorR13, "xor rot13"); + } + return formatUnknownEnum(Kind); +} + +static std::string formatRegisterId(RegisterId Id) { + switch (Id) { + RETURN_CASE(RegisterId, VFrame, "vframe"); + RETURN_CASE(RegisterId, AL, "al"); + RETURN_CASE(RegisterId, CL, "cl"); + RETURN_CASE(RegisterId, DL, "dl"); + RETURN_CASE(RegisterId, BL, "bl"); + RETURN_CASE(RegisterId, AH, "ah"); + RETURN_CASE(RegisterId, CH, "ch"); + RETURN_CASE(RegisterId, DH, "dh"); + RETURN_CASE(RegisterId, BH, "bh"); + RETURN_CASE(RegisterId, AX, "ax"); + RETURN_CASE(RegisterId, CX, "cx"); + RETURN_CASE(RegisterId, DX, "dx"); + RETURN_CASE(RegisterId, BX, "bx"); + RETURN_CASE(RegisterId, SP, "sp"); + RETURN_CASE(RegisterId, BP, "bp"); + RETURN_CASE(RegisterId, SI, "si"); + RETURN_CASE(RegisterId, DI, "di"); + RETURN_CASE(RegisterId, EAX, "eax"); + RETURN_CASE(RegisterId, ECX, "ecx"); + RETURN_CASE(RegisterId, EDX, "edx"); + RETURN_CASE(RegisterId, EBX, "ebx"); + RETURN_CASE(RegisterId, ESP, "esp"); + RETURN_CASE(RegisterId, EBP, "ebp"); + RETURN_CASE(RegisterId, ESI, "esi"); + RETURN_CASE(RegisterId, EDI, "edi"); + RETURN_CASE(RegisterId, ES, "es"); + RETURN_CASE(RegisterId, CS, "cs"); + RETURN_CASE(RegisterId, SS, "ss"); + RETURN_CASE(RegisterId, DS, "ds"); + RETURN_CASE(RegisterId, FS, "fs"); + RETURN_CASE(RegisterId, GS, "gs"); + RETURN_CASE(RegisterId, IP, "ip"); + RETURN_CASE(RegisterId, RAX, "rax"); + RETURN_CASE(RegisterId, RBX, "rbx"); + RETURN_CASE(RegisterId, RCX, "rcx"); + RETURN_CASE(RegisterId, RDX, "rdx"); + RETURN_CASE(RegisterId, RSI, "rsi"); + RETURN_CASE(RegisterId, RDI, "rdi"); + RETURN_CASE(RegisterId, RBP, "rbp"); + RETURN_CASE(RegisterId, RSP, "rsp"); + RETURN_CASE(RegisterId, R8, "r8"); + RETURN_CASE(RegisterId, R9, "r9"); + RETURN_CASE(RegisterId, R10, "r10"); + RETURN_CASE(RegisterId, R11, "r11"); + RETURN_CASE(RegisterId, R12, "r12"); + RETURN_CASE(RegisterId, R13, "r13"); + RETURN_CASE(RegisterId, R14, "r14"); + RETURN_CASE(RegisterId, R15, "r15"); + default: + return formatUnknownEnum(Id); + } +} + +static std::string formatRange(LocalVariableAddrRange Range) { + return formatv("[{0},+{1})", + formatSegmentOffset(Range.ISectStart, Range.OffsetStart), + Range.Range) + .str(); +} + +static std::string formatGaps(uint32_t IndentLevel, + ArrayRef<LocalVariableAddrGap> Gaps) { + std::vector<std::string> GapStrs; + for (const auto &G : Gaps) { + GapStrs.push_back(formatv("({0},{1})", G.GapStartOffset, G.Range).str()); + } + return typesetItemList(GapStrs, 7, IndentLevel, ", "); +} + +Error MinimalSymbolDumper::visitSymbolBegin(codeview::CVSymbol &Record) { + return visitSymbolBegin(Record, 0); +} + +Error MinimalSymbolDumper::visitSymbolBegin(codeview::CVSymbol &Record, + uint32_t Offset) { + // formatLine puts the newline at the beginning, so we use formatLine here + // to start a new line, and then individual visit methods use format to + // append to the existing line. + P.formatLine("{0} | {1} [size = {2}]", + fmt_align(Offset, AlignStyle::Right, 6), + getSymbolKindName(Record.Type), Record.length()); + P.Indent(); + return Error::success(); +} + +Error MinimalSymbolDumper::visitSymbolEnd(CVSymbol &Record) { + P.Unindent(); + return Error::success(); +} + +std::string MinimalSymbolDumper::typeIndex(TypeIndex TI) const { + if (TI.isSimple()) + return formatv("{0}", TI).str(); + StringRef Name = Types.getTypeName(TI); + if (Name.size() > 32) { + Name = Name.take_front(32); + return formatv("{0} ({1}...)", TI, Name); + } else + return formatv("{0} ({1})", TI, Name); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, BlockSym &Block) { + P.format(" `{0}`", Block.Name); + AutoIndent Indent(P, 7); + P.formatLine("parent = {0}, end = {1}", Block.Parent, Block.End); + P.formatLine("code size = {0}, addr = {1}", Block.CodeSize, + formatSegmentOffset(Block.Segment, Block.CodeOffset)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, Thunk32Sym &Thunk) { + P.format(" `{0}`", Thunk.Name); + AutoIndent Indent(P, 7); + P.formatLine("parent = {0}, end = {1}, next = {2}", Thunk.Parent, Thunk.End, + Thunk.Next); + P.formatLine("kind = {0}, size = {1}, addr = {2}", + formatThunkOrdinal(Thunk.Thunk), Thunk.Length, + formatSegmentOffset(Thunk.Segment, Thunk.Offset)); + + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + TrampolineSym &Tramp) { + AutoIndent Indent(P, 7); + P.formatLine("type = {0}, size = {1}, source = {2}, target = {3}", + formatTrampolineType(Tramp.Type), Tramp.Size, + formatSegmentOffset(Tramp.ThunkSection, Tramp.ThunkOffset), + formatSegmentOffset(Tramp.TargetSection, Tramp.ThunkOffset)); + + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + SectionSym &Section) { + P.format(" `{0}`", Section.Name); + AutoIndent Indent(P, 7); + P.formatLine("length = {0}, alignment = {1}, rva = {2}, section # = {3}, " + "characteristics = {4}", + Section.Length, Section.Alignment, Section.Rva, + Section.SectionNumber, Section.Characteristics); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, CoffGroupSym &CG) { + P.format(" `{0}`", CG.Name); + AutoIndent Indent(P, 7); + P.formatLine("length = {0}, addr = {1}, characteristics = {2}", CG.Size, + formatSegmentOffset(CG.Segment, CG.Offset), CG.Characteristics); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + BPRelativeSym &BPRel) { + P.format(" `{0}`", BPRel.Name); + AutoIndent Indent(P, 7); + P.formatLine("type = {0}, offset = {1}", typeIndex(BPRel.Type), BPRel.Offset); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + BuildInfoSym &BuildInfo) { + P.format(" BuildId = `{0}`", BuildInfo.BuildId); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + CallSiteInfoSym &CSI) { + AutoIndent Indent(P, 7); + P.formatLine("type = {0}, addr = {1}", typeIndex(CSI.Type), + formatSegmentOffset(CSI.Segment, CSI.CodeOffset)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + EnvBlockSym &EnvBlock) { + AutoIndent Indent(P, 7); + for (const auto &Entry : EnvBlock.Fields) { + P.formatLine("- {0}", Entry); + } + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FileStaticSym &FS) { + P.format(" `{0}`", FS.Name); + AutoIndent Indent(P, 7); + P.formatLine("type = {0}, file name offset = {1}, flags = {2}", + typeIndex(FS.Index), FS.ModFilenameOffset, + formatLocalSymFlags(P.getIndentLevel() + 9, FS.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, ExportSym &Export) { + P.format(" `{0}`", Export.Name); + AutoIndent Indent(P, 7); + P.formatLine("ordinal = {0}, flags = {1}", Export.Ordinal, + formatExportFlags(P.getIndentLevel() + 9, Export.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + Compile2Sym &Compile2) { + AutoIndent Indent(P, 7); + SourceLanguage Lang = static_cast<SourceLanguage>( + Compile2.Flags & CompileSym2Flags::SourceLanguageMask); + P.formatLine("machine = {0}, ver = {1}, language = {2}", + formatMachineType(Compile2.Machine), Compile2.Version, + formatSourceLanguage(Lang)); + P.formatLine("frontend = {0}.{1}.{2}, backend = {3}.{4}.{5}", + Compile2.VersionFrontendMajor, Compile2.VersionFrontendMinor, + Compile2.VersionFrontendBuild, Compile2.VersionBackendMajor, + Compile2.VersionBackendMinor, Compile2.VersionBackendBuild); + P.formatLine("flags = {0}", + formatCompileSym2Flags(P.getIndentLevel() + 9, Compile2.Flags)); + P.formatLine( + "extra strings = {0}", + typesetStringList(P.getIndentLevel() + 9 + 2, Compile2.ExtraStrings)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + Compile3Sym &Compile3) { + AutoIndent Indent(P, 7); + SourceLanguage Lang = static_cast<SourceLanguage>( + Compile3.Flags & CompileSym3Flags::SourceLanguageMask); + P.formatLine("machine = {0}, Ver = {1}, language = {2}", + formatMachineType(Compile3.Machine), Compile3.Version, + formatSourceLanguage(Lang)); + P.formatLine("frontend = {0}.{1}.{2}.{3}, backend = {4}.{5}.{6}.{7}", + Compile3.VersionFrontendMajor, Compile3.VersionFrontendMinor, + Compile3.VersionFrontendBuild, Compile3.VersionFrontendQFE, + Compile3.VersionBackendMajor, Compile3.VersionBackendMinor, + Compile3.VersionBackendBuild, Compile3.VersionBackendQFE); + P.formatLine("flags = {0}", + formatCompileSym3Flags(P.getIndentLevel() + 9, Compile3.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + ConstantSym &Constant) { + P.format(" `{0}`", Constant.Name); + AutoIndent Indent(P, 7); + P.formatLine("type = {0}, value = {1}", typeIndex(Constant.Type), + Constant.Value.toString(10)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, DataSym &Data) { + P.format(" `{0}`", Data.Name); + AutoIndent Indent(P, 7); + P.formatLine("type = {0}, addr = {1}", typeIndex(Data.Type), + formatSegmentOffset(Data.Segment, Data.DataOffset)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord( + CVSymbol &CVR, DefRangeFramePointerRelFullScopeSym &Def) { + P.format(" offset = {0}", Def.Offset); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + DefRangeFramePointerRelSym &Def) { + AutoIndent Indent(P, 7); + P.formatLine("offset = {0}, range = {1}", Def.Offset, formatRange(Def.Range)); + P.formatLine("gaps = {2}", Def.Offset, + formatGaps(P.getIndentLevel() + 9, Def.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + DefRangeRegisterRelSym &Def) { + AutoIndent Indent(P, 7); + P.formatLine("register = {0}, base ptr = {1}, offset in parent = {2}, has " + "spilled udt = {3}", + uint16_t(Def.Hdr.Register), int32_t(Def.Hdr.BasePointerOffset), + Def.offsetInParent(), Def.hasSpilledUDTMember()); + P.formatLine("range = {0}, gaps = {1}", formatRange(Def.Range), + formatGaps(P.getIndentLevel() + 9, Def.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord( + CVSymbol &CVR, DefRangeRegisterSym &DefRangeRegister) { + AutoIndent Indent(P, 7); + P.formatLine("register = {0}, may have no name = {1}, range start = " + "{2}, length = {3}", + uint16_t(DefRangeRegister.Hdr.Register), + uint16_t(DefRangeRegister.Hdr.MayHaveNoName), + formatSegmentOffset(DefRangeRegister.Range.ISectStart, + DefRangeRegister.Range.OffsetStart), + DefRangeRegister.Range.Range); + P.formatLine("gaps = [{0}]", + formatGaps(P.getIndentLevel() + 9, DefRangeRegister.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + DefRangeSubfieldRegisterSym &Def) { + AutoIndent Indent(P, 7); + bool NoName = !!(Def.Hdr.MayHaveNoName == 0); + P.formatLine("register = {0}, may have no name = {1}, offset in parent = {2}", + uint16_t(Def.Hdr.Register), NoName, + uint32_t(Def.Hdr.OffsetInParent)); + P.formatLine("range = {0}, gaps = {1}", formatRange(Def.Range), + formatGaps(P.getIndentLevel() + 9, Def.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + DefRangeSubfieldSym &Def) { + AutoIndent Indent(P, 7); + P.formatLine("program = {0}, offset in parent = {1}, range = {2}", + Def.Program, Def.OffsetInParent, formatRange(Def.Range)); + P.formatLine("gaps = {0}", formatGaps(P.getIndentLevel() + 9, Def.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, DefRangeSym &Def) { + AutoIndent Indent(P, 7); + P.formatLine("program = {0}, range = {1}", Def.Program, + formatRange(Def.Range)); + P.formatLine("gaps = {0}", formatGaps(P.getIndentLevel() + 9, Def.Gaps)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameCookieSym &FC) { + AutoIndent Indent(P, 7); + P.formatLine("code offset = {0}, Register = {1}, kind = {2}, flags = {3}", + FC.CodeOffset, FC.Register, formatCookieKind(FC.CookieKind), + FC.Flags); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameProcSym &FP) { + AutoIndent Indent(P, 7); + P.formatLine("size = {0}, padding size = {1}, offset to padding = {2}", + FP.TotalFrameBytes, FP.PaddingFrameBytes, FP.OffsetToPadding); + P.formatLine("bytes of callee saved registers = {0}, exception handler addr " + "= {1}", + FP.BytesOfCalleeSavedRegisters, + formatSegmentOffset(FP.SectionIdOfExceptionHandler, + FP.OffsetOfExceptionHandler)); + P.formatLine("flags = {0}", + formatFrameProcedureOptions(P.getIndentLevel() + 9, FP.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + HeapAllocationSiteSym &HAS) { + AutoIndent Indent(P, 7); + P.formatLine("type = {0}, addr = {1} call size = {2}", typeIndex(HAS.Type), + formatSegmentOffset(HAS.Segment, HAS.CodeOffset), + HAS.CallInstructionSize); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, InlineSiteSym &IS) { + AutoIndent Indent(P, 7); + auto Bytes = makeArrayRef(IS.AnnotationData); + StringRef Annotations(reinterpret_cast<const char *>(Bytes.begin()), + Bytes.size()); + + P.formatLine("inlinee = {0}, parent = {1}, end = {2}", typeIndex(IS.Inlinee), + IS.Parent, IS.End); + P.formatLine("annotations = {0}", toHex(Annotations)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + RegisterSym &Register) { + P.format(" `{0}`", Register.Name); + AutoIndent Indent(P, 7); + P.formatLine("register = {0}, type = {1}", + formatRegisterId(Register.Register), typeIndex(Register.Index)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + PublicSym32 &Public) { + P.format(" `{0}`", Public.Name); + AutoIndent Indent(P, 7); + P.formatLine("flags = {0}, addr = {1}", + formatPublicSymFlags(P.getIndentLevel() + 9, Public.Flags), + formatSegmentOffset(Public.Segment, Public.Offset)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, ProcRefSym &PR) { + P.format(" `{0}`", PR.Name); + AutoIndent Indent(P, 7); + P.formatLine("module = {0}, sum name = {1}, offset = {2}", PR.Module, + PR.SumName, PR.SymOffset); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, LabelSym &Label) { + P.format(" `{0}` (addr = {1})", Label.Name, + formatSegmentOffset(Label.Segment, Label.CodeOffset)); + AutoIndent Indent(P, 7); + P.formatLine("flags = {0}", + formatProcSymFlags(P.getIndentLevel() + 9, Label.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, LocalSym &Local) { + P.format(" `{0}`", Local.Name); + AutoIndent Indent(P, 7); + + std::string FlagStr = + formatLocalSymFlags(P.getIndentLevel() + 9, Local.Flags); + P.formatLine("type={0}, flags = {1}", typeIndex(Local.Type), FlagStr); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + ObjNameSym &ObjName) { + P.format(" sig={0}, `{1}`", ObjName.Signature, ObjName.Name); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, ProcSym &Proc) { + P.format(" `{0}`", Proc.Name); + AutoIndent Indent(P, 7); + P.formatLine("parent = {0}, end = {1}, addr = {2}, code size = {3}", + Proc.Parent, Proc.End, + formatSegmentOffset(Proc.Segment, Proc.CodeOffset), + Proc.CodeSize); + // FIXME: It seems FunctionType is sometimes an id and sometimes a type. + P.formatLine("type = `{0}`, debug start = {1}, debug end = {2}, flags = {3}", + typeIndex(Proc.FunctionType), Proc.DbgStart, Proc.DbgEnd, + formatProcSymFlags(P.getIndentLevel() + 9, Proc.Flags)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + ScopeEndSym &ScopeEnd) { + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, CallerSym &Caller) { + AutoIndent Indent(P, 7); + for (const auto &I : Caller.Indices) { + P.formatLine("callee: {0}", typeIndex(I)); + } + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + RegRelativeSym &RegRel) { + P.format(" `{0}`", RegRel.Name); + AutoIndent Indent(P, 7); + P.formatLine("type = {0}, register = {1}, offset = {2}", + typeIndex(RegRel.Type), formatRegisterId(RegRel.Register), + RegRel.Offset); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, + ThreadLocalDataSym &Data) { + P.format(" `{0}`", Data.Name); + AutoIndent Indent(P, 7); + P.formatLine("type = {0}, addr = {1}", typeIndex(Data.Type), + formatSegmentOffset(Data.Segment, Data.DataOffset)); + return Error::success(); +} + +Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, UDTSym &UDT) { + P.format(" `{0}`", UDT.Name); + AutoIndent Indent(P, 7); + P.formatLine("original type = {0}", UDT.Type); + return Error::success(); +} diff --git a/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h new file mode 100644 index 0000000..5e30959 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h @@ -0,0 +1,48 @@ +//===- MinimalSymbolDumper.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_LLVMPDBUTIL_MINIMAL_SYMBOL_DUMPER_H +#define LLVM_TOOLS_LLVMPDBUTIL_MINIMAL_SYMBOL_DUMPER_H + +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" + +namespace llvm { +namespace codeview { +class LazyRandomTypeCollection; +} + +namespace pdb { +class LinePrinter; + +class MinimalSymbolDumper : public codeview::SymbolVisitorCallbacks { +public: + MinimalSymbolDumper(LinePrinter &P, bool RecordBytes, + codeview::LazyRandomTypeCollection &Types) + : P(P), Types(Types) {} + + Error visitSymbolBegin(codeview::CVSymbol &Record) override; + Error visitSymbolBegin(codeview::CVSymbol &Record, uint32_t Offset) override; + Error visitSymbolEnd(codeview::CVSymbol &Record) override; + +#define SYMBOL_RECORD(EnumName, EnumVal, Name) \ + virtual Error visitKnownRecord(codeview::CVSymbol &CVR, \ + codeview::Name &Record) override; +#define SYMBOL_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" + +private: + std::string typeIndex(codeview::TypeIndex TI) const; + + LinePrinter &P; + codeview::LazyRandomTypeCollection &Types; +}; +} // namespace pdb +} // namespace llvm + +#endif
\ No newline at end of file diff --git a/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp new file mode 100644 index 0000000..0079b9e --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -0,0 +1,553 @@ +//===- MinimalTypeDumper.cpp ---------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MinimalTypeDumper.h" + +#include "FormatUtil.h" +#include "LinePrinter.h" + +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/Formatters.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MathExtras.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static StringRef getLeafTypeName(TypeLeafKind K) { + switch (K) { +#define TYPE_RECORD(EnumName, value, name) \ + case EnumName: \ + return #EnumName; +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + default: + llvm_unreachable("Unknown type leaf kind!"); + } + return ""; +} + +static std::string formatClassOptions(uint32_t IndentLevel, + ClassOptions Options) { + std::vector<std::string> Opts; + PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options, + "has ctor / dtor"); + PUSH_FLAG(ClassOptions, ContainsNestedClass, Options, + "contains nested class"); + PUSH_FLAG(ClassOptions, HasConversionOperator, Options, + "conversion operator"); + PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref"); + PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name"); + PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin"); + PUSH_FLAG(ClassOptions, Nested, Options, "is nested"); + PUSH_FLAG(ClassOptions, HasOverloadedOperator, Options, + "overloaded operator"); + PUSH_FLAG(ClassOptions, HasOverloadedAssignmentOperator, Options, + "overloaded operator="); + PUSH_FLAG(ClassOptions, Packed, Options, "packed"); + PUSH_FLAG(ClassOptions, Scoped, Options, "scoped"); + PUSH_FLAG(ClassOptions, Sealed, Options, "sealed"); + + return typesetItemList(Opts, 4, IndentLevel, " | "); +} + +static std::string pointerOptions(PointerOptions Options) { + std::vector<std::string> Opts; + PUSH_FLAG(PointerOptions, Flat32, Options, "flat32"); + PUSH_FLAG(PointerOptions, Volatile, Options, "volatile"); + PUSH_FLAG(PointerOptions, Const, Options, "const"); + PUSH_FLAG(PointerOptions, Unaligned, Options, "unaligned"); + PUSH_FLAG(PointerOptions, Restrict, Options, "restrict"); + PUSH_FLAG(PointerOptions, WinRTSmartPointer, Options, "winrt"); + if (Opts.empty()) + return "None"; + return join(Opts, " | "); +} + +static std::string modifierOptions(ModifierOptions Options) { + std::vector<std::string> Opts; + PUSH_FLAG(ModifierOptions, Const, Options, "const"); + PUSH_FLAG(ModifierOptions, Volatile, Options, "volatile"); + PUSH_FLAG(ModifierOptions, Unaligned, Options, "unaligned"); + if (Opts.empty()) + return "None"; + return join(Opts, " | "); +} + +static std::string formatCallingConvention(CallingConvention Convention) { + switch (Convention) { + RETURN_CASE(CallingConvention, AlphaCall, "alphacall"); + RETURN_CASE(CallingConvention, AM33Call, "am33call"); + RETURN_CASE(CallingConvention, ArmCall, "armcall"); + RETURN_CASE(CallingConvention, ClrCall, "clrcall"); + RETURN_CASE(CallingConvention, FarC, "far cdecl"); + RETURN_CASE(CallingConvention, FarFast, "far fastcall"); + RETURN_CASE(CallingConvention, FarPascal, "far pascal"); + RETURN_CASE(CallingConvention, FarStdCall, "far stdcall"); + RETURN_CASE(CallingConvention, FarSysCall, "far syscall"); + RETURN_CASE(CallingConvention, Generic, "generic"); + RETURN_CASE(CallingConvention, Inline, "inline"); + RETURN_CASE(CallingConvention, M32RCall, "m32rcall"); + RETURN_CASE(CallingConvention, MipsCall, "mipscall"); + RETURN_CASE(CallingConvention, NearC, "cdecl"); + RETURN_CASE(CallingConvention, NearFast, "fastcall"); + RETURN_CASE(CallingConvention, NearPascal, "pascal"); + RETURN_CASE(CallingConvention, NearStdCall, "stdcall"); + RETURN_CASE(CallingConvention, NearSysCall, "near syscall"); + RETURN_CASE(CallingConvention, NearVector, "vectorcall"); + RETURN_CASE(CallingConvention, PpcCall, "ppccall"); + RETURN_CASE(CallingConvention, SHCall, "shcall"); + RETURN_CASE(CallingConvention, SH5Call, "sh5call"); + RETURN_CASE(CallingConvention, ThisCall, "thiscall"); + RETURN_CASE(CallingConvention, TriCall, "tricall"); + } + return formatUnknownEnum(Convention); +} + +static std::string formatPointerMode(PointerMode Mode) { + switch (Mode) { + RETURN_CASE(PointerMode, LValueReference, "ref"); + RETURN_CASE(PointerMode, Pointer, "pointer"); + RETURN_CASE(PointerMode, PointerToDataMember, "data member pointer"); + RETURN_CASE(PointerMode, PointerToMemberFunction, "member fn pointer"); + RETURN_CASE(PointerMode, RValueReference, "rvalue ref"); + } + return formatUnknownEnum(Mode); +} + +static std::string memberAccess(MemberAccess Access) { + switch (Access) { + RETURN_CASE(MemberAccess, None, ""); + RETURN_CASE(MemberAccess, Private, "private"); + RETURN_CASE(MemberAccess, Protected, "protected"); + RETURN_CASE(MemberAccess, Public, "public"); + } + return formatUnknownEnum(Access); +} + +static std::string methodKind(MethodKind Kind) { + switch (Kind) { + RETURN_CASE(MethodKind, Vanilla, ""); + RETURN_CASE(MethodKind, Virtual, "virtual"); + RETURN_CASE(MethodKind, Static, "static"); + RETURN_CASE(MethodKind, Friend, "friend"); + RETURN_CASE(MethodKind, IntroducingVirtual, "intro virtual"); + RETURN_CASE(MethodKind, PureVirtual, "pure virtual"); + RETURN_CASE(MethodKind, PureIntroducingVirtual, "pure intro virtual"); + } + return formatUnknownEnum(Kind); +} + +static std::string pointerKind(PointerKind Kind) { + switch (Kind) { + RETURN_CASE(PointerKind, Near16, "ptr16"); + RETURN_CASE(PointerKind, Far16, "far ptr16"); + RETURN_CASE(PointerKind, Huge16, "huge ptr16"); + RETURN_CASE(PointerKind, BasedOnSegment, "segment based"); + RETURN_CASE(PointerKind, BasedOnValue, "value based"); + RETURN_CASE(PointerKind, BasedOnSegmentValue, "segment value based"); + RETURN_CASE(PointerKind, BasedOnAddress, "address based"); + RETURN_CASE(PointerKind, BasedOnSegmentAddress, "segment address based"); + RETURN_CASE(PointerKind, BasedOnType, "type based"); + RETURN_CASE(PointerKind, BasedOnSelf, "self based"); + RETURN_CASE(PointerKind, Near32, "ptr32"); + RETURN_CASE(PointerKind, Far32, "far ptr32"); + RETURN_CASE(PointerKind, Near64, "ptr64"); + } + return formatUnknownEnum(Kind); +} + +static std::string memberAttributes(const MemberAttributes &Attrs) { + std::vector<std::string> Opts; + std::string Access = memberAccess(Attrs.getAccess()); + std::string Kind = methodKind(Attrs.getMethodKind()); + if (!Access.empty()) + Opts.push_back(Access); + if (!Kind.empty()) + Opts.push_back(Kind); + MethodOptions Flags = Attrs.getFlags(); + PUSH_FLAG(MethodOptions, Pseudo, Flags, "pseudo"); + PUSH_FLAG(MethodOptions, NoInherit, Flags, "noinherit"); + PUSH_FLAG(MethodOptions, NoConstruct, Flags, "noconstruct"); + PUSH_FLAG(MethodOptions, CompilerGenerated, Flags, "compiler-generated"); + PUSH_FLAG(MethodOptions, Sealed, Flags, "sealed"); + return join(Opts, " "); +} + +static std::string formatPointerAttrs(const PointerRecord &Record) { + PointerMode Mode = Record.getMode(); + PointerOptions Opts = Record.getOptions(); + PointerKind Kind = Record.getPointerKind(); + return formatv("mode = {0}, opts = {1}, kind = {2}", formatPointerMode(Mode), + pointerOptions(Opts), pointerKind(Kind)); +} + +static std::string formatFunctionOptions(FunctionOptions Options) { + std::vector<std::string> Opts; + + PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt"); + PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options, + "constructor with virtual bases"); + PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor"); + if (Opts.empty()) + return "None"; + return join(Opts, " | "); +} + +Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { + // formatLine puts the newline at the beginning, so we use formatLine here + // to start a new line, and then individual visit methods use format to + // append to the existing line. + if (!Hashes) { + P.formatLine("{0} | {1} [size = {2}]", + fmt_align(Index, AlignStyle::Right, Width), + getLeafTypeName(Record.Type), Record.length()); + } else { + std::string H; + if (Index.toArrayIndex() >= HashValues.size()) { + H = "(not present)"; + } else { + uint32_t Hash = HashValues[Index.toArrayIndex()]; + Expected<uint32_t> MaybeHash = hashTypeRecord(Record); + if (!MaybeHash) + return MaybeHash.takeError(); + uint32_t OurHash = *MaybeHash; + OurHash %= NumHashBuckets; + if (Hash == OurHash) + H = "0x" + utohexstr(Hash); + else + H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash); + } + P.formatLine("{0} | {1} [size = {2}, hash = {3}]", + fmt_align(Index, AlignStyle::Right, Width), + getLeafTypeName(Record.Type), Record.length(), H); + } + P.Indent(Width + 3); + return Error::success(); +} +Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) { + P.Unindent(Width + 3); + if (RecordBytes) { + AutoIndent Indent(P, 9); + P.formatBinary("Bytes", Record.RecordData, 0); + } + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) { + P.formatLine("- {0}", getLeafTypeName(Record.Kind)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) { + if (RecordBytes) { + AutoIndent Indent(P, 2); + P.formatBinary("Bytes", Record.Data, 0); + } + return Error::success(); +} + +StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const { + if (TI.isNoneType()) + return ""; + return Types.getTypeName(TI); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + FieldListRecord &FieldList) { + if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this)) + return EC; + + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + StringIdRecord &String) { + P.format(" ID: {0}, String: {1}", String.getId(), String.getString()); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + ArgListRecord &Args) { + auto Indices = Args.getIndices(); + if (Indices.empty()) + return Error::success(); + + auto Max = std::max_element(Indices.begin(), Indices.end()); + uint32_t W = NumDigits(Max->getIndex()) + 2; + + for (auto I : Indices) + P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), + getTypeName(I)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + StringListRecord &Strings) { + auto Indices = Strings.getIndices(); + if (Indices.empty()) + return Error::success(); + + auto Max = std::max_element(Indices.begin(), Indices.end()); + uint32_t W = NumDigits(Max->getIndex()) + 2; + + for (auto I : Indices) + P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), + getTypeName(I)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + ClassRecord &Class) { + P.format(" `{0}`", Class.Name); + if (Class.hasUniqueName()) + P.formatLine("unique name: `{0}`", Class.UniqueName); + P.formatLine("vtable: {0}, base list: {1}, field list: {2}", + Class.VTableShape, Class.DerivationList, Class.FieldList); + P.formatLine("options: {0}", + formatClassOptions(P.getIndentLevel(), Class.Options)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + UnionRecord &Union) { + P.format(" `{0}`", Union.Name); + if (Union.hasUniqueName()) + P.formatLine("unique name: `{0}`", Union.UniqueName); + P.formatLine("field list: {0}", Union.FieldList); + P.formatLine("options: {0}", + formatClassOptions(P.getIndentLevel(), Union.Options)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { + P.format(" `{0}`", Enum.Name); + if (Enum.hasUniqueName()) + P.formatLine("unique name: `{0}`", Enum.UniqueName); + P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList, + Enum.UnderlyingType); + P.formatLine("options: {0}", + formatClassOptions(P.getIndentLevel(), Enum.Options)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) { + if (AT.Name.empty()) { + P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size, + AT.IndexType, AT.ElementType); + } else { + P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}", + AT.Name, AT.Size, AT.IndexType, AT.ElementType); + } + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + VFTableRecord &VFT) { + P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}", + VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable); + P.formatLine("method names: "); + if (!VFT.MethodNames.empty()) { + std::string Sep = + formatv("\n{0}", + fmt_repeat(' ', P.getIndentLevel() + strlen("method names: "))) + .str(); + P.print(join(VFT.MethodNames, Sep)); + } + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + MemberFuncIdRecord &Id) { + P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name, + Id.FunctionType, Id.ClassType); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + ProcedureRecord &Proc) { + P.formatLine("return type = {0}, # args = {1}, param list = {2}", + Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList); + P.formatLine("calling conv = {0}, options = {1}", + formatCallingConvention(Proc.CallConv), + formatFunctionOptions(Proc.Options)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + MemberFunctionRecord &MF) { + P.formatLine("return type = {0}, # args = {1}, param list = {2}", + MF.ReturnType, MF.ParameterCount, MF.ArgumentList); + P.formatLine("class type = {0}, this type = {1}, this adjust = {2}", + MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment); + P.formatLine("calling conv = {0}, options = {1}", + formatCallingConvention(MF.CallConv), + formatFunctionOptions(MF.Options)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + FuncIdRecord &Func) { + P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name, + Func.FunctionType, Func.ParentScope); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + TypeServer2Record &TS) { + P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, TS.Guid); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + PointerRecord &Ptr) { + P.formatLine("referent = {0}, {1}", Ptr.ReferentType, + formatPointerAttrs(Ptr)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + ModifierRecord &Mod) { + P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType, + modifierOptions(Mod.Modifiers)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + VFTableShapeRecord &Shape) { + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + UdtModSourceLineRecord &U) { + P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module, + U.SourceFile.getIndex(), U.LineNumber); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + UdtSourceLineRecord &U) { + P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT, + U.SourceFile.getIndex(), U.LineNumber); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + BitFieldRecord &BF) { + P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type, + BF.BitOffset, BF.BitSize); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord( + CVType &CVR, MethodOverloadListRecord &Overloads) { + for (auto &M : Overloads.Methods) + P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]", + M.Type, M.VFTableOffset, memberAttributes(M.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, + BuildInfoRecord &BI) { + auto Indices = BI.ArgIndices; + if (Indices.empty()) + return Error::success(); + + auto Max = std::max_element(Indices.begin(), Indices.end()); + uint32_t W = NumDigits(Max->getIndex()) + 2; + + for (auto I : Indices) + P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), + getTypeName(I)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) { + std::string Type = (R.Mode == LabelType::Far) ? "far" : "near"; + P.format(" type = {0}", Type); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + NestedTypeRecord &Nested) { + P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + OneMethodRecord &Method) { + P.format(" [name = `{0}`]", Method.Name); + AutoIndent Indent(P); + P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type, + Method.VFTableOffset, memberAttributes(Method.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + OverloadedMethodRecord &Method) { + P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]", + Method.Name, Method.NumOverloads, Method.MethodList); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + DataMemberRecord &Field) { + P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name, + Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + StaticDataMemberRecord &Field) { + P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type, + memberAttributes(Field.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + EnumeratorRecord &Enum) { + P.format(" [{0} = {1}]", Enum.Name, + Enum.Value.toString(10, Enum.Value.isSigned())); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + BaseClassRecord &Base) { + AutoIndent Indent(P); + P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset, + memberAttributes(Base.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + VirtualBaseClassRecord &Base) { + AutoIndent Indent(P); + P.formatLine( + "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}", + Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex); + P.formatLine("attrs = {0}", memberAttributes(Base.Attrs)); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + ListContinuationRecord &Cont) { + P.format(" continuation = {0}", Cont.ContinuationIndex); + return Error::success(); +} + +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, + VFPtrRecord &VFP) { + P.format(" type = {0}", VFP.Type); + return Error::success(); +} diff --git a/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h b/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h new file mode 100644 index 0000000..4227688 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h @@ -0,0 +1,63 @@ +//===- MinimalTypeDumper.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_LLVMPDBUTIL_MINIMAL_TYPE_DUMPER_H +#define LLVM_TOOLS_LLVMPDBUTIL_MINIMAL_TYPE_DUMPER_H + +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/Support/BinaryStreamArray.h" + +namespace llvm { +namespace codeview { +class LazyRandomTypeCollection; +} + +namespace pdb { +class LinePrinter; + +class MinimalTypeDumpVisitor : public codeview::TypeVisitorCallbacks { +public: + MinimalTypeDumpVisitor(LinePrinter &P, uint32_t Width, bool RecordBytes, + bool Hashes, codeview::LazyRandomTypeCollection &Types, + uint32_t NumHashBuckets, + FixedStreamArray<support::ulittle32_t> HashValues) + : P(P), Width(Width), RecordBytes(RecordBytes), Hashes(Hashes), + Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues) {} + + Error visitTypeBegin(codeview::CVType &Record, + codeview::TypeIndex Index) override; + Error visitTypeEnd(codeview::CVType &Record) override; + Error visitMemberBegin(codeview::CVMemberRecord &Record) override; + Error visitMemberEnd(codeview::CVMemberRecord &Record) override; + +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + Error visitKnownRecord(codeview::CVType &CVR, \ + codeview::Name##Record &Record) override; +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + Error visitKnownMember(codeview::CVMemberRecord &CVR, \ + codeview::Name##Record &Record) override; +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + +private: + StringRef getTypeName(codeview::TypeIndex TI) const; + + LinePrinter &P; + uint32_t Width; + bool RecordBytes = false; + bool Hashes = false; + codeview::LazyRandomTypeCollection &Types; + uint32_t NumHashBuckets; + FixedStreamArray<support::ulittle32_t> HashValues; +}; +} // namespace pdb +} // namespace llvm + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/OutputStyle.h b/contrib/llvm/tools/llvm-pdbutil/OutputStyle.h new file mode 100644 index 0000000..dfefc25 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/OutputStyle.h @@ -0,0 +1,28 @@ +//===- OutputStyle.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_OUTPUTSTYLE_H +#define LLVM_TOOLS_LLVMPDBDUMP_OUTPUTSTYLE_H + +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { +class PDBFile; + +class OutputStyle { +public: + virtual ~OutputStyle() {} + + virtual Error dump() = 0; +}; +} +} + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/PdbYaml.cpp b/contrib/llvm/tools/llvm-pdbutil/PdbYaml.cpp new file mode 100644 index 0000000..9c3beb5 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PdbYaml.cpp @@ -0,0 +1,190 @@ +//===- PdbYAML.cpp -------------------------------------------- *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PdbYaml.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.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/RawTypes.h" +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/ObjectYAML/CodeViewYAMLDebugSections.h" +#include "llvm/ObjectYAML/CodeViewYAMLTypes.h" + +using namespace llvm; +using namespace llvm::pdb; +using namespace llvm::pdb::yaml; +using namespace llvm::yaml; + +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::NamedStreamMapping) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::PdbDbiModuleInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::StreamBlockList) +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::pdb::PdbRaw_FeatureSig) + +namespace llvm { +namespace yaml { + +template <> struct ScalarEnumerationTraits<llvm::pdb::PDB_Machine> { + static void enumeration(IO &io, llvm::pdb::PDB_Machine &Value) { + io.enumCase(Value, "Invalid", PDB_Machine::Invalid); + io.enumCase(Value, "Am33", PDB_Machine::Am33); + io.enumCase(Value, "Amd64", PDB_Machine::Amd64); + io.enumCase(Value, "Arm", PDB_Machine::Arm); + io.enumCase(Value, "ArmNT", PDB_Machine::ArmNT); + io.enumCase(Value, "Ebc", PDB_Machine::Ebc); + io.enumCase(Value, "x86", PDB_Machine::x86); + io.enumCase(Value, "Ia64", PDB_Machine::Ia64); + io.enumCase(Value, "M32R", PDB_Machine::M32R); + io.enumCase(Value, "Mips16", PDB_Machine::Mips16); + io.enumCase(Value, "MipsFpu", PDB_Machine::MipsFpu); + io.enumCase(Value, "MipsFpu16", PDB_Machine::MipsFpu16); + io.enumCase(Value, "PowerPCFP", PDB_Machine::PowerPCFP); + io.enumCase(Value, "R4000", PDB_Machine::R4000); + io.enumCase(Value, "SH3", PDB_Machine::SH3); + io.enumCase(Value, "SH3DSP", PDB_Machine::SH3DSP); + io.enumCase(Value, "Thumb", PDB_Machine::Thumb); + io.enumCase(Value, "WceMipsV2", PDB_Machine::WceMipsV2); + } +}; + +template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_DbiVer> { + static void enumeration(IO &io, llvm::pdb::PdbRaw_DbiVer &Value) { + io.enumCase(Value, "V41", llvm::pdb::PdbRaw_DbiVer::PdbDbiVC41); + io.enumCase(Value, "V50", llvm::pdb::PdbRaw_DbiVer::PdbDbiV50); + io.enumCase(Value, "V60", llvm::pdb::PdbRaw_DbiVer::PdbDbiV60); + io.enumCase(Value, "V70", llvm::pdb::PdbRaw_DbiVer::PdbDbiV70); + io.enumCase(Value, "V110", llvm::pdb::PdbRaw_DbiVer::PdbDbiV110); + } +}; + +template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_ImplVer> { + static void enumeration(IO &io, llvm::pdb::PdbRaw_ImplVer &Value) { + io.enumCase(Value, "VC2", llvm::pdb::PdbRaw_ImplVer::PdbImplVC2); + io.enumCase(Value, "VC4", llvm::pdb::PdbRaw_ImplVer::PdbImplVC4); + io.enumCase(Value, "VC41", llvm::pdb::PdbRaw_ImplVer::PdbImplVC41); + io.enumCase(Value, "VC50", llvm::pdb::PdbRaw_ImplVer::PdbImplVC50); + io.enumCase(Value, "VC98", llvm::pdb::PdbRaw_ImplVer::PdbImplVC98); + io.enumCase(Value, "VC70Dep", llvm::pdb::PdbRaw_ImplVer::PdbImplVC70Dep); + io.enumCase(Value, "VC70", llvm::pdb::PdbRaw_ImplVer::PdbImplVC70); + io.enumCase(Value, "VC80", llvm::pdb::PdbRaw_ImplVer::PdbImplVC80); + io.enumCase(Value, "VC110", llvm::pdb::PdbRaw_ImplVer::PdbImplVC110); + io.enumCase(Value, "VC140", llvm::pdb::PdbRaw_ImplVer::PdbImplVC140); + } +}; + +template <> struct ScalarEnumerationTraits<llvm::pdb::PdbRaw_TpiVer> { + static void enumeration(IO &io, llvm::pdb::PdbRaw_TpiVer &Value) { + io.enumCase(Value, "VC40", llvm::pdb::PdbRaw_TpiVer::PdbTpiV40); + io.enumCase(Value, "VC41", llvm::pdb::PdbRaw_TpiVer::PdbTpiV41); + io.enumCase(Value, "VC50", llvm::pdb::PdbRaw_TpiVer::PdbTpiV50); + io.enumCase(Value, "VC70", llvm::pdb::PdbRaw_TpiVer::PdbTpiV70); + 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) { + 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.mapOptional("TpiStream", Obj.TpiStream); + IO.mapOptional("IpiStream", Obj.IpiStream); +} + +void MappingTraits<MSFHeaders>::mapping(IO &IO, MSFHeaders &Obj) { + 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) { + if (!IO.outputting()) { + ::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic)); + } + + 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) { + IO.mapRequired("Stream", SB.Blocks); +} + +void MappingTraits<PdbInfoStream>::mapping(IO &IO, PdbInfoStream &Obj) { + 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.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.mapOptional("Modules", Obj.ModInfos); +} + +void MappingTraits<PdbTpiStream>::mapping(IO &IO, + pdb::yaml::PdbTpiStream &Obj) { + IO.mapOptional("Version", Obj.Version, PdbTpiV80); + IO.mapRequired("Records", Obj.Records); +} + +void MappingTraits<NamedStreamMapping>::mapping(IO &IO, + NamedStreamMapping &Obj) { + IO.mapRequired("Name", Obj.StreamName); + IO.mapRequired("StreamNum", Obj.StreamNumber); +} + +void MappingTraits<PdbModiStream>::mapping(IO &IO, PdbModiStream &Obj) { + IO.mapOptional("Signature", Obj.Signature, 4U); + IO.mapRequired("Records", Obj.Symbols); +} + +void MappingTraits<PdbDbiModuleInfo>::mapping(IO &IO, PdbDbiModuleInfo &Obj) { + IO.mapRequired("Module", Obj.Mod); + IO.mapOptional("ObjFile", Obj.Obj, Obj.Mod); + IO.mapOptional("SourceFiles", Obj.SourceFiles); + IO.mapOptional("Subsections", Obj.Subsections); + IO.mapOptional("Modi", Obj.Modi); +} diff --git a/contrib/llvm/tools/llvm-pdbutil/PdbYaml.h b/contrib/llvm/tools/llvm-pdbutil/PdbYaml.h new file mode 100644 index 0000000..91e0544 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PdbYaml.h @@ -0,0 +1,125 @@ +//===- PdbYAML.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_PDBYAML_H +#define LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H + +#include "OutputStyle.h" + +#include "llvm/ADT/Optional.h" +#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/ObjectYAML/CodeViewYAMLDebugSections.h" +#include "llvm/ObjectYAML/CodeViewYAMLSymbols.h" +#include "llvm/ObjectYAML/CodeViewYAMLTypes.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/YAMLTraits.h" + +#include <vector> + +namespace llvm { +namespace codeview { +class DebugStringTableSubsection; +} +namespace pdb { + +namespace yaml { +struct SerializationContext; + +struct MSFHeaders { + msf::SuperBlock SuperBlock; + uint32_t NumDirectoryBlocks = 0; + std::vector<uint32_t> DirectoryBlocks; + uint32_t NumStreams = 0; + uint32_t FileSize = 0; +}; + +struct StreamBlockList { + std::vector<uint32_t> Blocks; +}; + +struct NamedStreamMapping { + StringRef StreamName; + uint32_t StreamNumber; +}; + +struct PdbInfoStream { + PdbRaw_ImplVer Version = PdbImplVC70; + uint32_t Signature = 0; + uint32_t Age = 1; + codeview::GUID Guid; + std::vector<PdbRaw_FeatureSig> Features; + std::vector<NamedStreamMapping> NamedStreams; +}; + +struct PdbModiStream { + uint32_t Signature; + std::vector<CodeViewYAML::SymbolRecord> Symbols; +}; + +struct PdbDbiModuleInfo { + StringRef Obj; + StringRef Mod; + std::vector<StringRef> SourceFiles; + std::vector<CodeViewYAML::YAMLDebugSubsection> Subsections; + Optional<PdbModiStream> Modi; +}; + +struct PdbDbiStream { + 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; +}; + +struct PdbTpiStream { + PdbRaw_TpiVer Version = PdbTpiV80; + std::vector<CodeViewYAML::LeafRecord> Records; +}; + +struct PdbObject { + explicit PdbObject(BumpPtrAllocator &Allocator) : Allocator(Allocator) {} + + Optional<MSFHeaders> Headers; + Optional<std::vector<uint32_t>> StreamSizes; + Optional<std::vector<StreamBlockList>> StreamMap; + Optional<PdbInfoStream> PdbStream; + Optional<PdbDbiStream> DbiStream; + Optional<PdbTpiStream> TpiStream; + Optional<PdbTpiStream> IpiStream; + + Optional<std::vector<StringRef>> StringTable; + + BumpPtrAllocator &Allocator; +}; +} +} +} + +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbObject) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::MSFHeaders) +LLVM_YAML_DECLARE_MAPPING_TRAITS(msf::SuperBlock) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::StreamBlockList) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbInfoStream) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbDbiStream) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbTpiStream) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::NamedStreamMapping) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbModiStream) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbDbiModuleInfo) + +#endif // LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp new file mode 100644 index 0000000..fcda312 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp @@ -0,0 +1,94 @@ +//===- PrettyBuiltinDumper.cpp ---------------------------------- *- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyBuiltinDumper.h" +#include "LinePrinter.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" + +using namespace llvm; +using namespace llvm::pdb; + +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); +} + +StringRef BuiltinDumper::getTypeName(const PDBSymbolTypeBuiltin &Symbol) { + PDB_BuiltinType Type = Symbol.getBuiltinType(); + switch (Type) { + case PDB_BuiltinType::Float: + if (Symbol.getLength() == 4) + return "float"; + return "double"; + case PDB_BuiltinType::UInt: + switch (Symbol.getLength()) { + case 8: + return "unsigned __int64"; + case 4: + return "unsigned int"; + case 2: + return "unsigned short"; + case 1: + return "unsigned char"; + default: + return "unsigned"; + } + case PDB_BuiltinType::Int: + switch (Symbol.getLength()) { + case 8: + return "__int64"; + case 4: + return "int"; + case 2: + return "short"; + case 1: + return "char"; + default: + return "int"; + } + case PDB_BuiltinType::Char: + return "char"; + case PDB_BuiltinType::WCharT: + return "wchar_t"; + case PDB_BuiltinType::Void: + return "void"; + case PDB_BuiltinType::Long: + return "long"; + case PDB_BuiltinType::ULong: + return "unsigned long"; + case PDB_BuiltinType::Bool: + return "bool"; + case PDB_BuiltinType::Currency: + return "CURRENCY"; + case PDB_BuiltinType::Date: + return "DATE"; + case PDB_BuiltinType::Variant: + return "VARIANT"; + case PDB_BuiltinType::Complex: + return "complex"; + case PDB_BuiltinType::Bitfield: + return "bitfield"; + case PDB_BuiltinType::BSTR: + return "BSTR"; + case PDB_BuiltinType::HResult: + return "HRESULT"; + case PDB_BuiltinType::BCD: + return "HRESULT"; + default: + return "void"; + } +} diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.h new file mode 100644 index 0000000..fb6b0b1 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.h @@ -0,0 +1,35 @@ +//===- PrettyBuiltinDumper.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_PRETTYBUILTINDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYBUILTINDUMPER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { + +class LinePrinter; + +class BuiltinDumper : public PDBSymDumper { +public: + BuiltinDumper(LinePrinter &P); + + void start(const PDBSymbolTypeBuiltin &Symbol); + +private: + StringRef getTypeName(const PDBSymbolTypeBuiltin &Symbol); + + LinePrinter &Printer; +}; +} +} + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp new file mode 100644 index 0000000..651cb8b --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp @@ -0,0 +1,108 @@ +//===- PrettyClassDefinitionDumper.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyClassDefinitionDumper.h" + +#include "LinePrinter.h" +#include "PrettyClassLayoutGraphicalDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/SmallString.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; + +ClassDefinitionDumper::ClassDefinitionDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) { + assert(opts::pretty::ClassFormat != + opts::pretty::ClassDefinitionFormat::None); + + ClassLayout Layout(Class); + start(Layout); +} + +void ClassDefinitionDumper::start(const ClassLayout &Layout) { + prettyPrintClassIntro(Layout); + + PrettyClassLayoutGraphicalDumper Dumper(Printer, 1, 0); + DumpedAnything |= Dumper.start(Layout); + + prettyPrintClassOutro(Layout); +} + +void ClassDefinitionDumper::prettyPrintClassIntro(const ClassLayout &Layout) { + DumpedAnything = false; + Printer.NewLine(); + + uint32_t Size = Layout.getSize(); + const PDBSymbolTypeUDT &Class = Layout.getClass(); + + 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(); + if (BaseCount > 0) { + Printer.Indent(); + char NextSeparator = ':'; + for (auto BC : Layout.bases()) { + const auto &Base = BC->getBase(); + if (Base.isIndirectVirtualBaseClass()) + continue; + + Printer.NewLine(); + Printer << NextSeparator << " "; + WithColor(Printer, PDB_ColorItem::Keyword).get() << Base.getAccess(); + if (BC->isVirtualBase()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " virtual"; + + WithColor(Printer, PDB_ColorItem::Type).get() << " " << Base.getName(); + NextSeparator = ','; + } + + Printer.Unindent(); + } + + Printer << " {"; + Printer.Indent(); +} + +void ClassDefinitionDumper::prettyPrintClassOutro(const ClassLayout &Layout) { + Printer.Unindent(); + if (DumpedAnything) + Printer.NewLine(); + Printer << "}"; + Printer.NewLine(); + if (Layout.deepPaddingSize() > 0) { + APFloat Pct(100.0 * (double)Layout.deepPaddingSize() / + (double)Layout.getSize()); + SmallString<8> PctStr; + Pct.toString(PctStr, 4); + WithColor(Printer, PDB_ColorItem::Padding).get() + << "Total padding " << Layout.deepPaddingSize() << " bytes (" << PctStr + << "% of class size)"; + Printer.NewLine(); + APFloat Pct2(100.0 * (double)Layout.immediatePadding() / + (double)Layout.getSize()); + PctStr.clear(); + Pct2.toString(PctStr, 4); + WithColor(Printer, PDB_ColorItem::Padding).get() + << "Immediate padding " << Layout.immediatePadding() << " bytes (" + << PctStr << "% of class size)"; + Printer.NewLine(); + } +} diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h new file mode 100644 index 0000000..6569a1d --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyClassDefinitionDumper.h @@ -0,0 +1,47 @@ +//===- PrettyClassDefinitionDumper.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_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" + +#include <list> +#include <memory> +#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 &Class); + void start(const ClassLayout &Class); + +private: + void prettyPrintClassIntro(const ClassLayout &Class); + void prettyPrintClassOutro(const ClassLayout &Class); + + LinePrinter &Printer; + bool DumpedAnything = false; +}; +} +} +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp new file mode 100644 index 0000000..66c29fc --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp @@ -0,0 +1,216 @@ +//===- 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 "PrettyEnumDumper.h" +#include "PrettyFunctionDumper.h" +#include "PrettyTypedefDumper.h" +#include "PrettyVariableDumper.h" +#include "PrettyVariableDumper.h" +#include "llvm-pdbutil.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 RecurseLevel, uint32_t InitialOffset) + : PDBSymDumper(true), Printer(P), RecursionLevel(RecurseLevel), + ClassOffsetZero(InitialOffset), CurrentAbsoluteOffset(InitialOffset) {} + +bool PrettyClassLayoutGraphicalDumper::start(const UDTLayoutBase &Layout) { + + if (RecursionLevel == 1 && + opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::All) { + for (auto &Other : Layout.other_items()) + Other->dump(*this); + for (auto &Func : Layout.funcs()) + Func->dump(*this); + } + + 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; + if (Item->isVBPtr()) { + VTableLayoutItem &Layout = static_cast<VTableLayoutItem &>(*CurrentItem); + + VariableDumper VarDumper(Printer); + VarDumper.startVbptr(CurrentAbsoluteOffset, Layout.getSize()); + } else { + if (auto Sym = Item->getSymbol()) + Sym->dump(*this); + } + + if (Item->getLayoutSize() > 0) { + uint32_t Prev = RelativeOffset + Item->getLayoutSize() - 1; + if (Prev < UseMap.size()) + NextPaddingByte = UseMap.find_next_unset(Prev); + } + } + + auto TailPadding = Layout.tailPadding(); + if (TailPadding > 0) { + if (TailPadding != 1 || Layout.getSize() != 1) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Padding).get() + << "<padding> (" << TailPadding << " 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 = "base"; + if (Layout.isVirtualBase()) { + Label.insert(Label.begin(), 'v'); + if (Layout.getBase().isIndirectVirtualBaseClass()) + Label.insert(Label.begin(), 'i'); + } + Printer << Label << " "; + + uint32_t Size = Layout.isEmptyBase() ? 1 : Layout.getLayoutSize(); + + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(CurrentAbsoluteOffset, 4) << " [sizeof=" << Size + << "] "; + + WithColor(Printer, PDB_ColorItem::Identifier).get() << Layout.getName(); + + if (shouldRecurse()) { + Printer.Indent(); + uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent(); + PrettyClassLayoutGraphicalDumper BaseDumper(Printer, RecursionLevel + 1, + ChildOffsetZero); + DumpedAnything |= BaseDumper.start(Layout); + Printer.Unindent(); + } + + DumpedAnything = true; +} + +bool PrettyClassLayoutGraphicalDumper::shouldRecurse() const { + uint32_t Limit = opts::pretty::ClassRecursionDepth; + if (Limit == 0) + return true; + return RecursionLevel < Limit; +} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolData &Symbol) { + VariableDumper VarDumper(Printer); + VarDumper.start(Symbol, ClassOffsetZero); + + if (CurrentItem != nullptr) { + DataMemberLayoutItem &Layout = + static_cast<DataMemberLayoutItem &>(*CurrentItem); + + if (Layout.hasUDTLayout() && shouldRecurse()) { + uint32_t ChildOffsetZero = ClassOffsetZero + Layout.getOffsetInParent(); + Printer.Indent(); + PrettyClassLayoutGraphicalDumper TypeDumper(Printer, RecursionLevel + 1, + ChildOffsetZero); + TypeDumper.start(Layout.getUDTLayout()); + Printer.Unindent(); + } + } + + DumpedAnything = true; +} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeVTable &Symbol) { + assert(CurrentItem != nullptr); + + VariableDumper VarDumper(Printer); + VarDumper.start(Symbol, ClassOffsetZero); + + DumpedAnything = true; +} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeEnum &Symbol) { + DumpedAnything = true; + Printer.NewLine(); + EnumDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void PrettyClassLayoutGraphicalDumper::dump( + const PDBSymbolTypeTypedef &Symbol) { + DumpedAnything = true; + Printer.NewLine(); + TypedefDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void PrettyClassLayoutGraphicalDumper::dump( + const PDBSymbolTypeBuiltin &Symbol) {} + +void PrettyClassLayoutGraphicalDumper::dump(const PDBSymbolTypeUDT &Symbol) {} + +void PrettyClassLayoutGraphicalDumper::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); +} diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h new file mode 100644 index 0000000..f83f1a6 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.h @@ -0,0 +1,58 @@ +//===- 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 LayoutItemBase; +class LinePrinter; + +class PrettyClassLayoutGraphicalDumper : public PDBSymDumper { +public: + PrettyClassLayoutGraphicalDumper(LinePrinter &P, uint32_t RecurseLevel, + uint32_t InitialOffset); + + bool start(const UDTLayoutBase &Layout); + + // Layout based symbol types. + void dump(const PDBSymbolTypeBaseClass &Symbol) override; + void dump(const PDBSymbolData &Symbol) override; + void dump(const PDBSymbolTypeVTable &Symbol) override; + + // Non layout-based symbol types. + 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 PDBSymbolTypeBuiltin &Symbol) override; + +private: + bool shouldRecurse() const; + void printPaddingRow(uint32_t Amount); + + LinePrinter &Printer; + + LayoutItemBase *CurrentItem = nullptr; + uint32_t RecursionLevel = 0; + uint32_t ClassOffsetZero = 0; + uint32_t CurrentAbsoluteOffset = 0; + bool DumpedAnything = false; +}; +} +} +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp new file mode 100644 index 0000000..65e8bad --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp @@ -0,0 +1,207 @@ +//===- PrettyCompilandDumper.cpp - llvm-pdbutil compiland dumper -*- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyCompilandDumper.h" + +#include "LinePrinter.h" +#include "PrettyFunctionDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBLineNumber.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbol.h" +#include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" +#include "llvm/DebugInfo/PDB/PDBSymbolLabel.h" +#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolUnknown.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +#include <utility> + +using namespace llvm; +using namespace llvm::pdb; + +CompilandDumper::CompilandDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void CompilandDumper::dump(const PDBSymbolCompilandDetails &Symbol) {} + +void CompilandDumper::dump(const PDBSymbolCompilandEnv &Symbol) {} + +void CompilandDumper::start(const PDBSymbolCompiland &Symbol, + CompilandDumpFlags opts) { + std::string FullName = Symbol.getName(); + if (Printer.IsCompilandExcluded(FullName)) + return; + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Path).get() << FullName; + + if (opts & Flags::Lines) { + const IPDBSession &Session = Symbol.getSession(); + auto Files = Session.getSourceFilesForCompiland(Symbol); + Printer.Indent(); + while (auto File = Files->getNext()) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Path).get() << File->getFileName(); + + auto Lines = Session.findLineNumbers(Symbol, *File); + Printer.Indent(); + while (auto Line = Lines->getNext()) { + Printer.NewLine(); + uint32_t LineStart = Line->getLineNumber(); + uint32_t LineEnd = Line->getLineNumberEnd(); + + Printer << "Line "; + PDB_ColorItem StatementColor = Line->isStatement() + ? PDB_ColorItem::Keyword + : PDB_ColorItem::LiteralValue; + WithColor(Printer, StatementColor).get() << LineStart; + if (LineStart != LineEnd) + WithColor(Printer, StatementColor).get() << " - " << LineEnd; + + uint32_t ColumnStart = Line->getColumnNumber(); + uint32_t ColumnEnd = Line->getColumnNumberEnd(); + if (ColumnStart != 0 || ColumnEnd != 0) { + Printer << ", Column: "; + WithColor(Printer, StatementColor).get() << ColumnStart; + if (ColumnEnd != ColumnStart) + WithColor(Printer, StatementColor).get() << " - " << ColumnEnd; + } + + Printer << ", Address: "; + if (Line->getLength() > 0) { + uint64_t AddrStart = Line->getVirtualAddress(); + uint64_t AddrEnd = AddrStart + Line->getLength() - 1; + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(AddrStart, 10) << " - " + << format_hex(AddrEnd, 10) << "]"; + Printer << " (" << Line->getLength() << " bytes)"; + } else { + uint64_t AddrStart = Line->getVirtualAddress(); + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(AddrStart, 10) << "] "; + Printer << "(0 bytes)"; + } + } + Printer.Unindent(); + } + Printer.Unindent(); + } + + if (opts & Flags::Children) { + auto ChildrenEnum = Symbol.findAllChildren(); + Printer.Indent(); + while (auto Child = ChildrenEnum->getNext()) + Child->dump(*this); + Printer.Unindent(); + } +} + +void CompilandDumper::dump(const PDBSymbolData &Symbol) { + if (!shouldDumpSymLevel(opts::pretty::SymLevel::Data)) + return; + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + + switch (auto LocType = Symbol.getLocationType()) { + case PDB_LocType::Static: + Printer << "data: "; + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(Symbol.getVirtualAddress(), 10) << "]"; + + WithColor(Printer, PDB_ColorItem::Comment).get() + << " [sizeof = " << getTypeLength(Symbol) << "]"; + + break; + case PDB_LocType::Constant: + Printer << "constant: "; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() + << "[" << Symbol.getValue() << "]"; + WithColor(Printer, PDB_ColorItem::Comment).get() + << " [sizeof = " << getTypeLength(Symbol) << "]"; + break; + default: + Printer << "data(unexpected type=" << LocType << ")"; + } + + Printer << " "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); +} + +void CompilandDumper::dump(const PDBSymbolFunc &Symbol) { + if (!shouldDumpSymLevel(opts::pretty::SymLevel::Functions)) + return; + if (Symbol.getLength() == 0) + return; + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + FunctionDumper Dumper(Printer); + Dumper.start(Symbol, FunctionDumper::PointerType::None); +} + +void CompilandDumper::dump(const PDBSymbolLabel &Symbol) { + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + Printer << "label "; + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(Symbol.getVirtualAddress(), 10) << "] "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); +} + +void CompilandDumper::dump(const PDBSymbolThunk &Symbol) { + if (!shouldDumpSymLevel(opts::pretty::SymLevel::Thunks)) + return; + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + Printer << "thunk "; + codeview::ThunkOrdinal Ordinal = Symbol.getThunkOrdinal(); + uint64_t VA = Symbol.getVirtualAddress(); + if (Ordinal == codeview::ThunkOrdinal::TrampIncremental) { + uint64_t Target = Symbol.getTargetVirtualAddress(); + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(VA, 10); + Printer << " -> "; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(Target, 10); + } else { + WithColor(Printer, PDB_ColorItem::Address).get() + << "[" << format_hex(VA, 10) << " - " + << format_hex(VA + Symbol.getLength(), 10) << "]"; + } + Printer << " ("; + WithColor(Printer, PDB_ColorItem::Register).get() << Ordinal; + Printer << ") "; + std::string Name = Symbol.getName(); + if (!Name.empty()) + WithColor(Printer, PDB_ColorItem::Identifier).get() << Name; +} + +void CompilandDumper::dump(const PDBSymbolTypeTypedef &Symbol) {} + +void CompilandDumper::dump(const PDBSymbolUnknown &Symbol) { + Printer.NewLine(); + Printer << "unknown (" << Symbol.getSymTag() << ")"; +} diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.h new file mode 100644 index 0000000..cae196e --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.h @@ -0,0 +1,44 @@ +//===- PrettyCompilandDumper.h - llvm-pdbutil compiland 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_TOOLS_LLVMPDBDUMP_PRETTYCOMPILANDDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCOMPILANDDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { + +class LinePrinter; + +typedef int CompilandDumpFlags; +class CompilandDumper : public PDBSymDumper { +public: + enum Flags { None = 0x0, Children = 0x1, Symbols = 0x2, Lines = 0x4 }; + + CompilandDumper(LinePrinter &P); + + void start(const PDBSymbolCompiland &Symbol, CompilandDumpFlags flags); + + void dump(const PDBSymbolCompilandDetails &Symbol) override; + void dump(const PDBSymbolCompilandEnv &Symbol) override; + void dump(const PDBSymbolData &Symbol) override; + void dump(const PDBSymbolFunc &Symbol) override; + void dump(const PDBSymbolLabel &Symbol) override; + void dump(const PDBSymbolThunk &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolUnknown &Symbol) override; + +private: + LinePrinter &Printer; +}; +} +} + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.cpp new file mode 100644 index 0000000..7aff5b9 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.cpp @@ -0,0 +1,53 @@ +//===- PrettyEnumDumper.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyEnumDumper.h" + +#include "LinePrinter.h" +#include "PrettyBuiltinDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" + +using namespace llvm; +using namespace llvm::pdb; + +EnumDumper::EnumDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} + +void EnumDumper::start(const PDBSymbolTypeEnum &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum "; + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); + if (!opts::pretty::NoEnumDefs) { + auto BuiltinType = Symbol.getUnderlyingType(); + if (BuiltinType->getBuiltinType() != PDB_BuiltinType::Int || + BuiltinType->getLength() != 4) { + Printer << " : "; + BuiltinDumper Dumper(Printer); + Dumper.start(*BuiltinType); + } + Printer << " {"; + Printer.Indent(); + auto EnumValues = Symbol.findAllChildren<PDBSymbolData>(); + while (auto EnumValue = EnumValues->getNext()) { + if (EnumValue->getDataKind() != PDB_DataKind::Constant) + continue; + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() + << EnumValue->getName(); + Printer << " = "; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() + << EnumValue->getValue(); + } + Printer.Unindent(); + Printer.NewLine(); + Printer << "}"; + } +} diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.h new file mode 100644 index 0000000..c6e65a6 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyEnumDumper.h @@ -0,0 +1,31 @@ +//===- PrettyEnumDumper.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_PRETTYENUMDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYENUMDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { + +class LinePrinter; + +class EnumDumper : public PDBSymDumper { +public: + EnumDumper(LinePrinter &P); + + void start(const PDBSymbolTypeEnum &Symbol); + +private: + LinePrinter &Printer; +}; +} +} +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp new file mode 100644 index 0000000..fc40d90 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp @@ -0,0 +1,41 @@ +//===- PrettyExternalSymbolDumper.cpp -------------------------- *- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyExternalSymbolDumper.h" +#include "LinePrinter.h" + +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h" +#include "llvm/Support/Format.h" + +using namespace llvm; +using namespace llvm::pdb; + +ExternalSymbolDumper::ExternalSymbolDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void ExternalSymbolDumper::start(const PDBSymbolExe &Symbol) { + auto Vars = Symbol.findAllChildren<PDBSymbolPublicSymbol>(); + while (auto Var = Vars->getNext()) + Var->dump(*this); +} + +void ExternalSymbolDumper::dump(const PDBSymbolPublicSymbol &Symbol) { + std::string LinkageName = Symbol.getName(); + if (Printer.IsSymbolExcluded(LinkageName)) + return; + + Printer.NewLine(); + uint64_t Addr = Symbol.getVirtualAddress(); + + Printer << "["; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(Addr, 10); + Printer << "] "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << LinkageName; +} diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h new file mode 100644 index 0000000..6a00986 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.h @@ -0,0 +1,34 @@ +//===- PrettyExternalSymbolDumper.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_PRETTYEXTERNALSYMBOLDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYEXTERNALSYMBOLDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { + +class LinePrinter; + +class ExternalSymbolDumper : public PDBSymDumper { +public: + ExternalSymbolDumper(LinePrinter &P); + + void start(const PDBSymbolExe &Symbol); + + void dump(const PDBSymbolPublicSymbol &Symbol) override; + +private: + LinePrinter &Printer; +}; +} +} + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp new file mode 100644 index 0000000..06d7241 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp @@ -0,0 +1,259 @@ +//===- PrettyFunctionDumper.cpp --------------------------------- *- C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyFunctionDumper.h" +#include "LinePrinter.h" +#include "PrettyBuiltinDumper.h" +#include "llvm-pdbutil.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/PDBSymbolFuncDebugEnd.h" +#include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +namespace { +template <class T> +void dumpClassParentWithScopeOperator(const T &Symbol, LinePrinter &Printer, + FunctionDumper &Dumper) { + uint32_t ClassParentId = Symbol.getClassParentId(); + auto ClassParent = + Symbol.getSession().template getConcreteSymbolById<PDBSymbolTypeUDT>( + ClassParentId); + if (!ClassParent) + return; + + WithColor(Printer, PDB_ColorItem::Type).get() << ClassParent->getName(); + Printer << "::"; +} +} + +FunctionDumper::FunctionDumper(LinePrinter &P) + : PDBSymDumper(true), Printer(P) {} + +void FunctionDumper::start(const PDBSymbolTypeFunctionSig &Symbol, + const char *Name, PointerType Pointer) { + auto ReturnType = Symbol.getReturnType(); + ReturnType->dump(*this); + Printer << " "; + uint32_t ClassParentId = Symbol.getClassParentId(); + auto ClassParent = + Symbol.getSession().getConcreteSymbolById<PDBSymbolTypeUDT>( + ClassParentId); + + PDB_CallingConv CC = Symbol.getCallingConvention(); + bool ShouldDumpCallingConvention = true; + if ((ClassParent && CC == CallingConvention::ThisCall) || + (!ClassParent && CC == CallingConvention::NearStdCall)) { + ShouldDumpCallingConvention = false; + } + + if (Pointer == PointerType::None) { + if (ShouldDumpCallingConvention) + WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " "; + if (ClassParent) { + Printer << "("; + WithColor(Printer, PDB_ColorItem::Identifier).get() + << ClassParent->getName(); + Printer << "::)"; + } + } else { + Printer << "("; + if (ShouldDumpCallingConvention) + WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " "; + if (ClassParent) { + WithColor(Printer, PDB_ColorItem::Identifier).get() + << ClassParent->getName(); + Printer << "::"; + } + if (Pointer == PointerType::Reference) + Printer << "&"; + else + Printer << "*"; + if (Name) + WithColor(Printer, PDB_ColorItem::Identifier).get() << Name; + Printer << ")"; + } + + Printer << "("; + if (auto ChildEnum = Symbol.getArguments()) { + uint32_t Index = 0; + while (auto Arg = ChildEnum->getNext()) { + Arg->dump(*this); + if (++Index < ChildEnum->getChildCount()) + Printer << ", "; + } + } + Printer << ")"; + + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " const"; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile"; +} + +void FunctionDumper::start(const PDBSymbolFunc &Symbol, PointerType Pointer) { + uint64_t FuncStart = Symbol.getVirtualAddress(); + uint64_t FuncEnd = FuncStart + Symbol.getLength(); + + Printer << "func ["; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncStart, 10); + if (auto DebugStart = Symbol.findOneChild<PDBSymbolFuncDebugStart>()) { + uint64_t Prologue = DebugStart->getVirtualAddress() - FuncStart; + WithColor(Printer, PDB_ColorItem::Offset).get() + << formatv("+{0,2}", Prologue); + } + Printer << " - "; + WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(FuncEnd, 10); + if (auto DebugEnd = Symbol.findOneChild<PDBSymbolFuncDebugEnd>()) { + uint64_t Epilogue = FuncEnd - DebugEnd->getVirtualAddress(); + WithColor(Printer, PDB_ColorItem::Offset).get() + << formatv("-{0,2}", Epilogue); + } + + WithColor(Printer, PDB_ColorItem::Comment).get() + << formatv(" | sizeof={0,3}", Symbol.getLength()); + Printer << "] ("; + + if (Symbol.hasFramePointer()) { + WithColor(Printer, PDB_ColorItem::Register).get() + << Symbol.getLocalBasePointerRegisterId(); + } else { + WithColor(Printer, PDB_ColorItem::Register).get() << "FPO"; + } + Printer << ") "; + + if (Symbol.isVirtual() || Symbol.isPureVirtual()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "virtual "; + + auto Signature = Symbol.getSignature(); + if (!Signature) { + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); + if (Pointer == PointerType::Pointer) + Printer << "*"; + else if (Pointer == FunctionDumper::PointerType::Reference) + Printer << "&"; + return; + } + + auto ReturnType = Signature->getReturnType(); + ReturnType->dump(*this); + Printer << " "; + + auto ClassParent = Symbol.getClassParent(); + CallingConvention CC = Signature->getCallingConvention(); + if (Pointer != FunctionDumper::PointerType::None) + Printer << "("; + + if ((ClassParent && CC != CallingConvention::ThisCall) || + (!ClassParent && CC != CallingConvention::NearStdCall)) { + WithColor(Printer, PDB_ColorItem::Keyword).get() + << Signature->getCallingConvention() << " "; + } + WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName(); + if (Pointer != FunctionDumper::PointerType::None) { + if (Pointer == PointerType::Pointer) + Printer << "*"; + else if (Pointer == FunctionDumper::PointerType::Reference) + Printer << "&"; + Printer << ")"; + } + + Printer << "("; + if (auto Arguments = Symbol.getArguments()) { + uint32_t Index = 0; + while (auto Arg = Arguments->getNext()) { + auto ArgType = Arg->getType(); + ArgType->dump(*this); + WithColor(Printer, PDB_ColorItem::Identifier).get() << " " + << Arg->getName(); + 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"; + if (Symbol.isPureVirtual()) + Printer << " = 0"; +} + +void FunctionDumper::dump(const PDBSymbolTypeArray &Symbol) { + auto ElementType = Symbol.getElementType(); + + ElementType->dump(*this); + Printer << "["; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Symbol.getLength(); + Printer << "]"; +} + +void FunctionDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { + BuiltinDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void FunctionDumper::dump(const PDBSymbolTypeEnum &Symbol) { + dumpClassParentWithScopeOperator(Symbol, Printer, *this); + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) { + // PDBSymbolTypeFunctionArg is just a shim over the real argument. Just drill + // through to the real thing and dump it. + uint32_t TypeId = Symbol.getTypeId(); + auto Type = Symbol.getSession().getSymbolById(TypeId); + if (!Type) + return; + Type->dump(*this); +} + +void FunctionDumper::dump(const PDBSymbolTypeTypedef &Symbol) { + dumpClassParentWithScopeOperator(Symbol, Printer, *this); + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void FunctionDumper::dump(const PDBSymbolTypePointer &Symbol) { + auto PointeeType = Symbol.getPointeeType(); + if (!PointeeType) + return; + + if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) { + FunctionDumper NestedDumper(Printer); + PointerType Pointer = + Symbol.isReference() ? PointerType::Reference : PointerType::Pointer; + NestedDumper.start(*FuncSig, nullptr, 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 FunctionDumper::dump(const PDBSymbolTypeUDT &Symbol) { + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.h new file mode 100644 index 0000000..1a6f543 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.h @@ -0,0 +1,43 @@ +//===- PrettyFunctionDumper.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_PRETTYFUNCTIONDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYFUNCTIONDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { +class LinePrinter; + +class FunctionDumper : public PDBSymDumper { +public: + FunctionDumper(LinePrinter &P); + + enum class PointerType { None, Pointer, Reference }; + + void start(const PDBSymbolTypeFunctionSig &Symbol, const char *Name, + PointerType Pointer); + void start(const PDBSymbolFunc &Symbol, PointerType Pointer); + + void dump(const PDBSymbolTypeArray &Symbol) override; + void dump(const PDBSymbolTypeBuiltin &Symbol) override; + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeFunctionArg &Symbol) override; + void dump(const PDBSymbolTypePointer &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + +private: + LinePrinter &Printer; +}; +} +} + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp new file mode 100644 index 0000000..0f60863 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp @@ -0,0 +1,255 @@ +//===- PrettyTypeDumper.cpp - PDBSymDumper type dumper *------------ C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyTypeDumper.h" + +#include "LinePrinter.h" +#include "PrettyBuiltinDumper.h" +#include "PrettyClassDefinitionDumper.h" +#include "PrettyEnumDumper.h" +#include "PrettyTypedefDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" +#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->getName() < S2->getName(); +} + +static bool CompareSizes(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->getSize() < S2->getSize(); +} + +static bool ComparePadding(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->deepPaddingSize() < S2->deepPaddingSize(); +} + +static bool ComparePaddingPct(const LayoutPtr &S1, const LayoutPtr &S2) { + double Pct1 = (double)S1->deepPaddingSize() / (double)S1->getSize(); + double Pct2 = (double)S2->deepPaddingSize() / (double)S2->getSize(); + return Pct1 < Pct2; +} + +static bool ComparePaddingImmediate(const LayoutPtr &S1, const LayoutPtr &S2) { + return S1->immediatePadding() < S2->immediatePadding(); +} + +static bool ComparePaddingPctImmediate(const LayoutPtr &S1, + const LayoutPtr &S2) { + double Pct1 = (double)S1->immediatePadding() / (double)S1->getSize(); + double Pct2 = (double)S2->immediatePadding() / (double)S2->getSize(); + return Pct1 < Pct2; +} + +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; + case opts::pretty::ClassSortMode::PaddingPct: + return ComparePaddingPct; + case opts::pretty::ClassSortMode::PaddingImmediate: + return ComparePaddingImmediate; + case opts::pretty::ClassSortMode::PaddingPctImmediate: + return ComparePaddingPctImmediate; + 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); + + if (UnfilteredCount > 10000) { + errs() << formatv("Filtering and sorting {0} types", UnfilteredCount); + errs().flush(); + } + uint32_t Examined = 0; + uint32_t Discarded = 0; + while (auto Class = E.getNext()) { + ++Examined; + if (Examined % 10000 == 0) { + errs() << formatv("Examined {0}/{1} items. {2} items discarded\n", + Examined, UnfilteredCount, Discarded); + errs().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; + } + if (Layout->immediatePadding() < opts::pretty::ImmediatePaddingThreshold) { + ++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 Children = Exe.findAllChildren(); + if (opts::pretty::Enums) { + if (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) { + if (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) { + if (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 (Class->getUnmodifiedTypeId() != 0) + continue; + + 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) { + assert(opts::pretty::Enums); + + if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength())) + return; + // Dump member enums when dumping their class definition. + if (nullptr != Symbol.getClassParent()) + return; + + Printer.NewLine(); + EnumDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) { + assert(opts::pretty::Typedefs); + + if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength())) + return; + + Printer.NewLine(); + TypedefDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void TypeDumper::dumpClassLayout(const ClassLayout &Class) { + assert(opts::pretty::Classes); + + if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Keyword).get() << "class "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Class.getName(); + } else { + ClassDefinitionDumper Dumper(Printer); + Dumper.start(Class); + } +} diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.h new file mode 100644 index 0000000..68a2f02 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.h @@ -0,0 +1,36 @@ +//===- PrettyTypeDumper.h - PDBSymDumper implementation for types *- 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_PRETTYTYPEDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYTYPEDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { +class LinePrinter; +class ClassLayout; + +class TypeDumper : public PDBSymDumper { +public: + TypeDumper(LinePrinter &P); + + void start(const PDBSymbolExe &Exe); + + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeTypedef &Symbol) override; + + void dumpClassLayout(const ClassLayout &Class); + +private: + LinePrinter &Printer; +}; +} +} +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp new file mode 100644 index 0000000..2266e6e --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp @@ -0,0 +1,77 @@ +//===- PrettyTypedefDumper.cpp - PDBSymDumper impl for typedefs -- * C++ *-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyTypedefDumper.h" + +#include "LinePrinter.h" +#include "PrettyBuiltinDumper.h" +#include "PrettyFunctionDumper.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDBExtras.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" + +using namespace llvm; +using namespace llvm::pdb; + +TypedefDumper::TypedefDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} + +void TypedefDumper::start(const PDBSymbolTypeTypedef &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "typedef "; + uint32_t TargetId = Symbol.getTypeId(); + if (auto TypeSymbol = Symbol.getSession().getSymbolById(TargetId)) + TypeSymbol->dump(*this); + WithColor(Printer, PDB_ColorItem::Identifier).get() << " " + << Symbol.getName(); +} + +void TypedefDumper::dump(const PDBSymbolTypeArray &Symbol) {} + +void TypedefDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { + BuiltinDumper Dumper(Printer); + Dumper.start(Symbol); +} + +void TypedefDumper::dump(const PDBSymbolTypeEnum &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum "; + WithColor(Printer, PDB_ColorItem::Type).get() << " " << Symbol.getName(); +} + +void TypedefDumper::dump(const PDBSymbolTypePointer &Symbol) { + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; + 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; + FunctionDumper NestedDumper(Printer); + NestedDumper.start(*FuncSig, nullptr, Pointer); + } else { + PointeeType->dump(*this); + Printer << ((Symbol.isReference()) ? "&" : "*"); + } +} + +void TypedefDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) { + FunctionDumper Dumper(Printer); + Dumper.start(Symbol, nullptr, FunctionDumper::PointerType::None); +} + +void TypedefDumper::dump(const PDBSymbolTypeUDT &Symbol) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "class "; + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.h new file mode 100644 index 0000000..133bbfb --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.h @@ -0,0 +1,39 @@ +//===- PrettyTypedefDumper.h - llvm-pdbutil typedef 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_TOOLS_LLVMPDBDUMP_PRETTYTYPEDEFDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYTYPEDEFDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { +namespace pdb { + +class LinePrinter; + +class TypedefDumper : public PDBSymDumper { +public: + TypedefDumper(LinePrinter &P); + + void start(const PDBSymbolTypeTypedef &Symbol); + + void dump(const PDBSymbolTypeArray &Symbol) override; + void dump(const PDBSymbolTypeBuiltin &Symbol) override; + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeFunctionSig &Symbol) override; + void dump(const PDBSymbolTypePointer &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; + +private: + LinePrinter &Printer; +}; +} +} + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp new file mode 100644 index 0000000..4884fc8 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp @@ -0,0 +1,220 @@ +//===- PrettyVariableDumper.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PrettyVariableDumper.h" + +#include "LinePrinter.h" +#include "PrettyBuiltinDumper.h" +#include "PrettyFunctionDumper.h" +#include "llvm-pdbutil.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" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#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, uint32_t Offset) { + if (Var.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated) + return; + if (Printer.IsSymbolExcluded(Var.getName())) + return; + + 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 << ", sizeof=" << Length << "] "; + WithColor(Printer, PDB_ColorItem::Keyword).get() << "static "; + dumpSymbolTypeAndName(*VarType, Var.getName()); + break; + case PDB_LocType::Constant: + if (isa<PDBSymbolTypeEnum>(*VarType)) + break; + Printer.NewLine(); + Printer << "data [sizeof=" << Length << "] "; + dumpSymbolTypeAndName(*VarType, Var.getName()); + Printer << " = "; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getValue(); + break; + case PDB_LocType::ThisRel: + Printer.NewLine(); + Printer << "data "; + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << 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(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 [sizeof=" << Length << "] "; + Printer << "unknown(" << LocType << ") "; + WithColor(Printer, PDB_ColorItem::Identifier).get() << Var.getName(); + break; + } +} + +void VariableDumper::startVbptr(uint32_t Offset, uint32_t Size) { + Printer.NewLine(); + Printer << "vbptr "; + + WithColor(Printer, PDB_ColorItem::Offset).get() + << "+" << format_hex(Offset, 4) << " [sizeof=" << Size << "] "; +} + +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); +} + +void VariableDumper::dump(const PDBSymbolTypeEnum &Symbol) { + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +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 "; +} + +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) { + WithColor(Printer, PDB_ColorItem::Keyword).get() << "typedef "; + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void VariableDumper::dump(const PDBSymbolTypeUDT &Symbol) { + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); +} + +void VariableDumper::dumpSymbolTypeAndName(const PDBSymbol &Type, + StringRef Name) { + Type.dump(*this); + WithColor(Printer, PDB_ColorItem::Identifier).get() << " " << Name; + Type.dumpRight(*this); +} diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.h b/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.h new file mode 100644 index 0000000..cacf1ce --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.h @@ -0,0 +1,50 @@ +//===- PrettyVariableDumper.h - PDBSymDumper variable 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_TOOLS_LLVMPDBDUMP_PRETTYVARIABLEDUMPER_H +#define LLVM_TOOLS_LLVMPDBDUMP_PRETTYVARIABLEDUMPER_H + +#include "llvm/DebugInfo/PDB/PDBSymDumper.h" + +namespace llvm { + +class StringRef; + +namespace pdb { + +class LinePrinter; + +class VariableDumper : public PDBSymDumper { +public: + VariableDumper(LinePrinter &P); + + void start(const PDBSymbolData &Var, uint32_t Offset = 0); + void start(const PDBSymbolTypeVTable &Var, uint32_t Offset = 0); + void startVbptr(uint32_t Offset, uint32_t Size); + + void dump(const PDBSymbolTypeArray &Symbol) override; + void dump(const PDBSymbolTypeBuiltin &Symbol) override; + void dump(const PDBSymbolTypeEnum &Symbol) override; + void dump(const PDBSymbolTypeFunctionSig &Symbol) override; + void dump(const PDBSymbolTypePointer &Symbol) override; + 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); + + LinePrinter &Printer; +}; +} +} +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp new file mode 100644 index 0000000..4d35200 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp @@ -0,0 +1,152 @@ +//===- 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 "FormatUtil.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" +#include "llvm/DebugInfo/PDB/Native/DbiModuleList.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +using namespace llvm; +using namespace llvm::pdb; + +void llvm::pdb::discoverStreamPurposes( + PDBFile &File, + SmallVectorImpl<std::pair<StreamPurpose, 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, DbiModuleDescriptor> ModStreams; + DenseMap<uint16_t, std::string> NamedStreams; + + if (Dbi) { + const DbiModuleList &Modules = Dbi->modules(); + for (uint32_t I = 0; I < Modules.getModuleCount(); ++I) { + DbiModuleDescriptor Descriptor = Modules.getModuleDescriptor(I); + uint16_t SN = Descriptor.getModuleStreamIndex(); + if (SN != kInvalidStreamIndex) + ModStreams[SN] = Descriptor; + } + } + 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::pair<StreamPurpose, std::string> Value; + if (StreamIdx == OldMSFDirectory) + Value = std::make_pair(StreamPurpose::Other, "Old MSF Directory"); + else if (StreamIdx == StreamPDB) + Value = std::make_pair(StreamPurpose::Other, "PDB Stream"); + else if (StreamIdx == StreamDBI) + Value = std::make_pair(StreamPurpose::Other, "DBI Stream"); + else if (StreamIdx == StreamTPI) + Value = std::make_pair(StreamPurpose::Other, "TPI Stream"); + else if (StreamIdx == StreamIPI) + Value = std::make_pair(StreamPurpose::Other, "IPI Stream"); + else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex()) + Value = std::make_pair(StreamPurpose::Other, "Global Symbol Hash"); + else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex()) + Value = std::make_pair(StreamPurpose::Other, "Public Symbol Hash"); + else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex()) + Value = std::make_pair(StreamPurpose::Other, "Public Symbol Records"); + else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex()) + Value = std::make_pair(StreamPurpose::Other, "TPI Hash"); + else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex()) + Value = std::make_pair(StreamPurpose::Other, "TPI Aux Hash"); + else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex()) + Value = std::make_pair(StreamPurpose::Other, "IPI Hash"); + else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex()) + Value = std::make_pair(StreamPurpose::Other, "IPI Aux Hash"); + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception)) + Value = std::make_pair(StreamPurpose::Other, "Exception Data"); + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup)) + Value = std::make_pair(StreamPurpose::Other, "Fixup Data"); + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO)) + Value = std::make_pair(StreamPurpose::Other, "FPO Data"); + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO)) + Value = std::make_pair(StreamPurpose::Other, "New FPO Data"); + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc)) + Value = std::make_pair(StreamPurpose::Other, "Omap From Source Data"); + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc)) + Value = std::make_pair(StreamPurpose::Other, "Omap To Source Data"); + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata)) + Value = std::make_pair(StreamPurpose::Other, "Pdata"); + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr)) + Value = std::make_pair(StreamPurpose::Other, "Section Header Data"); + else if (Dbi && + StreamIdx == + Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)) + Value = + std::make_pair(StreamPurpose::Other, "Section Header Original Data"); + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap)) + Value = std::make_pair(StreamPurpose::Other, "Token Rid Data"); + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata)) + Value = std::make_pair(StreamPurpose::Other, "Xdata"); + else { + auto ModIter = ModStreams.find(StreamIdx); + auto NSIter = NamedStreams.find(StreamIdx); + if (ModIter != ModStreams.end()) { + Value = std::make_pair(StreamPurpose::ModuleStream, + ModIter->second.getModuleName()); + } else if (NSIter != NamedStreams.end()) { + Value = std::make_pair(StreamPurpose::NamedStream, NSIter->second); + } else { + Value = std::make_pair(StreamPurpose::Other, "???"); + } + } + 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()); +} + +void llvm::pdb::discoverStreamPurposes(PDBFile &File, + SmallVectorImpl<std::string> &Purposes) { + SmallVector<std::pair<StreamPurpose, std::string>, 24> SP; + discoverStreamPurposes(File, SP); + Purposes.reserve(SP.size()); + for (const auto &P : SP) { + if (P.first == StreamPurpose::NamedStream) + Purposes.push_back(formatv("Named Stream \"{0}\"", P.second)); + else if (P.first == StreamPurpose::ModuleStream) + Purposes.push_back(formatv("Module \"{0}\"", P.second)); + else + Purposes.push_back(P.second); + } +} diff --git a/contrib/llvm/tools/llvm-pdbutil/StreamUtil.h b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.h new file mode 100644 index 0000000..f49c0a0 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.h @@ -0,0 +1,30 @@ +//===- 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; +enum class StreamPurpose { NamedStream, ModuleStream, Other }; + +void discoverStreamPurposes(PDBFile &File, + SmallVectorImpl<std::string> &Purposes); +void discoverStreamPurposes( + PDBFile &File, + SmallVectorImpl<std::pair<StreamPurpose, std::string>> &Purposes); +} +} + +#endif diff --git a/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.cpp b/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.cpp new file mode 100644 index 0000000..ae3138e --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.cpp @@ -0,0 +1,333 @@ +//===- YAMLOutputStyle.cpp ------------------------------------ *- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "YAMLOutputStyle.h" + +#include "PdbYaml.h" +#include "llvm-pdbutil.h" + +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugUnknownSubsection.h" +#include "llvm/DebugInfo/CodeView/Line.h" +#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" +#include "llvm/DebugInfo/MSF/MappedBlockStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::pdb; + +static bool checkModuleSubsection(opts::ModuleSubsection MS) { + return any_of(opts::pdb2yaml::DumpModuleSubsections, + [=](opts::ModuleSubsection M) { + return M == MS || M == opts::ModuleSubsection::All; + }); +} + +YAMLOutputStyle::YAMLOutputStyle(PDBFile &File) + : File(File), Out(outs()), Obj(File.getAllocator()) { + Out.setWriteDefaultValues(!opts::pdb2yaml::Minimal); +} + +Error YAMLOutputStyle::dump() { + if (opts::pdb2yaml::StreamDirectory) + opts::pdb2yaml::StreamMetadata = true; + + if (auto EC = dumpFileHeaders()) + return EC; + + if (auto EC = dumpStreamMetadata()) + return EC; + + if (auto EC = dumpStreamDirectory()) + return EC; + + if (auto EC = dumpStringTable()) + return EC; + + if (auto EC = dumpPDBStream()) + return EC; + + if (auto EC = dumpDbiStream()) + return EC; + + if (auto EC = dumpTpiStream()) + return EC; + + if (auto EC = dumpIpiStream()) + return EC; + + flush(); + return Error::success(); +} + + +Error YAMLOutputStyle::dumpFileHeaders() { + if (opts::pdb2yaml::NoFileHeaders) + return Error::success(); + + yaml::MSFHeaders Headers; + Obj.Headers.emplace(); + Obj.Headers->SuperBlock.NumBlocks = File.getBlockCount(); + Obj.Headers->SuperBlock.BlockMapAddr = File.getBlockMapIndex(); + Obj.Headers->SuperBlock.BlockSize = File.getBlockSize(); + auto Blocks = File.getDirectoryBlockArray(); + Obj.Headers->DirectoryBlocks.assign(Blocks.begin(), Blocks.end()); + Obj.Headers->NumDirectoryBlocks = File.getNumDirectoryBlocks(); + Obj.Headers->SuperBlock.NumDirectoryBytes = File.getNumDirectoryBytes(); + Obj.Headers->NumStreams = + opts::pdb2yaml::StreamMetadata ? File.getNumStreams() : 0; + Obj.Headers->SuperBlock.FreeBlockMapBlock = File.getFreeBlockMapBlock(); + Obj.Headers->SuperBlock.Unknown1 = File.getUnknown1(); + Obj.Headers->FileSize = File.getFileSize(); + + return Error::success(); +} + +Error YAMLOutputStyle::dumpStringTable() { + bool RequiresStringTable = opts::pdb2yaml::DumpModuleFiles || + !opts::pdb2yaml::DumpModuleSubsections.empty(); + bool RequestedStringTable = opts::pdb2yaml::StringTable; + if (!RequiresStringTable && !RequestedStringTable) + return Error::success(); + + auto ExpectedST = File.getStringTable(); + if (!ExpectedST) + return ExpectedST.takeError(); + + Obj.StringTable.emplace(); + const auto &ST = ExpectedST.get(); + for (auto ID : ST.name_ids()) { + auto S = ST.getStringForID(ID); + if (!S) + return S.takeError(); + if (S->empty()) + continue; + Obj.StringTable->push_back(*S); + } + return Error::success(); +} + +Error YAMLOutputStyle::dumpStreamMetadata() { + if (!opts::pdb2yaml::StreamMetadata) + return Error::success(); + + Obj.StreamSizes.emplace(); + Obj.StreamSizes->assign(File.getStreamSizes().begin(), + File.getStreamSizes().end()); + return Error::success(); +} + +Error YAMLOutputStyle::dumpStreamDirectory() { + if (!opts::pdb2yaml::StreamDirectory) + return Error::success(); + + auto StreamMap = File.getStreamMap(); + Obj.StreamMap.emplace(); + for (auto &Stream : StreamMap) { + pdb::yaml::StreamBlockList BlockList; + BlockList.Blocks.assign(Stream.begin(), Stream.end()); + Obj.StreamMap->push_back(BlockList); + } + + return Error::success(); +} + +Error YAMLOutputStyle::dumpPDBStream() { + if (!opts::pdb2yaml::PdbStream) + return Error::success(); + + auto IS = File.getPDBInfoStream(); + if (!IS) + return IS.takeError(); + + auto &InfoS = IS.get(); + Obj.PdbStream.emplace(); + Obj.PdbStream->Age = InfoS.getAge(); + Obj.PdbStream->Guid = InfoS.getGuid(); + Obj.PdbStream->Signature = InfoS.getSignature(); + Obj.PdbStream->Version = InfoS.getVersion(); + Obj.PdbStream->Features = InfoS.getFeatureSignatures(); + + return Error::success(); +} + +static opts::ModuleSubsection convertSubsectionKind(DebugSubsectionKind K) { + switch (K) { + case DebugSubsectionKind::CrossScopeExports: + return opts::ModuleSubsection::CrossScopeExports; + case DebugSubsectionKind::CrossScopeImports: + return opts::ModuleSubsection::CrossScopeImports; + case DebugSubsectionKind::FileChecksums: + return opts::ModuleSubsection::FileChecksums; + case DebugSubsectionKind::InlineeLines: + return opts::ModuleSubsection::InlineeLines; + case DebugSubsectionKind::Lines: + return opts::ModuleSubsection::Lines; + case DebugSubsectionKind::Symbols: + return opts::ModuleSubsection::Symbols; + case DebugSubsectionKind::StringTable: + return opts::ModuleSubsection::StringTable; + case DebugSubsectionKind::FrameData: + return opts::ModuleSubsection::FrameData; + default: + return opts::ModuleSubsection::Unknown; + } + llvm_unreachable("Unreachable!"); +} + +Error YAMLOutputStyle::dumpDbiStream() { + if (!opts::pdb2yaml::DbiStream) + return Error::success(); + + auto DbiS = File.getPDBDbiStream(); + if (!DbiS) + return DbiS.takeError(); + + auto &DS = DbiS.get(); + Obj.DbiStream.emplace(); + Obj.DbiStream->Age = DS.getAge(); + Obj.DbiStream->BuildNumber = DS.getBuildNumber(); + Obj.DbiStream->Flags = DS.getFlags(); + Obj.DbiStream->MachineType = DS.getMachineType(); + Obj.DbiStream->PdbDllRbld = DS.getPdbDllRbld(); + Obj.DbiStream->PdbDllVersion = DS.getPdbDllVersion(); + Obj.DbiStream->VerHeader = DS.getDbiVersion(); + if (opts::pdb2yaml::DumpModules) { + const auto &Modules = DS.modules(); + for (uint32_t I = 0; I < Modules.getModuleCount(); ++I) { + DbiModuleDescriptor MI = Modules.getModuleDescriptor(I); + + Obj.DbiStream->ModInfos.emplace_back(); + yaml::PdbDbiModuleInfo &DMI = Obj.DbiStream->ModInfos.back(); + + DMI.Mod = MI.getModuleName(); + DMI.Obj = MI.getObjFileName(); + if (opts::pdb2yaml::DumpModuleFiles) { + auto Files = Modules.source_files(I); + DMI.SourceFiles.assign(Files.begin(), Files.end()); + } + + uint16_t ModiStream = MI.getModuleStreamIndex(); + if (ModiStream == kInvalidStreamIndex) + continue; + + auto ModStreamData = msf::MappedBlockStream::createIndexedStream( + File.getMsfLayout(), File.getMsfBuffer(), ModiStream, + File.getAllocator()); + + pdb::ModuleDebugStreamRef ModS(MI, std::move(ModStreamData)); + if (auto EC = ModS.reload()) + return EC; + + auto ExpectedST = File.getStringTable(); + if (!ExpectedST) + return ExpectedST.takeError(); + if (!opts::pdb2yaml::DumpModuleSubsections.empty() && + ModS.hasDebugSubsections()) { + auto ExpectedChecksums = ModS.findChecksumsSubsection(); + if (!ExpectedChecksums) + return ExpectedChecksums.takeError(); + + StringsAndChecksumsRef SC(ExpectedST->getStringTable(), + *ExpectedChecksums); + + for (const auto &SS : ModS.subsections()) { + opts::ModuleSubsection OptionKind = convertSubsectionKind(SS.kind()); + if (!checkModuleSubsection(OptionKind)) + continue; + + auto Converted = + CodeViewYAML::YAMLDebugSubsection::fromCodeViewSubection(SC, SS); + if (!Converted) + return Converted.takeError(); + DMI.Subsections.push_back(*Converted); + } + } + + if (opts::pdb2yaml::DumpModuleSyms) { + DMI.Modi.emplace(); + + DMI.Modi->Signature = ModS.signature(); + bool HadError = false; + for (auto &Sym : ModS.symbols(&HadError)) { + auto ES = CodeViewYAML::SymbolRecord::fromCodeViewSymbol(Sym); + if (!ES) + return ES.takeError(); + + DMI.Modi->Symbols.push_back(*ES); + } + } + } + } + return Error::success(); +} + +Error YAMLOutputStyle::dumpTpiStream() { + if (!opts::pdb2yaml::TpiStream) + return Error::success(); + + auto TpiS = File.getPDBTpiStream(); + if (!TpiS) + return TpiS.takeError(); + + auto &TS = TpiS.get(); + Obj.TpiStream.emplace(); + Obj.TpiStream->Version = TS.getTpiVersion(); + for (auto &Record : TS.types(nullptr)) { + auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Record); + if (!ExpectedRecord) + return ExpectedRecord.takeError(); + Obj.TpiStream->Records.push_back(*ExpectedRecord); + } + + return Error::success(); +} + +Error YAMLOutputStyle::dumpIpiStream() { + if (!opts::pdb2yaml::IpiStream) + return Error::success(); + + auto InfoS = File.getPDBInfoStream(); + if (!InfoS) + return InfoS.takeError(); + if (!InfoS->containsIdStream()) + return Error::success(); + + auto IpiS = File.getPDBIpiStream(); + if (!IpiS) + return IpiS.takeError(); + + auto &IS = IpiS.get(); + Obj.IpiStream.emplace(); + Obj.IpiStream->Version = IS.getTpiVersion(); + for (auto &Record : IS.types(nullptr)) { + auto ExpectedRecord = CodeViewYAML::LeafRecord::fromCodeViewRecord(Record); + if (!ExpectedRecord) + return ExpectedRecord.takeError(); + + Obj.IpiStream->Records.push_back(*ExpectedRecord); + } + + return Error::success(); +} + +void YAMLOutputStyle::flush() { + Out << Obj; + outs().flush(); +} diff --git a/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.h b/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.h new file mode 100644 index 0000000..3690e35 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/YAMLOutputStyle.h @@ -0,0 +1,49 @@ +//===- YAMLOutputStyle.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_YAMLOUTPUTSTYLE_H +#define LLVM_TOOLS_LLVMPDBDUMP_YAMLOUTPUTSTYLE_H + +#include "OutputStyle.h" +#include "PdbYaml.h" + +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/YAMLTraits.h" + +namespace llvm { +namespace pdb { +class ModuleDebugStreamRef; + +class YAMLOutputStyle : public OutputStyle { +public: + YAMLOutputStyle(PDBFile &File); + + Error dump() override; + +private: + Error dumpStringTable(); + Error dumpFileHeaders(); + Error dumpStreamMetadata(); + Error dumpStreamDirectory(); + Error dumpPDBStream(); + Error dumpDbiStream(); + Error dumpTpiStream(); + Error dumpIpiStream(); + + void flush(); + + PDBFile &File; + llvm::yaml::Output Out; + + yaml::PdbObject Obj; +}; +} // namespace pdb +} // namespace llvm + +#endif // LLVM_TOOLS_LLVMPDBDUMP_YAMLOUTPUTSTYLE_H diff --git a/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp new file mode 100644 index 0000000..f2bd194 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -0,0 +1,1179 @@ +//===- llvm-pdbutil.cpp - Dump debug info from a PDB file -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Dumps debug information present in PDB files. +// +//===----------------------------------------------------------------------===// + +#include "llvm-pdbutil.h" + +#include "Analyze.h" +#include "BytesOutputStyle.h" +#include "Diff.h" +#include "DumpOutputStyle.h" +#include "LinePrinter.h" +#include "OutputStyle.h" +#include "PrettyCompilandDumper.h" +#include "PrettyExternalSymbolDumper.h" +#include "PrettyFunctionDumper.h" +#include "PrettyTypeDumper.h" +#include "PrettyVariableDumper.h" +#include "YAMLOutputStyle.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Config/config.h" +#include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" +#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" +#include "llvm/DebugInfo/CodeView/TypeTableBuilder.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/DbiModuleDescriptorBuilder.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/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/RawError.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/Support/BinaryByteStream.h" +#include "llvm/Support/COM.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::msf; +using namespace llvm::pdb; + +namespace opts { + +cl::SubCommand DumpSubcommand("dump", "Dump MSF and CodeView debug info"); +cl::SubCommand BytesSubcommand("bytes", "Dump raw bytes from 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"); +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::SubCommand MergeSubcommand("merge", + "Merge multiple PDBs into a single PDB"); + +cl::OptionCategory TypeCategory("Symbol Type Options"); +cl::OptionCategory FilterCategory("Filtering and Sorting Options"); +cl::OptionCategory OtherOptions("Other Options"); + +cl::ValuesClass ChunkValues = cl::values( + clEnumValN(ModuleSubsection::CrossScopeExports, "cme", + "Cross module exports (DEBUG_S_CROSSSCOPEEXPORTS subsection)"), + clEnumValN(ModuleSubsection::CrossScopeImports, "cmi", + "Cross module imports (DEBUG_S_CROSSSCOPEIMPORTS subsection)"), + clEnumValN(ModuleSubsection::FileChecksums, "fc", + "File checksums (DEBUG_S_CHECKSUMS subsection)"), + clEnumValN(ModuleSubsection::InlineeLines, "ilines", + "Inlinee lines (DEBUG_S_INLINEELINES subsection)"), + clEnumValN(ModuleSubsection::Lines, "lines", + "Lines (DEBUG_S_LINES subsection)"), + clEnumValN(ModuleSubsection::StringTable, "strings", + "String Table (DEBUG_S_STRINGTABLE subsection) (not " + "typically present in PDB file)"), + clEnumValN(ModuleSubsection::FrameData, "frames", + "Frame Data (DEBUG_S_FRAMEDATA subsection)"), + clEnumValN(ModuleSubsection::Symbols, "symbols", + "Symbols (DEBUG_S_SYMBOLS subsection) (not typically " + "present in PDB file)"), + clEnumValN(ModuleSubsection::CoffSymbolRVAs, "rvas", + "COFF Symbol RVAs (DEBUG_S_COFF_SYMBOL_RVA subsection)"), + clEnumValN(ModuleSubsection::Unknown, "unknown", + "Any subsection not covered by another option"), + clEnumValN(ModuleSubsection::All, "all", "All known subsections")); + +namespace pretty { +cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input PDB files>"), + cl::OneOrMore, cl::sub(PrettySubcommand)); + +cl::opt<bool> Compilands("compilands", cl::desc("Display compilands"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt<bool> Symbols("module-syms", + cl::desc("Display symbols for each compiland"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +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::list<SymLevel> SymTypes( + "sym-types", cl::desc("Type of symbols to dump (default all)"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand), cl::ZeroOrMore, + cl::values( + clEnumValN(SymLevel::Thunks, "thunks", "Display thunk symbols"), + clEnumValN(SymLevel::Data, "data", "Display data symbols"), + clEnumValN(SymLevel::Functions, "funcs", "Display function symbols"), + clEnumValN(SymLevel::All, "all", "Display all symbols (default)"))); + +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<SymbolSortMode> SymbolOrder( + "symbol-order", cl::desc("symbol sort order"), + cl::init(SymbolSortMode::None), + cl::values(clEnumValN(SymbolSortMode::None, "none", + "Undefined / no particular sort order"), + clEnumValN(SymbolSortMode::Name, "name", "Sort symbols by name"), + clEnumValN(SymbolSortMode::Size, "size", + "Sort symbols by size")), + 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"), + clEnumValN(ClassSortMode::PaddingPct, "padding-pct", + "Sort classes by percentage of space consumed by padding"), + clEnumValN(ClassSortMode::PaddingImmediate, "padding-imm", + "Sort classes by amount of immediate padding"), + clEnumValN(ClassSortMode::PaddingPctImmediate, "padding-pct-imm", + "Sort classes by percentage of space consumed by immediate " + "padding")), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); + +cl::opt<ClassDefinitionFormat> ClassFormat( + "class-definitions", cl::desc("Class definition format"), + cl::init(ClassDefinitionFormat::All), + cl::values( + clEnumValN(ClassDefinitionFormat::All, "all", + "Display all class members including data, constants, " + "typedefs, functions, etc"), + clEnumValN(ClassDefinitionFormat::Layout, "layout", + "Only display members that contribute to class size."), + clEnumValN(ClassDefinitionFormat::None, "none", + "Don't display class definitions")), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt<uint32_t> ClassRecursionDepth( + "class-recurse-depth", cl::desc("Class recursion depth (0=no limit)"), + cl::init(0), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); + +cl::opt<bool> Lines("lines", cl::desc("Line tables"), cl::cat(TypeCategory), + cl::sub(PrettySubcommand)); +cl::opt<bool> + All("all", cl::desc("Implies all other options in 'Symbol Types' category"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); + +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)); +cl::list<std::string> ExcludeSymbols( + "exclude-symbols", cl::desc("Exclude symbols by regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::list<std::string> ExcludeCompilands( + "exclude-compilands", cl::desc("Exclude compilands by regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); + +cl::list<std::string> IncludeTypes( + "include-types", + cl::desc("Include only types which match a regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::list<std::string> IncludeSymbols( + "include-symbols", + cl::desc("Include only symbols which match a regular expression"), + cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +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<uint32_t> ImmediatePaddingThreshold( + "min-class-padding-imm", + cl::desc("Displays only those classes which have at least the specified " + "amount of immediate padding, ignoring padding internal to bases " + "and aggregates."), + cl::init(0), cl::cat(FilterCategory), cl::sub(PrettySubcommand)); + +cl::opt<bool> ExcludeCompilerGenerated( + "no-compiler-generated", + cl::desc("Don't show compiler generated types and symbols"), + cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +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> NoEnumDefs("no-enum-definitions", + cl::desc("Don't display full enum definitions"), + cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +} + +namespace diff { +cl::opt<bool> PrintValueColumns( + "values", cl::init(true), + cl::desc("Print one column for each PDB with the field value"), + cl::Optional, cl::sub(DiffSubcommand)); +cl::opt<bool> + PrintResultColumn("result", cl::init(false), + cl::desc("Print a column with the result status"), + cl::Optional, cl::sub(DiffSubcommand)); + +cl::opt<std::string> LeftRoot( + "left-bin-root", cl::Optional, + cl::desc("Treats the specified path as the root of the tree containing " + "binaries referenced by the left PDB. The root is stripped from " + "embedded paths when doing equality comparisons."), + cl::sub(DiffSubcommand)); +cl::opt<std::string> RightRoot( + "right-bin-root", cl::Optional, + cl::desc("Treats the specified path as the root of the tree containing " + "binaries referenced by the right PDB. The root is stripped from " + "embedded paths when doing equality comparisons"), + cl::sub(DiffSubcommand)); + +cl::opt<std::string> Left(cl::Positional, cl::desc("<left>"), + cl::sub(DiffSubcommand)); +cl::opt<std::string> Right(cl::Positional, cl::desc("<right>"), + cl::sub(DiffSubcommand)); +} + +cl::OptionCategory FileOptions("Module & File Options"); + +namespace bytes { +cl::OptionCategory MsfBytes("MSF File Options"); +cl::OptionCategory DbiBytes("Dbi Stream Options"); +cl::OptionCategory PdbBytes("PDB Stream Options"); +cl::OptionCategory Types("Type Options"); +cl::OptionCategory ModuleCategory("Module Options"); + +llvm::Optional<NumberRange> DumpBlockRange; +llvm::Optional<NumberRange> DumpByteRange; + +cl::opt<std::string> DumpBlockRangeOpt( + "block-range", cl::value_desc("start[-end]"), + cl::desc("Dump binary data from specified range of blocks."), + cl::sub(BytesSubcommand), cl::cat(MsfBytes)); + +cl::opt<std::string> + DumpByteRangeOpt("byte-range", cl::value_desc("start[-end]"), + cl::desc("Dump binary data from specified range of bytes"), + cl::sub(BytesSubcommand), cl::cat(MsfBytes)); + +cl::list<std::string> + DumpStreamData("stream-data", cl::CommaSeparated, cl::ZeroOrMore, + cl::desc("Dump binary data from specified streams. Format " + "is SN[:Start][@Size]"), + cl::sub(BytesSubcommand), cl::cat(MsfBytes)); + +cl::opt<bool> NameMap("name-map", cl::desc("Dump bytes of PDB Name Map"), + cl::sub(BytesSubcommand), cl::cat(PdbBytes)); + +cl::opt<bool> SectionContributions("sc", cl::desc("Dump section contributions"), + cl::sub(BytesSubcommand), cl::cat(DbiBytes)); +cl::opt<bool> SectionMap("sm", cl::desc("Dump section map"), + cl::sub(BytesSubcommand), cl::cat(DbiBytes)); +cl::opt<bool> ModuleInfos("modi", cl::desc("Dump module info"), + cl::sub(BytesSubcommand), cl::cat(DbiBytes)); +cl::opt<bool> FileInfo("files", cl::desc("Dump source file info"), + cl::sub(BytesSubcommand), cl::cat(DbiBytes)); +cl::opt<bool> TypeServerMap("type-server", cl::desc("Dump type server map"), + cl::sub(BytesSubcommand), cl::cat(DbiBytes)); +cl::opt<bool> ECData("ec", cl::desc("Dump edit and continue map"), + cl::sub(BytesSubcommand), cl::cat(DbiBytes)); + +cl::list<uint32_t> + TypeIndex("type", + cl::desc("Dump the type record with the given type index"), + cl::ZeroOrMore, cl::CommaSeparated, cl::sub(BytesSubcommand), + cl::cat(TypeCategory)); +cl::list<uint32_t> + IdIndex("id", cl::desc("Dump the id record with the given type index"), + cl::ZeroOrMore, cl::CommaSeparated, cl::sub(BytesSubcommand), + cl::cat(TypeCategory)); + +cl::opt<uint32_t> ModuleIndex( + "mod", + cl::desc( + "Limit options in the Modules category to the specified module index"), + cl::Optional, cl::sub(BytesSubcommand), cl::cat(ModuleCategory)); +cl::opt<bool> ModuleSyms("syms", cl::desc("Dump symbol record substream"), + cl::sub(BytesSubcommand), cl::cat(ModuleCategory)); +cl::opt<bool> ModuleC11("c11-chunks", cl::Hidden, + cl::desc("Dump C11 CodeView debug chunks"), + cl::sub(BytesSubcommand), cl::cat(ModuleCategory)); +cl::opt<bool> ModuleC13("chunks", + cl::desc("Dump C13 CodeView debug chunk subsection"), + cl::sub(BytesSubcommand), cl::cat(ModuleCategory)); +cl::opt<bool> SplitChunks( + "split-chunks", + cl::desc( + "When dumping debug chunks, show a different section for each chunk"), + cl::sub(BytesSubcommand), cl::cat(ModuleCategory)); +cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input PDB files>"), + cl::OneOrMore, cl::sub(BytesSubcommand)); + +} // namespace bytes + +namespace dump { + +cl::OptionCategory MsfOptions("MSF Container Options"); +cl::OptionCategory TypeOptions("Type Record Options"); +cl::OptionCategory SymbolOptions("Symbol Options"); +cl::OptionCategory MiscOptions("Miscellaneous Options"); + +// MSF OPTIONS +cl::opt<bool> DumpSummary("summary", cl::desc("dump file summary"), + cl::cat(MsfOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> DumpStreams("streams", + cl::desc("dump summary of the PDB streams"), + cl::cat(MsfOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> DumpStreamBlocks( + "stream-blocks", + cl::desc("Add block information to the output of -streams"), + cl::cat(MsfOptions), cl::sub(DumpSubcommand)); + +// TYPE OPTIONS +cl::opt<bool> DumpTypes("types", + cl::desc("dump CodeView type records from TPI stream"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> DumpTypeData( + "type-data", + cl::desc("dump CodeView type record raw bytes from TPI stream"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); + +cl::opt<bool> DumpTypeExtras("type-extras", + cl::desc("dump type hashes and index offsets"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); + +cl::list<uint32_t> DumpTypeIndex( + "type-index", cl::ZeroOrMore, cl::CommaSeparated, + cl::desc("only dump types with the specified hexadecimal type index"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); + +cl::opt<bool> DumpIds("ids", + cl::desc("dump CodeView type records from IPI stream"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> + DumpIdData("id-data", + cl::desc("dump CodeView type record raw bytes from IPI stream"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); + +cl::opt<bool> DumpIdExtras("id-extras", + cl::desc("dump id hashes and index offsets"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); +cl::list<uint32_t> DumpIdIndex( + "id-index", cl::ZeroOrMore, cl::CommaSeparated, + cl::desc("only dump ids with the specified hexadecimal type index"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); + +cl::opt<bool> DumpTypeDependents( + "dependents", + cl::desc("In conjunection with -type-index and -id-index, dumps the entire " + "dependency graph for the specified index instead of " + "just the single record with the specified index"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); + +// SYMBOL OPTIONS +cl::opt<bool> DumpPublics("publics", cl::desc("dump Publics stream data"), + cl::cat(SymbolOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> DumpSymbols("symbols", cl::desc("dump module symbols"), + cl::cat(SymbolOptions), cl::sub(DumpSubcommand)); + +cl::opt<bool> + DumpSymRecordBytes("sym-data", + cl::desc("dump CodeView symbol record raw bytes"), + cl::cat(SymbolOptions), cl::sub(DumpSubcommand)); + +// MODULE & FILE OPTIONS +cl::opt<bool> DumpModules("modules", cl::desc("dump compiland information"), + cl::cat(FileOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> DumpModuleFiles( + "files", + cl::desc("Dump the source files that contribute to each module's."), + cl::cat(FileOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> DumpLines( + "l", + cl::desc("dump source file/line information (DEBUG_S_LINES subsection)"), + cl::cat(FileOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> DumpInlineeLines( + "il", + cl::desc("dump inlinee line information (DEBUG_S_INLINEELINES subsection)"), + cl::cat(FileOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> DumpXmi( + "xmi", + cl::desc( + "dump cross module imports (DEBUG_S_CROSSSCOPEIMPORTS subsection)"), + cl::cat(FileOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> DumpXme( + "xme", + cl::desc( + "dump cross module exports (DEBUG_S_CROSSSCOPEEXPORTS subsection)"), + cl::cat(FileOptions), cl::sub(DumpSubcommand)); + +// MISCELLANEOUS OPTIONS +cl::opt<bool> DumpStringTable("string-table", cl::desc("dump PDB String Table"), + cl::cat(MiscOptions), cl::sub(DumpSubcommand)); + +cl::opt<bool> DumpSectionContribs("section-contribs", + cl::desc("dump section contributions"), + cl::cat(MiscOptions), + cl::sub(DumpSubcommand)); +cl::opt<bool> DumpSectionMap("section-map", cl::desc("dump section map"), + cl::cat(MiscOptions), cl::sub(DumpSubcommand)); + +cl::opt<bool> RawAll("all", cl::desc("Implies most other options."), + cl::cat(MiscOptions), cl::sub(DumpSubcommand)); + +cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input PDB files>"), + cl::OneOrMore, cl::sub(DumpSubcommand)); +} + +namespace yaml2pdb { +cl::opt<std::string> + YamlPdbOutputFile("pdb", cl::desc("the name of the PDB file to write"), + cl::sub(YamlToPdbSubcommand)); + +cl::opt<std::string> InputFilename(cl::Positional, + cl::desc("<input YAML file>"), cl::Required, + cl::sub(YamlToPdbSubcommand)); +} + +namespace pdb2yaml { +cl::opt<bool> All("all", + cl::desc("Dump everything we know how to dump."), + cl::sub(PdbToYamlSubcommand), cl::init(false)); +cl::opt<bool> NoFileHeaders("no-file-headers", + cl::desc("Do not dump MSF file headers"), + 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", + cl::desc("Dump the number of streams and each stream's size"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); +cl::opt<bool> StreamDirectory( + "stream-directory", + cl::desc("Dump each stream's block map (implies -stream-metadata)"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); +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 Headers (Stream 2)"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); + +cl::opt<bool> TpiStream("tpi-stream", + cl::desc("Dump the TPI Stream (Stream 3)"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); + +cl::opt<bool> IpiStream("ipi-stream", + cl::desc("Dump the IPI Stream (Stream 5)"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); + +// MODULE & FILE OPTIONS +cl::opt<bool> DumpModules("modules", cl::desc("dump compiland information"), + cl::cat(FileOptions), cl::sub(PdbToYamlSubcommand)); +cl::opt<bool> DumpModuleFiles("module-files", cl::desc("dump file information"), + cl::cat(FileOptions), + cl::sub(PdbToYamlSubcommand)); +cl::list<ModuleSubsection> DumpModuleSubsections( + "subsections", cl::ZeroOrMore, cl::CommaSeparated, + cl::desc("dump subsections from each module's debug stream"), ChunkValues, + cl::cat(FileOptions), cl::sub(PdbToYamlSubcommand)); +cl::opt<bool> DumpModuleSyms("module-syms", cl::desc("dump module symbols"), + cl::cat(FileOptions), + cl::sub(PdbToYamlSubcommand)); + +cl::list<std::string> InputFilename(cl::Positional, + cl::desc("<input PDB file>"), cl::Required, + cl::sub(PdbToYamlSubcommand)); +} // namespace pdb2yaml + +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)); +} + +namespace merge { +cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input PDB files>"), + cl::OneOrMore, cl::sub(MergeSubcommand)); +cl::opt<std::string> + PdbOutputFile("pdb", cl::desc("the name of the PDB file to write"), + cl::sub(MergeSubcommand)); +} +} + +static ExitOnError ExitOnErr; + +static void yamlToPdb(StringRef Path) { + BumpPtrAllocator Allocator; + ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer = + MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + + if (ErrorOrBuffer.getError()) { + ExitOnErr(make_error<GenericError>(generic_error_code::invalid_path, Path)); + } + + std::unique_ptr<MemoryBuffer> &Buffer = ErrorOrBuffer.get(); + + llvm::yaml::Input In(Buffer->getBuffer()); + pdb::yaml::PdbObject YamlObj(Allocator); + In >> YamlObj; + + PDBFileBuilder Builder(Allocator); + + 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 + // PDB, but maybe we only dump a subset of those 20 streams, so we will + // have fewer, and the ones we do have may end up with different indices + // than the ones in the original PDB. So we just start with a clean slate. + for (uint32_t I = 0; I < kSpecialStreamCount; ++I) + ExitOnErr(Builder.getMsfBuilder().addStream(0)); + + StringsAndChecksums Strings; + Strings.setStrings(std::make_shared<DebugStringTableSubsection>()); + + if (YamlObj.StringTable.hasValue()) { + for (auto S : *YamlObj.StringTable) + Strings.strings()->insert(S); + } + + pdb::yaml::PdbInfoStream DefaultInfoStream; + pdb::yaml::PdbDbiStream DefaultDbiStream; + pdb::yaml::PdbTpiStream DefaultTpiStream; + pdb::yaml::PdbTpiStream DefaultIpiStream; + + 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)); + ModiBuilder.setObjFileName(MI.Obj); + + for (auto S : MI.SourceFiles) + ExitOnErr(DbiBuilder.addModuleSourceFile(MI.Mod, S)); + if (MI.Modi.hasValue()) { + const auto &ModiStream = *MI.Modi; + for (auto Symbol : ModiStream.Symbols) { + ModiBuilder.addSymbol( + Symbol.toCodeViewSymbol(Allocator, CodeViewContainer::Pdb)); + } + } + + // Each module has its own checksum subsection, so scan for it every time. + Strings.setChecksums(nullptr); + CodeViewYAML::initializeStringsAndChecksums(MI.Subsections, Strings); + + auto CodeViewSubsections = ExitOnErr(CodeViewYAML::toCodeViewSubsectionList( + Allocator, MI.Subsections, Strings)); + for (auto &SS : CodeViewSubsections) { + ModiBuilder.addDebugSubsection(SS); + } + } + + auto &TpiBuilder = Builder.getTpiBuilder(); + const auto &Tpi = YamlObj.TpiStream.getValueOr(DefaultTpiStream); + TpiBuilder.setVersionHeader(Tpi.Version); + for (const auto &R : Tpi.Records) { + CVType Type = R.toCodeViewRecord(Allocator); + TpiBuilder.addTypeRecord(Type.RecordData, None); + } + + const auto &Ipi = YamlObj.IpiStream.getValueOr(DefaultIpiStream); + auto &IpiBuilder = Builder.getIpiBuilder(); + IpiBuilder.setVersionHeader(Ipi.Version); + for (const auto &R : Ipi.Records) { + CVType Type = R.toCodeViewRecord(Allocator); + IpiBuilder.addTypeRecord(Type.RecordData, None); + } + + Builder.getStringTableBuilder().setStrings(*Strings.strings()); + + 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; + auto &File = loadPDB(Path, Session); + + auto O = llvm::make_unique<YAMLOutputStyle>(File); + O = llvm::make_unique<YAMLOutputStyle>(File); + + ExitOnErr(O->dump()); +} + +static void dumpRaw(StringRef Path) { + std::unique_ptr<IPDBSession> Session; + auto &File = loadPDB(Path, Session); + + auto O = llvm::make_unique<DumpOutputStyle>(File); + + ExitOnErr(O->dump()); +} + +static void dumpBytes(StringRef Path) { + std::unique_ptr<IPDBSession> Session; + auto &File = loadPDB(Path, Session); + + auto O = llvm::make_unique<BytesOutputStyle>(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()); +} + +bool opts::pretty::shouldDumpSymLevel(SymLevel Search) { + if (SymTypes.empty()) + return true; + if (llvm::find(SymTypes, Search) != SymTypes.end()) + return true; + if (llvm::find(SymTypes, SymLevel::All) != SymTypes.end()) + return true; + return false; +} + +uint32_t llvm::pdb::getTypeLength(const PDBSymbolData &Symbol) { + auto SymbolType = Symbol.getType(); + const IPDBRawSymbol &RawType = SymbolType->getRawSymbol(); + + return RawType.getLength(); +} + +bool opts::pretty::compareFunctionSymbols( + const std::unique_ptr<PDBSymbolFunc> &F1, + const std::unique_ptr<PDBSymbolFunc> &F2) { + assert(opts::pretty::SymbolOrder != opts::pretty::SymbolSortMode::None); + + if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::Name) + return F1->getName() < F2->getName(); + + // Note that we intentionally sort in descending order on length, since + // long functions are more interesting than short functions. + return F1->getLength() > F2->getLength(); +} + +bool opts::pretty::compareDataSymbols( + const std::unique_ptr<PDBSymbolData> &F1, + const std::unique_ptr<PDBSymbolData> &F2) { + assert(opts::pretty::SymbolOrder != opts::pretty::SymbolSortMode::None); + + if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::Name) + return F1->getName() < F2->getName(); + + // Note that we intentionally sort in descending order on length, since + // large types are more interesting than short ones. + return getTypeLength(*F1) > getTypeLength(*F2); +} + +static void dumpPretty(StringRef Path) { + std::unique_ptr<IPDBSession> 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); + + 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()); + + WithColor(Printer, PDB_ColorItem::None).get() << "Summary for "; + WithColor(Printer, PDB_ColorItem::Path).get() << FileName; + Printer.Indent(); + uint64_t FileSize = 0; + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Size"; + if (!sys::fs::file_size(FileName, FileSize)) { + Printer << ": " << FileSize << " bytes"; + } else { + Printer << ": (Unable to obtain file size)"; + } + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Guid"; + Printer << ": " << GlobalScope->getGuid(); + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Age"; + Printer << ": " << GlobalScope->getAge(); + + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << "Attributes"; + Printer << ": "; + if (GlobalScope->hasCTypes()) + outs() << "HasCTypes "; + if (GlobalScope->hasPrivateSymbols()) + outs() << "HasPrivateSymbols "; + Printer.Unindent(); + + if (opts::pretty::Compilands) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() + << "---COMPILANDS---"; + Printer.Indent(); + auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>(); + CompilandDumper Dumper(Printer); + CompilandDumpFlags options = CompilandDumper::Flags::None; + if (opts::pretty::Lines) + options = options | CompilandDumper::Flags::Lines; + while (auto Compiland = Compilands->getNext()) + Dumper.start(*Compiland, options); + Printer.Unindent(); + } + + if (opts::pretty::Classes || opts::pretty::Enums || opts::pretty::Typedefs) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---TYPES---"; + Printer.Indent(); + TypeDumper Dumper(Printer); + Dumper.start(*GlobalScope); + Printer.Unindent(); + } + + if (opts::pretty::Symbols) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---SYMBOLS---"; + Printer.Indent(); + auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>(); + CompilandDumper Dumper(Printer); + while (auto Compiland = Compilands->getNext()) + Dumper.start(*Compiland, true); + Printer.Unindent(); + } + + if (opts::pretty::Globals) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---GLOBALS---"; + Printer.Indent(); + if (shouldDumpSymLevel(opts::pretty::SymLevel::Functions)) { + FunctionDumper Dumper(Printer); + auto Functions = GlobalScope->findAllChildren<PDBSymbolFunc>(); + if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::None) { + while (auto Function = Functions->getNext()) { + Printer.NewLine(); + Dumper.start(*Function, FunctionDumper::PointerType::None); + } + } else { + std::vector<std::unique_ptr<PDBSymbolFunc>> Funcs; + while (auto Func = Functions->getNext()) + Funcs.push_back(std::move(Func)); + std::sort(Funcs.begin(), Funcs.end(), + opts::pretty::compareFunctionSymbols); + for (const auto &Func : Funcs) { + Printer.NewLine(); + Dumper.start(*Func, FunctionDumper::PointerType::None); + } + } + } + if (shouldDumpSymLevel(opts::pretty::SymLevel::Data)) { + auto Vars = GlobalScope->findAllChildren<PDBSymbolData>(); + VariableDumper Dumper(Printer); + if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::None) { + while (auto Var = Vars->getNext()) + Dumper.start(*Var); + } else { + std::vector<std::unique_ptr<PDBSymbolData>> Datas; + while (auto Var = Vars->getNext()) + Datas.push_back(std::move(Var)); + std::sort(Datas.begin(), Datas.end(), opts::pretty::compareDataSymbols); + for (const auto &Var : Datas) + Dumper.start(*Var); + } + } + if (shouldDumpSymLevel(opts::pretty::SymLevel::Thunks)) { + auto Thunks = GlobalScope->findAllChildren<PDBSymbolThunk>(); + CompilandDumper Dumper(Printer); + while (auto Thunk = Thunks->getNext()) + Dumper.dump(*Thunk); + } + Printer.Unindent(); + } + if (opts::pretty::Externals) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---EXTERNALS---"; + Printer.Indent(); + ExternalSymbolDumper Dumper(Printer); + Dumper.start(*GlobalScope); + } + if (opts::pretty::Lines) { + Printer.NewLine(); + } + outs().flush(); +} + +static void mergePdbs() { + BumpPtrAllocator Allocator; + TypeTableBuilder MergedTpi(Allocator); + TypeTableBuilder MergedIpi(Allocator); + + // Create a Tpi and Ipi type table with all types from all input files. + for (const auto &Path : opts::merge::InputFilenames) { + std::unique_ptr<IPDBSession> Session; + auto &File = loadPDB(Path, Session); + SmallVector<TypeIndex, 128> TypeMap; + SmallVector<TypeIndex, 128> IdMap; + if (File.hasPDBTpiStream()) { + auto &Tpi = ExitOnErr(File.getPDBTpiStream()); + ExitOnErr( + codeview::mergeTypeRecords(MergedTpi, TypeMap, Tpi.typeArray())); + } + if (File.hasPDBIpiStream()) { + auto &Ipi = ExitOnErr(File.getPDBIpiStream()); + ExitOnErr(codeview::mergeIdRecords(MergedIpi, TypeMap, IdMap, + Ipi.typeArray())); + } + } + + // Then write the PDB. + PDBFileBuilder Builder(Allocator); + ExitOnErr(Builder.initialize(4096)); + // Add each of the reserved streams. We might not put any data in them, + // but at least they have to be present. + for (uint32_t I = 0; I < kSpecialStreamCount; ++I) + ExitOnErr(Builder.getMsfBuilder().addStream(0)); + + auto &DestTpi = Builder.getTpiBuilder(); + auto &DestIpi = Builder.getIpiBuilder(); + MergedTpi.ForEachRecord([&DestTpi](TypeIndex TI, ArrayRef<uint8_t> Data) { + DestTpi.addTypeRecord(Data, None); + }); + MergedIpi.ForEachRecord([&DestIpi](TypeIndex TI, ArrayRef<uint8_t> Data) { + DestIpi.addTypeRecord(Data, None); + }); + Builder.getInfoBuilder().addFeature(PdbRaw_FeatureSig::VC140); + + SmallString<64> OutFile(opts::merge::PdbOutputFile); + if (OutFile.empty()) { + OutFile = opts::merge::InputFilenames[0]; + llvm::sys::path::replace_extension(OutFile, "merged.pdb"); + } + ExitOnErr(Builder.commit(OutFile)); +} + +static bool parseRange(StringRef Str, + Optional<opts::bytes::NumberRange> &Parsed) { + if (Str.empty()) + return true; + + llvm::Regex R("^([^-]+)(-([^-]+))?$"); + llvm::SmallVector<llvm::StringRef, 2> Matches; + if (!R.match(Str, &Matches)) + return false; + + Parsed.emplace(); + if (!to_integer(Matches[1], Parsed->Min)) + return false; + + if (!Matches[3].empty()) { + Parsed->Max.emplace(); + if (!to_integer(Matches[3], *Parsed->Max)) + return false; + } + return true; +} + +static void simplifyChunkList(llvm::cl::list<opts::ModuleSubsection> &Chunks) { + // If this list contains "All" plus some other stuff, remove the other stuff + // and just keep "All" in the list. + if (!llvm::is_contained(Chunks, opts::ModuleSubsection::All)) + return; + Chunks.reset(); + Chunks.push_back(opts::ModuleSubsection::All); +} + +int main(int argc_, const char *argv_[]) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(argv_[0]); + PrettyStackTraceProgram X(argc_, argv_); + + ExitOnErr.setBanner("llvm-pdbutil: "); + + SmallVector<const char *, 256> argv; + SpecificBumpPtrAllocator<char> ArgAllocator; + ExitOnErr(errorCodeToError(sys::Process::GetArgumentVector( + argv, makeArrayRef(argv_, argc_), ArgAllocator))); + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + cl::ParseCommandLineOptions(argv.size(), argv.data(), "LLVM PDB Dumper\n"); + + if (opts::BytesSubcommand) { + if (!parseRange(opts::bytes::DumpBlockRangeOpt, + opts::bytes::DumpBlockRange)) { + errs() << "Argument '" << opts::bytes::DumpBlockRangeOpt + << "' invalid format.\n"; + errs().flush(); + exit(1); + } + if (!parseRange(opts::bytes::DumpByteRangeOpt, + opts::bytes::DumpByteRange)) { + errs() << "Argument '" << opts::bytes::DumpByteRangeOpt + << "' invalid format.\n"; + errs().flush(); + exit(1); + } + } + + if (opts::DumpSubcommand) { + if (opts::dump::RawAll) { + opts::dump::DumpLines = true; + opts::dump::DumpInlineeLines = true; + opts::dump::DumpXme = true; + opts::dump::DumpXmi = true; + opts::dump::DumpIds = true; + opts::dump::DumpPublics = true; + opts::dump::DumpSectionContribs = true; + opts::dump::DumpSectionMap = true; + opts::dump::DumpStreams = true; + opts::dump::DumpStreamBlocks = true; + opts::dump::DumpStringTable = true; + opts::dump::DumpSummary = true; + opts::dump::DumpSymbols = true; + opts::dump::DumpIds = true; + opts::dump::DumpIdExtras = true; + opts::dump::DumpTypes = true; + opts::dump::DumpTypeExtras = true; + opts::dump::DumpModules = true; + opts::dump::DumpModuleFiles = true; + } + } + if (opts::PdbToYamlSubcommand) { + if (opts::pdb2yaml::All) { + opts::pdb2yaml::StreamMetadata = true; + opts::pdb2yaml::StreamDirectory = true; + opts::pdb2yaml::PdbStream = true; + opts::pdb2yaml::StringTable = true; + opts::pdb2yaml::DbiStream = true; + opts::pdb2yaml::TpiStream = true; + opts::pdb2yaml::IpiStream = true; + opts::pdb2yaml::DumpModules = true; + opts::pdb2yaml::DumpModuleFiles = true; + opts::pdb2yaml::DumpModuleSyms = true; + opts::pdb2yaml::DumpModuleSubsections.push_back( + opts::ModuleSubsection::All); + } + simplifyChunkList(opts::pdb2yaml::DumpModuleSubsections); + + if (opts::pdb2yaml::DumpModuleSyms || opts::pdb2yaml::DumpModuleFiles) + opts::pdb2yaml::DumpModules = true; + + if (opts::pdb2yaml::DumpModules) + opts::pdb2yaml::DbiStream = true; + } + if (opts::DiffSubcommand) { + if (!opts::diff::PrintResultColumn && !opts::diff::PrintValueColumns) { + llvm::errs() << "WARNING: No diff columns specified\n"; + } + } + + llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); + + if (opts::PdbToYamlSubcommand) { + pdb2Yaml(opts::pdb2yaml::InputFilename.front()); + } else if (opts::YamlToPdbSubcommand) { + if (opts::yaml2pdb::YamlPdbOutputFile.empty()) { + SmallString<16> OutputFilename(opts::yaml2pdb::InputFilename.getValue()); + sys::path::replace_extension(OutputFilename, ".pdb"); + opts::yaml2pdb::YamlPdbOutputFile = OutputFilename.str(); + } + yamlToPdb(opts::yaml2pdb::InputFilename); + } else if (opts::AnalyzeSubcommand) { + dumpAnalysis(opts::analyze::InputFilename.front()); + } else if (opts::PrettySubcommand) { + if (opts::pretty::Lines) + opts::pretty::Compilands = true; + + if (opts::pretty::All) { + opts::pretty::Compilands = true; + opts::pretty::Symbols = true; + opts::pretty::Globals = true; + opts::pretty::Types = true; + opts::pretty::Externals = true; + 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 + // it needs to be escaped again in the C++. So matching a single \ in the + // input requires 4 \es in the C++. + if (opts::pretty::ExcludeCompilerGenerated) { + opts::pretty::ExcludeTypes.push_back("__vc_attributes"); + opts::pretty::ExcludeCompilands.push_back("\\* Linker \\*"); + } + if (opts::pretty::ExcludeSystemLibraries) { + opts::pretty::ExcludeCompilands.push_back( + "f:\\\\binaries\\\\Intermediate\\\\vctools\\\\crt_bld"); + opts::pretty::ExcludeCompilands.push_back("f:\\\\dd\\\\vctools\\\\crt"); + opts::pretty::ExcludeCompilands.push_back( + "d:\\\\th.obj.x86fre\\\\minkernel"); + } + std::for_each(opts::pretty::InputFilenames.begin(), + opts::pretty::InputFilenames.end(), dumpPretty); + } else if (opts::DumpSubcommand) { + std::for_each(opts::dump::InputFilenames.begin(), + opts::dump::InputFilenames.end(), dumpRaw); + } else if (opts::BytesSubcommand) { + std::for_each(opts::bytes::InputFilenames.begin(), + opts::bytes::InputFilenames.end(), dumpBytes); + } else if (opts::DiffSubcommand) { + diff(opts::diff::Left, opts::diff::Right); + } else if (opts::MergeSubcommand) { + if (opts::merge::InputFilenames.size() < 2) { + errs() << "merge subcommand requires at least 2 input files.\n"; + exit(1); + } + mergePdbs(); + } + + outs().flush(); + return 0; +} diff --git a/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h new file mode 100644 index 0000000..4e92e63 --- /dev/null +++ b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h @@ -0,0 +1,180 @@ +//===- llvm-pdbutil.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_LLVMPDBDUMP_H +#define LLVM_TOOLS_LLVMPDBDUMP_LLVMPDBDUMP_H + +#include "llvm/ADT/Optional.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" + +#include <memory> +#include <stdint.h> + +namespace llvm { +namespace pdb { +class PDBSymbolData; +class PDBSymbolFunc; +uint32_t getTypeLength(const PDBSymbolData &Symbol); +} +} + +namespace opts { + +enum class DumpLevel { None, Basic, Verbose }; + +enum class ModuleSubsection { + Unknown, + Lines, + FileChecksums, + InlineeLines, + CrossScopeImports, + CrossScopeExports, + StringTable, + Symbols, + FrameData, + CoffSymbolRVAs, + All +}; + +namespace pretty { + +enum class ClassDefinitionFormat { None, Layout, All }; +enum class ClassSortMode { + None, + Name, + Size, + Padding, + PaddingPct, + PaddingImmediate, + PaddingPctImmediate +}; + +enum class SymbolSortMode { None, Name, Size }; + +enum class SymLevel { Functions, Data, Thunks, All }; + +bool shouldDumpSymLevel(SymLevel Level); +bool compareFunctionSymbols( + const std::unique_ptr<llvm::pdb::PDBSymbolFunc> &F1, + const std::unique_ptr<llvm::pdb::PDBSymbolFunc> &F2); +bool compareDataSymbols(const std::unique_ptr<llvm::pdb::PDBSymbolData> &F1, + const std::unique_ptr<llvm::pdb::PDBSymbolData> &F2); + +extern llvm::cl::opt<bool> Compilands; +extern llvm::cl::opt<bool> Symbols; +extern llvm::cl::opt<bool> Globals; +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> NoEnumDefs; +extern llvm::cl::list<std::string> ExcludeTypes; +extern llvm::cl::list<std::string> ExcludeSymbols; +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<SymbolSortMode> SymbolOrder; +extern llvm::cl::opt<ClassSortMode> ClassOrder; +extern llvm::cl::opt<uint32_t> SizeThreshold; +extern llvm::cl::opt<uint32_t> PaddingThreshold; +extern llvm::cl::opt<uint32_t> ImmediatePaddingThreshold; +extern llvm::cl::opt<ClassDefinitionFormat> ClassFormat; +extern llvm::cl::opt<uint32_t> ClassRecursionDepth; +} + +namespace bytes { +struct NumberRange { + uint64_t Min; + llvm::Optional<uint64_t> Max; +}; + +extern llvm::Optional<NumberRange> DumpBlockRange; +extern llvm::Optional<NumberRange> DumpByteRange; +extern llvm::cl::list<std::string> DumpStreamData; +extern llvm::cl::opt<bool> NameMap; + +extern llvm::cl::opt<bool> SectionContributions; +extern llvm::cl::opt<bool> SectionMap; +extern llvm::cl::opt<bool> ModuleInfos; +extern llvm::cl::opt<bool> FileInfo; +extern llvm::cl::opt<bool> TypeServerMap; +extern llvm::cl::opt<bool> ECData; + +extern llvm::cl::list<uint32_t> TypeIndex; +extern llvm::cl::list<uint32_t> IdIndex; + +extern llvm::cl::opt<uint32_t> ModuleIndex; +extern llvm::cl::opt<bool> ModuleSyms; +extern llvm::cl::opt<bool> ModuleC11; +extern llvm::cl::opt<bool> ModuleC13; +extern llvm::cl::opt<bool> SplitChunks; +} // namespace bytes + +namespace dump { + +extern llvm::cl::opt<bool> DumpSummary; +extern llvm::cl::opt<bool> DumpStreams; +extern llvm::cl::opt<bool> DumpStreamBlocks; + +extern llvm::cl::opt<bool> DumpLines; +extern llvm::cl::opt<bool> DumpInlineeLines; +extern llvm::cl::opt<bool> DumpXmi; +extern llvm::cl::opt<bool> DumpXme; +extern llvm::cl::opt<bool> DumpStringTable; +extern llvm::cl::opt<bool> DumpTypes; +extern llvm::cl::opt<bool> DumpTypeData; +extern llvm::cl::opt<bool> DumpTypeExtras; +extern llvm::cl::list<uint32_t> DumpTypeIndex; +extern llvm::cl::opt<bool> DumpTypeDependents; + +extern llvm::cl::opt<bool> DumpIds; +extern llvm::cl::opt<bool> DumpIdData; +extern llvm::cl::opt<bool> DumpIdExtras; +extern llvm::cl::list<uint32_t> DumpIdIndex; +extern llvm::cl::opt<bool> DumpSymbols; +extern llvm::cl::opt<bool> DumpSymRecordBytes; +extern llvm::cl::opt<bool> DumpPublics; +extern llvm::cl::opt<bool> DumpSectionContribs; +extern llvm::cl::opt<bool> DumpSectionMap; +extern llvm::cl::opt<bool> DumpModules; +extern llvm::cl::opt<bool> DumpModuleFiles; +extern llvm::cl::opt<bool> RawAll; +} + +namespace pdb2yaml { +extern llvm::cl::opt<bool> All; +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> TpiStream; +extern llvm::cl::opt<bool> IpiStream; +extern llvm::cl::list<std::string> InputFilename; +extern llvm::cl::opt<bool> DumpModules; +extern llvm::cl::opt<bool> DumpModuleFiles; +extern llvm::cl::list<ModuleSubsection> DumpModuleSubsections; +extern llvm::cl::opt<bool> DumpModuleSyms; +} // namespace pdb2yaml + +namespace diff { +extern llvm::cl::opt<bool> PrintValueColumns; +extern llvm::cl::opt<bool> PrintResultColumn; +extern llvm::cl::opt<std::string> LeftRoot; +extern llvm::cl::opt<std::string> RightRoot; +} // namespace diff +} + +#endif |