1 //===-- LVCompare.cpp -----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This implements the LVCompare class.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"
14 #include "llvm/DebugInfo/LogicalView/Core/LVOptions.h"
15 #include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
19 using namespace llvm::logicalview
;
21 #define DEBUG_TYPE "Compare"
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();
37 constexpr unsigned getHeader() {
38 return static_cast<unsigned>(LVCompareIndex::Header
);
40 constexpr unsigned getExpected() {
41 return static_cast<unsigned>(LVCompareIndex::Expected
);
43 constexpr unsigned getMissing() {
44 return static_cast<unsigned>(LVCompareIndex::Missing
);
46 constexpr unsigned getAdded() {
47 return static_cast<unsigned>(LVCompareIndex::Added
);
50 LVCompare
*CurrentComparator
= nullptr;
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;
59 IterTotal
= Results
.find(LVCompareItem::Total
);
60 assert(IterTotal
!= Results
.end());
63 LVCompareInfo::iterator
getResultsEntry(LVElement
*Element
) {
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
;
72 Kind
= LVCompareItem::Type
;
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());
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
);
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
);
94 ++std::get
<getAdded()>(IterTotal
->second
);
95 ++std::get
<getAdded()>(Iter
->second
);
101 LVCompare
&LVCompare::getInstance() {
102 static LVCompare
DefaultComparator(outs());
103 return CurrentComparator
? *CurrentComparator
: DefaultComparator
;
106 void LVCompare::setInstance(LVCompare
*Comparator
) {
107 CurrentComparator
= Comparator
;
110 LVCompare::LVCompare(raw_ostream
&OS
) : OS(OS
) {
111 PrintLines
= options().getPrintLines();
112 PrintSymbols
= options().getPrintSymbols();
113 PrintTypes
= options().getPrintTypes();
115 options().getPrintScopes() || PrintLines
|| PrintSymbols
|| PrintTypes
;
118 Error
LVCompare::execute(LVReader
*ReferenceReader
, LVReader
*TargetReader
) {
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
);
124 auto PrintHeader
= [this](LVScopeRoot
*LHS
, LVScopeRoot
*RHS
) {
126 dbgs() << "[Reference] " << LHS
->getName() << "\n"
127 << "[Target] " << RHS
->getName() << "\n";
129 OS
<< "\nReference: " << formattedName(LHS
->getName()) << "\n"
130 << "Target: " << formattedName(RHS
->getName()) << "\n";
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
138 LVScopeRoot
*ReferenceRoot
= ReferenceReader
->getScopesRoot();
139 LVScopeRoot
*TargetRoot
= TargetReader
->getScopesRoot();
140 ReferenceRoot
->setIsInCompare();
141 TargetRoot
->setIsInCompare();
143 // Reset possible previous results.
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,
159 options().resetPrintFormatting();
162 return Error::success();
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();
170 PrintHeader(ReferenceRoot
, TargetRoot
);
171 Reader
= ReferenceReader
;
172 if (Error Err
= CompareViews(ReferenceRoot
, TargetRoot
))
175 ReferenceRoot
->report(LVComparePass::Missing
);
177 PrintHeader(TargetRoot
, ReferenceRoot
);
178 Reader
= TargetReader
;
179 if (Error Err
= CompareViews(TargetRoot
, ReferenceRoot
))
182 TargetRoot
->report(LVComparePass::Added
);
184 options().setPrintFormatting();
186 // Display a summary with the elements missing and/or added.
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
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
{
201 for (LVElement
*Reference
: References
) {
202 // Report elements that can be printed; ignore logical elements that
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
);
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
));
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
);
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,
247 return Error::success();
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"))
256 if (options().getCompareSymbols())
258 FindMatch(LHS
->getSymbols(), RHS
->getSymbols(), "Symbols"))
260 if (options().getCompareTypes())
261 if (Error Err
= FindMatch(LHS
->getTypes(), RHS
->getTypes(), "Types"))
263 if (options().getCompareLines())
264 if (Error Err
= FindMatch(LHS
->getLines(), RHS
->getLines(), "Lines"))
267 return Error::success();
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();
275 PrintHeader(ReferenceRoot
, TargetRoot
);
276 // Include the root in the expected count.
277 updateExpected(ReferenceRoot
);
279 LVElements ElementsToAdd
;
280 Reader
= ReferenceReader
;
281 if (Error Err
= CompareReaders(ReferenceReader
, TargetReader
, ElementsToAdd
,
282 LVComparePass::Missing
))
284 Reader
= TargetReader
;
285 if (Error Err
= CompareReaders(TargetReader
, ReferenceReader
, ElementsToAdd
,
286 LVComparePass::Added
))
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())
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
) {
303 dbgs() << "Element to Insert: " << hexSquareString(Element
->getOffset())
305 << hexSquareString(Element
->getParentScope()->getOffset())
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())
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
];
318 dbgs() << "Inserted at: "
319 << hexSquareString(InsertionPoint
->getOffset()) << "\n";
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);
330 options().setPrintFormatting();
332 // Display the augmented reference scopes tree.
333 if (options().getReportAnyView())
334 if (Error Err
= ReferenceReader
->doPrint())
338 dbgs() << "\nModified Reference Reader";
339 if (Error Err
= ReferenceReader
->doPrint())
341 dbgs() << "\nModified Target Reader";
342 if (Error Err
= TargetReader
->doPrint())
346 // Display a summary with the elements missing and/or added.
350 return Error::success();
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";
361 void LVCompare::printItem(LVElement
*Element
, LVComparePass Pass
) {
362 // Record expected, missing, added.
363 updateExpected(Element
);
364 updateMissingOrAdded(Element
, Pass
);
366 // Record missing/added element.
367 if (Element
->getIsMissing())
368 addPassEntry(Reader
, Element
, Pass
);
370 if ((!PrintLines
&& Element
->getIsLine()) ||
371 (!PrintScopes
&& Element
->getIsScope()) ||
372 (!PrintSymbols
&& Element
->getIsSymbol()) ||
373 (!PrintTypes
&& Element
->getIsType()))
376 if (Element
->getIsMissing()) {
379 FirstMissing
= false;
382 StringRef Kind
= Element
->kind();
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();
391 if (options().getReportList()) {
393 Element
->printAttributes(OS
);
394 OS
<< Element
->lineNumberAsString(/*ShowZero=*/true) << " " << Kind
<< " "
400 void LVCompare::printSummary() const {
401 if (!options().getPrintSummary())
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
,
407 OS
<< format("%-9s%9s %9s %9s\n", T
, U
, V
, W
);
409 auto PrintDataRow
= [&](const char *T
, unsigned U
, unsigned V
, unsigned W
) {
410 OS
<< format("%-9s%9d %9d %9d\n", T
, U
, V
, W
);
415 PrintHeadingRow("Element", "Expected", "Missing", "Added");
417 for (LVCompareInfo::reference Entry
: Results
) {
418 if (Entry
.first
== LVCompareItem::Total
)
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
));
427 void LVCompare::print(raw_ostream
&OS
) const { OS
<< "LVCompare\n"; }