1 //===- Diff.cpp - PDB diff utility ------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
12 #include "StreamUtil.h"
13 #include "llvm-pdbutil.h"
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"
21 #include "llvm/Support/FormatAdapters.h"
22 #include "llvm/Support/FormatProviders.h"
23 #include "llvm/Support/FormatVariadic.h"
26 using namespace llvm::pdb;
29 template <> struct format_provider<PdbRaw_FeatureSig> {
30 static void format(const PdbRaw_FeatureSig &Sig, raw_ostream &Stream,
33 case PdbRaw_FeatureSig::MinimalDebugInfo:
34 Stream << "MinimalDebugInfo";
36 case PdbRaw_FeatureSig::NoTypeMerge:
37 Stream << "NoTypeMerge";
39 case PdbRaw_FeatureSig::VC110:
42 case PdbRaw_FeatureSig::VC140:
50 template <typename R> using ValueOfRange = llvm::detail::ValueOfRange<R>;
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,
59 std::sort(R1.begin(), R1.end(), Comparator);
60 std::sort(R2.begin(), R2.end(), Comparator);
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));
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));
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));
82 template <typename Range>
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);
93 DiffStyle::DiffStyle(PDBFile &File1, PDBFile &File2)
94 : File1(File1), File2(File2) {}
96 Error DiffStyle::dump() {
97 if (auto EC = diffSuperBlock())
100 if (auto EC = diffFreePageMap())
103 if (auto EC = diffStreamDirectory())
106 if (auto EC = diffStringTable())
109 if (auto EC = diffInfoStream())
112 if (auto EC = diffDbiStream())
115 if (auto EC = diffSectionContribs())
118 if (auto EC = diffSectionMap())
121 if (auto EC = diffFpoStream())
124 if (auto EC = diffTpiStream(StreamTPI))
127 if (auto EC = diffTpiStream(StreamIPI))
130 if (auto EC = diffPublics())
133 if (auto EC = diffGlobals())
136 return Error::success();
139 template <typename T>
140 static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2, T V1,
143 outs() << formatv(" {0}: No differences detected!\n", Label);
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);
153 template <typename T>
154 static bool diffAndPrint(StringRef Label, PDBFile &File1, PDBFile &File2,
155 ArrayRef<T> V1, ArrayRef<T> V2) {
157 outs() << formatv(" {0}: No differences detected!\n", Label);
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()));
169 template <typename T>
170 static bool printSymmetricDifferences(PDBFile &File1, PDBFile &File2,
171 T &&OnlyRange1, T &&OnlyRange2,
173 bool HasDiff = false;
174 if (!OnlyRange1.empty()) {
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);
181 if (!OnlyRange2.empty()) {
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);
191 Error DiffStyle::diffSuperBlock() {
192 outs() << "MSF Super Block: Searching for differences...\n";
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());
202 outs() << "MSF Super Block: No differences detected...\n";
203 return Error::success();
206 Error DiffStyle::diffStreamDirectory() {
207 SmallVector<std::string, 32> P;
208 SmallVector<std::string, 32> Q;
209 discoverStreamPurposes(File1, P);
210 discoverStreamPurposes(File2, Q);
211 outs() << "Stream Directory: Searching for differences...\n";
213 bool HasDifferences = false;
214 auto PI = to_vector<32>(enumerate(P));
215 auto QI = to_vector<32>(enumerate(Q));
217 typedef decltype(PI) ContainerType;
218 typedef typename ContainerType::value_type value_type;
220 auto Comparator = [](const value_type &I1, const value_type &I2) {
221 return I1.value() < I2.value();
228 set_differences(PI, QI, &OnlyP, &OnlyQ, &Common, Comparator);
230 if (!OnlyP.empty()) {
231 HasDifferences = true;
232 outs().indent(2) << formatv("{0} Stream(s) only in ({1})\n", OnlyP.size(),
233 File1.getFilePath());
234 for (auto &Item : OnlyP) {
235 outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(),
240 if (!OnlyQ.empty()) {
241 HasDifferences = true;
242 outs().indent(2) << formatv("{0} Streams(s) only in ({1})\n", OnlyQ.size(),
243 File2.getFilePath());
244 for (auto &Item : OnlyQ) {
245 outs().indent(4) << formatv("Stream {0} - {1}\n", Item.index(),
249 if (!Common.empty()) {
250 outs().indent(2) << formatv("Found {0} common streams. Searching for "
251 "intra-stream differences.\n",
253 bool HasCommonDifferences = false;
254 for (const auto &Left : Common) {
255 // Left was copied from the first range so its index refers to a stream
256 // index in the first file. Find the corresponding stream index in the
259 std::equal_range(QI.begin(), QI.end(), Left,
260 [](const value_type &L, const value_type &R) {
261 return L.value() < R.value();
263 const auto &Right = *Range.first;
264 assert(Left.value() == Right.value());
265 uint32_t LeftSize = File1.getStreamByteSize(Left.index());
266 uint32_t RightSize = File2.getStreamByteSize(Right.index());
267 if (LeftSize != RightSize) {
268 HasDifferences = true;
269 HasCommonDifferences = true;
270 outs().indent(4) << formatv("{0} ({1}: {2} bytes, {3}: {4} bytes)\n",
271 Left.value(), File1.getFilePath(), LeftSize,
272 File2.getFilePath(), RightSize);
275 if (!HasCommonDifferences)
276 outs().indent(2) << "Common Streams: No differences detected!\n";
279 outs() << "Stream Directory: No differences detected!\n";
281 return Error::success();
284 Error DiffStyle::diffStringTable() {
285 auto ExpectedST1 = File1.getStringTable();
286 auto ExpectedST2 = File2.getStringTable();
287 outs() << "String Table: Searching for differences...\n";
288 bool Has1 = !!ExpectedST1;
289 bool Has2 = !!ExpectedST2;
290 if (!(Has1 && Has2)) {
291 // If one has a string table and the other doesn't, we can print less
295 outs() << formatv(" {0}: ({1} strings)\n", File1.getFilePath(),
296 ExpectedST1->getNameCount());
297 outs() << formatv(" {0}: (string table not present)\n",
298 File2.getFilePath());
300 outs() << formatv(" {0}: (string table not present)\n",
301 File1.getFilePath());
302 outs() << formatv(" {0}: ({1})\n", File2.getFilePath(),
303 ExpectedST2->getNameCount());
306 consumeError(ExpectedST1.takeError());
307 consumeError(ExpectedST2.takeError());
308 return Error::success();
311 bool HasDiff = false;
312 auto &ST1 = *ExpectedST1;
313 auto &ST2 = *ExpectedST2;
315 if (ST1.getByteSize() != ST2.getByteSize()) {
316 outs() << " Stream Size\n";
317 outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(),
319 outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(),
321 outs() << formatv(" Difference: {0} bytes\n",
322 AbsoluteDifference(ST1.getByteSize(), ST2.getByteSize()));
325 HasDiff |= diffAndPrint("Hash Version", File1, File2, ST1.getHashVersion(),
326 ST1.getHashVersion());
327 HasDiff |= diffAndPrint("Signature", File1, File2, ST1.getSignature(),
330 // Both have a valid string table, dive in and compare individual strings.
332 auto IdList1 = ST1.name_ids();
333 auto IdList2 = ST2.name_ids();
334 std::vector<StringRef> Strings1, Strings2;
335 Strings1.reserve(IdList1.size());
336 Strings2.reserve(IdList2.size());
337 for (auto ID : IdList1) {
338 auto S = ST1.getStringForID(ID);
340 return S.takeError();
341 Strings1.push_back(*S);
343 for (auto ID : IdList2) {
344 auto S = ST2.getStringForID(ID);
346 return S.takeError();
347 Strings2.push_back(*S);
350 SmallVector<StringRef, 64> OnlyP;
351 SmallVector<StringRef, 64> OnlyQ;
352 auto End1 = std::remove(Strings1.begin(), Strings1.end(), "");
353 auto End2 = std::remove(Strings2.begin(), Strings2.end(), "");
354 uint32_t Empty1 = std::distance(End1, Strings1.end());
355 uint32_t Empty2 = std::distance(End2, Strings2.end());
356 Strings1.erase(End1, Strings1.end());
357 Strings2.erase(End2, Strings2.end());
358 set_differences(Strings1, Strings2, &OnlyP, &OnlyQ);
359 printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "String");
361 if (Empty1 != Empty2) {
362 PDBFile &MoreF = (Empty1 > Empty2) ? File1 : File2;
363 PDBFile &LessF = (Empty1 < Empty2) ? File1 : File2;
364 uint32_t Difference = AbsoluteDifference(Empty1, Empty2);
365 outs() << formatv(" {0} had {1} more empty strings than {2}\n",
366 MoreF.getFilePath(), Difference, LessF.getFilePath());
369 outs() << "String Table: No differences detected!\n";
370 return Error::success();
373 Error DiffStyle::diffFreePageMap() { return Error::success(); }
375 Error DiffStyle::diffInfoStream() {
376 auto ExpectedInfo1 = File1.getPDBInfoStream();
377 auto ExpectedInfo2 = File2.getPDBInfoStream();
379 outs() << "PDB Stream: Searching for differences...\n";
380 bool Has1 = !!ExpectedInfo1;
381 bool Has2 = !!ExpectedInfo2;
382 if (!(Has1 && Has2)) {
384 outs() << formatv("{0} does not have a PDB Stream!\n",
385 Has1 ? File1.getFilePath() : File2.getFilePath());
386 consumeError(ExpectedInfo2.takeError());
387 consumeError(ExpectedInfo2.takeError());
388 return Error::success();
391 bool HasDiff = false;
392 auto &IS1 = *ExpectedInfo1;
393 auto &IS2 = *ExpectedInfo2;
394 if (IS1.getStreamSize() != IS2.getStreamSize()) {
395 outs() << " Stream Size\n";
396 outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(),
397 IS1.getStreamSize());
398 outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(),
399 IS2.getStreamSize());
401 " Difference: {0} bytes\n",
402 AbsoluteDifference(IS1.getStreamSize(), IS2.getStreamSize()));
405 HasDiff |= diffAndPrint("Age", File1, File2, IS1.getAge(), IS2.getAge());
406 HasDiff |= diffAndPrint("Guid", File1, File2, IS1.getGuid(), IS2.getGuid());
407 HasDiff |= diffAndPrint("Signature", File1, File2, IS1.getSignature(),
410 diffAndPrint("Version", File1, File2, IS1.getVersion(), IS2.getVersion());
411 HasDiff |= diffAndPrint("Features", File1, File2, IS1.getFeatureSignatures(),
412 IS2.getFeatureSignatures());
413 HasDiff |= diffAndPrint("Named Stream Byte Size", File1, File2,
414 IS1.getNamedStreamMapByteSize(),
415 IS2.getNamedStreamMapByteSize());
416 SmallVector<StringRef, 4> NS1;
417 SmallVector<StringRef, 4> NS2;
418 for (const auto &X : IS1.getNamedStreams().entries())
419 NS1.push_back(X.getKey());
420 for (const auto &X : IS2.getNamedStreams().entries())
421 NS2.push_back(X.getKey());
422 SmallVector<StringRef, 4> OnlyP;
423 SmallVector<StringRef, 4> OnlyQ;
424 set_differences(NS1, NS2, &OnlyP, &OnlyQ);
425 printSymmetricDifferences(File1, File2, OnlyP, OnlyQ, "Named Streams");
427 outs() << "PDB Stream: No differences detected!\n";
429 return Error::success();
432 Error DiffStyle::diffDbiStream() { return Error::success(); }
434 Error DiffStyle::diffSectionContribs() { return Error::success(); }
436 Error DiffStyle::diffSectionMap() { return Error::success(); }
438 Error DiffStyle::diffFpoStream() { return Error::success(); }
440 Error DiffStyle::diffTpiStream(int Index) { return Error::success(); }
442 Error DiffStyle::diffModuleInfoStream(int Index) { return Error::success(); }
444 Error DiffStyle::diffPublics() { return Error::success(); }
446 Error DiffStyle::diffGlobals() { return Error::success(); }