1 //===-- LVCompare.cpp -----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This implements the LVCompare class.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"
14 #include "llvm/DebugInfo/LogicalView/Core/LVOptions.h"
15 #include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
16 #include <tuple>
17 
18 using namespace llvm;
19 using namespace llvm::logicalview;
20 
21 #define DEBUG_TYPE "Compare"
22 
23 namespace {
24 
25 enum class LVCompareItem { Scope, Symbol, Type, Line, Total };
26 enum class LVCompareIndex { Header, Expected, Missing, Added };
27 using LVCompareEntry = std::tuple<const char *, unsigned, unsigned, unsigned>;
28 using LVCompareInfo = std::map<LVCompareItem, LVCompareEntry>;
29 LVCompareInfo Results = {
30     {LVCompareItem::Line, LVCompareEntry("Lines", 0, 0, 0)},
31     {LVCompareItem::Scope, LVCompareEntry("Scopes", 0, 0, 0)},
32     {LVCompareItem::Symbol, LVCompareEntry("Symbols", 0, 0, 0)},
33     {LVCompareItem::Type, LVCompareEntry("Types", 0, 0, 0)},
34     {LVCompareItem::Total, LVCompareEntry("Total", 0, 0, 0)}};
35 static LVCompareInfo::iterator IterTotal = Results.end();
36 
getHeader()37 constexpr unsigned getHeader() {
38   return static_cast<unsigned>(LVCompareIndex::Header);
39 }
getExpected()40 constexpr unsigned getExpected() {
41   return static_cast<unsigned>(LVCompareIndex::Expected);
42 }
getMissing()43 constexpr unsigned getMissing() {
44   return static_cast<unsigned>(LVCompareIndex::Missing);
45 }
getAdded()46 constexpr unsigned getAdded() {
47   return static_cast<unsigned>(LVCompareIndex::Added);
48 }
49 
50 LVCompare *CurrentComparator = nullptr;
51 
zeroResults()52 void zeroResults() {
53   // In case the same reader instance is used.
54   for (LVCompareInfo::reference Entry : Results) {
55     std::get<getExpected()>(Entry.second) = 0;
56     std::get<getMissing()>(Entry.second) = 0;
57     std::get<getAdded()>(Entry.second) = 0;
58   }
59   IterTotal = Results.find(LVCompareItem::Total);
60   assert(IterTotal != Results.end());
61 }
62 
getResultsEntry(LVElement * Element)63 LVCompareInfo::iterator getResultsEntry(LVElement *Element) {
64   LVCompareItem Kind;
65   if (Element->getIsLine())
66     Kind = LVCompareItem::Line;
67   else if (Element->getIsScope())
68     Kind = LVCompareItem::Scope;
69   else if (Element->getIsSymbol())
70     Kind = LVCompareItem::Symbol;
71   else
72     Kind = LVCompareItem::Type;
73 
74   // Used to update the expected, missing or added entry for the given kind.
75   LVCompareInfo::iterator Iter = Results.find(Kind);
76   assert(Iter != Results.end());
77   return Iter;
78 }
79 
updateExpected(LVElement * Element)80 void updateExpected(LVElement *Element) {
81   LVCompareInfo::iterator Iter = getResultsEntry(Element);
82   // Update total for expected.
83   ++std::get<getExpected()>(IterTotal->second);
84   // Update total for specific element kind.
85   ++std::get<getExpected()>(Iter->second);
86 }
87 
updateMissingOrAdded(LVElement * Element,LVComparePass Pass)88 void updateMissingOrAdded(LVElement *Element, LVComparePass Pass) {
89   LVCompareInfo::iterator Iter = getResultsEntry(Element);
90   if (Pass == LVComparePass::Missing) {
91     ++std::get<getMissing()>(IterTotal->second);
92     ++std::get<getMissing()>(Iter->second);
93   } else {
94     ++std::get<getAdded()>(IterTotal->second);
95     ++std::get<getAdded()>(Iter->second);
96   }
97 }
98 
99 } // namespace
100 
getInstance()101 LVCompare &LVCompare::getInstance() {
102   static LVCompare DefaultComparator(outs());
103   return CurrentComparator ? *CurrentComparator : DefaultComparator;
104 }
105 
setInstance(LVCompare * Comparator)106 void LVCompare::setInstance(LVCompare *Comparator) {
107   CurrentComparator = Comparator;
108 }
109 
LVCompare(raw_ostream & OS)110 LVCompare::LVCompare(raw_ostream &OS) : OS(OS) {
111   PrintLines = options().getPrintLines();
112   PrintSymbols = options().getPrintSymbols();
113   PrintTypes = options().getPrintTypes();
114   PrintScopes =
115       options().getPrintScopes() || PrintLines || PrintSymbols || PrintTypes;
116 }
117 
execute(LVReader * ReferenceReader,LVReader * TargetReader)118 Error LVCompare::execute(LVReader *ReferenceReader, LVReader *TargetReader) {
119   setInstance(this);
120   // In the case of added elements, the 'Reference' reader will be modified;
121   // those elements will be added to it. Update the current reader instance.
122   LVReader::setInstance(ReferenceReader);
123 
124   auto PrintHeader = [this](LVScopeRoot *LHS, LVScopeRoot *RHS) {
125     LLVM_DEBUG({
126       dbgs() << "[Reference] " << LHS->getName() << "\n"
127              << "[Target] " << RHS->getName() << "\n";
128     });
129     OS << "\nReference: " << formattedName(LHS->getName()) << "\n"
130        << "Target:    " << formattedName(RHS->getName()) << "\n";
131   };
132 
133   // We traverse the given scopes tree ('Reference' and 'Target') twice.
134   // The first time we look for missing items from the 'Reference' and the
135   // second time we look for items added to the 'Target'.
136   // The comparison test includes the name, lexical level, type, source
137   // location, etc.
138   LVScopeRoot *ReferenceRoot = ReferenceReader->getScopesRoot();
139   LVScopeRoot *TargetRoot = TargetReader->getScopesRoot();
140   ReferenceRoot->setIsInCompare();
141   TargetRoot->setIsInCompare();
142 
143   // Reset possible previous results.
144   zeroResults();
145 
146   if (options().getCompareContext()) {
147     // Perform a logical view comparison as a whole unit. We start at the
148     // root reference; at each scope an equal test is applied to its children.
149     // If a difference is found, the current path is marked as missing.
150     auto CompareViews = [this](LVScopeRoot *LHS, LVScopeRoot *RHS) -> Error {
151       LHS->markMissingParents(RHS, /*TraverseChildren=*/true);
152       if (LHS->getIsMissingLink() && options().getReportAnyView()) {
153         // As we are printing a missing tree, enable formatting.
154         options().setPrintFormatting();
155         OS << "\nMissing Tree:\n";
156         if (Error Err = LHS->doPrint(/*Split=*/false, /*Match=*/false,
157                                      /*Print=*/true, OS))
158           return Err;
159         options().resetPrintFormatting();
160       }
161 
162       return Error::success();
163     };
164 
165     // If the user has requested printing details for the comparison, we
166     // disable the indentation and the added/missing tags ('+'/'-'), as the
167     // details are just a list of elements.
168     options().resetPrintFormatting();
169 
170     PrintHeader(ReferenceRoot, TargetRoot);
171     Reader = ReferenceReader;
172     if (Error Err = CompareViews(ReferenceRoot, TargetRoot))
173       return Err;
174     FirstMissing = true;
175     ReferenceRoot->report(LVComparePass::Missing);
176 
177     PrintHeader(TargetRoot, ReferenceRoot);
178     Reader = TargetReader;
179     if (Error Err = CompareViews(TargetRoot, ReferenceRoot))
180       return Err;
181     FirstMissing = true;
182     TargetRoot->report(LVComparePass::Added);
183 
184     options().setPrintFormatting();
185 
186     // Display a summary with the elements missing and/or added.
187     printSummary();
188   } else {
189     // Perform logical elements comparison. An equal test is apply to each
190     // element. If a difference is found, the reference element is marked as
191     // 'missing'.
192     // The final comparison result will show the 'Reference' scopes tree,
193     // having both missing and added elements.
194     using LVScopeLink = std::map<LVScope *, LVScope *>;
195     LVScopeLink ScopeLinks;
196     auto CompareReaders = [&](LVReader *LHS, LVReader *RHS, LVElements &Set,
197                               LVComparePass Pass) -> Error {
198       auto FindMatch = [&](auto &References, auto &Targets,
199                            const char *Category) -> Error {
200         LVElements Elements;
201         for (LVElement *Reference : References) {
202           // Report elements that can be printed; ignore logical elements that
203           // have qualifiers.
204           if (Reference->getIncludeInPrint()) {
205             if (Pass == LVComparePass::Missing)
206               updateExpected(Reference);
207             Reference->setIsInCompare();
208             LVElement *CurrentTarget = nullptr;
209             if (llvm::any_of(Targets, [&](auto Target) -> bool {
210                   CurrentTarget = Target;
211                   return Reference->equals(Target);
212                 })) {
213               if (Pass == LVComparePass::Missing && Reference->getIsScope()) {
214                 // If the elements being compared are scopes and are a match,
215                 // they are recorded, to be used when creating the augmented
216                 // tree, as insertion points for the "added" items.
217                 ScopeLinks.emplace(static_cast<LVScope *>(CurrentTarget),
218                                    static_cast<LVScope *>(Reference));
219               }
220             } else {
221               // Element is missing or added.
222               Pass == LVComparePass::Missing ? Reference->setIsMissing()
223                                              : Reference->setIsAdded();
224               Elements.push_back(Reference);
225               updateMissingOrAdded(Reference, Pass);
226               // Record missing/added element.
227               addPassEntry(Reader, Reference, Pass);
228             }
229           }
230         }
231         if (Pass == LVComparePass::Added)
232           // Record all the current missing elements for this category.
233           Set.insert(Set.end(), Elements.begin(), Elements.end());
234         if (options().getReportList()) {
235           if (Elements.size()) {
236             OS << "\n(" << Elements.size() << ") "
237                << (Pass == LVComparePass::Missing ? "Missing" : "Added") << " "
238                << Category << ":\n";
239             for (const LVElement *Element : Elements) {
240               if (Error Err = Element->doPrint(/*Split=*/false, /*Match=*/false,
241                                                /*Print=*/true, OS))
242                 return Err;
243             }
244           }
245         }
246 
247         return Error::success();
248       };
249 
250       // First compare the scopes, so they will be inserted at the front of
251       // the missing elements list. When they are moved, their children are
252       // moved as well and no additional work is required.
253       if (options().getCompareScopes())
254         if (Error Err = FindMatch(LHS->getScopes(), RHS->getScopes(), "Scopes"))
255           return Err;
256       if (options().getCompareSymbols())
257         if (Error Err =
258                 FindMatch(LHS->getSymbols(), RHS->getSymbols(), "Symbols"))
259           return Err;
260       if (options().getCompareTypes())
261         if (Error Err = FindMatch(LHS->getTypes(), RHS->getTypes(), "Types"))
262           return Err;
263       if (options().getCompareLines())
264         if (Error Err = FindMatch(LHS->getLines(), RHS->getLines(), "Lines"))
265           return Err;
266 
267       return Error::success();
268     };
269 
270     // If the user has requested printing details for the comparison, we
271     // disable the indentation and the added/missing tags ('+'/'-'), as the
272     // details are just a list of elements.
273     options().resetPrintFormatting();
274 
275     PrintHeader(ReferenceRoot, TargetRoot);
276     // Include the root in the expected count.
277     updateExpected(ReferenceRoot);
278 
279     LVElements ElementsToAdd;
280     Reader = ReferenceReader;
281     if (Error Err = CompareReaders(ReferenceReader, TargetReader, ElementsToAdd,
282                                    LVComparePass::Missing))
283       return Err;
284     Reader = TargetReader;
285     if (Error Err = CompareReaders(TargetReader, ReferenceReader, ElementsToAdd,
286                                    LVComparePass::Added))
287       return Err;
288 
289     LLVM_DEBUG({
290       dbgs() << "\nReference/Target Scope links:\n";
291       for (LVScopeLink::const_reference Entry : ScopeLinks)
292         dbgs() << "Source: " << hexSquareString(Entry.first->getOffset()) << " "
293                << "Destination: " << hexSquareString(Entry.second->getOffset())
294                << "\n";
295       dbgs() << "\n";
296     });
297 
298     // Add the 'missing' elements from the 'Target' into the 'Reference'.
299     // First insert the missing scopes, as they include any missing children.
300     LVScope *Parent = nullptr;
301     for (LVElement *Element : ElementsToAdd) {
302       LLVM_DEBUG({
303         dbgs() << "Element to Insert: " << hexSquareString(Element->getOffset())
304                << ", Parent: "
305                << hexSquareString(Element->getParentScope()->getOffset())
306                << "\n";
307       });
308       // Skip already inserted elements. They were inserted, if their parents
309       // were missing. When inserting them, all the children are moved.
310       if (Element->getHasMoved())
311         continue;
312 
313       // We need to find an insertion point in the reference scopes tree.
314       Parent = Element->getParentScope();
315       if (ScopeLinks.find(Parent) != ScopeLinks.end()) {
316         LVScope *InsertionPoint = ScopeLinks[Parent];
317         LLVM_DEBUG({
318           dbgs() << "Inserted at: "
319                  << hexSquareString(InsertionPoint->getOffset()) << "\n";
320         });
321         if (Parent->removeElement(Element)) {
322           // Be sure we have a current compile unit.
323           getReader().setCompileUnit(InsertionPoint->getCompileUnitParent());
324           InsertionPoint->addElement(Element);
325           Element->updateLevel(InsertionPoint, /*Moved=*/true);
326         }
327       }
328     }
329 
330     options().setPrintFormatting();
331 
332     // Display the augmented reference scopes tree.
333     if (options().getReportAnyView())
334       if (Error Err = ReferenceReader->doPrint())
335         return Err;
336 
337     LLVM_DEBUG({
338       dbgs() << "\nModified Reference Reader";
339       if (Error Err = ReferenceReader->doPrint())
340         return Err;
341       dbgs() << "\nModified Target Reader";
342       if (Error Err = TargetReader->doPrint())
343         return Err;
344     });
345 
346     // Display a summary with the elements missing and/or added.
347     printSummary();
348   }
349 
350   return Error::success();
351 }
352 
printCurrentStack()353 void LVCompare::printCurrentStack() {
354   for (const LVScope *Scope : ScopeStack) {
355     Scope->printAttributes(OS);
356     OS << Scope->lineNumberAsString(/*ShowZero=*/true) << " " << Scope->kind()
357        << " " << formattedName(Scope->getName()) << "\n";
358   }
359 }
360 
printItem(LVElement * Element,LVComparePass Pass)361 void LVCompare::printItem(LVElement *Element, LVComparePass Pass) {
362   // Record expected, missing, added.
363   updateExpected(Element);
364   updateMissingOrAdded(Element, Pass);
365 
366   // Record missing/added element.
367   if (Element->getIsMissing())
368     addPassEntry(Reader, Element, Pass);
369 
370   if ((!PrintLines && Element->getIsLine()) ||
371       (!PrintScopes && Element->getIsScope()) ||
372       (!PrintSymbols && Element->getIsSymbol()) ||
373       (!PrintTypes && Element->getIsType()))
374     return;
375 
376   if (Element->getIsMissing()) {
377     if (FirstMissing) {
378       OS << "\n";
379       FirstMissing = false;
380     }
381 
382     StringRef Kind = Element->kind();
383     StringRef Name =
384         Element->getIsLine() ? Element->getPathname() : Element->getName();
385     StringRef Status = (Pass == LVComparePass::Missing) ? "Missing" : "Added";
386     OS << Status << " " << Kind << " '" << Name << "'";
387     if (Element->getLineNumber() > 0)
388       OS << " at line " << Element->getLineNumber();
389     OS << "\n";
390 
391     if (options().getReportList()) {
392       printCurrentStack();
393       Element->printAttributes(OS);
394       OS << Element->lineNumberAsString(/*ShowZero=*/true) << " " << Kind << " "
395          << Name << "\n";
396     }
397   }
398 }
399 
printSummary() const400 void LVCompare::printSummary() const {
401   if (!options().getPrintSummary())
402     return;
403   std::string Separator = std::string(40, '-');
404   auto PrintSeparator = [&]() { OS << Separator << "\n"; };
405   auto PrintHeadingRow = [&](const char *T, const char *U, const char *V,
406                              const char *W) {
407     OS << format("%-9s%9s  %9s  %9s\n", T, U, V, W);
408   };
409   auto PrintDataRow = [&](const char *T, unsigned U, unsigned V, unsigned W) {
410     OS << format("%-9s%9d  %9d  %9d\n", T, U, V, W);
411   };
412 
413   OS << "\n";
414   PrintSeparator();
415   PrintHeadingRow("Element", "Expected", "Missing", "Added");
416   PrintSeparator();
417   for (LVCompareInfo::reference Entry : Results) {
418     if (Entry.first == LVCompareItem::Total)
419       PrintSeparator();
420     PrintDataRow(std::get<getHeader()>(Entry.second),
421                  std::get<getExpected()>(Entry.second),
422                  std::get<getMissing()>(Entry.second),
423                  std::get<getAdded()>(Entry.second));
424   }
425 }
426 
print(raw_ostream & OS) const427 void LVCompare::print(raw_ostream &OS) const { OS << "LVCompare\n"; }
428