]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/llvm-pdbutil/Diff.cpp
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r308421, 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 "DiffPrinter.h"
13 #include "FormatUtil.h"
14 #include "StreamUtil.h"
15 #include "llvm-pdbutil.h"
16
17 #include "llvm/ADT/StringSet.h"
18
19 #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
20 #include "llvm/DebugInfo/PDB/Native/Formatters.h"
21 #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
22 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
23 #include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
24 #include "llvm/DebugInfo/PDB/Native/RawConstants.h"
25
26 #include "llvm/Support/FileSystem.h"
27 #include "llvm/Support/FormatAdapters.h"
28 #include "llvm/Support/FormatProviders.h"
29 #include "llvm/Support/FormatVariadic.h"
30 #include "llvm/Support/Path.h"
31
32 using namespace llvm;
33 using namespace llvm::pdb;
34
35 namespace {
36 // Compare and format two stream numbers.  Stream numbers are considered
37 // identical if they contain the same value, equivalent if they are both
38 // the invalid stream or neither is the invalid stream, and different if
39 // one is the invalid stream and another isn't.
40 struct StreamNumberProvider {
41   static DiffResult compare(uint16_t L, uint16_t R) {
42     if (L == R)
43       return DiffResult::IDENTICAL;
44     bool LP = L != kInvalidStreamIndex;
45     bool RP = R != kInvalidStreamIndex;
46     if (LP != RP)
47       return DiffResult::DIFFERENT;
48     return DiffResult::EQUIVALENT;
49   }
50
51   static std::string format(uint16_t SN, bool Right) {
52     if (SN == kInvalidStreamIndex)
53       return "(not present)";
54     return formatv("{0}", SN).str();
55   }
56 };
57
58 // Compares and formats two module indices.  Modis are considered identical
59 // if they are identical, equivalent if they either both contain a value or
60 // both don't contain a value, and different if one contains a value and the
61 // other doesn't.
62 struct ModiProvider {
63   DiffResult compare(Optional<uint32_t> L, Optional<uint32_t> R) {
64     if (L == R)
65       return DiffResult::IDENTICAL;
66     if (L.hasValue() != R.hasValue())
67       return DiffResult::DIFFERENT;
68     return DiffResult::EQUIVALENT;
69   }
70
71   std::string format(Optional<uint32_t> Modi, bool Right) {
72     if (!Modi.hasValue())
73       return "(not present)";
74     return formatv("{0}", *Modi).str();
75   }
76 };
77
78 // Compares and formats two paths embedded in the PDB, ignoring the beginning
79 // of the path if the user specified it as a "root path" on the command line.
80 struct BinaryPathProvider {
81   explicit BinaryPathProvider(uint32_t MaxLen) : MaxLen(MaxLen) {}
82
83   DiffResult compare(StringRef L, StringRef R) {
84     if (L == R)
85       return DiffResult::IDENTICAL;
86
87     SmallString<64> LN = removeRoot(L, false);
88     SmallString<64> RN = removeRoot(R, true);
89
90     return (LN.equals_lower(RN)) ? DiffResult::EQUIVALENT
91                                  : DiffResult::DIFFERENT;
92   }
93
94   std::string format(StringRef S, bool Right) {
95     if (S.empty())
96       return "(empty)";
97
98     SmallString<64> Native = removeRoot(S, Right);
99     return truncateStringFront(Native.str(), MaxLen);
100   }
101
102   SmallString<64> removeRoot(StringRef Path, bool IsRight) const {
103     SmallString<64> Native(Path);
104     auto &RootOpt = IsRight ? opts::diff::RightRoot : opts::diff::LeftRoot;
105     SmallString<64> Root(static_cast<std::string>(RootOpt));
106     // pdb paths always use windows syntax, convert slashes to backslashes.
107     sys::path::native(Root, sys::path::Style::windows);
108     if (sys::path::has_stem(Root, sys::path::Style::windows))
109       sys::path::append(Root, sys::path::Style::windows,
110                         sys::path::get_separator(sys::path::Style::windows));
111
112     sys::path::replace_path_prefix(Native, Root, "", sys::path::Style::windows);
113     return Native;
114   }
115   uint32_t MaxLen;
116 };
117
118 // Compare and format two stream purposes.  For general streams, this just
119 // compares the description.  For module streams it uses the path comparison
120 // algorithm taking into consideration the binary root, described above.
121 // Formatting stream purposes just prints the stream purpose, except for
122 // module streams and named streams, where it prefixes the name / module
123 // with an identifier.  Example:
124 //
125 //   Named Stream "\names"
126 //   Module Stream "foo.obj"
127 //
128 // If a named stream is too long to fit in a column, it is truncated at the
129 // end, and if a module is too long to fit in a column, it is truncated at the
130 // beginning.  Example:
131 //
132 //  Named Stream "\Really Long Str..."
133 //  Module Stream "...puts\foo.obj"
134 //
135 struct StreamPurposeProvider {
136   explicit StreamPurposeProvider(uint32_t MaxLen) : MaxLen(MaxLen) {}
137
138   DiffResult compare(const std::pair<StreamPurpose, std::string> &L,
139                      const std::pair<StreamPurpose, std::string> &R) {
140     if (L.first != R.first)
141       return DiffResult::DIFFERENT;
142     if (L.first == StreamPurpose::ModuleStream) {
143       BinaryPathProvider PathProvider(MaxLen);
144       return PathProvider.compare(L.second, R.second);
145     }
146     return (L.second == R.second) ? DiffResult::IDENTICAL
147                                   : DiffResult::DIFFERENT;
148   }
149
150   std::string format(const std::pair<StreamPurpose, std::string> &P,
151                      bool Right) {
152     if (P.first == StreamPurpose::Other)
153       return truncateStringBack(P.second, MaxLen);
154     if (P.first == StreamPurpose::NamedStream)
155       return truncateQuotedNameBack("Named Stream", P.second, MaxLen);
156
157     assert(P.first == StreamPurpose::ModuleStream);
158     uint32_t ExtraChars = strlen("Module \"\"");
159     BinaryPathProvider PathProvider(MaxLen - ExtraChars);
160     std::string Result = PathProvider.format(P.second, Right);
161     return formatv("Module \"{0}\"", Result);
162   }
163
164   uint32_t MaxLen;
165 };
166 } // namespace
167
168 namespace llvm {
169 template <> struct format_provider<PdbRaw_FeatureSig> {
170   static void format(const PdbRaw_FeatureSig &Sig, raw_ostream &Stream,
171                      StringRef Style) {
172     switch (Sig) {
173     case PdbRaw_FeatureSig::MinimalDebugInfo:
174       Stream << "MinimalDebugInfo";
175       break;
176     case PdbRaw_FeatureSig::NoTypeMerge:
177       Stream << "NoTypeMerge";
178       break;
179     case PdbRaw_FeatureSig::VC110:
180       Stream << "VC110";
181       break;
182     case PdbRaw_FeatureSig::VC140:
183       Stream << "VC140";
184       break;
185     }
186   }
187 };
188 }
189
190 template <typename R> using ValueOfRange = llvm::detail::ValueOfRange<R>;
191
192 DiffStyle::DiffStyle(PDBFile &File1, PDBFile &File2)
193     : File1(File1), File2(File2) {}
194
195 Error DiffStyle::dump() {
196   if (auto EC = diffSuperBlock())
197     return EC;
198
199   if (auto EC = diffFreePageMap())
200     return EC;
201
202   if (auto EC = diffStreamDirectory())
203     return EC;
204
205   if (auto EC = diffStringTable())
206     return EC;
207
208   if (auto EC = diffInfoStream())
209     return EC;
210
211   if (auto EC = diffDbiStream())
212     return EC;
213
214   if (auto EC = diffSectionContribs())
215     return EC;
216
217   if (auto EC = diffSectionMap())
218     return EC;
219
220   if (auto EC = diffFpoStream())
221     return EC;
222
223   if (auto EC = diffTpiStream(StreamTPI))
224     return EC;
225
226   if (auto EC = diffTpiStream(StreamIPI))
227     return EC;
228
229   if (auto EC = diffPublics())
230     return EC;
231
232   if (auto EC = diffGlobals())
233     return EC;
234
235   return Error::success();
236 }
237
238 Error DiffStyle::diffSuperBlock() {
239   DiffPrinter D(2, "MSF Super Block", 16, 20, opts::diff::PrintResultColumn,
240                 opts::diff::PrintValueColumns, outs());
241   D.printExplicit("File", DiffResult::UNSPECIFIED,
242                   truncateStringFront(File1.getFilePath(), 18),
243                   truncateStringFront(File2.getFilePath(), 18));
244   D.print("Block Size", File1.getBlockSize(), File2.getBlockSize());
245   D.print("Block Count", File1.getBlockCount(), File2.getBlockCount());
246   D.print("Unknown 1", File1.getUnknown1(), File2.getUnknown1());
247   D.print("Directory Size", File1.getNumDirectoryBytes(),
248           File2.getNumDirectoryBytes());
249   return Error::success();
250 }
251
252 Error DiffStyle::diffStreamDirectory() {
253   DiffPrinter D(2, "Stream Directory", 30, 20, opts::diff::PrintResultColumn,
254                 opts::diff::PrintValueColumns, outs());
255   D.printExplicit("File", DiffResult::UNSPECIFIED,
256                   truncateStringFront(File1.getFilePath(), 18),
257                   truncateStringFront(File2.getFilePath(), 18));
258
259   SmallVector<std::pair<StreamPurpose, std::string>, 32> P;
260   SmallVector<std::pair<StreamPurpose, std::string>, 32> Q;
261   discoverStreamPurposes(File1, P);
262   discoverStreamPurposes(File2, Q);
263   D.print("Stream Count", File1.getNumStreams(), File2.getNumStreams());
264   auto PI = to_vector<32>(enumerate(P));
265   auto QI = to_vector<32>(enumerate(Q));
266
267   // Scan all streams in the left hand side, looking for ones that are also
268   // in the right.  Each time we find one, remove it.  When we're done, Q
269   // should contain all the streams that are in the right but not in the left.
270   StreamPurposeProvider StreamProvider(28);
271   for (const auto &P : PI) {
272     typedef decltype(PI) ContainerType;
273     typedef typename ContainerType::value_type value_type;
274
275     auto Iter = llvm::find_if(QI, [P, &StreamProvider](const value_type &V) {
276       DiffResult Result = StreamProvider.compare(P.value(), V.value());
277       return Result == DiffResult::EQUIVALENT ||
278              Result == DiffResult::IDENTICAL;
279     });
280
281     if (Iter == QI.end()) {
282       D.printExplicit(StreamProvider.format(P.value(), false),
283                       DiffResult::DIFFERENT, P.index(), "(not present)");
284       continue;
285     }
286
287     D.print<EquivalentDiffProvider>(StreamProvider.format(P.value(), false),
288                                     P.index(), Iter->index());
289     QI.erase(Iter);
290   }
291
292   for (const auto &Q : QI) {
293     D.printExplicit(StreamProvider.format(Q.value(), true),
294                     DiffResult::DIFFERENT, "(not present)", Q.index());
295   }
296
297   return Error::success();
298 }
299
300 Error DiffStyle::diffStringTable() {
301   DiffPrinter D(2, "String Table", 30, 20, opts::diff::PrintResultColumn,
302                 opts::diff::PrintValueColumns, outs());
303   D.printExplicit("File", DiffResult::UNSPECIFIED,
304                   truncateStringFront(File1.getFilePath(), 18),
305                   truncateStringFront(File2.getFilePath(), 18));
306
307   auto ExpectedST1 = File1.getStringTable();
308   auto ExpectedST2 = File2.getStringTable();
309   bool Has1 = !!ExpectedST1;
310   bool Has2 = !!ExpectedST2;
311   std::string Count1 = Has1 ? llvm::utostr(ExpectedST1->getNameCount())
312                             : "(string table not present)";
313   std::string Count2 = Has2 ? llvm::utostr(ExpectedST2->getNameCount())
314                             : "(string table not present)";
315   D.print("Number of Strings", Count1, Count2);
316
317   if (!Has1 || !Has2) {
318     consumeError(ExpectedST1.takeError());
319     consumeError(ExpectedST2.takeError());
320     return Error::success();
321   }
322
323   auto &ST1 = *ExpectedST1;
324   auto &ST2 = *ExpectedST2;
325
326   D.print("Hash Version", ST1.getHashVersion(), ST2.getHashVersion());
327   D.print("Byte Size", ST1.getByteSize(), ST2.getByteSize());
328   D.print("Signature", ST1.getSignature(), ST2.getSignature());
329
330   // Both have a valid string table, dive in and compare individual strings.
331
332   auto IdList1 = ST1.name_ids();
333   auto IdList2 = ST2.name_ids();
334   StringSet<> LS;
335   StringSet<> RS;
336   uint32_t Empty1 = 0;
337   uint32_t Empty2 = 0;
338   for (auto ID : IdList1) {
339     auto S = ST1.getStringForID(ID);
340     if (!S)
341       return S.takeError();
342     if (S->empty())
343       ++Empty1;
344     else
345       LS.insert(*S);
346   }
347   for (auto ID : IdList2) {
348     auto S = ST2.getStringForID(ID);
349     if (!S)
350       return S.takeError();
351     if (S->empty())
352       ++Empty2;
353     else
354       RS.insert(*S);
355   }
356   D.print("Empty Strings", Empty1, Empty2);
357
358   for (const auto &S : LS) {
359     auto R = RS.find(S.getKey());
360     std::string Truncated = truncateStringMiddle(S.getKey(), 28);
361     uint32_t I = cantFail(ST1.getIDForString(S.getKey()));
362     if (R == RS.end()) {
363       D.printExplicit(Truncated, DiffResult::DIFFERENT, I, "(not present)");
364       continue;
365     }
366
367     uint32_t J = cantFail(ST2.getIDForString(R->getKey()));
368     D.print<EquivalentDiffProvider>(Truncated, I, J);
369     RS.erase(R);
370   }
371
372   for (const auto &S : RS) {
373     auto L = LS.find(S.getKey());
374     std::string Truncated = truncateStringMiddle(S.getKey(), 28);
375     uint32_t J = cantFail(ST2.getIDForString(S.getKey()));
376     if (L == LS.end()) {
377       D.printExplicit(Truncated, DiffResult::DIFFERENT, "(not present)", J);
378       continue;
379     }
380
381     uint32_t I = cantFail(ST1.getIDForString(L->getKey()));
382     D.print<EquivalentDiffProvider>(Truncated, I, J);
383   }
384   return Error::success();
385 }
386
387 Error DiffStyle::diffFreePageMap() { return Error::success(); }
388
389 Error DiffStyle::diffInfoStream() {
390   DiffPrinter D(2, "PDB Stream", 22, 40, opts::diff::PrintResultColumn,
391                 opts::diff::PrintValueColumns, outs());
392   D.printExplicit("File", DiffResult::UNSPECIFIED,
393                   truncateStringFront(File1.getFilePath(), 38),
394                   truncateStringFront(File2.getFilePath(), 38));
395
396   auto ExpectedInfo1 = File1.getPDBInfoStream();
397   auto ExpectedInfo2 = File2.getPDBInfoStream();
398
399   bool Has1 = !!ExpectedInfo1;
400   bool Has2 = !!ExpectedInfo2;
401   if (!(Has1 && Has2)) {
402     std::string L = Has1 ? "(present)" : "(not present)";
403     std::string R = Has2 ? "(present)" : "(not present)";
404     D.print("Stream", L, R);
405
406     consumeError(ExpectedInfo1.takeError());
407     consumeError(ExpectedInfo2.takeError());
408     return Error::success();
409   }
410
411   auto &IS1 = *ExpectedInfo1;
412   auto &IS2 = *ExpectedInfo2;
413   D.print("Stream Size", IS1.getStreamSize(), IS2.getStreamSize());
414   D.print("Age", IS1.getAge(), IS2.getAge());
415   D.print("Guid", IS1.getGuid(), IS2.getGuid());
416   D.print("Signature", IS1.getSignature(), IS2.getSignature());
417   D.print("Version", IS1.getVersion(), IS2.getVersion());
418   D.diffUnorderedArray("Feature", IS1.getFeatureSignatures(),
419                        IS2.getFeatureSignatures());
420   D.print("Named Stream Size", IS1.getNamedStreamMapByteSize(),
421           IS2.getNamedStreamMapByteSize());
422   StringMap<uint32_t> NSL = IS1.getNamedStreams().getStringMap();
423   StringMap<uint32_t> NSR = IS2.getNamedStreams().getStringMap();
424   D.diffUnorderedMap<EquivalentDiffProvider>("Named Stream", NSL, NSR);
425   return Error::success();
426 }
427
428 static std::vector<std::pair<uint32_t, DbiModuleDescriptor>>
429 getModuleDescriptors(const DbiModuleList &ML) {
430   std::vector<std::pair<uint32_t, DbiModuleDescriptor>> List;
431   List.reserve(ML.getModuleCount());
432   for (uint32_t I = 0; I < ML.getModuleCount(); ++I)
433     List.emplace_back(I, ML.getModuleDescriptor(I));
434   return List;
435 }
436
437 static void
438 diffOneModule(DiffPrinter &D,
439               const std::pair<uint32_t, DbiModuleDescriptor> Item,
440               std::vector<std::pair<uint32_t, DbiModuleDescriptor>> &Other,
441               bool ItemIsRight) {
442   StreamPurposeProvider HeaderProvider(70);
443   std::pair<StreamPurpose, std::string> Header;
444   Header.first = StreamPurpose::ModuleStream;
445   Header.second = Item.second.getModuleName();
446   D.printFullRow(HeaderProvider.format(Header, ItemIsRight));
447
448   const auto *L = &Item;
449
450   BinaryPathProvider PathProvider(28);
451   auto Iter = llvm::find_if(
452       Other, [&PathProvider, ItemIsRight,
453               L](const std::pair<uint32_t, DbiModuleDescriptor> &Other) {
454         const auto *Left = L;
455         const auto *Right = &Other;
456         if (ItemIsRight)
457           std::swap(Left, Right);
458         DiffResult Result = PathProvider.compare(Left->second.getModuleName(),
459                                                  Right->second.getModuleName());
460         return Result == DiffResult::EQUIVALENT ||
461                Result == DiffResult::IDENTICAL;
462       });
463   if (Iter == Other.end()) {
464     // We didn't find this module at all on the other side.  Just print one row
465     // and continue.
466     D.print<ModiProvider>("- Modi", Item.first, None);
467     return;
468   }
469
470   // We did find this module.  Go through and compare each field.
471   const auto *R = &*Iter;
472   if (ItemIsRight)
473     std::swap(L, R);
474
475   D.print<ModiProvider>("- Modi", L->first, R->first);
476   D.print<BinaryPathProvider>("- Obj File Name", L->second.getObjFileName(),
477                               R->second.getObjFileName(), PathProvider);
478   D.print<StreamNumberProvider>("- Debug Stream",
479                                 L->second.getModuleStreamIndex(),
480                                 R->second.getModuleStreamIndex());
481   D.print("- C11 Byte Size", L->second.getC11LineInfoByteSize(),
482           R->second.getC11LineInfoByteSize());
483   D.print("- C13 Byte Size", L->second.getC13LineInfoByteSize(),
484           R->second.getC13LineInfoByteSize());
485   D.print("- # of files", L->second.getNumberOfFiles(),
486           R->second.getNumberOfFiles());
487   D.print("- Pdb File Path Index", L->second.getPdbFilePathNameIndex(),
488           R->second.getPdbFilePathNameIndex());
489   D.print("- Source File Name Index", L->second.getSourceFileNameIndex(),
490           R->second.getSourceFileNameIndex());
491   D.print("- Symbol Byte Size", L->second.getSymbolDebugInfoByteSize(),
492           R->second.getSymbolDebugInfoByteSize());
493   Other.erase(Iter);
494 }
495
496 Error DiffStyle::diffDbiStream() {
497   DiffPrinter D(2, "DBI Stream", 40, 30, opts::diff::PrintResultColumn,
498                 opts::diff::PrintValueColumns, outs());
499   D.printExplicit("File", DiffResult::UNSPECIFIED,
500                   truncateStringFront(File1.getFilePath(), 28),
501                   truncateStringFront(File2.getFilePath(), 28));
502
503   auto ExpectedDbi1 = File1.getPDBDbiStream();
504   auto ExpectedDbi2 = File2.getPDBDbiStream();
505
506   bool Has1 = !!ExpectedDbi1;
507   bool Has2 = !!ExpectedDbi2;
508   if (!(Has1 && Has2)) {
509     std::string L = Has1 ? "(present)" : "(not present)";
510     std::string R = Has2 ? "(present)" : "(not present)";
511     D.print("Stream", L, R);
512
513     consumeError(ExpectedDbi1.takeError());
514     consumeError(ExpectedDbi2.takeError());
515     return Error::success();
516   }
517
518   auto &DL = *ExpectedDbi1;
519   auto &DR = *ExpectedDbi2;
520
521   D.print("Dbi Version", (uint32_t)DL.getDbiVersion(),
522           (uint32_t)DR.getDbiVersion());
523   D.print("Age", DL.getAge(), DR.getAge());
524   D.print("Machine", (uint16_t)DL.getMachineType(),
525           (uint16_t)DR.getMachineType());
526   D.print("Flags", DL.getFlags(), DR.getFlags());
527   D.print("Build Major", DL.getBuildMajorVersion(), DR.getBuildMajorVersion());
528   D.print("Build Minor", DL.getBuildMinorVersion(), DR.getBuildMinorVersion());
529   D.print("Build Number", DL.getBuildNumber(), DR.getBuildNumber());
530   D.print("PDB DLL Version", DL.getPdbDllVersion(), DR.getPdbDllVersion());
531   D.print("PDB DLL RBLD", DL.getPdbDllRbld(), DR.getPdbDllRbld());
532   D.print<StreamNumberProvider>("DBG (FPO)",
533                                 DL.getDebugStreamIndex(DbgHeaderType::FPO),
534                                 DR.getDebugStreamIndex(DbgHeaderType::FPO));
535   D.print<StreamNumberProvider>(
536       "DBG (Exception)", DL.getDebugStreamIndex(DbgHeaderType::Exception),
537       DR.getDebugStreamIndex(DbgHeaderType::Exception));
538   D.print<StreamNumberProvider>("DBG (Fixup)",
539                                 DL.getDebugStreamIndex(DbgHeaderType::Fixup),
540                                 DR.getDebugStreamIndex(DbgHeaderType::Fixup));
541   D.print<StreamNumberProvider>(
542       "DBG (OmapToSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapToSrc),
543       DR.getDebugStreamIndex(DbgHeaderType::OmapToSrc));
544   D.print<StreamNumberProvider>(
545       "DBG (OmapFromSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapFromSrc),
546       DR.getDebugStreamIndex(DbgHeaderType::OmapFromSrc));
547   D.print<StreamNumberProvider>(
548       "DBG (SectionHdr)", DL.getDebugStreamIndex(DbgHeaderType::SectionHdr),
549       DR.getDebugStreamIndex(DbgHeaderType::SectionHdr));
550   D.print<StreamNumberProvider>(
551       "DBG (TokenRidMap)", DL.getDebugStreamIndex(DbgHeaderType::TokenRidMap),
552       DR.getDebugStreamIndex(DbgHeaderType::TokenRidMap));
553   D.print<StreamNumberProvider>("DBG (Xdata)",
554                                 DL.getDebugStreamIndex(DbgHeaderType::Xdata),
555                                 DR.getDebugStreamIndex(DbgHeaderType::Xdata));
556   D.print<StreamNumberProvider>("DBG (Pdata)",
557                                 DL.getDebugStreamIndex(DbgHeaderType::Pdata),
558                                 DR.getDebugStreamIndex(DbgHeaderType::Pdata));
559   D.print<StreamNumberProvider>("DBG (NewFPO)",
560                                 DL.getDebugStreamIndex(DbgHeaderType::NewFPO),
561                                 DR.getDebugStreamIndex(DbgHeaderType::NewFPO));
562   D.print<StreamNumberProvider>(
563       "DBG (SectionHdrOrig)",
564       DL.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig),
565       DR.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig));
566   D.print<StreamNumberProvider>("Globals Stream",
567                                 DL.getGlobalSymbolStreamIndex(),
568                                 DR.getGlobalSymbolStreamIndex());
569   D.print<StreamNumberProvider>("Publics Stream",
570                                 DL.getPublicSymbolStreamIndex(),
571                                 DR.getPublicSymbolStreamIndex());
572   D.print<StreamNumberProvider>("Symbol Records", DL.getSymRecordStreamIndex(),
573                                 DR.getSymRecordStreamIndex());
574   D.print("Has CTypes", DL.hasCTypes(), DR.hasCTypes());
575   D.print("Is Incrementally Linked", DL.isIncrementallyLinked(),
576           DR.isIncrementallyLinked());
577   D.print("Is Stripped", DL.isStripped(), DR.isStripped());
578   const DbiModuleList &ML = DL.modules();
579   const DbiModuleList &MR = DR.modules();
580   D.print("Module Count", ML.getModuleCount(), MR.getModuleCount());
581   D.print("Source File Count", ML.getSourceFileCount(),
582           MR.getSourceFileCount());
583   auto MDL = getModuleDescriptors(ML);
584   auto MDR = getModuleDescriptors(MR);
585   // Scan all module descriptors from the left, and look for corresponding
586   // module descriptors on the right.
587   for (const auto &L : MDL)
588     diffOneModule(D, L, MDR, false);
589
590   for (const auto &R : MDR)
591     diffOneModule(D, R, MDL, true);
592
593   return Error::success();
594 }
595
596 Error DiffStyle::diffSectionContribs() { return Error::success(); }
597
598 Error DiffStyle::diffSectionMap() { return Error::success(); }
599
600 Error DiffStyle::diffFpoStream() { return Error::success(); }
601
602 Error DiffStyle::diffTpiStream(int Index) { return Error::success(); }
603
604 Error DiffStyle::diffModuleInfoStream(int Index) { return Error::success(); }
605
606 Error DiffStyle::diffPublics() { return Error::success(); }
607
608 Error DiffStyle::diffGlobals() { return Error::success(); }