Index: llvm/tools/llvm-pdbdump/CMakeLists.txt =================================================================== --- llvm/tools/llvm-pdbdump/CMakeLists.txt +++ llvm/tools/llvm-pdbdump/CMakeLists.txt @@ -8,6 +8,7 @@ add_llvm_tool(llvm-pdbdump Analyze.cpp + Diff.cpp CompactTypeDumpVisitor.cpp llvm-pdbdump.cpp YamlSymbolDumper.cpp @@ -24,6 +25,7 @@ PrettyTypeDumper.cpp PrettyTypedefDumper.cpp PrettyVariableDumper.cpp + StreamUtil.cpp YAMLOutputStyle.cpp ) Index: llvm/tools/llvm-pdbdump/Diff.h =================================================================== --- /dev/null +++ llvm/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 Index: llvm/tools/llvm-pdbdump/Diff.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-pdbdump/Diff.cpp @@ -0,0 +1,263 @@ +//===- 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 X = enumerate(P); + typedef detail::result_pair PairType; + 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.data(), Comparator); + auto QEnd = std::set_difference(QI.begin(), QI.end(), PI.begin(), PI.end(), + OnlyQ.data(), Comparator); + auto ComEnd = std::set_intersection(PI.begin(), PI.end(), QI.begin(), + QI.end(), Common.data(), Comparator); + OnlyP.set_size(std::distance(OnlyP.data(), PEnd)); + OnlyQ.set_size(std::distance(OnlyQ.data(), QEnd)); + Common.set_size(std::distance(Common.data(), 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} (Left: {1} bytes, Right: {2} bytes)\n", Left.value(), + LeftSize, 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(); } Index: llvm/tools/llvm-pdbdump/LLVMOutputStyle.h =================================================================== --- llvm/tools/llvm-pdbdump/LLVMOutputStyle.h +++ llvm/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 @@ Error dump() override; private: - void discoverStreamPurposes(); - Error dumpFileHeaders(); Error dumpStreamSummary(); Error dumpFreePageMap(); @@ -51,7 +52,7 @@ PDBFile &File; ScopedPrinter P; codeview::TypeDatabase TypeDB; - std::vector StreamPurposes; + SmallVector StreamPurposes; }; } } Index: llvm/tools/llvm-pdbdump/LLVMOutputStyle.cpp =================================================================== --- llvm/tools/llvm-pdbdump/LLVMOutputStyle.cpp +++ llvm/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 @@ 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 @@ 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) { Index: llvm/tools/llvm-pdbdump/StreamUtil.h =================================================================== --- /dev/null +++ llvm/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 Index: llvm/tools/llvm-pdbdump/StreamUtil.cpp =================================================================== --- /dev/null +++ llvm/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()); +} +} +} Index: llvm/tools/llvm-pdbdump/llvm-pdbdump.h =================================================================== --- llvm/tools/llvm-pdbdump/llvm-pdbdump.h +++ llvm/tools/llvm-pdbdump/llvm-pdbdump.h @@ -67,6 +67,10 @@ extern llvm::cl::opt DumpStringTable; } +namespace diff { +extern llvm::cl::opt Pedantic; +} + namespace pdb2yaml { extern llvm::cl::opt NoFileHeaders; extern llvm::cl::opt StreamMetadata; Index: llvm/tools/llvm-pdbdump/llvm-pdbdump.cpp =================================================================== --- llvm/tools/llvm-pdbdump/llvm-pdbdump.cpp +++ llvm/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 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::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 @@ 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 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 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 @@ } 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();