]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/llvm-pdbutil/Diff.cpp
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r305575, and update
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / llvm-pdbutil / Diff.cpp
1 //===- Diff.cpp - PDB diff utility ------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "Diff.h"
11
12 #include "StreamUtil.h"
13 #include "llvm-pdbutil.h"
14
15 #include "llvm/DebugInfo/PDB/Native/Formatters.h"
16 #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
17 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
18 #include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
19 #include "llvm/DebugInfo/PDB/Native/RawConstants.h"
20
21 #include "llvm/Support/FormatAdapters.h"
22 #include "llvm/Support/FormatProviders.h"
23 #include "llvm/Support/FormatVariadic.h"
24
25 using namespace llvm;
26 using namespace llvm::pdb;
27
28 namespace llvm {
29 template <> struct format_provider<PdbRaw_FeatureSig> {
30   static void format(const PdbRaw_FeatureSig &Sig, raw_ostream &Stream,
31                      StringRef Style) {
32     switch (Sig) {
33     case PdbRaw_FeatureSig::MinimalDebugInfo:
34       Stream << "MinimalDebugInfo";
35       break;
36     case PdbRaw_FeatureSig::NoTypeMerge:
37       Stream << "NoTypeMerge";
38       break;
39     case PdbRaw_FeatureSig::VC110:
40       Stream << "VC110";
41       break;
42     case PdbRaw_FeatureSig::VC140:
43       Stream << "VC140";
44       break;
45     }
46   }
47 };
48 }
49
50 template <typename R> using ValueOfRange = llvm::detail::ValueOfRange<R>;
51
52 template <typename Range, typename Comp>
53 static void set_differences(Range &&R1, Range &&R2,
54                             SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft,
55                             SmallVectorImpl<ValueOfRange<Range>> *OnlyRight,
56                             SmallVectorImpl<ValueOfRange<Range>> *Intersection,
57                             Comp Comparator) {
58
59   std::sort(R1.begin(), R1.end(), Comparator);
60   std::sort(R2.begin(), R2.end(), Comparator);
61
62   if (OnlyLeft) {
63     OnlyLeft->reserve(R1.size());
64     auto End = std::set_difference(R1.begin(), R1.end(), R2.begin(), R2.end(),
65                                    OnlyLeft->begin(), Comparator);
66     OnlyLeft->set_size(std::distance(OnlyLeft->begin(), End));
67   }
68   if (OnlyRight) {
69     OnlyLeft->reserve(R2.size());
70     auto End = std::set_difference(R2.begin(), R2.end(), R1.begin(), R1.end(),
71                                    OnlyRight->begin(), Comparator);
72     OnlyRight->set_size(std::distance(OnlyRight->begin(), End));
73   }
74   if (Intersection) {
75     Intersection->reserve(std::min(R1.size(), R2.size()));
76     auto End = std::set_intersection(R1.begin(), R1.end(), R2.begin(), R2.end(),
77                                      Intersection->begin(), Comparator);
78     Intersection->set_size(std::distance(Intersection->begin(), End));
79   }
80 }
81
82 template <typename Range>
83 static void
84 set_differences(Range &&R1, Range &&R2,
85                 SmallVectorImpl<ValueOfRange<Range>> *OnlyLeft,
86                 SmallVectorImpl<ValueOfRange<Range>> *OnlyRight,
87                 SmallVectorImpl<ValueOfRange<Range>> *Intersection = nullptr) {
88   std::less<ValueOfRange<Range>> Comp;
89   set_differences(std::forward<Range>(R1), std::forward<Range>(R2), OnlyLeft,
90                   OnlyRight, Intersection, Comp);
91 }
92
93 DiffStyle::DiffStyle(PDBFile &File1, PDBFile &File2)
94     : File1(File1), File2(File2) {}
95
96 Error DiffStyle::dump() {
97   if (auto EC = diffSuperBlock())
98     return EC;
99
100   if (auto EC = diffFreePageMap())
101     return EC;
102
103   if (auto EC = diffStreamDirectory())
104     return EC;
105
106   if (auto EC = diffStringTable())
107     return EC;
108
109   if (auto EC = diffInfoStream())
110     return EC;
111
112   if (auto EC = diffDbiStream())
113     return EC;
114
115   if (auto EC = diffSectionContribs())
116     return EC;
117
118   if (auto EC = diffSectionMap())
119     return EC;
120
121   if (auto EC = diffFpoStream())
122     return EC;
123
124   if (auto EC = diffTpiStream(StreamTPI))
125     return EC;
126
127   if (auto EC = diffTpiStream(StreamIPI))
128     return EC;
129
130   if (auto EC = diffPublics())
131     return EC;
132
133   if (auto EC = diffGlobals())
134     return EC;
135
136   return Error::success();
137 }
138
139 template <typename T>
140 static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, T V1,
141                          T V2) {
142   if (V1 == V2) {
143     outs() << formatv("  {0}: No differences detected!\n", Label);
144     return false;
145   }
146
147   outs().indent(2) << Label << "\n";
148   outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(), V1);
149   outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(), V2);
150   return true;
151 }
152
153 template <typename T>
154 static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2,
155                          ArrayRef<T> V1, ArrayRef<T> V2) {
156   if (V1 == V2) {
157     outs() << formatv("  {0}: No differences detected!\n", Label);
158     return false;
159   }
160
161   outs().indent(2) << Label << "\n";
162   outs().indent(4) << formatv("{0}: {1}\n", File1.getFilePath(),
163                               make_range(V1.begin(), V1.end()));
164   outs().indent(4) << formatv("{0}: {1}\n", File2.getFilePath(),
165                               make_range(V2.begin(), V2.end()));
166   return true;
167 }
168
169 template <typename T>
170 static bool printSymmetricDifferences(PDBFile &File1, PDBFile &File2,
171                                       T &&OnlyRange1, T &&OnlyRange2,
172                                       StringRef Label) {
173   bool HasDiff = false;
174   if (!OnlyRange1.empty()) {
175     HasDiff = true;
176     outs() << formatv("  {0} {1}(s) only in ({2})\n", OnlyRange1.size(), Label,
177                       File1.getFilePath());
178     for (const auto &Item : OnlyRange1)
179       outs() << formatv("    {0}\n", Label, Item);
180   }
181   if (!OnlyRange2.empty()) {
182     HasDiff = true;
183     outs() << formatv("  {0} {1}(s) only in ({2})\n", OnlyRange2.size(),
184                       File2.getFilePath());
185     for (const auto &Item : OnlyRange2)
186       outs() << formatv("    {0}\n", Item);
187   }
188   return HasDiff;
189 }
190
191 Error DiffStyle::diffSuperBlock() {
192   outs() << "MSF Super Block: Searching for differences...\n";
193   bool Diffs = false;
194
195   Diffs |= diffAndPrint("Block Size", File1, File2, File1.getBlockSize(),
196                         File2.getBlockSize());
197   Diffs |= diffAndPrint("Block Count", File1, File2, File1.getBlockCount(),
198                         File2.getBlockCount());
199   Diffs |= diffAndPrint("Unknown 1", File1, File2, File1.getUnknown1(),
200                         File2.getUnknown1());
201
202   if (opts::diff::Pedantic) {
203     Diffs |= diffAndPrint("Free Block Map", File1, File2,
204                           File1.getFreeBlockMapBlock(),
205                           File2.getFreeBlockMapBlock());
206     Diffs |= diffAndPrint("Directory Size", File1, File2,
207                           File1.getNumDirectoryBytes(),
208                           File2.getNumDirectoryBytes());
209     Diffs |= diffAndPrint("Block Map Addr", File1, File2,
210                           File1.getBlockMapOffset(), File2.getBlockMapOffset());
211   }
212   if (!Diffs)
213     outs() << "MSF Super Block: No differences detected...\n";
214   return Error::success();
215 }
216
217 Error DiffStyle::diffStreamDirectory() {
218   SmallVector<std::string, 32> P;
219   SmallVector<std::string, 32> Q;
220   discoverStreamPurposes(File1, P);
221   discoverStreamPurposes(File2, Q);
222   outs() << "Stream Directory: Searching for differences...\n";
223
224   bool HasDifferences = false;
225   if (opts::diff::Pedantic) {
226     size_t Min = std::min(P.size(), Q.size());
227     for (size_t I = 0; I < Min; ++I) {
228       StringRef Names[] = {P[I], Q[I]};
229       uint32_t Sizes[] = {File1.getStreamByteSize(I),
230                           File2.getStreamByteSize(I)};
231       bool NamesDiffer = Names[0] != Names[1];
232       bool SizesDiffer = Sizes[0] != Sizes[1];
233       if (NamesDiffer) {
234         HasDifferences = true;
235         outs().indent(2) << formatv("Stream {0} - {1}: {2}, {3}: {4}\n", I,
236                                     File1.getFilePath(), Names[0],
237                                     File2.getFilePath(), Names[1]);
238         continue;
239       }
240       if (SizesDiffer) {
241         HasDifferences = true;
242         outs().indent(2) << formatv(
243             "Stream {0} ({1}): {2}: {3} bytes, {4}: {5} bytes\n", I, Names[0],
244             File1.getFilePath(), Sizes[0], File2.getFilePath(), Sizes[1]);
245         continue;
246       }
247     }
248
249     ArrayRef<std::string> MaxNames = (P.size() > Q.size() ? P : Q);
250     size_t Max = std::max(P.size(), Q.size());
251     PDBFile &MaxFile = (P.size() > Q.size() ? File1 : File2);
252     StringRef MinFileName =
253         (P.size() < Q.size() ? File1.getFilePath() : File2.getFilePath());
254     for (size_t I = Min; I < Max; ++I) {
255       HasDifferences = true;
256       StringRef StreamName = MaxNames[I];
257
258       outs().indent(2) << formatv(
259           "Stream {0} - {1}: <not present>, {2}: Index {3}, {4} bytes\n",
260           StreamName, MinFileName, MaxFile.getFilePath(), I,
261           MaxFile.getStreamByteSize(I));
262     }
263     if (!HasDifferences)
264       outs() << "Stream Directory: No differences detected...\n";
265   } else {
266     auto PI = to_vector<32>(enumerate(P));
267     auto QI = to_vector<32>(enumerate(Q));
268
269     typedef decltype(PI) ContainerType;
270     typedef typename ContainerType::value_type value_type;
271
272     auto Comparator = [](const value_type &I1, const value_type &I2) {
273       return I1.value() < I2.value();
274     };
275
276     decltype(PI) OnlyP;
277     decltype(QI) OnlyQ;
278     decltype(PI) Common;
279
280     set_differences(PI, QI, &OnlyP, &OnlyQ, &Common, Comparator);
281
282     if (!OnlyP.empty()) {
283       HasDifferences = true;
284       outs().indent(2) << formatv("{0} Stream(s) only in ({1})\n", OnlyP.size(),
285                                   File1.getFilePath());
286       for (auto &Item : OnlyP) {
287         outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(),
288                                     Item.value());
289       }
290     }
291
292     if (!OnlyQ.empty()) {
293       HasDifferences = true;
294       outs().indent(2) << formatv("{0} Streams(s) only in ({1})\n",
295                                   OnlyQ.size(), File2.getFilePath());
296       for (auto &Item : OnlyQ) {
297         outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(),
298                                     Item.value());
299       }
300     }
301     if (!Common.empty()) {
302       outs().indent(2) << formatv("Found {0} common streams.  Searching for "
303                                   "intra-stream differences.\n",
304                                   Common.size());
305       bool HasCommonDifferences = false;
306       for (const auto &Left : Common) {
307         // Left was copied from the first range so its index refers to a stream
308         // index in the first file.  Find the corresponding stream index in the
309         // second file.
310         auto Range =
311             std::equal_range(QI.begin(), QI.end(), Left,
312                              [](const value_type &L, const value_type &R) {
313                                return L.value() < R.value();
314                              });
315         const auto &Right = *Range.first;
316         assert(Left.value() == Right.value());
317         uint32_t LeftSize = File1.getStreamByteSize(Left.index());
318         uint32_t RightSize = File2.getStreamByteSize(Right.index());
319         if (LeftSize != RightSize) {
320           HasDifferences = true;
321           HasCommonDifferences = true;
322           outs().indent(4) << formatv("{0} ({1}: {2} bytes, {3}: {4} bytes)\n",
323                                       Left.value(), File1.getFilePath(),
324                                       LeftSize, File2.getFilePath(), RightSize);
325         }
326       }
327       if (!HasCommonDifferences)
328         outs().indent(2) << "Common Streams:  No differences detected!\n";
329     }
330     if (!HasDifferences)
331       outs() << "Stream Directory: No differences detected!\n";
332   }
333
334   return Error::success();
335 }
336
337 Error DiffStyle::diffStringTable() {
338   auto ExpectedST1 = File1.getStringTable();
339   auto ExpectedST2 = File2.getStringTable();
340   outs() << "String Table: Searching for differences...\n";
341   bool Has1 = !!ExpectedST1;
342   bool Has2 = !!ExpectedST2;
343   if (!(Has1 && Has2)) {
344     // If one has a string table and the other doesn't, we can print less
345     // output.
346     if (Has1 != Has2) {
347       if (Has1) {
348         outs() << formatv("  {0}: ({1} strings)\n", File1.getFilePath(),
349                           ExpectedST1->getNameCount());
350         outs() << formatv("  {0}: (string table not present)\n",
351                           File2.getFilePath());
352       } else {
353         outs() << formatv("  {0}: (string table not present)\n",
354                           File1.getFilePath());
355         outs() << formatv("  {0}: ({1})\n", File2.getFilePath(),
356                           ExpectedST2->getNameCount());
357       }
358     }
359     consumeError(ExpectedST1.takeError());
360     consumeError(ExpectedST2.takeError());
361     return Error::success();
362   }
363
364   bool HasDiff = false;
365   auto &ST1 = *ExpectedST1;
366   auto &ST2 = *ExpectedST2;
367
368   if (ST1.getByteSize() != ST2.getByteSize()) {
369     outs() << "  Stream Size\n";
370     outs() << formatv("    {0} - {1} byte(s)\n", File1.getFilePath(),
371                       ST1.getByteSize());
372     outs() << formatv("    {0} - {1} byte(s)\n", File2.getFilePath(),
373                       ST2.getByteSize());
374     outs() << formatv("    Difference: {0} bytes\n",
375                       AbsoluteDifference(ST1.getByteSize(), ST2.getByteSize()));
376     HasDiff = true;
377   }
378   HasDiff |= diffAndPrint("Hash Version", File1, File2, ST1.getHashVersion(),
379                           ST1.getHashVersion());
380   HasDiff |= diffAndPrint("Signature", File1, File2, ST1.getSignature(),
381                           ST1.getSignature());
382
383   // Both have a valid string table, dive in and compare individual strings.
384
385   auto IdList1 = ST1.name_ids();
386   auto IdList2 = ST2.name_ids();
387   if (opts::diff::Pedantic) {
388     // In pedantic mode, we compare index by index (i.e. the strings are in the
389     // same order
390     // in both tables.
391     uint32_t Max = std::max(IdList1.size(), IdList2.size());
392     for (uint32_t I = 0; I < Max; ++I) {
393       Optional<uint32_t> Id1, Id2;
394       StringRef S1, S2;
395       if (I < IdList1.size()) {
396         Id1 = IdList1[I];
397         if (auto Result = ST1.getStringForID(*Id1))
398           S1 = *Result;
399         else
400           return Result.takeError();
401       }
402       if (I < IdList2.size()) {
403         Id2 = IdList2[I];
404         if (auto Result = ST2.getStringForID(*Id2))
405           S2 = *Result;
406         else
407           return Result.takeError();
408       }
409       if (Id1 == Id2 && S1 == S2)
410         continue;
411
412       std::string OutId1 =
413           Id1 ? formatv("{0}", *Id1).str() : "(index not present)";
414       std::string OutId2 =
415           Id2 ? formatv("{0}", *Id2).str() : "(index not present)";
416       outs() << formatv("  String {0}\n", I);
417       outs() << formatv("    {0}: Hash - {1}, Value - {2}\n",
418                         File1.getFilePath(), OutId1, S1);
419       outs() << formatv("    {0}: Hash - {1}, Value - {2}\n",
420                         File2.getFilePath(), OutId2, S2);
421       HasDiff = true;
422     }
423   } else {
424     std::vector<StringRef> Strings1, Strings2;
425     Strings1.reserve(IdList1.size());
426     Strings2.reserve(IdList2.size());
427     for (auto ID : IdList1) {
428       auto S = ST1.getStringForID(ID);
429       if (!S)
430         return S.takeError();
431       Strings1.push_back(*S);
432     }
433     for (auto ID : IdList2) {
434       auto S = ST2.getStringForID(ID);
435       if (!S)
436         return S.takeError();
437       Strings2.push_back(*S);
438     }
439
440     SmallVector<StringRef, 64> OnlyP;
441     SmallVector<StringRef, 64> OnlyQ;
442     auto End1 = std::remove(Strings1.begin(), Strings1.end(), "");
443     auto End2 = std::remove(Strings2.begin(), Strings2.end(), "");
444     uint32_t Empty1 = std::distance(End1, Strings1.end());
445     uint32_t Empty2 = std::distance(End2, Strings2.end());
446     Strings1.erase(End1, Strings1.end());
447     Strings2.erase(End2, Strings2.end());
448     set_differences(Strings1, Strings2, &OnlyP, &OnlyQ);
449     printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "String");
450
451     if (Empty1 != Empty2) {
452       PDBFile &MoreF = (Empty1 > Empty2) ? File1 : File2;
453       PDBFile &LessF = (Empty1 < Empty2) ? File1 : File2;
454       uint32_t Difference = AbsoluteDifference(Empty1, Empty2);
455       outs() << formatv("  {0} had {1} more empty strings than {2}\n",
456                         MoreF.getFilePath(), Difference, LessF.getFilePath());
457     }
458   }
459   if (!HasDiff)
460     outs() << "String Table: No differences detected!\n";
461   return Error::success();
462 }
463
464 Error DiffStyle::diffFreePageMap() { return Error::success(); }
465
466 Error DiffStyle::diffInfoStream() {
467   auto ExpectedInfo1 = File1.getPDBInfoStream();
468   auto ExpectedInfo2 = File2.getPDBInfoStream();
469
470   outs() << "PDB Stream: Searching for differences...\n";
471   bool Has1 = !!ExpectedInfo1;
472   bool Has2 = !!ExpectedInfo2;
473   if (!(Has1 && Has2)) {
474     if (Has1 != Has2)
475       outs() << formatv("{0} does not have a PDB Stream!\n",
476                         Has1 ? File1.getFilePath() : File2.getFilePath());
477     consumeError(ExpectedInfo2.takeError());
478     consumeError(ExpectedInfo2.takeError());
479     return Error::success();
480   }
481
482   bool HasDiff = false;
483   auto &IS1 = *ExpectedInfo1;
484   auto &IS2 = *ExpectedInfo2;
485   if (IS1.getStreamSize() != IS2.getStreamSize()) {
486     outs() << "  Stream Size\n";
487     outs() << formatv("    {0} - {1} byte(s)\n", File1.getFilePath(),
488                       IS1.getStreamSize());
489     outs() << formatv("    {0} - {1} byte(s)\n", File2.getFilePath(),
490                       IS2.getStreamSize());
491     outs() << formatv(
492         "    Difference: {0} bytes\n",
493         AbsoluteDifference(IS1.getStreamSize(), IS2.getStreamSize()));
494     HasDiff = true;
495   }
496   HasDiff |= diffAndPrint("Age", File1, File2, IS1.getAge(), IS2.getAge());
497   HasDiff |= diffAndPrint("Guid", File1, File2, IS1.getGuid(), IS2.getGuid());
498   HasDiff |= diffAndPrint("Signature", File1, File2, IS1.getSignature(),
499                           IS2.getSignature());
500   HasDiff |=
501       diffAndPrint("Version", File1, File2, IS1.getVersion(), IS2.getVersion());
502   HasDiff |= diffAndPrint("Features", File1, File2, IS1.getFeatureSignatures(),
503                           IS2.getFeatureSignatures());
504   HasDiff |= diffAndPrint("Named Stream Byte Size", File1, File2,
505                           IS1.getNamedStreamMapByteSize(),
506                           IS2.getNamedStreamMapByteSize());
507   SmallVector<StringRef, 4> NS1;
508   SmallVector<StringRef, 4> NS2;
509   for (const auto &X : IS1.getNamedStreams().entries())
510     NS1.push_back(X.getKey());
511   for (const auto &X : IS2.getNamedStreams().entries())
512     NS2.push_back(X.getKey());
513   SmallVector<StringRef, 4> OnlyP;
514   SmallVector<StringRef, 4> OnlyQ;
515   set_differences(NS1, NS2, &OnlyP, &OnlyQ);
516   printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "Named Streams");
517   if (!HasDiff)
518     outs() << "PDB Stream: No differences detected!\n";
519
520   return Error::success();
521 }
522
523 Error DiffStyle::diffDbiStream() { return Error::success(); }
524
525 Error DiffStyle::diffSectionContribs() { return Error::success(); }
526
527 Error DiffStyle::diffSectionMap() { return Error::success(); }
528
529 Error DiffStyle::diffFpoStream() { return Error::success(); }
530
531 Error DiffStyle::diffTpiStream(int Index) { return Error::success(); }
532
533 Error DiffStyle::diffModuleInfoStream(int Index) { return Error::success(); }
534
535 Error DiffStyle::diffPublics() { return Error::success(); }
536
537 Error DiffStyle::diffGlobals() { return Error::success(); }