1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
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) { |
| 3 | | Assuming 'OnlyLeft' is null | |
|
| |
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) { |
| 5 | | Assuming 'OnlyRight' is non-null | |
|
| |
69 | OnlyLeft->reserve(R2.size()); |
| 7 | | Called C++ object pointer is null |
|
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, |
| 1 | Passing value via 3rd parameter 'OnlyLeft' | |
|
| 2 | | Calling 'set_differences' | |
|
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 | if (!Diffs) |
202 | outs() << "MSF Super Block: No differences detected...\n"; |
203 | return Error::success(); |
204 | } |
205 | |
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"; |
212 | |
213 | bool HasDifferences = false; |
214 | auto PI = to_vector<32>(enumerate(P)); |
215 | auto QI = to_vector<32>(enumerate(Q)); |
216 | |
217 | typedef decltype(PI) ContainerType; |
218 | typedef typename ContainerType::value_type value_type; |
219 | |
220 | auto Comparator = [](const value_type &I1, const value_type &I2) { |
221 | return I1.value() < I2.value(); |
222 | }; |
223 | |
224 | decltype(PI) OnlyP; |
225 | decltype(QI) OnlyQ; |
226 | decltype(PI) Common; |
227 | |
228 | set_differences(PI, QI, &OnlyP, &OnlyQ, &Common, Comparator); |
229 | |
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(), |
236 | Item.value()); |
237 | } |
238 | } |
239 | |
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(), |
246 | Item.value()); |
247 | } |
248 | } |
249 | if (!Common.empty()) { |
250 | outs().indent(2) << formatv("Found {0} common streams. Searching for " |
251 | "intra-stream differences.\n", |
252 | Common.size()); |
253 | bool HasCommonDifferences = false; |
254 | for (const auto &Left : Common) { |
255 | |
256 | |
257 | |
258 | auto Range = |
259 | std::equal_range(QI.begin(), QI.end(), Left, |
260 | [](const value_type &L, const value_type &R) { |
261 | return L.value() < R.value(); |
262 | }); |
263 | const auto &Right = *Range.first; |
264 | assert(Left.value() == Right.value())((Left.value() == Right.value()) ? static_cast<void> (0 ) : __assert_fail ("Left.value() == Right.value()", "/tmp/buildd/llvm-toolchain-snapshot-5.0~svn306458/tools/llvm-pdbutil/Diff.cpp" , 264, __PRETTY_FUNCTION__)); |
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); |
273 | } |
274 | } |
275 | if (!HasCommonDifferences) |
276 | outs().indent(2) << "Common Streams: No differences detected!\n"; |
277 | } |
278 | if (!HasDifferences) |
279 | outs() << "Stream Directory: No differences detected!\n"; |
280 | |
281 | return Error::success(); |
282 | } |
283 | |
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 | |
292 | |
293 | if (Has1 != Has2) { |
294 | if (Has1) { |
295 | outs() << formatv(" {0}: ({1} strings)\n", File1.getFilePath(), |
296 | ExpectedST1->getNameCount()); |
297 | outs() << formatv(" {0}: (string table not present)\n", |
298 | File2.getFilePath()); |
299 | } else { |
300 | outs() << formatv(" {0}: (string table not present)\n", |
301 | File1.getFilePath()); |
302 | outs() << formatv(" {0}: ({1})\n", File2.getFilePath(), |
303 | ExpectedST2->getNameCount()); |
304 | } |
305 | } |
306 | consumeError(ExpectedST1.takeError()); |
307 | consumeError(ExpectedST2.takeError()); |
308 | return Error::success(); |
309 | } |
310 | |
311 | bool HasDiff = false; |
312 | auto &ST1 = *ExpectedST1; |
313 | auto &ST2 = *ExpectedST2; |
314 | |
315 | if (ST1.getByteSize() != ST2.getByteSize()) { |
316 | outs() << " Stream Size\n"; |
317 | outs() << formatv(" {0} - {1} byte(s)\n", File1.getFilePath(), |
318 | ST1.getByteSize()); |
319 | outs() << formatv(" {0} - {1} byte(s)\n", File2.getFilePath(), |
320 | ST2.getByteSize()); |
321 | outs() << formatv(" Difference: {0} bytes\n", |
322 | AbsoluteDifference(ST1.getByteSize(), ST2.getByteSize())); |
323 | HasDiff = true; |
324 | } |
325 | HasDiff |= diffAndPrint("Hash Version", File1, File2, ST1.getHashVersion(), |
326 | ST1.getHashVersion()); |
327 | HasDiff |= diffAndPrint("Signature", File1, File2, ST1.getSignature(), |
328 | ST1.getSignature()); |
329 | |
330 | |
331 | |
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); |
339 | if (!S) |
340 | return S.takeError(); |
341 | Strings1.push_back(*S); |
342 | } |
343 | for (auto ID : IdList2) { |
344 | auto S = ST2.getStringForID(ID); |
345 | if (!S) |
346 | return S.takeError(); |
347 | Strings2.push_back(*S); |
348 | } |
349 | |
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"); |
360 | |
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()); |
367 | } |
368 | if (!HasDiff) |
369 | outs() << "String Table: No differences detected!\n"; |
370 | return Error::success(); |
371 | } |
372 | |
373 | Error DiffStyle::diffFreePageMap() { return Error::success(); } |
374 | |
375 | Error DiffStyle::diffInfoStream() { |
376 | auto ExpectedInfo1 = File1.getPDBInfoStream(); |
377 | auto ExpectedInfo2 = File2.getPDBInfoStream(); |
378 | |
379 | outs() << "PDB Stream: Searching for differences...\n"; |
380 | bool Has1 = !!ExpectedInfo1; |
381 | bool Has2 = !!ExpectedInfo2; |
382 | if (!(Has1 && Has2)) { |
383 | 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(); |
389 | } |
390 | |
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()); |
400 | outs() << formatv( |
401 | " Difference: {0} bytes\n", |
402 | AbsoluteDifference(IS1.getStreamSize(), IS2.getStreamSize())); |
403 | HasDiff = true; |
404 | } |
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(), |
408 | IS2.getSignature()); |
409 | HasDiff |= |
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"); |
426 | if (!HasDiff) |
427 | outs() << "PDB Stream: No differences detected!\n"; |
428 | |
429 | return Error::success(); |
430 | } |
431 | |
432 | Error DiffStyle::diffDbiStream() { return Error::success(); } |
433 | |
434 | Error DiffStyle::diffSectionContribs() { return Error::success(); } |
435 | |
436 | Error DiffStyle::diffSectionMap() { return Error::success(); } |
437 | |
438 | Error DiffStyle::diffFpoStream() { return Error::success(); } |
439 | |
440 | Error DiffStyle::diffTpiStream(int Index) { return Error::success(); } |
441 | |
442 | Error DiffStyle::diffModuleInfoStream(int Index) { return Error::success(); } |
443 | |
444 | Error DiffStyle::diffPublics() { return Error::success(); } |
445 | |
446 | Error DiffStyle::diffGlobals() { return Error::success(); } |