]> granicus.if.org Git - llvm/commitdiff
Add the beginning of PDB diffing support.
authorZachary Turner <zturner@google.com>
Mon, 13 Mar 2017 23:28:25 +0000 (23:28 +0000)
committerZachary Turner <zturner@google.com>
Mon, 13 Mar 2017 23:28:25 +0000 (23:28 +0000)
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

tools/llvm-pdbdump/CMakeLists.txt
tools/llvm-pdbdump/Diff.cpp [new file with mode: 0644]
tools/llvm-pdbdump/Diff.h [new file with mode: 0644]
tools/llvm-pdbdump/LLVMOutputStyle.cpp
tools/llvm-pdbdump/LLVMOutputStyle.h
tools/llvm-pdbdump/StreamUtil.cpp [new file with mode: 0644]
tools/llvm-pdbdump/StreamUtil.h [new file with mode: 0644]
tools/llvm-pdbdump/llvm-pdbdump.cpp
tools/llvm-pdbdump/llvm-pdbdump.h

index f4c1c143daff2931c6880209be423802cbc5e120..900508a02131f040105b738a2fe081d8e75510a7 100644 (file)
@@ -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 (file)
index 0000000..9e90c5a
--- /dev/null
@@ -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 <typename T>
+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<std::string, 32> P;
+  SmallVector<std::string, 32> Q;
+  discoverStreamPurposes(File1, P);
+  discoverStreamPurposes(File2, Q);
+  outs() << "Stream Directory: Searching for differences...\n";
+
+  bool HasDifferences = false;
+  if (opts::diff::Pedantic) {
+    size_t Min = std::min(P.size(), Q.size());
+    for (size_t I = 0; I < Min; ++I) {
+      StringRef Names[] = {P[I], Q[I]};
+      uint32_t Sizes[] = {File1.getStreamByteSize(I),
+                          File2.getStreamByteSize(I)};
+      bool NamesDiffer = Names[0] != Names[1];
+      bool SizesDiffer = Sizes[0] != Sizes[1];
+      if (NamesDiffer) {
+        HasDifferences = true;
+        outs().indent(2) << formatv("Stream {0} - {1}: {2}, {3}: {4}\n", I,
+                                    File1.getFilePath(), Names[0],
+                                    File2.getFilePath(), Names[1]);
+        continue;
+      }
+      if (SizesDiffer) {
+        HasDifferences = true;
+        outs().indent(2) << formatv(
+            "Stream {0} ({1}): {2}: {3} bytes, {4}: {5} bytes\n", I, Names[0],
+            File1.getFilePath(), Sizes[0], File2.getFilePath(), Sizes[1]);
+        continue;
+      }
+    }
+
+    ArrayRef<std::string> MaxNames = (P.size() > Q.size() ? P : Q);
+    size_t Max = std::max(P.size(), Q.size());
+    PDBFile &MaxFile = (P.size() > Q.size() ? File1 : File2);
+    StringRef MinFileName =
+        (P.size() < Q.size() ? File1.getFilePath() : File2.getFilePath());
+    for (size_t I = Min; I < Max; ++I) {
+      HasDifferences = true;
+      StringRef StreamName = MaxNames[I];
+
+      outs().indent(2) << formatv(
+          "Stream {0} - {1}: <not present>, {2}: Index {3}, {4} bytes\n",
+          StreamName, MinFileName, MaxFile.getFilePath(), I,
+          MaxFile.getStreamByteSize(I));
+    }
+    if (!HasDifferences)
+      outs() << "Stream Directory: No differences detected...\n";
+  } else {
+    auto PI = to_vector<32>(enumerate(P));
+    auto QI = to_vector<32>(enumerate(Q));
+
+    typedef decltype(PI) ContainerType;
+    typedef typename ContainerType::value_type value_type;
+
+    auto Comparator = [](const value_type &I1, const value_type &I2) {
+      return I1.value() < I2.value();
+    };
+    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 (file)
index 0000000..6037576
--- /dev/null
@@ -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 1d60b4a0d8b926455ba162c3324aae5b608b21d9..04a9c41e47ba7d9039bb3770d007c965825ff673 100644 (file)
@@ -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<uint16_t, const ModuleInfoEx *> ModStreams;
-  std::unordered_map<uint16_t, std::string> NamedStreams;
-
-  if (Dbi) {
-    for (auto &ModI : Dbi->modules()) {
-      uint16_t SN = ModI.Info.getModuleStreamIndex();
-      ModStreams[SN] = &ModI;
-    }
-  }
-  if (Info) {
-    for (auto &NSE : Info->named_streams()) {
-      NamedStreams[NSE.second] = NSE.first();
-    }
-  }
-
-  StreamPurposes.resize(StreamCount);
-  for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
-    std::string Value;
-    if (StreamIdx == OldMSFDirectory)
-      Value = "Old MSF Directory";
-    else if (StreamIdx == StreamPDB)
-      Value = "PDB Stream";
-    else if (StreamIdx == StreamDBI)
-      Value = "DBI Stream";
-    else if (StreamIdx == StreamTPI)
-      Value = "TPI Stream";
-    else if (StreamIdx == StreamIPI)
-      Value = "IPI Stream";
-    else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex())
-      Value = "Global Symbol Hash";
-    else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex())
-      Value = "Public Symbol Hash";
-    else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex())
-      Value = "Public Symbol Records";
-    else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex())
-      Value = "TPI Hash";
-    else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex())
-      Value = "TPI Aux Hash";
-    else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex())
-      Value = "IPI Hash";
-    else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex())
-      Value = "IPI Aux Hash";
-    else if (Dbi &&
-             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception))
-      Value = "Exception Data";
-    else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup))
-      Value = "Fixup Data";
-    else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO))
-      Value = "FPO Data";
-    else if (Dbi &&
-             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO))
-      Value = "New FPO Data";
-    else if (Dbi &&
-             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc))
-      Value = "Omap From Source Data";
-    else if (Dbi &&
-             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc))
-      Value = "Omap To Source Data";
-    else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata))
-      Value = "Pdata";
-    else if (Dbi &&
-             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr))
-      Value = "Section Header Data";
-    else if (Dbi &&
-             StreamIdx ==
-                 Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig))
-      Value = "Section Header Original Data";
-    else if (Dbi &&
-             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap))
-      Value = "Token Rid Data";
-    else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata))
-      Value = "Xdata";
-    else {
-      auto ModIter = ModStreams.find(StreamIdx);
-      auto NSIter = NamedStreams.find(StreamIdx);
-      if (ModIter != ModStreams.end()) {
-        Value = "Module \"";
-        Value += ModIter->second->Info.getModuleName().str();
-        Value += "\"";
-      } else if (NSIter != NamedStreams.end()) {
-        Value = "Named Stream \"";
-        Value += NSIter->second;
-        Value += "\"";
-      } else {
-        Value = "???";
-      }
-    }
-    StreamPurposes[StreamIdx] = Value;
-  }
-
-  // Consume errors from missing streams.
-  if (!Dbi)
-    consumeError(Dbi.takeError());
-  if (!Tpi)
-    consumeError(Tpi.takeError());
-  if (!Ipi)
-    consumeError(Ipi.takeError());
-  if (!Info)
-    consumeError(Info.takeError());
-}
-
 Error LLVMOutputStyle::dumpStreamSummary() {
   if (!opts::raw::DumpStreamSummary)
     return Error::success();
 
-  discoverStreamPurposes();
+  if (StreamPurposes.empty())
+    discoverStreamPurposes(File, StreamPurposes);
 
   uint32_t StreamCount = File.getNumStreams();
 
@@ -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) {
index 4aef78d3253f8725c83d80d108510d40c8638154..40d37525b94eaa853c8ae43ef7d38dc0cf3d4a02 100644 (file)
 
 #include "OutputStyle.h"
 
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/DebugInfo/CodeView/TypeDatabase.h"
 #include "llvm/Support/ScopedPrinter.h"
 
+#include <string>
+
 namespace llvm {
 class BitVector;
 namespace pdb {
@@ -25,8 +28,6 @@ public:
   Error dump() override;
 
 private:
-  void discoverStreamPurposes();
-
   Error dumpFileHeaders();
   Error dumpStreamSummary();
   Error dumpFreePageMap();
@@ -51,7 +52,7 @@ private:
   PDBFile &File;
   ScopedPrinter P;
   codeview::TypeDatabase TypeDB;
-  std::vector<std::string> StreamPurposes;
+  SmallVector<std::string, 32> StreamPurposes;
 };
 }
 }
diff --git a/tools/llvm-pdbdump/StreamUtil.cpp b/tools/llvm-pdbdump/StreamUtil.cpp
new file mode 100644 (file)
index 0000000..db1e01a
--- /dev/null
@@ -0,0 +1,136 @@
+//===- StreamUtil.cpp - PDB stream utilities --------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StreamUtil.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/ModInfo.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+
+namespace llvm {
+namespace pdb {
+void discoverStreamPurposes(PDBFile &File,
+                            SmallVectorImpl<std::string> &Purposes) {
+
+  // It's OK if we fail to load some of these streams, we still attempt to print
+  // what we can.
+  auto Dbi = File.getPDBDbiStream();
+  auto Tpi = File.getPDBTpiStream();
+  auto Ipi = File.getPDBIpiStream();
+  auto Info = File.getPDBInfoStream();
+
+  uint32_t StreamCount = File.getNumStreams();
+  DenseMap<uint16_t, const ModuleInfoEx *> ModStreams;
+  DenseMap<uint16_t, std::string> NamedStreams;
+
+  if (Dbi) {
+    for (auto &ModI : Dbi->modules()) {
+      uint16_t SN = ModI.Info.getModuleStreamIndex();
+      if (SN != kInvalidStreamIndex)
+        ModStreams[SN] = &ModI;
+    }
+  }
+  if (Info) {
+    for (auto &NSE : Info->named_streams()) {
+      if (NSE.second != kInvalidStreamIndex)
+        NamedStreams[NSE.second] = NSE.first();
+    }
+  }
+
+  Purposes.resize(StreamCount);
+  for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
+    std::string Value;
+    if (StreamIdx == OldMSFDirectory)
+      Value = "Old MSF Directory";
+    else if (StreamIdx == StreamPDB)
+      Value = "PDB Stream";
+    else if (StreamIdx == StreamDBI)
+      Value = "DBI Stream";
+    else if (StreamIdx == StreamTPI)
+      Value = "TPI Stream";
+    else if (StreamIdx == StreamIPI)
+      Value = "IPI Stream";
+    else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex())
+      Value = "Global Symbol Hash";
+    else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex())
+      Value = "Public Symbol Hash";
+    else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex())
+      Value = "Public Symbol Records";
+    else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex())
+      Value = "TPI Hash";
+    else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex())
+      Value = "TPI Aux Hash";
+    else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex())
+      Value = "IPI Hash";
+    else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex())
+      Value = "IPI Aux Hash";
+    else if (Dbi &&
+             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception))
+      Value = "Exception Data";
+    else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup))
+      Value = "Fixup Data";
+    else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO))
+      Value = "FPO Data";
+    else if (Dbi &&
+             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO))
+      Value = "New FPO Data";
+    else if (Dbi &&
+             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc))
+      Value = "Omap From Source Data";
+    else if (Dbi &&
+             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc))
+      Value = "Omap To Source Data";
+    else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata))
+      Value = "Pdata";
+    else if (Dbi &&
+             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr))
+      Value = "Section Header Data";
+    else if (Dbi &&
+             StreamIdx ==
+                 Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig))
+      Value = "Section Header Original Data";
+    else if (Dbi &&
+             StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap))
+      Value = "Token Rid Data";
+    else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata))
+      Value = "Xdata";
+    else {
+      auto ModIter = ModStreams.find(StreamIdx);
+      auto NSIter = NamedStreams.find(StreamIdx);
+      if (ModIter != ModStreams.end()) {
+        Value = "Module \"";
+        Value += ModIter->second->Info.getModuleName().str();
+        Value += "\"";
+      } else if (NSIter != NamedStreams.end()) {
+        Value = "Named Stream \"";
+        Value += NSIter->second;
+        Value += "\"";
+      } else {
+        Value = "???";
+      }
+    }
+    Purposes[StreamIdx] = Value;
+  }
+
+  // Consume errors from missing streams.
+  if (!Dbi)
+    consumeError(Dbi.takeError());
+  if (!Tpi)
+    consumeError(Tpi.takeError());
+  if (!Ipi)
+    consumeError(Ipi.takeError());
+  if (!Info)
+    consumeError(Info.takeError());
+}
+}
+}
diff --git a/tools/llvm-pdbdump/StreamUtil.h b/tools/llvm-pdbdump/StreamUtil.h
new file mode 100644 (file)
index 0000000..b5c0beb
--- /dev/null
@@ -0,0 +1,25 @@
+//===- Streamutil.h - PDB stream utilities ----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_STREAMUTIL_H
+#define LLVM_TOOLS_LLVMPDBDUMP_STREAMUTIL_H
+
+#include "llvm/ADT/SmallVector.h"
+
+#include <string>
+
+namespace llvm {
+namespace pdb {
+class PDBFile;
+void discoverStreamPurposes(PDBFile &File,
+                            SmallVectorImpl<std::string> &Purposes);
+}
+}
+
+#endif
index 783d3cfb743c9d924f48118d182491e292f60681..cb2b7d6a1ed78f79ca2071456077cdc731e7db12 100644 (file)
@@ -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<bool> NoEnumDefs("no-enum-definitions",
                          cl::cat(FilterCategory), cl::sub(PrettySubcommand));
 }
 
+namespace diff {
+cl::opt<bool> Pedantic("pedantic",
+                       cl::desc("Finds all differences (even structural ones "
+                                "that produce otherwise identical PDBs)"),
+                       cl::sub(DiffSubcommand));
+
+cl::list<std::string> InputFilenames(cl::Positional,
+                                     cl::desc("<first> <second>"),
+                                     cl::OneOrMore, cl::sub(DiffSubcommand));
+}
+
 namespace raw {
 
 cl::OptionCategory MsfOptions("MSF Container Options");
@@ -414,12 +427,17 @@ static void yamlToPdb(StringRef Path) {
   ExitOnErr(Builder.commit(opts::yaml2pdb::YamlPdbOutputFile));
 }
 
+static PDBFile &loadPDB(StringRef Path, std::unique_ptr<IPDBSession> &Session) {
+  ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session));
+
+  NativeSession *NS = static_cast<NativeSession *>(Session.get());
+  return NS->getPDBFile();
+}
+
 static void pdb2Yaml(StringRef Path) {
   std::unique_ptr<IPDBSession> Session;
-  ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session));
+  auto &File = loadPDB(Path, Session);
 
-  NativeSession *RS = static_cast<NativeSession *>(Session.get());
-  PDBFile &File = RS->getPDBFile();
   auto O = llvm::make_unique<YAMLOutputStyle>(File);
   O = llvm::make_unique<YAMLOutputStyle>(File);
 
@@ -428,10 +446,8 @@ static void pdb2Yaml(StringRef Path) {
 
 static void dumpRaw(StringRef Path) {
   std::unique_ptr<IPDBSession> Session;
-  ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session));
+  auto &File = loadPDB(Path, Session);
 
-  NativeSession *RS = static_cast<NativeSession *>(Session.get());
-  PDBFile &File = RS->getPDBFile();
   auto O = llvm::make_unique<LLVMOutputStyle>(File);
 
   ExitOnErr(O->dump());
@@ -439,15 +455,24 @@ static void dumpRaw(StringRef Path) {
 
 static void dumpAnalysis(StringRef Path) {
   std::unique_ptr<IPDBSession> Session;
-  ExitOnErr(loadDataForPDB(PDB_ReaderType::Native, Path, Session));
-
-  NativeSession *NS = static_cast<NativeSession *>(Session.get());
-  PDBFile &File = NS->getPDBFile();
+  auto &File = loadPDB(Path, Session);
   auto O = llvm::make_unique<AnalysisStyle>(File);
 
   ExitOnErr(O->dump());
 }
 
+static void diff(StringRef Path1, StringRef Path2) {
+  std::unique_ptr<IPDBSession> Session1;
+  std::unique_ptr<IPDBSession> Session2;
+
+  auto &File1 = loadPDB(Path1, Session1);
+  auto &File2 = loadPDB(Path2, Session2);
+
+  auto O = llvm::make_unique<DiffStyle>(File1, File2);
+
+  ExitOnErr(O->dump());
+}
+
 static void dumpPretty(StringRef Path) {
   std::unique_ptr<IPDBSession> Session;
 
@@ -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();
index d4f082cae7c4596c5d144b1cfda083e1c391a3c1..cdebc20fb5f1bf026c3adb1eb505cddd8380ffc3 100644 (file)
@@ -67,6 +67,10 @@ extern llvm::cl::opt<bool> DumpFpo;
 extern llvm::cl::opt<bool> DumpStringTable;
 }
 
+namespace diff {
+extern llvm::cl::opt<bool> Pedantic;
+}
+
 namespace pdb2yaml {
 extern llvm::cl::opt<bool> NoFileHeaders;
 extern llvm::cl::opt<bool> StreamMetadata;