From: Zachary Turner Date: Mon, 13 Mar 2017 23:28:25 +0000 (+0000) Subject: Add the beginning of PDB diffing support. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=435ba4ae281a17b1382e0860cbe05aacc77b59b3;p=llvm Add the beginning of PDB diffing support. For now this only diffs the stream directory and the MSF Superblock. Future patches will drill down into individual streams to find out where the differences lie. Differential Revision: https://reviews.llvm.org/D30908 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@297689 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/tools/llvm-pdbdump/CMakeLists.txt b/tools/llvm-pdbdump/CMakeLists.txt index f4c1c143daf..900508a0213 100644 --- a/tools/llvm-pdbdump/CMakeLists.txt +++ b/tools/llvm-pdbdump/CMakeLists.txt @@ -8,6 +8,7 @@ set(LLVM_LINK_COMPONENTS add_llvm_tool(llvm-pdbdump Analyze.cpp + Diff.cpp CompactTypeDumpVisitor.cpp llvm-pdbdump.cpp YamlSymbolDumper.cpp @@ -24,6 +25,7 @@ add_llvm_tool(llvm-pdbdump PrettyTypeDumper.cpp PrettyTypedefDumper.cpp PrettyVariableDumper.cpp + StreamUtil.cpp YAMLOutputStyle.cpp ) diff --git a/tools/llvm-pdbdump/Diff.cpp b/tools/llvm-pdbdump/Diff.cpp new file mode 100644 index 00000000000..9e90c5af7e6 --- /dev/null +++ b/tools/llvm-pdbdump/Diff.cpp @@ -0,0 +1,261 @@ +//===- Diff.cpp - PDB diff utility ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Diff.h" + +#include "StreamUtil.h" +#include "llvm-pdbdump.h" + +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" + +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::pdb; + +DiffStyle::DiffStyle(PDBFile &File1, PDBFile &File2) + : File1(File1), File2(File2) {} + +Error DiffStyle::dump() { + if (auto EC = diffSuperBlock()) + return EC; + + if (auto EC = diffFreePageMap()) + return EC; + + if (auto EC = diffStreamDirectory()) + return EC; + + if (auto EC = diffStringTable()) + return EC; + + if (auto EC = diffInfoStream()) + return EC; + + if (auto EC = diffDbiStream()) + return EC; + + if (auto EC = diffSectionContribs()) + return EC; + + if (auto EC = diffSectionMap()) + return EC; + + if (auto EC = diffFpoStream()) + return EC; + + if (auto EC = diffTpiStream(StreamTPI)) + return EC; + + if (auto EC = diffTpiStream(StreamIPI)) + return EC; + + if (auto EC = diffPublics()) + return EC; + + if (auto EC = diffGlobals()) + return EC; + + return Error::success(); +} + +template +static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, T V1, + T V2) { + if (V1 != V2) { + outs().indent(2) << Label << "\n"; + outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), V1); + outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), V2); + } + return (V1 != V2); +} + +Error DiffStyle::diffSuperBlock() { + outs() << "MSF Super Block: Searching for differences...\n"; + bool Diffs = false; + + Diffs |= diffAndPrint("Block Size", File1, File2, File1.getBlockSize(), + File2.getBlockSize()); + Diffs |= diffAndPrint("Block Count", File1, File2, File1.getBlockCount(), + File2.getBlockCount()); + Diffs |= diffAndPrint("Unknown 1", File1, File2, File1.getUnknown1(), + File2.getUnknown1()); + + if (opts::diff::Pedantic) { + Diffs |= diffAndPrint("Free Block Map", File1, File2, + File1.getFreeBlockMapBlock(), + File2.getFreeBlockMapBlock()); + Diffs |= diffAndPrint("Directory Size", File1, File2, + File1.getNumDirectoryBytes(), + File2.getNumDirectoryBytes()); + Diffs |= diffAndPrint("Block Map Addr", File1, File2, + File1.getBlockMapOffset(), File2.getBlockMapOffset()); + } + if (!Diffs) + outs() << "MSF Super Block: No differences detected...\n"; + return Error::success(); +} + +Error DiffStyle::diffStreamDirectory() { + SmallVector P; + SmallVector Q; + discoverStreamPurposes(File1, P); + discoverStreamPurposes(File2, Q); + outs() << "Stream Directory: Searching for differences...\n"; + + bool HasDifferences = false; + if (opts::diff::Pedantic) { + size_t Min = std::min(P.size(), Q.size()); + for (size_t I = 0; I < Min; ++I) { + StringRef Names[] = {P[I], Q[I]}; + uint32_t Sizes[] = {File1.getStreamByteSize(I), + File2.getStreamByteSize(I)}; + bool NamesDiffer = Names[0] != Names[1]; + bool SizesDiffer = Sizes[0] != Sizes[1]; + if (NamesDiffer) { + HasDifferences = true; + outs().indent(2) << formatv("Stream {0} - {1}: {2}, {3}: {4}\n", I, + File1.getFilePath(), Names[0], + File2.getFilePath(), Names[1]); + continue; + } + if (SizesDiffer) { + HasDifferences = true; + outs().indent(2) << formatv( + "Stream {0} ({1}): {2}: {3} bytes, {4}: {5} bytes\n", I, Names[0], + File1.getFilePath(), Sizes[0], File2.getFilePath(), Sizes[1]); + continue; + } + } + + ArrayRef MaxNames = (P.size() > Q.size() ? P : Q); + size_t Max = std::max(P.size(), Q.size()); + PDBFile &MaxFile = (P.size() > Q.size() ? File1 : File2); + StringRef MinFileName = + (P.size() < Q.size() ? File1.getFilePath() : File2.getFilePath()); + for (size_t I = Min; I < Max; ++I) { + HasDifferences = true; + StringRef StreamName = MaxNames[I]; + + outs().indent(2) << formatv( + "Stream {0} - {1}: , {2}: Index {3}, {4} bytes\n", + StreamName, MinFileName, MaxFile.getFilePath(), I, + MaxFile.getStreamByteSize(I)); + } + if (!HasDifferences) + outs() << "Stream Directory: No differences detected...\n"; + } else { + auto PI = to_vector<32>(enumerate(P)); + auto QI = to_vector<32>(enumerate(Q)); + + typedef decltype(PI) ContainerType; + typedef typename ContainerType::value_type value_type; + + auto Comparator = [](const value_type &I1, const value_type &I2) { + return I1.value() < I2.value(); + }; + std::sort(PI.begin(), PI.end(), Comparator); + std::sort(QI.begin(), QI.end(), Comparator); + + decltype(PI) OnlyP; + decltype(QI) OnlyQ; + decltype(PI) Common; + + OnlyP.reserve(P.size()); + OnlyQ.reserve(Q.size()); + Common.reserve(Q.size()); + + auto PEnd = std::set_difference(PI.begin(), PI.end(), QI.begin(), QI.end(), + OnlyP.begin(), Comparator); + auto QEnd = std::set_difference(QI.begin(), QI.end(), PI.begin(), PI.end(), + OnlyQ.begin(), Comparator); + auto ComEnd = std::set_intersection(PI.begin(), PI.end(), QI.begin(), + QI.end(), Common.begin(), Comparator); + OnlyP.set_size(std::distance(OnlyP.begin(), PEnd)); + OnlyQ.set_size(std::distance(OnlyQ.begin(), QEnd)); + Common.set_size(std::distance(Common.begin(), ComEnd)); + + if (!OnlyP.empty()) { + HasDifferences = true; + outs().indent(2) << formatv("{0} Stream(s) only in ({1})\n", OnlyP.size(), + File1.getFilePath()); + for (auto &Item : OnlyP) { + outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(), + Item.value()); + } + } + + if (!OnlyQ.empty()) { + HasDifferences = true; + outs().indent(2) << formatv("{0} Streams(s) only in ({1})\n", + OnlyQ.size(), File2.getFilePath()); + for (auto &Item : OnlyQ) { + outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(), + Item.value()); + } + } + if (!Common.empty()) { + outs().indent(2) << formatv("Found {0} common streams. Searching for " + "intra-stream differences.\n", + Common.size()); + bool HasCommonDifferences = false; + for (const auto &Left : Common) { + // Left was copied from the first range so its index refers to a stream + // index in the first file. Find the corresponding stream index in the + // second file. + auto Range = + std::equal_range(QI.begin(), QI.end(), Left, + [](const value_type &L, const value_type &R) { + return L.value() < R.value(); + }); + const auto &Right = *Range.first; + assert(Left.value() == Right.value()); + uint32_t LeftSize = File1.getStreamByteSize(Left.index()); + uint32_t RightSize = File2.getStreamByteSize(Right.index()); + if (LeftSize != RightSize) { + HasDifferences = true; + HasCommonDifferences = true; + outs().indent(4) << formatv("{0} ({1}: {2} bytes, {3}: {4} bytes)\n", + Left.value(), File1.getFilePath(), + LeftSize, File2.getFilePath(), RightSize); + } + } + if (!HasCommonDifferences) + outs().indent(2) << "Common Streams: No differences detected!\n"; + } + if (!HasDifferences) + outs() << "Stream Directory: No differences detected!\n"; + } + + return Error::success(); +} + +Error DiffStyle::diffStringTable() { return Error::success(); } + +Error DiffStyle::diffFreePageMap() { return Error::success(); } + +Error DiffStyle::diffInfoStream() { return Error::success(); } + +Error DiffStyle::diffDbiStream() { return Error::success(); } + +Error DiffStyle::diffSectionContribs() { return Error::success(); } + +Error DiffStyle::diffSectionMap() { return Error::success(); } + +Error DiffStyle::diffFpoStream() { return Error::success(); } + +Error DiffStyle::diffTpiStream(int Index) { return Error::success(); } + +Error DiffStyle::diffModuleInfoStream(int Index) { return Error::success(); } + +Error DiffStyle::diffPublics() { return Error::success(); } + +Error DiffStyle::diffGlobals() { return Error::success(); } diff --git a/tools/llvm-pdbdump/Diff.h b/tools/llvm-pdbdump/Diff.h new file mode 100644 index 00000000000..6037576e21b --- /dev/null +++ b/tools/llvm-pdbdump/Diff.h @@ -0,0 +1,45 @@ +//===- Diff.h - PDB diff utility --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_DIFF_H +#define LLVM_TOOLS_LLVMPDBDUMP_DIFF_H + +#include "OutputStyle.h" + +namespace llvm { +namespace pdb { +class PDBFile; +class DiffStyle : public OutputStyle { +public: + explicit DiffStyle(PDBFile &File1, PDBFile &File2); + + Error dump() override; + +private: + Error diffSuperBlock(); + Error diffStreamDirectory(); + Error diffStringTable(); + Error diffFreePageMap(); + Error diffInfoStream(); + Error diffDbiStream(); + Error diffSectionContribs(); + Error diffSectionMap(); + Error diffFpoStream(); + Error diffTpiStream(int Index); + Error diffModuleInfoStream(int Index); + Error diffPublics(); + Error diffGlobals(); + + PDBFile &File1; + PDBFile &File2; +}; +} +} + +#endif diff --git a/tools/llvm-pdbdump/LLVMOutputStyle.cpp b/tools/llvm-pdbdump/LLVMOutputStyle.cpp index 1d60b4a0d8b..04a9c41e47b 100644 --- a/tools/llvm-pdbdump/LLVMOutputStyle.cpp +++ b/tools/llvm-pdbdump/LLVMOutputStyle.cpp @@ -10,6 +10,7 @@ #include "LLVMOutputStyle.h" #include "CompactTypeDumpVisitor.h" +#include "StreamUtil.h" #include "llvm-pdbdump.h" #include "llvm/DebugInfo/CodeView/CVTypeDumper.h" @@ -172,124 +173,12 @@ Error LLVMOutputStyle::dumpFileHeaders() { return Error::success(); } -void LLVMOutputStyle::discoverStreamPurposes() { - if (!StreamPurposes.empty()) - return; - - // It's OK if we fail to load some of these streams, we still attempt to print - // what we can. - auto Dbi = File.getPDBDbiStream(); - auto Tpi = File.getPDBTpiStream(); - auto Ipi = File.getPDBIpiStream(); - auto Info = File.getPDBInfoStream(); - - uint32_t StreamCount = File.getNumStreams(); - std::unordered_map ModStreams; - std::unordered_map NamedStreams; - - if (Dbi) { - for (auto &ModI : Dbi->modules()) { - uint16_t SN = ModI.Info.getModuleStreamIndex(); - ModStreams[SN] = &ModI; - } - } - if (Info) { - for (auto &NSE : Info->named_streams()) { - NamedStreams[NSE.second] = NSE.first(); - } - } - - StreamPurposes.resize(StreamCount); - for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { - std::string Value; - if (StreamIdx == OldMSFDirectory) - Value = "Old MSF Directory"; - else if (StreamIdx == StreamPDB) - Value = "PDB Stream"; - else if (StreamIdx == StreamDBI) - Value = "DBI Stream"; - else if (StreamIdx == StreamTPI) - Value = "TPI Stream"; - else if (StreamIdx == StreamIPI) - Value = "IPI Stream"; - else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex()) - Value = "Global Symbol Hash"; - else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex()) - Value = "Public Symbol Hash"; - else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex()) - Value = "Public Symbol Records"; - else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex()) - Value = "TPI Hash"; - else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex()) - Value = "TPI Aux Hash"; - else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex()) - Value = "IPI Hash"; - else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex()) - Value = "IPI Aux Hash"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception)) - Value = "Exception Data"; - else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup)) - Value = "Fixup Data"; - else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO)) - Value = "FPO Data"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO)) - Value = "New FPO Data"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc)) - Value = "Omap From Source Data"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc)) - Value = "Omap To Source Data"; - else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata)) - Value = "Pdata"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr)) - Value = "Section Header Data"; - else if (Dbi && - StreamIdx == - Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)) - Value = "Section Header Original Data"; - else if (Dbi && - StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap)) - Value = "Token Rid Data"; - else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata)) - Value = "Xdata"; - else { - auto ModIter = ModStreams.find(StreamIdx); - auto NSIter = NamedStreams.find(StreamIdx); - if (ModIter != ModStreams.end()) { - Value = "Module \""; - Value += ModIter->second->Info.getModuleName().str(); - Value += "\""; - } else if (NSIter != NamedStreams.end()) { - Value = "Named Stream \""; - Value += NSIter->second; - Value += "\""; - } else { - Value = "???"; - } - } - StreamPurposes[StreamIdx] = Value; - } - - // Consume errors from missing streams. - if (!Dbi) - consumeError(Dbi.takeError()); - if (!Tpi) - consumeError(Tpi.takeError()); - if (!Ipi) - consumeError(Ipi.takeError()); - if (!Info) - consumeError(Info.takeError()); -} - Error LLVMOutputStyle::dumpStreamSummary() { if (!opts::raw::DumpStreamSummary) return Error::success(); - discoverStreamPurposes(); + if (StreamPurposes.empty()) + discoverStreamPurposes(File, StreamPurposes); uint32_t StreamCount = File.getNumStreams(); @@ -431,7 +320,8 @@ Error LLVMOutputStyle::dumpStreamBytes() { if (opts::raw::DumpStreamData.empty()) return Error::success(); - discoverStreamPurposes(); + if (StreamPurposes.empty()) + discoverStreamPurposes(File, StreamPurposes); DictScope D(P, "Stream Data"); for (uint32_t SI : opts::raw::DumpStreamData) { diff --git a/tools/llvm-pdbdump/LLVMOutputStyle.h b/tools/llvm-pdbdump/LLVMOutputStyle.h index 4aef78d3253..40d37525b94 100644 --- a/tools/llvm-pdbdump/LLVMOutputStyle.h +++ b/tools/llvm-pdbdump/LLVMOutputStyle.h @@ -12,9 +12,12 @@ #include "OutputStyle.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/DebugInfo/CodeView/TypeDatabase.h" #include "llvm/Support/ScopedPrinter.h" +#include + namespace llvm { class BitVector; namespace pdb { @@ -25,8 +28,6 @@ public: Error dump() override; private: - void discoverStreamPurposes(); - Error dumpFileHeaders(); Error dumpStreamSummary(); Error dumpFreePageMap(); @@ -51,7 +52,7 @@ private: PDBFile &File; ScopedPrinter P; codeview::TypeDatabase TypeDB; - std::vector StreamPurposes; + SmallVector StreamPurposes; }; } } diff --git a/tools/llvm-pdbdump/StreamUtil.cpp b/tools/llvm-pdbdump/StreamUtil.cpp new file mode 100644 index 00000000000..db1e01aa015 --- /dev/null +++ b/tools/llvm-pdbdump/StreamUtil.cpp @@ -0,0 +1,136 @@ +//===- StreamUtil.cpp - PDB stream utilities --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "StreamUtil.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/ModInfo.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" + +namespace llvm { +namespace pdb { +void discoverStreamPurposes(PDBFile &File, + SmallVectorImpl &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 ModStreams; + DenseMap NamedStreams; + + if (Dbi) { + for (auto &ModI : Dbi->modules()) { + uint16_t SN = ModI.Info.getModuleStreamIndex(); + if (SN != kInvalidStreamIndex) + ModStreams[SN] = &ModI; + } + } + if (Info) { + for (auto &NSE : Info->named_streams()) { + if (NSE.second != kInvalidStreamIndex) + NamedStreams[NSE.second] = NSE.first(); + } + } + + Purposes.resize(StreamCount); + for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { + std::string Value; + if (StreamIdx == OldMSFDirectory) + Value = "Old MSF Directory"; + else if (StreamIdx == StreamPDB) + Value = "PDB Stream"; + else if (StreamIdx == StreamDBI) + Value = "DBI Stream"; + else if (StreamIdx == StreamTPI) + Value = "TPI Stream"; + else if (StreamIdx == StreamIPI) + Value = "IPI Stream"; + else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex()) + Value = "Global Symbol Hash"; + else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex()) + Value = "Public Symbol Hash"; + else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex()) + Value = "Public Symbol Records"; + else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex()) + Value = "TPI Hash"; + else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex()) + Value = "TPI Aux Hash"; + else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex()) + Value = "IPI Hash"; + else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex()) + Value = "IPI Aux Hash"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception)) + Value = "Exception Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup)) + Value = "Fixup Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO)) + Value = "FPO Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO)) + Value = "New FPO Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc)) + Value = "Omap From Source Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc)) + Value = "Omap To Source Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata)) + Value = "Pdata"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr)) + Value = "Section Header Data"; + else if (Dbi && + StreamIdx == + Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig)) + Value = "Section Header Original Data"; + else if (Dbi && + StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap)) + Value = "Token Rid Data"; + else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata)) + Value = "Xdata"; + else { + auto ModIter = ModStreams.find(StreamIdx); + auto NSIter = NamedStreams.find(StreamIdx); + if (ModIter != ModStreams.end()) { + Value = "Module \""; + Value += ModIter->second->Info.getModuleName().str(); + Value += "\""; + } else if (NSIter != NamedStreams.end()) { + Value = "Named Stream \""; + Value += NSIter->second; + Value += "\""; + } else { + Value = "???"; + } + } + Purposes[StreamIdx] = Value; + } + + // Consume errors from missing streams. + if (!Dbi) + consumeError(Dbi.takeError()); + if (!Tpi) + consumeError(Tpi.takeError()); + if (!Ipi) + consumeError(Ipi.takeError()); + if (!Info) + consumeError(Info.takeError()); +} +} +} diff --git a/tools/llvm-pdbdump/StreamUtil.h b/tools/llvm-pdbdump/StreamUtil.h new file mode 100644 index 00000000000..b5c0beba44f --- /dev/null +++ b/tools/llvm-pdbdump/StreamUtil.h @@ -0,0 +1,25 @@ +//===- Streamutil.h - PDB stream utilities ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_STREAMUTIL_H +#define LLVM_TOOLS_LLVMPDBDUMP_STREAMUTIL_H + +#include "llvm/ADT/SmallVector.h" + +#include + +namespace llvm { +namespace pdb { +class PDBFile; +void discoverStreamPurposes(PDBFile &File, + SmallVectorImpl &Purposes); +} +} + +#endif diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp index 783d3cfb743..cb2b7d6a1ed 100644 --- a/tools/llvm-pdbdump/llvm-pdbdump.cpp +++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -7,15 +7,14 @@ // //===----------------------------------------------------------------------===// // -// Dumps debug information present in PDB files. This utility makes use of -// the Microsoft Windows SDK, so will not compile or run on non-Windows -// platforms. +// Dumps debug information present in PDB files. // //===----------------------------------------------------------------------===// #include "llvm-pdbdump.h" #include "Analyze.h" +#include "Diff.h" #include "LLVMOutputStyle.h" #include "LinePrinter.h" #include "OutputStyle.h" @@ -81,6 +80,9 @@ cl::SubCommand RawSubcommand("raw", "Dump raw structure of the PDB file"); cl::SubCommand PrettySubcommand("pretty", "Dump semantic information about types and symbols"); + +cl::SubCommand DiffSubcommand("diff", "Diff the contents of 2 PDB files"); + cl::SubCommand YamlToPdbSubcommand("yaml2pdb", "Generate a PDB file from a YAML description"); @@ -160,6 +162,17 @@ cl::opt NoEnumDefs("no-enum-definitions", cl::cat(FilterCategory), cl::sub(PrettySubcommand)); } +namespace diff { +cl::opt Pedantic("pedantic", + cl::desc("Finds all differences (even structural ones " + "that produce otherwise identical PDBs)"), + cl::sub(DiffSubcommand)); + +cl::list InputFilenames(cl::Positional, + cl::desc(" "), + cl::OneOrMore, cl::sub(DiffSubcommand)); +} + namespace raw { cl::OptionCategory MsfOptions("MSF Container Options"); @@ -414,12 +427,17 @@ static void yamlToPdb(StringRef Path) { ExitOnErr(Builder.commit(opts::yaml2pdb::YamlPdbOutputFile)); } +static PDBFile &loadPDB(StringRef Path, std::unique_ptr &Session) { + ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session)); + + NativeSession *NS = static_cast(Session.get()); + return NS->getPDBFile(); +} + static void pdb2Yaml(StringRef Path) { std::unique_ptr Session; - ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session)); + auto &File = loadPDB(Path, Session); - NativeSession *RS = static_cast(Session.get()); - PDBFile &File = RS->getPDBFile(); auto O = llvm::make_unique(File); O = llvm::make_unique(File); @@ -428,10 +446,8 @@ static void pdb2Yaml(StringRef Path) { static void dumpRaw(StringRef Path) { std::unique_ptr Session; - ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session)); + auto &File = loadPDB(Path, Session); - NativeSession *RS = static_cast(Session.get()); - PDBFile &File = RS->getPDBFile(); auto O = llvm::make_unique(File); ExitOnErr(O->dump()); @@ -439,15 +455,24 @@ static void dumpRaw(StringRef Path) { static void dumpAnalysis(StringRef Path) { std::unique_ptr Session; - ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session)); - - NativeSession *NS = static_cast(Session.get()); - PDBFile &File = NS->getPDBFile(); + auto &File = loadPDB(Path, Session); auto O = llvm::make_unique(File); ExitOnErr(O->dump()); } +static void diff(StringRef Path1, StringRef Path2) { + std::unique_ptr Session1; + std::unique_ptr Session2; + + auto &File1 = loadPDB(Path1, Session1); + auto &File2 = loadPDB(Path2, Session2); + + auto O = llvm::make_unique(File1, File2); + + ExitOnErr(O->dump()); +} + static void dumpPretty(StringRef Path) { std::unique_ptr Session; @@ -669,6 +694,12 @@ int main(int argc_, const char *argv_[]) { } else if (opts::RawSubcommand) { std::for_each(opts::raw::InputFilenames.begin(), opts::raw::InputFilenames.end(), dumpRaw); + } else if (opts::DiffSubcommand) { + if (opts::diff::InputFilenames.size() != 2) { + errs() << "diff subcommand expects exactly 2 arguments.\n"; + exit(1); + } + diff(opts::diff::InputFilenames[0], opts::diff::InputFilenames[1]); } outs().flush(); diff --git a/tools/llvm-pdbdump/llvm-pdbdump.h b/tools/llvm-pdbdump/llvm-pdbdump.h index d4f082cae7c..cdebc20fb5f 100644 --- a/tools/llvm-pdbdump/llvm-pdbdump.h +++ b/tools/llvm-pdbdump/llvm-pdbdump.h @@ -67,6 +67,10 @@ extern llvm::cl::opt DumpFpo; extern llvm::cl::opt DumpStringTable; } +namespace diff { +extern llvm::cl::opt Pedantic; +} + namespace pdb2yaml { extern llvm::cl::opt NoFileHeaders; extern llvm::cl::opt StreamMetadata;