]> granicus.if.org Git - llvm/commitdiff
[llvm-pdbutil] Improve diff mode.
authorZachary Turner <zturner@google.com>
Fri, 7 Jul 2017 18:45:37 +0000 (18:45 +0000)
committerZachary Turner <zturner@google.com>
Fri, 7 Jul 2017 18:45:37 +0000 (18:45 +0000)
We're getting to the point that some MS tools (e.g. DIA) can recognize
our PDBs but others (e.g. link.exe) cannot. I think the way forward is
to improve our tooling to help us find differences more easily. For
example, if we can compile the same program with clang-cl and cl and
have a tool tell us all the places where the PDBs differ, this could
tell us what we're doing wrong. It's tricky though, because there are a
lot of "benign" differences in a PDB. For example, if the string table
in one PDB consists of "foo" followed by "bar" and in the other PDB it
consists of "bar" followed by "foo", this is not necessarily a critical
difference, as long as the uses of these strings also refer to the
correct location. On the other hand, if the second PDB doesn't even
contain the string "foo" at all, this is a critical difference.

diff mode has been in llvm-pdbutil for quite a while, but because of the
above challenge along with some others, it's been hard to make it
useful. I think this patch addresses that. It looks for all the same
things, but it now prints the output in tabular format (carefully
formatted and aligned into tables and fields), and it highlights
critical differences in red, non-critical differences in yellow, and
identical fields in green.  This makes it easy to spot the places we
differ, and the general concept of outputting arbitrary fields in
tabular format can be extended to provide analysis into many of the
different types of information that show up in a PDB.

Differential Revision: https://reviews.llvm.org/D35039

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@307421 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/DebugInfo/PDB/Native/NamedStreamMap.h
include/llvm/DebugInfo/PDB/Native/PDBStringTable.h
lib/DebugInfo/PDB/Native/PDBStringTable.cpp
tools/llvm-pdbutil/CMakeLists.txt
tools/llvm-pdbutil/Diff.cpp
tools/llvm-pdbutil/DiffPrinter.cpp [new file with mode: 0644]
tools/llvm-pdbutil/DiffPrinter.h [new file with mode: 0644]
tools/llvm-pdbutil/FormatUtil.cpp
tools/llvm-pdbutil/FormatUtil.h

index 25f66240a6a24f5cd1d8d7086f2ec1599db7a4de..17a82b7ce12db2a9d32b788ec0255931e1bbc9ec 100644 (file)
@@ -44,7 +44,7 @@ public:
   bool get(StringRef Stream, uint32_t &StreamNo) const;
   void set(StringRef Stream, uint32_t StreamNo);
   void remove(StringRef Stream);
-
+  const StringMap<uint32_t> &getStringMap() const { return Mapping; }
   iterator_range<StringMapConstIterator<uint32_t>> entries() const;
 
 private:
index 86ef1136b41d778b465e7ffc9554bfda6931defc..29167c966d427f9973053b34d81ee818f5f8b106 100644 (file)
@@ -56,7 +56,6 @@ private:
   const PDBStringTableHeader *Header = nullptr;
   codeview::DebugStringTableSubsectionRef Strings;
   FixedStreamArray<support::ulittle32_t> IDs;
-  uint32_t ByteSize = 0;
   uint32_t NameCount = 0;
 };
 
index f9f8ac219d357a066ddf32385c4b3709538fadfe..acd45f7a62192e993a5df0e091c1f348a7cb8d44 100644 (file)
@@ -21,7 +21,7 @@ using namespace llvm;
 using namespace llvm::support;
 using namespace llvm::pdb;
 
-uint32_t PDBStringTable::getByteSize() const { return ByteSize; }
+uint32_t PDBStringTable::getByteSize() const { return Header->ByteSize; }
 uint32_t PDBStringTable::getNameCount() const { return NameCount; }
 uint32_t PDBStringTable::getHashVersion() const { return Header->HashVersion; }
 uint32_t PDBStringTable::getSignature() const { return Header->Signature; }
index 7a3245424efc6da891816375acfefd8a8e324f6d..bc28e6bdd7eaa4dffc9cced2455f698f9dc06512 100644 (file)
@@ -11,6 +11,7 @@ add_llvm_tool(llvm-pdbutil
   Analyze.cpp
   BytesOutputStyle.cpp
   Diff.cpp
+  DiffPrinter.cpp
   DumpOutputStyle.cpp
   llvm-pdbutil.cpp
   FormatUtil.cpp
index 9b38ae1d603ef4c25c54a8a9a81ea7d661bc8787..eda0ce275affd153775cd6a04178f96b4de7d6fb 100644 (file)
@@ -9,9 +9,14 @@
 
 #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"
@@ -49,47 +54,6 @@ template <> struct format_provider<PdbRaw_FeatureSig> {
 
 template <typename R> using ValueOfRange = llvm::detail::ValueOfRange<R>;
 
-template <typename Range, typename Comp>
-static void set_differences(Range &&R1, Range &&R2,
-                            SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft,
-                            SmallVectorImpl<ValueOfRange<Range>> *OnlyRight,
-                            SmallVectorImpl<ValueOfRange<Range>> *Intersection,
-                            Comp Comparator) {
-
-  std::sort(R1.begin(), R1.end(), Comparator);
-  std::sort(R2.begin(), R2.end(), Comparator);
-
-  if (OnlyLeft) {
-    OnlyLeft->reserve(R1.size());
-    auto End = std::set_difference(R1.begin(), R1.end(), R2.begin(), R2.end(),
-                                   OnlyLeft->begin(), Comparator);
-    OnlyLeft->set_size(std::distance(OnlyLeft->begin(), End));
-  }
-  if (OnlyRight) {
-    OnlyLeft->reserve(R2.size());
-    auto End = std::set_difference(R2.begin(), R2.end(), R1.begin(), R1.end(),
-                                   OnlyRight->begin(), Comparator);
-    OnlyRight->set_size(std::distance(OnlyRight->begin(), End));
-  }
-  if (Intersection) {
-    Intersection->reserve(std::min(R1.size(), R2.size()));
-    auto End = std::set_intersection(R1.begin(), R1.end(), R2.begin(), R2.end(),
-                                     Intersection->begin(), Comparator);
-    Intersection->set_size(std::distance(Intersection->begin(), End));
-  }
-}
-
-template <typename Range>
-static void
-set_differences(Range &&R1, Range &&R2,
-                SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft,
-                SmallVectorImpl<ValueOfRange<Range>> *OnlyRight,
-                SmallVectorImpl<ValueOfRange<Range>> *Intersection = nullptr) {
-  std::less<ValueOfRange<Range>> Comp;
-  set_differences(std::forward<Range>(R1), std::forward<Range>(R2), OnlyLeft,
-                  OnlyRight, Intersection, Comp);
-}
-
 DiffStyle::DiffStyle(PDBFile &File1, PDBFile &File2)
     : File1(File1), File2(File2) {}
 
@@ -136,300 +100,382 @@ Error DiffStyle::dump() {
   return Error::success();
 }
 
-template <typename T>
-static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, T V1,
-                         T V2) {
-  if (V1 == V2) {
-    outs() << formatv("  {0}: No differences detected!\n", Label);
-    return false;
-  }
-
-  outs().indent(2) << Label << "\n";
-  outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), V1);
-  outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), V2);
-  return true;
-}
-
-template <typename T>
-static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2,
-                         ArrayRef<T> V1, ArrayRef<T> V2) {
-  if (V1 == V2) {
-    outs() << formatv("  {0}: No differences detected!\n", Label);
-    return false;
-  }
-
-  outs().indent(2) << Label << "\n";
-  outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(),
-                              make_range(V1.begin(), V1.end()));
-  outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(),
-                              make_range(V2.begin(), V2.end()));
-  return true;
-}
-
-template <typename T>
-static bool printSymmetricDifferences(PDBFile &File1, PDBFile &File2,
-                                      T &&OnlyRange1, T &&OnlyRange2,
-                                      StringRef Label) {
-  bool HasDiff = false;
-  if (!OnlyRange1.empty()) {
-    HasDiff = true;
-    outs() << formatv("  {0} {1}(s) only in ({2})\n", OnlyRange1.size(), Label,
-                      File1.getFilePath());
-    for (const auto &Item : OnlyRange1)
-      outs() << formatv("    {0}\n", Label, Item);
-  }
-  if (!OnlyRange2.empty()) {
-    HasDiff = true;
-    outs() << formatv("  {0} {1}(s) only in ({2})\n", OnlyRange2.size(),
-                      File2.getFilePath());
-    for (const auto &Item : OnlyRange2)
-      outs() << formatv("    {0}\n", Item);
-  }
-  return HasDiff;
+static std::string shortFilePath(StringRef Path, uint32_t Width) {
+  if (Path.size() <= Width)
+    return Path;
+  Path = Path.take_back(Width - 3);
+  return std::string("...") + Path.str();
 }
 
 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 (!Diffs)
-    outs() << "MSF Super Block: No differences detected...\n";
+  DiffPrinter D(2, "MSF Super Block", 16, 20, outs());
+  D.printExplicit("File", DiffResult::UNSPECIFIED,
+                  shortFilePath(File1.getFilePath(), 18),
+                  shortFilePath(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, outs());
+  D.printExplicit("File", DiffResult::UNSPECIFIED,
+                  shortFilePath(File1.getFilePath(), 18),
+                  shortFilePath(File2.getFilePath(), 18));
+
   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;
+  discoverStreamPurposes(File1, P, 28);
+  discoverStreamPurposes(File2, Q, 28);
+  D.print("Stream Count", File1.getNumStreams(), File2.getNumStreams());
   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();
-  };
+  // 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.
+  for (const auto &P : PI) {
+    typedef decltype(PI) ContainerType;
+    typedef typename ContainerType::value_type value_type;
 
-  decltype(PI) OnlyP;
-  decltype(QI) OnlyQ;
-  decltype(PI) Common;
+    auto Iter = llvm::find_if(
+        QI, [P](const value_type &V) { return V.value() == P.value(); });
 
-  set_differences(PI, QI, &OnlyP, &OnlyQ, &Common, Comparator);
-
-  if (!OnlyP.empty()) {
-    HasDifferences = true;
-    outs().indent(2) << formatv("{0} Stream(s) only in ({1})\n", OnlyP.size(),
-                                File1.getFilePath());
-    for (auto &Item : OnlyP) {
-      outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(),
-                                  Item.value());
+    if (Iter == QI.end()) {
+      D.printExplicit(P.value(), DiffResult::DIFFERENT, P.index(),
+                      "(not present)");
+      continue;
     }
-  }
 
-  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());
-    }
+    D.print<EquivalentDiffProvider>(P.value(), P.index(), Iter->index());
+    QI.erase(Iter);
   }
-  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";
+
+  for (const auto &Q : QI) {
+    D.printExplicit(Q.value(), DiffResult::DIFFERENT, "(not present)",
+                    Q.index());
   }
-  if (!HasDifferences)
-    outs() << "Stream Directory: No differences detected!\n";
 
   return Error::success();
 }
 
 Error DiffStyle::diffStringTable() {
+  DiffPrinter D(2, "String Table", 30, 20, outs());
+  D.printExplicit("File", DiffResult::UNSPECIFIED,
+                  shortFilePath(File1.getFilePath(), 18),
+                  shortFilePath(File2.getFilePath(), 18));
+
   auto ExpectedST1 = File1.getStringTable();
   auto ExpectedST2 = File2.getStringTable();
-  outs() << "String Table: Searching for differences...\n";
   bool Has1 = !!ExpectedST1;
   bool Has2 = !!ExpectedST2;
-  if (!(Has1 && Has2)) {
-    // If one has a string table and the other doesn't, we can print less
-    // output.
-    if (Has1 != Has2) {
-      if (Has1) {
-        outs() << formatv("  {0}: ({1} strings)\n", File1.getFilePath(),
-                          ExpectedST1->getNameCount());
-        outs() << formatv("  {0}: (string table not present)\n",
-                          File2.getFilePath());
-      } else {
-        outs() << formatv("  {0}: (string table not present)\n",
-                          File1.getFilePath());
-        outs() << formatv("  {0}: ({1})\n", File2.getFilePath(),
-                          ExpectedST2->getNameCount());
-      }
-    }
+  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();
   }
 
-  bool HasDiff = false;
   auto &ST1 = *ExpectedST1;
   auto &ST2 = *ExpectedST2;
 
-  if (ST1.getByteSize() != ST2.getByteSize()) {
-    outs() << "  Stream Size\n";
-    outs() << formatv("    {0} - {1} byte(s)\n", File1.getFilePath(),
-                      ST1.getByteSize());
-    outs() << formatv("    {0} - {1} byte(s)\n", File2.getFilePath(),
-                      ST2.getByteSize());
-    outs() << formatv("    Difference: {0} bytes\n",
-                      AbsoluteDifference(ST1.getByteSize(), ST2.getByteSize()));
-    HasDiff = true;
-  }
-  HasDiff |= diffAndPrint("Hash Version", File1, File2, ST1.getHashVersion(),
-                          ST1.getHashVersion());
-  HasDiff |= diffAndPrint("Signature", File1, File2, ST1.getSignature(),
-                          ST1.getSignature());
+  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();
-  std::vector<StringRef> Strings1, Strings2;
-  Strings1.reserve(IdList1.size());
-  Strings2.reserve(IdList2.size());
+  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();
-    Strings1.push_back(*S);
+    if (S->empty())
+      ++Empty1;
+    else
+      LS.insert(*S);
   }
   for (auto ID : IdList2) {
     auto S = ST2.getStringForID(ID);
     if (!S)
       return S.takeError();
-    Strings2.push_back(*S);
+    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;
+    }
 
-  SmallVector<StringRef, 64> OnlyP;
-  SmallVector<StringRef, 64> OnlyQ;
-  auto End1 = std::remove(Strings1.begin(), Strings1.end(), "");
-  auto End2 = std::remove(Strings2.begin(), Strings2.end(), "");
-  uint32_t Empty1 = std::distance(End1, Strings1.end());
-  uint32_t Empty2 = std::distance(End2, Strings2.end());
-  Strings1.erase(End1, Strings1.end());
-  Strings2.erase(End2, Strings2.end());
-  set_differences(Strings1, Strings2, &OnlyP, &OnlyQ);
-  printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "String");
-
-  if (Empty1 != Empty2) {
-    PDBFile &MoreF = (Empty1 > Empty2) ? File1 : File2;
-    PDBFile &LessF = (Empty1 < Empty2) ? File1 : File2;
-    uint32_t Difference = AbsoluteDifference(Empty1, Empty2);
-    outs() << formatv("  {0} had {1} more empty strings than {2}\n",
-                      MoreF.getFilePath(), Difference, LessF.getFilePath());
+    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);
   }
-  if (!HasDiff)
-    outs() << "String Table: No differences detected!\n";
   return Error::success();
 }
 
 Error DiffStyle::diffFreePageMap() { return Error::success(); }
 
 Error DiffStyle::diffInfoStream() {
+  DiffPrinter D(2, "PDB Stream", 22, 40, outs());
+  D.printExplicit("File", DiffResult::UNSPECIFIED,
+                  shortFilePath(File1.getFilePath(), 38),
+                  shortFilePath(File2.getFilePath(), 38));
+
   auto ExpectedInfo1 = File1.getPDBInfoStream();
   auto ExpectedInfo2 = File2.getPDBInfoStream();
 
-  outs() << "PDB Stream: Searching for differences...\n";
   bool Has1 = !!ExpectedInfo1;
   bool Has2 = !!ExpectedInfo2;
   if (!(Has1 && Has2)) {
-    if (Has1 != Has2)
-      outs() << formatv("{0} does not have a PDB Stream!\n",
-                        Has1 ? File1.getFilePath() : File2.getFilePath());
-    consumeError(ExpectedInfo2.takeError());
+    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();
   }
 
-  bool HasDiff = false;
   auto &IS1 = *ExpectedInfo1;
   auto &IS2 = *ExpectedInfo2;
-  if (IS1.getStreamSize() != IS2.getStreamSize()) {
-    outs() << "  Stream Size\n";
-    outs() << formatv("    {0} - {1} byte(s)\n", File1.getFilePath(),
-                      IS1.getStreamSize());
-    outs() << formatv("    {0} - {1} byte(s)\n", File2.getFilePath(),
-                      IS2.getStreamSize());
-    outs() << formatv(
-        "    Difference: {0} bytes\n",
-        AbsoluteDifference(IS1.getStreamSize(), IS2.getStreamSize()));
-    HasDiff = true;
+  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();
+}
+
+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;
   }
-  HasDiff |= diffAndPrint("Age", File1, File2, IS1.getAge(), IS2.getAge());
-  HasDiff |= diffAndPrint("Guid", File1, File2, IS1.getGuid(), IS2.getGuid());
-  HasDiff |= diffAndPrint("Signature", File1, File2, IS1.getSignature(),
-                          IS2.getSignature());
-  HasDiff |=
-      diffAndPrint("Version", File1, File2, IS1.getVersion(), IS2.getVersion());
-  HasDiff |= diffAndPrint("Features", File1, File2, IS1.getFeatureSignatures(),
-                          IS2.getFeatureSignatures());
-  HasDiff |= diffAndPrint("Named Stream Byte Size", File1, File2,
-                          IS1.getNamedStreamMapByteSize(),
-                          IS2.getNamedStreamMapByteSize());
-  SmallVector<StringRef, 4> NS1;
-  SmallVector<StringRef, 4> NS2;
-  for (const auto &X : IS1.getNamedStreams().entries())
-    NS1.push_back(X.getKey());
-  for (const auto &X : IS2.getNamedStreams().entries())
-    NS2.push_back(X.getKey());
-  SmallVector<StringRef, 4> OnlyP;
-  SmallVector<StringRef, 4> OnlyQ;
-  set_differences(NS1, NS2, &OnlyP, &OnlyQ);
-  printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "Named Streams");
-  if (!HasDiff)
-    outs() << "PDB Stream: No differences detected!\n";
 
-  return Error::success();
+  static std::string format(uint16_t SN) {
+    if (SN == kInvalidStreamIndex)
+      return "(not present)";
+    return formatv("{0}", SN).str();
+  }
+};
+
+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) {
+    if (!Modi.hasValue())
+      return "(not present)";
+    return formatv("{0}", *Modi).str();
+  }
+};
+
+struct StringProvider {
+  DiffResult compare(StringRef L, StringRef R) {
+    IdenticalDiffProvider I;
+    return I.compare(L, R);
+  }
+
+  std::string format(StringRef S) {
+    if (S.empty())
+      return "(empty)";
+    return S;
+  }
+};
+
+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;
 }
 
-Error DiffStyle::diffDbiStream() { return Error::success(); }
+Error DiffStyle::diffDbiStream() {
+  DiffPrinter D(2, "DBI Stream", 40, 30, outs());
+  D.printExplicit("File", DiffResult::UNSPECIFIED,
+                  shortFilePath(File1.getFilePath(), 38),
+                  shortFilePath(File2.getFilePath(), 38));
+
+  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) {
+    D.printFullRow(
+        truncateQuotedNameFront("Module", L.second.getModuleName(), 70));
+
+    auto Iter = llvm::find_if(
+        MDR, [&L](const std::pair<uint32_t, DbiModuleDescriptor> &R) {
+          return R.second.getModuleName().equals_lower(
+              L.second.getModuleName());
+        });
+    if (Iter == MDR.end()) {
+      // We didn't find this module at all on the right.  Just print one row
+      // and continue.
+      D.print<ModiProvider>("- Modi", L.first, None);
+      continue;
+    }
+
+    // We did find this module.  Go through and compare each field.
+    const auto &R = *Iter;
+    D.print<ModiProvider>("- Modi", L.first, R.first);
+    D.print<StringProvider>("- Obj File Name",
+                            shortFilePath(L.second.getObjFileName(), 28),
+                            shortFilePath(R.second.getObjFileName(), 28));
+    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());
+    MDR.erase(Iter);
+  }
+
+  return Error::success();
+}
 
 Error DiffStyle::diffSectionContribs() { return Error::success(); }
 
diff --git a/tools/llvm-pdbutil/DiffPrinter.cpp b/tools/llvm-pdbutil/DiffPrinter.cpp
new file mode 100644 (file)
index 0000000..b608d54
--- /dev/null
@@ -0,0 +1,106 @@
+
+#include "DiffPrinter.h"
+
+#include "llvm/Support/FormatAdapters.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+static void setColor(llvm::raw_ostream &OS, DiffResult Result) {
+  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;
+  }
+}
+
+DiffPrinter::DiffPrinter(uint32_t Indent, StringRef Header,
+                         uint32_t PropertyWidth, uint32_t FieldWidth,
+                         raw_ostream &Stream)
+    : Indent(Indent), PropertyWidth(PropertyWidth), FieldWidth(FieldWidth),
+      OS(Stream) {
+  printHeaderRow();
+  printFullRow(Header);
+}
+
+DiffPrinter::~DiffPrinter() {}
+
+void DiffPrinter::printFullRow(StringRef Text) {
+  newLine();
+  printField(Text, DiffResult::UNSPECIFIED, AlignStyle::Center,
+             PropertyWidth + 1 + FieldWidth + 1 + FieldWidth);
+  printSeparatorRow();
+}
+
+void DiffPrinter::printSeparatorRow() {
+  newLine();
+  OS << formatv("{0}", fmt_repeat('-', PropertyWidth));
+  OS << '+';
+  OS << formatv("{0}", fmt_repeat('-', FieldWidth));
+  OS << '+';
+  OS << formatv("{0}", fmt_repeat('-', FieldWidth));
+  OS << '|';
+}
+
+void DiffPrinter::printHeaderRow() {
+  newLine('-');
+  OS << formatv("{0}", fmt_repeat('-', PropertyWidth + 2 * FieldWidth + 3));
+}
+
+void DiffPrinter::newLine(char InitialChar) {
+  OS << "\n";
+  OS.indent(Indent) << InitialChar;
+}
+
+void DiffPrinter::printExplicit(StringRef Property, DiffResult C,
+                                StringRef Left, StringRef Right) {
+  newLine();
+  printField(Property, DiffResult::UNSPECIFIED, AlignStyle::Right,
+             PropertyWidth);
+  printField(Left, C, AlignStyle::Center, FieldWidth);
+  printField(Right, C, AlignStyle::Center, FieldWidth);
+  printSeparatorRow();
+}
+
+void DiffPrinter::printSame(StringRef Property, StringRef Value) {
+  newLine();
+  printField(Property, DiffResult::UNSPECIFIED, AlignStyle::Right,
+             PropertyWidth);
+  printField(Value, DiffResult::IDENTICAL, AlignStyle::Center,
+             FieldWidth + 1 + FieldWidth);
+  printSeparatorRow();
+}
+
+void DiffPrinter::printDifferent(StringRef Property, StringRef Left,
+                                 StringRef Right) {
+  newLine();
+  printField(Property, DiffResult::UNSPECIFIED, AlignStyle::Right,
+             PropertyWidth);
+  printField(Left, DiffResult::DIFFERENT, AlignStyle::Center, FieldWidth);
+  printField(Right, DiffResult::DIFFERENT, AlignStyle::Center, FieldWidth);
+  printSeparatorRow();
+}
+
+void DiffPrinter::printField(StringRef Value, DiffResult C, AlignStyle Style,
+                             uint32_t Width) {
+  if (Style == AlignStyle::Right)
+    --Width;
+
+  std::string FormattedItem =
+      formatv("{0}", fmt_align(Value, Style, Width)).str();
+  if (C != DiffResult::UNSPECIFIED) {
+    setColor(OS, C);
+    OS << FormattedItem;
+    OS.resetColor();
+  } else
+    OS << FormattedItem;
+  if (Style == AlignStyle::Right)
+    OS << ' ';
+  OS << '|';
+}
diff --git a/tools/llvm-pdbutil/DiffPrinter.h b/tools/llvm-pdbutil/DiffPrinter.h
new file mode 100644 (file)
index 0000000..8a4527c
--- /dev/null
@@ -0,0 +1,158 @@
+//===- 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/Support/FormatVariadic.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <list>
+#include <unordered_set>
+
+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) {
+    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) {
+    return formatv("{0}", Item).str();
+  }
+};
+
+class DiffPrinter {
+public:
+  DiffPrinter(uint32_t Indent, StringRef Header, uint32_t PropertyWidth,
+              uint32_t FieldWidth, 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);
+    std::string R = P.format(Right);
+
+    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:
+  void printSame(StringRef Property, StringRef Value);
+  void printDifferent(StringRef Property, StringRef Left, StringRef Right);
+
+  void printHeaderRow();
+  void printSeparatorRow();
+  void newLine(char InitialChar = '|');
+  void printField(StringRef Value, DiffResult C, AlignStyle Style,
+                  uint32_t Width);
+
+  uint32_t Indent;
+  uint32_t PropertyWidth;
+  uint32_t FieldWidth;
+  raw_ostream &OS;
+};
+} // namespace pdb
+} // namespace llvm
+
+#endif
index 46a1774f166967eda19e8819445aff6b426f3cf6..02030272dd4da7ef7dc52d49a7cbf7a71e4c1d85 100644 (file)
@@ -26,6 +26,17 @@ std::string llvm::pdb::truncateStringBack(StringRef S, uint32_t MaxLen) {
   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;
index 31e4be34958f25e48b83de415aa45a5dde5cebb1..df32ed9360fba87735518c632f59cd3f896829f2 100644 (file)
@@ -23,6 +23,7 @@ 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);