1//===- llvm/unittest/DebugInfo/LogicalView/CodeViewReaderTest.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#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"
10#include "llvm/DebugInfo/LogicalView/Core/LVLine.h"
11#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
12#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
13#include "llvm/DebugInfo/LogicalView/Core/LVType.h"
14#include "llvm/DebugInfo/LogicalView/LVReaderHandler.h"
15#include "llvm/MC/TargetRegistry.h"
16#include "llvm/Support/COM.h"
17#include "llvm/Support/InitLLVM.h"
18#include "llvm/Support/ScopedPrinter.h"
19#include "llvm/Support/TargetSelect.h"
20#include "llvm/Support/ToolOutputFile.h"
21#include "llvm/Testing/Support/Error.h"
22
23#include "gtest/gtest.h"
24
25using namespace llvm;
26using namespace llvm::logicalview;
27
28extern const char *TestMainArgv0;
29
30namespace {
31
32const char *CodeViewClang = "test-codeview-clang.o";
33const char *CodeViewMsvc = "test-codeview-msvc.o";
34const char *CodeViewPdbMsvc = "test-codeview-pdb-msvc.o";
35
36// Helper function to get the first scope child from the given parent.
37LVScope *getFirstScopeChild(LVScope *Parent) {
38 EXPECT_NE(Parent, nullptr);
39 const LVScopes *Scopes = Parent->getScopes();
40 EXPECT_NE(Scopes, nullptr);
41 EXPECT_EQ(Scopes->size(), 1u);
42
43 LVScopes::const_iterator Iter = Scopes->begin();
44 LVScope *Child = *Iter;
45 EXPECT_NE(Child, nullptr);
46 return Child;
47}
48
49// Helper function to create a reader.
50std::unique_ptr<LVReader> createReader(LVReaderHandler &ReaderHandler,
51 SmallString<128> &InputsDir,
52 StringRef Filename) {
53 SmallString<128> ObjectName(InputsDir);
54 llvm::sys::path::append(path&: ObjectName, a: Filename);
55
56 Expected<std::unique_ptr<LVReader>> ReaderOrErr =
57 ReaderHandler.createReader(Pathname: std::string(ObjectName));
58 EXPECT_THAT_EXPECTED(ReaderOrErr, Succeeded());
59 std::unique_ptr<LVReader> Reader = std::move(*ReaderOrErr);
60 EXPECT_NE(Reader, nullptr);
61 return Reader;
62}
63
64// Check the logical elements basic properties (Clang - Codeview).
65void checkElementPropertiesClangCodeview(LVReader *Reader) {
66 LVScopeRoot *Root = Reader->getScopesRoot();
67 LVScopeCompileUnit *CompileUnit =
68 static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Parent: Root));
69 LVScopeFunction *Function =
70 static_cast<LVScopeFunction *>(getFirstScopeChild(Parent: CompileUnit));
71
72 EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64");
73 EXPECT_EQ(Root->getName(), CodeViewClang);
74
75 EXPECT_EQ(CompileUnit->getBaseAddress(), 0u);
76 EXPECT_TRUE(CompileUnit->getProducer().starts_with("clang"));
77 EXPECT_EQ(CompileUnit->getName(), "test.cpp");
78
79 EXPECT_EQ(Function->lineCount(), 16u);
80 EXPECT_EQ(Function->scopeCount(), 1u);
81 EXPECT_EQ(Function->symbolCount(), 3u);
82 EXPECT_EQ(Function->typeCount(), 1u);
83 EXPECT_EQ(Function->rangeCount(), 1u);
84
85 const LVLocations *Ranges = Function->getRanges();
86 ASSERT_NE(Ranges, nullptr);
87 ASSERT_EQ(Ranges->size(), 1u);
88 LVLocations::const_iterator IterLocation = Ranges->begin();
89 LVLocation *Location = (*IterLocation);
90 EXPECT_STREQ(Location->getIntervalInfo().c_str(),
91 "{Range} Lines 2:9 [0x0000000000:0x0000000046]");
92
93 LVRange RangeList;
94 Function->getRanges(RangeList);
95
96 const LVRangeEntries &RangeEntries = RangeList.getEntries();
97 ASSERT_EQ(RangeEntries.size(), 2u);
98 LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin();
99 LVRangeEntry RangeEntry = *IterRanges;
100 EXPECT_EQ(RangeEntry.lower(), 0u);
101 EXPECT_EQ(RangeEntry.upper(), 0x46u);
102 EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
103 EXPECT_EQ(RangeEntry.scope()->getName(), "foo");
104 EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
105
106 ++IterRanges;
107 RangeEntry = *IterRanges;
108 EXPECT_EQ(RangeEntry.lower(), 0x21u);
109 EXPECT_EQ(RangeEntry.upper(), 0x35u);
110 EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
111 EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?");
112 EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
113
114 const LVPublicNames &PublicNames = CompileUnit->getPublicNames();
115 ASSERT_EQ(PublicNames.size(), 1u);
116 LVPublicNames::const_iterator IterNames = PublicNames.cbegin();
117 LVScope *Foo = (*IterNames).first;
118 EXPECT_EQ(Foo->getName(), "foo");
119 EXPECT_EQ(Foo->getLineNumber(), 0u);
120 LVNameInfo NameInfo = (*IterNames).second;
121 EXPECT_EQ(NameInfo.first, 0u);
122 EXPECT_EQ(NameInfo.second, 0x46u);
123
124 // Lines (debug and assembler) for 'foo'.
125 const LVLines *Lines = Foo->getLines();
126 ASSERT_NE(Lines, nullptr);
127 EXPECT_EQ(Lines->size(), 0x10u);
128}
129
130// Check the logical elements basic properties (MSVC - Codeview).
131void checkElementPropertiesMsvcCodeview(LVReader *Reader) {
132 LVScopeRoot *Root = Reader->getScopesRoot();
133 LVScopeCompileUnit *CompileUnit =
134 static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Parent: Root));
135 LVScopeFunction *Function =
136 static_cast<LVScopeFunction *>(getFirstScopeChild(Parent: CompileUnit));
137
138 EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64");
139 EXPECT_EQ(Root->getName(), CodeViewMsvc);
140
141 EXPECT_EQ(CompileUnit->getBaseAddress(), 0u);
142 EXPECT_TRUE(CompileUnit->getProducer().starts_with("Microsoft"));
143 EXPECT_EQ(CompileUnit->getName(), "test.cpp");
144
145 EXPECT_EQ(Function->lineCount(), 14u);
146 EXPECT_EQ(Function->scopeCount(), 1u);
147 EXPECT_EQ(Function->symbolCount(), 3u);
148 EXPECT_EQ(Function->typeCount(), 0u);
149 EXPECT_EQ(Function->rangeCount(), 1u);
150
151 const LVLocations *Ranges = Function->getRanges();
152 ASSERT_NE(Ranges, nullptr);
153 ASSERT_EQ(Ranges->size(), 1u);
154 LVLocations::const_iterator IterLocation = Ranges->begin();
155 LVLocation *Location = (*IterLocation);
156 EXPECT_STREQ(Location->getIntervalInfo().c_str(),
157 "{Range} Lines 2:9 [0x0000000000:0x0000000031]");
158
159 LVRange RangeList;
160 Function->getRanges(RangeList);
161
162 const LVRangeEntries &RangeEntries = RangeList.getEntries();
163 ASSERT_EQ(RangeEntries.size(), 2u);
164 LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin();
165 LVRangeEntry RangeEntry = *IterRanges;
166 EXPECT_EQ(RangeEntry.lower(), 0u);
167 EXPECT_EQ(RangeEntry.upper(), 0x31u);
168 EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
169 EXPECT_EQ(RangeEntry.scope()->getName(), "foo");
170 EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
171
172 ++IterRanges;
173 RangeEntry = *IterRanges;
174 EXPECT_EQ(RangeEntry.lower(), 0x1bu);
175 EXPECT_EQ(RangeEntry.upper(), 0x28u);
176 EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
177 EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?");
178 EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
179
180 const LVPublicNames &PublicNames = CompileUnit->getPublicNames();
181 ASSERT_EQ(PublicNames.size(), 1u);
182 LVPublicNames::const_iterator IterNames = PublicNames.cbegin();
183 LVScope *Foo = (*IterNames).first;
184 EXPECT_EQ(Foo->getName(), "foo");
185 EXPECT_EQ(Foo->getLineNumber(), 0u);
186 LVNameInfo NameInfo = (*IterNames).second;
187 EXPECT_EQ(NameInfo.first, 0u);
188 EXPECT_EQ(NameInfo.second, 0x31u);
189
190 // Lines (debug and assembler) for 'foo'.
191 const LVLines *Lines = Foo->getLines();
192 ASSERT_NE(Lines, nullptr);
193 EXPECT_EQ(Lines->size(), 0x0eu);
194}
195
196// Check the logical elements basic properties (MSVC - PDB).
197void checkElementPropertiesMsvcCodeviewPdb(LVReader *Reader) {
198 LVScopeRoot *Root = Reader->getScopesRoot();
199 LVScopeCompileUnit *CompileUnit =
200 static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Parent: Root));
201 LVScopeFunction *Function =
202 static_cast<LVScopeFunction *>(getFirstScopeChild(Parent: CompileUnit));
203
204 EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64");
205 EXPECT_EQ(Root->getName(), CodeViewPdbMsvc);
206
207 EXPECT_EQ(CompileUnit->getBaseAddress(), 0u);
208 EXPECT_TRUE(CompileUnit->getProducer().starts_with("Microsoft"));
209 EXPECT_EQ(CompileUnit->getName(), "test.cpp");
210
211 EXPECT_EQ(Function->lineCount(), 14u);
212 EXPECT_EQ(Function->scopeCount(), 1u);
213 EXPECT_EQ(Function->symbolCount(), 3u);
214 EXPECT_EQ(Function->typeCount(), 0u);
215 EXPECT_EQ(Function->rangeCount(), 1u);
216
217 const LVLocations *Ranges = Function->getRanges();
218 ASSERT_NE(Ranges, nullptr);
219 ASSERT_EQ(Ranges->size(), 1u);
220 LVLocations::const_iterator IterLocation = Ranges->begin();
221 LVLocation *Location = (*IterLocation);
222 EXPECT_STREQ(Location->getIntervalInfo().c_str(),
223 "{Range} Lines 2:9 [0x0000000000:0x0000000031]");
224
225 LVRange RangeList;
226 Function->getRanges(RangeList);
227
228 const LVRangeEntries &RangeEntries = RangeList.getEntries();
229 ASSERT_EQ(RangeEntries.size(), 2u);
230 LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin();
231 LVRangeEntry RangeEntry = *IterRanges;
232 EXPECT_EQ(RangeEntry.lower(), 0u);
233 EXPECT_EQ(RangeEntry.upper(), 0x31u);
234 EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
235 EXPECT_EQ(RangeEntry.scope()->getName(), "foo");
236 EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
237
238 ++IterRanges;
239 RangeEntry = *IterRanges;
240 EXPECT_EQ(RangeEntry.lower(), 0x1bu);
241 EXPECT_EQ(RangeEntry.upper(), 0x28u);
242 EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
243 EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?");
244 EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
245
246 const LVPublicNames &PublicNames = CompileUnit->getPublicNames();
247 ASSERT_EQ(PublicNames.size(), 1u);
248 LVPublicNames::const_iterator IterNames = PublicNames.cbegin();
249 LVScope *Foo = (*IterNames).first;
250 EXPECT_EQ(Foo->getName(), "foo");
251 EXPECT_EQ(Foo->getLineNumber(), 0u);
252 LVNameInfo NameInfo = (*IterNames).second;
253 EXPECT_EQ(NameInfo.first, 0u);
254 EXPECT_EQ(NameInfo.second, 0x31u);
255
256 // Lines (debug and assembler) for 'foo'.
257 const LVLines *Lines = Foo->getLines();
258 ASSERT_NE(Lines, nullptr);
259 EXPECT_EQ(Lines->size(), 0x0eu);
260}
261
262struct SelectionInfo {
263 const char *Name;
264 LVElementGetFunction Function;
265};
266
267// Check the logical elements selection.
268void checkElementSelection(LVReader *Reader, std::vector<SelectionInfo> &Data,
269 size_t Size) {
270 LVScopeRoot *Root = Reader->getScopesRoot();
271 LVScopeCompileUnit *CompileUnit =
272 static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Parent: Root));
273
274 // Get the matched elements.
275 LVElements MatchedElements = CompileUnit->getMatchedElements();
276 std::map<StringRef, LVElement *> MapElements;
277 for (LVElement *Element : MatchedElements)
278 MapElements[Element->getName()] = Element;
279 ASSERT_EQ(MapElements.size(), Size);
280
281 std::map<StringRef, LVElement *>::iterator Iter = MapElements.begin();
282 for (const SelectionInfo &Entry : Data) {
283 // Get matched element.
284 EXPECT_NE(Iter, MapElements.end());
285 LVElement *Element = Iter->second;
286 ASSERT_NE(Element, nullptr);
287 EXPECT_NE(Element->getName().find(Entry.Name), StringRef::npos);
288 EXPECT_EQ((Element->*Entry.Function)(), 1u);
289 ++Iter;
290 }
291
292 // Get the parents for the matched elements.
293 LVScopes MatchedScopes = CompileUnit->getMatchedScopes();
294 std::set<StringRef> SetScopes;
295 for (LVScope *Scope : MatchedScopes)
296 SetScopes.insert(x: Scope->getName());
297 ASSERT_EQ(SetScopes.size(), 3u);
298
299 // Parents of selected elements.
300 std::set<StringRef>::iterator IterScope;
301 IterScope = SetScopes.find(x: "foo");
302 EXPECT_NE(IterScope, SetScopes.end());
303 IterScope = SetScopes.find(x: "foo::?");
304 EXPECT_NE(IterScope, SetScopes.end());
305 IterScope = SetScopes.find(x: "test.cpp");
306 EXPECT_NE(IterScope, SetScopes.end());
307}
308
309// Check the logical elements comparison.
310void checkElementComparison(LVReader *Reference, LVReader *Target) {
311 LVCompare Compare(nulls());
312 Error Err = Compare.execute(ReferenceReader: Reference, TargetReader: Target);
313 ASSERT_THAT_ERROR(std::move(Err), Succeeded());
314
315 // Get comparison table.
316 LVPassTable PassTable = Compare.getPassTable();
317 ASSERT_EQ(PassTable.size(), 2u);
318
319 LVReader *Reader;
320 LVElement *Element;
321 LVComparePass Pass;
322
323 // Reference: Missing TypeDefinition 'INTEGER'
324 std::tie(args&: Reader, args&: Element, args&: Pass) = PassTable[0];
325 ASSERT_NE(Reader, nullptr);
326 ASSERT_NE(Element, nullptr);
327 EXPECT_EQ(Reader, Reference);
328 EXPECT_EQ(Element->getLevel(), 3u);
329 EXPECT_EQ(Element->getLineNumber(), 0u);
330 EXPECT_EQ(Element->getName(), "INTEGER");
331 EXPECT_EQ(Pass, LVComparePass::Missing);
332
333 // Target: Added TypeDefinition 'INTEGER'
334 std::tie(args&: Reader, args&: Element, args&: Pass) = PassTable[1];
335 ASSERT_NE(Reader, nullptr);
336 ASSERT_NE(Element, nullptr);
337 EXPECT_EQ(Reader, Target);
338 EXPECT_EQ(Element->getLevel(), 4u);
339 EXPECT_EQ(Element->getLineNumber(), 0u);
340 EXPECT_EQ(Element->getName(), "INTEGER");
341 EXPECT_EQ(Pass, LVComparePass::Added);
342}
343
344// Logical elements properties.
345void elementProperties(SmallString<128> &InputsDir) {
346 // Reader options.
347 LVOptions ReaderOptions;
348 ReaderOptions.setAttributeOffset();
349 ReaderOptions.setAttributeFormat();
350 ReaderOptions.setAttributeFilename();
351 ReaderOptions.setAttributeProducer();
352 ReaderOptions.setAttributePublics();
353 ReaderOptions.setAttributeRange();
354 ReaderOptions.setAttributeLocation();
355 ReaderOptions.setPrintAll();
356 ReaderOptions.resolveDependencies();
357
358 std::vector<std::string> Objects;
359 ScopedPrinter W(outs());
360 LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
361
362 // Check logical elements properties.
363 {
364 std::unique_ptr<LVReader> Reader =
365 createReader(ReaderHandler, InputsDir, Filename: CodeViewClang);
366 checkElementPropertiesClangCodeview(Reader: Reader.get());
367 }
368 {
369 std::unique_ptr<LVReader> Reader =
370 createReader(ReaderHandler, InputsDir, Filename: CodeViewMsvc);
371 checkElementPropertiesMsvcCodeview(Reader: Reader.get());
372 }
373 {
374 std::unique_ptr<LVReader> Reader =
375 createReader(ReaderHandler, InputsDir, Filename: CodeViewPdbMsvc);
376 checkElementPropertiesMsvcCodeviewPdb(Reader: Reader.get());
377 }
378}
379
380// Logical elements selection.
381void elementSelection(SmallString<128> &InputsDir) {
382 // Reader options.
383 LVOptions ReaderOptions;
384 ReaderOptions.setAttributeOffset();
385 ReaderOptions.setPrintAll();
386
387 ReaderOptions.setSelectIgnoreCase();
388 ReaderOptions.setSelectUseRegex();
389
390 ReaderOptions.setReportList(); // Matched elements.
391 ReaderOptions.setReportView(); // Parents for matched elements.
392
393 // Add patterns.
394 ReaderOptions.Select.Generic.insert(key: "foo");
395 ReaderOptions.Select.Generic.insert(key: "movl[ \t]?%");
396 ReaderOptions.Select.Generic.insert(key: "INT[a-z]*");
397 ReaderOptions.Select.Generic.insert(key: "CONSTANT");
398
399 ReaderOptions.resolveDependencies();
400
401 std::vector<std::string> Objects;
402 ScopedPrinter W(outs());
403 LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
404
405 // Check logical elements selection.
406 {
407 std::vector<SelectionInfo> DataClang = {
408 {.Name: "* const int", .Function: &LVElement::getIsType},
409 {.Name: "CONSTANT", .Function: &LVElement::getIsSymbol},
410 {.Name: "INTEGER", .Function: &LVElement::getIsType},
411 {.Name: "INTPTR", .Function: &LVElement::getIsType},
412 {.Name: "ParamPtr", .Function: &LVElement::getIsSymbol},
413 {.Name: "const int", .Function: &LVElement::getIsType},
414 {.Name: "foo", .Function: &LVElement::getIsScope},
415 {.Name: "foo::?", .Function: &LVElement::getIsScope},
416 {.Name: "int", .Function: &LVElement::getIsType},
417 {.Name: "movl", .Function: &LVElement::getIsLine},
418 {.Name: "movl", .Function: &LVElement::getIsLine}};
419 std::unique_ptr<LVReader> Reader =
420 createReader(ReaderHandler, InputsDir, Filename: CodeViewClang);
421 checkElementSelection(Reader: Reader.get(), Data&: DataClang, Size: DataClang.size());
422 }
423 {
424 std::vector<SelectionInfo> DataMsvc = {
425 {.Name: "* const int", .Function: &LVElement::getIsType},
426 {.Name: "CONSTANT", .Function: &LVElement::getIsSymbol},
427 {.Name: "INTEGER", .Function: &LVElement::getIsType},
428 {.Name: "INTPTR", .Function: &LVElement::getIsType},
429 {.Name: "ParamPtr", .Function: &LVElement::getIsSymbol},
430 {.Name: "const int", .Function: &LVElement::getIsType},
431 {.Name: "foo", .Function: &LVElement::getIsScope},
432 {.Name: "foo::?", .Function: &LVElement::getIsScope},
433 {.Name: "int", .Function: &LVElement::getIsType},
434 {.Name: "movl", .Function: &LVElement::getIsLine}};
435 std::unique_ptr<LVReader> Reader =
436 createReader(ReaderHandler, InputsDir, Filename: CodeViewMsvc);
437 checkElementSelection(Reader: Reader.get(), Data&: DataMsvc, Size: DataMsvc.size());
438 }
439}
440
441// Compare logical elements.
442void compareElements(SmallString<128> &InputsDir) {
443 // Reader options.
444 LVOptions ReaderOptions;
445 ReaderOptions.setAttributeOffset();
446 ReaderOptions.setPrintLines();
447 ReaderOptions.setPrintSymbols();
448 ReaderOptions.setPrintTypes();
449 ReaderOptions.setCompareLines();
450 ReaderOptions.setCompareSymbols();
451 ReaderOptions.setCompareTypes();
452
453 ReaderOptions.resolveDependencies();
454
455 std::vector<std::string> Objects;
456 ScopedPrinter W(outs());
457 LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
458
459 // Check logical comparison.
460 std::unique_ptr<LVReader> Reference =
461 createReader(ReaderHandler, InputsDir, Filename: CodeViewClang);
462 std::unique_ptr<LVReader> Target =
463 createReader(ReaderHandler, InputsDir, Filename: CodeViewMsvc);
464 checkElementComparison(Reference: Reference.get(), Target: Target.get());
465}
466
467TEST(LogicalViewTest, CodeViewReader) {
468 // Initialize targets and assembly printers/parsers.
469 llvm::InitializeAllTargetInfos();
470 llvm::InitializeAllTargetMCs();
471 InitializeAllDisassemblers();
472
473 llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
474
475 // This test requires a x86-registered-target.
476 Triple TT;
477 TT.setArch(Kind: Triple::x86_64);
478 TT.setVendor(Triple::UnknownVendor);
479 TT.setOS(Triple::UnknownOS);
480
481 std::string TargetLookupError;
482 if (!TargetRegistry::lookupTarget(Triple: std::string(TT.str()), Error&: TargetLookupError))
483 return;
484
485 SmallString<128> InputsDir = unittest::getInputFileDirectory(Argv0: TestMainArgv0);
486
487 // Logical elements general properties and selection.
488 elementProperties(InputsDir);
489 elementSelection(InputsDir);
490
491 // Compare logical elements.
492 compareElements(InputsDir);
493}
494
495} // namespace
496

source code of llvm/unittests/DebugInfo/LogicalView/CodeViewReaderTest.cpp