1 | //===-- DWARFASTParserClangTests.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 "Plugins/SymbolFile/DWARF/DWARFASTParserClang.h" |
10 | #include "Plugins/SymbolFile/DWARF/DWARFCompileUnit.h" |
11 | #include "Plugins/SymbolFile/DWARF/DWARFDIE.h" |
12 | #include "TestingSupport/Symbol/ClangTestUtils.h" |
13 | #include "TestingSupport/Symbol/YAMLModuleTester.h" |
14 | #include "lldb/Core/Debugger.h" |
15 | #include "gmock/gmock.h" |
16 | #include "gtest/gtest.h" |
17 | |
18 | using namespace lldb; |
19 | using namespace lldb_private; |
20 | using namespace lldb_private::dwarf; |
21 | using namespace lldb_private::plugin::dwarf; |
22 | |
23 | namespace { |
24 | static std::once_flag debugger_initialize_flag; |
25 | |
26 | class DWARFASTParserClangTests : public testing::Test { |
27 | void SetUp() override { |
28 | std::call_once(once&: debugger_initialize_flag, |
29 | f: []() { Debugger::Initialize(load_plugin_callback: nullptr); }); |
30 | } |
31 | }; |
32 | |
33 | class DWARFASTParserClangStub : public DWARFASTParserClang { |
34 | public: |
35 | using DWARFASTParserClang::DWARFASTParserClang; |
36 | using DWARFASTParserClang::LinkDeclContextToDIE; |
37 | |
38 | std::vector<const clang::DeclContext *> GetDeclContextToDIEMapKeys() { |
39 | std::vector<const clang::DeclContext *> keys; |
40 | for (const auto &it : m_decl_ctx_to_die) |
41 | keys.push_back(x: it.first); |
42 | return keys; |
43 | } |
44 | }; |
45 | } // namespace |
46 | |
47 | // If your implementation needs to dereference the dummy pointers we are |
48 | // defining here, causing this test to fail, feel free to delete it. |
49 | TEST_F(DWARFASTParserClangTests, |
50 | EnsureAllDIEsInDeclContextHaveBeenParsedParsesOnlyMatchingEntries) { |
51 | |
52 | /// Auxiliary debug info. |
53 | const char *yamldata = R"( |
54 | --- !ELF |
55 | FileHeader: |
56 | Class: ELFCLASS64 |
57 | Data: ELFDATA2LSB |
58 | Type: ET_EXEC |
59 | Machine: EM_386 |
60 | DWARF: |
61 | debug_abbrev: |
62 | - Table: |
63 | - Code: 0x00000001 |
64 | Tag: DW_TAG_compile_unit |
65 | Children: DW_CHILDREN_yes |
66 | Attributes: |
67 | - Attribute: DW_AT_language |
68 | Form: DW_FORM_data2 |
69 | - Code: 0x00000002 |
70 | Tag: DW_TAG_base_type |
71 | Children: DW_CHILDREN_no |
72 | Attributes: |
73 | - Attribute: DW_AT_encoding |
74 | Form: DW_FORM_data1 |
75 | - Attribute: DW_AT_byte_size |
76 | Form: DW_FORM_data1 |
77 | debug_info: |
78 | - Version: 4 |
79 | AddrSize: 8 |
80 | Entries: |
81 | - AbbrCode: 0x00000001 |
82 | Values: |
83 | - Value: 0x000000000000000C |
84 | - AbbrCode: 0x00000002 |
85 | Values: |
86 | - Value: 0x0000000000000007 # DW_ATE_unsigned |
87 | - Value: 0x0000000000000004 |
88 | - AbbrCode: 0x00000002 |
89 | Values: |
90 | - Value: 0x0000000000000007 # DW_ATE_unsigned |
91 | - Value: 0x0000000000000008 |
92 | - AbbrCode: 0x00000002 |
93 | Values: |
94 | - Value: 0x0000000000000005 # DW_ATE_signed |
95 | - Value: 0x0000000000000008 |
96 | - AbbrCode: 0x00000002 |
97 | Values: |
98 | - Value: 0x0000000000000008 # DW_ATE_unsigned_char |
99 | - Value: 0x0000000000000001 |
100 | - AbbrCode: 0x00000000 |
101 | )" ; |
102 | |
103 | YAMLModuleTester t(yamldata); |
104 | ASSERT_TRUE((bool)t.GetDwarfUnit()); |
105 | |
106 | auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>(args: "ast" ); |
107 | auto &ast_ctx = *holder->GetAST(); |
108 | |
109 | DWARFASTParserClangStub ast_parser(ast_ctx); |
110 | |
111 | DWARFUnit *unit = t.GetDwarfUnit(); |
112 | const DWARFDebugInfoEntry *die_first = unit->DIE().GetDIE(); |
113 | const DWARFDebugInfoEntry *die_child0 = die_first->GetFirstChild(); |
114 | const DWARFDebugInfoEntry *die_child1 = die_child0->GetSibling(); |
115 | const DWARFDebugInfoEntry *die_child2 = die_child1->GetSibling(); |
116 | const DWARFDebugInfoEntry *die_child3 = die_child2->GetSibling(); |
117 | std::vector<DWARFDIE> dies = { |
118 | DWARFDIE(unit, die_child0), DWARFDIE(unit, die_child1), |
119 | DWARFDIE(unit, die_child2), DWARFDIE(unit, die_child3)}; |
120 | std::vector<clang::DeclContext *> decl_ctxs = { |
121 | (clang::DeclContext *)1LL, (clang::DeclContext *)2LL, |
122 | (clang::DeclContext *)2LL, (clang::DeclContext *)3LL}; |
123 | for (int i = 0; i < 4; ++i) |
124 | ast_parser.LinkDeclContextToDIE(decl_ctx: decl_ctxs[i], die: dies[i]); |
125 | ast_parser.EnsureAllDIEsInDeclContextHaveBeenParsed( |
126 | decl_context: CompilerDeclContext(nullptr, decl_ctxs[1])); |
127 | |
128 | EXPECT_THAT(ast_parser.GetDeclContextToDIEMapKeys(), |
129 | testing::UnorderedElementsAre(decl_ctxs[0], decl_ctxs[3])); |
130 | } |
131 | |
132 | TEST_F(DWARFASTParserClangTests, TestCallingConventionParsing) { |
133 | // Tests parsing DW_AT_calling_convention values. |
134 | |
135 | // The DWARF below just declares a list of function types with |
136 | // DW_AT_calling_convention on them. |
137 | const char *yamldata = R"( |
138 | --- !ELF |
139 | FileHeader: |
140 | Class: ELFCLASS32 |
141 | Data: ELFDATA2LSB |
142 | Type: ET_EXEC |
143 | Machine: EM_386 |
144 | DWARF: |
145 | debug_str: |
146 | - func1 |
147 | - func2 |
148 | - func3 |
149 | - func4 |
150 | - func5 |
151 | - func6 |
152 | - func7 |
153 | - func8 |
154 | - func9 |
155 | debug_abbrev: |
156 | - ID: 0 |
157 | Table: |
158 | - Code: 0x1 |
159 | Tag: DW_TAG_compile_unit |
160 | Children: DW_CHILDREN_yes |
161 | Attributes: |
162 | - Attribute: DW_AT_language |
163 | Form: DW_FORM_data2 |
164 | - Code: 0x2 |
165 | Tag: DW_TAG_subprogram |
166 | Children: DW_CHILDREN_no |
167 | Attributes: |
168 | - Attribute: DW_AT_low_pc |
169 | Form: DW_FORM_addr |
170 | - Attribute: DW_AT_high_pc |
171 | Form: DW_FORM_data4 |
172 | - Attribute: DW_AT_name |
173 | Form: DW_FORM_strp |
174 | - Attribute: DW_AT_calling_convention |
175 | Form: DW_FORM_data1 |
176 | - Attribute: DW_AT_external |
177 | Form: DW_FORM_flag_present |
178 | debug_info: |
179 | - Version: 4 |
180 | AddrSize: 4 |
181 | Entries: |
182 | - AbbrCode: 0x1 |
183 | Values: |
184 | - Value: 0xC |
185 | - AbbrCode: 0x2 |
186 | Values: |
187 | - Value: 0x0 |
188 | - Value: 0x5 |
189 | - Value: 0x00 |
190 | - Value: 0xCB |
191 | - Value: 0x1 |
192 | - AbbrCode: 0x2 |
193 | Values: |
194 | - Value: 0x10 |
195 | - Value: 0x5 |
196 | - Value: 0x06 |
197 | - Value: 0xB3 |
198 | - Value: 0x1 |
199 | - AbbrCode: 0x2 |
200 | Values: |
201 | - Value: 0x20 |
202 | - Value: 0x5 |
203 | - Value: 0x0C |
204 | - Value: 0xB1 |
205 | - Value: 0x1 |
206 | - AbbrCode: 0x2 |
207 | Values: |
208 | - Value: 0x30 |
209 | - Value: 0x5 |
210 | - Value: 0x12 |
211 | - Value: 0xC0 |
212 | - Value: 0x1 |
213 | - AbbrCode: 0x2 |
214 | Values: |
215 | - Value: 0x40 |
216 | - Value: 0x5 |
217 | - Value: 0x18 |
218 | - Value: 0xB2 |
219 | - Value: 0x1 |
220 | - AbbrCode: 0x2 |
221 | Values: |
222 | - Value: 0x50 |
223 | - Value: 0x5 |
224 | - Value: 0x1E |
225 | - Value: 0xC1 |
226 | - Value: 0x1 |
227 | - AbbrCode: 0x2 |
228 | Values: |
229 | - Value: 0x60 |
230 | - Value: 0x5 |
231 | - Value: 0x24 |
232 | - Value: 0xC2 |
233 | - Value: 0x1 |
234 | - AbbrCode: 0x2 |
235 | Values: |
236 | - Value: 0x70 |
237 | - Value: 0x5 |
238 | - Value: 0x2a |
239 | - Value: 0xEE |
240 | - Value: 0x1 |
241 | - AbbrCode: 0x2 |
242 | Values: |
243 | - Value: 0x80 |
244 | - Value: 0x5 |
245 | - Value: 0x30 |
246 | - Value: 0x01 |
247 | - Value: 0x1 |
248 | - AbbrCode: 0x0 |
249 | ... |
250 | )" ; |
251 | YAMLModuleTester t(yamldata); |
252 | |
253 | DWARFUnit *unit = t.GetDwarfUnit(); |
254 | ASSERT_NE(unit, nullptr); |
255 | const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE(); |
256 | ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit); |
257 | DWARFDIE cu_die(unit, cu_entry); |
258 | |
259 | auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>(args: "ast" ); |
260 | auto &ast_ctx = *holder->GetAST(); |
261 | DWARFASTParserClangStub ast_parser(ast_ctx); |
262 | |
263 | std::vector<std::string> found_function_types; |
264 | // The DWARF above is just a list of functions. Parse all of them to |
265 | // extract the function types and their calling convention values. |
266 | for (DWARFDIE func : cu_die.children()) { |
267 | ASSERT_EQ(func.Tag(), DW_TAG_subprogram); |
268 | SymbolContext sc; |
269 | bool new_type = false; |
270 | lldb::TypeSP type = ast_parser.ParseTypeFromDWARF(sc, die: func, type_is_new_ptr: &new_type); |
271 | found_function_types.push_back( |
272 | x: type->GetForwardCompilerType().GetTypeName().AsCString()); |
273 | } |
274 | |
275 | // Compare the parsed function types against the expected list of types. |
276 | const std::vector<std::string> expected_function_types = { |
277 | "void () __attribute__((regcall))" , |
278 | "void () __attribute__((fastcall))" , |
279 | "void () __attribute__((stdcall))" , |
280 | "void () __attribute__((vectorcall))" , |
281 | "void () __attribute__((pascal))" , |
282 | "void () __attribute__((ms_abi))" , |
283 | "void () __attribute__((sysv_abi))" , |
284 | "void ()" , // invalid calling convention. |
285 | "void ()" , // DW_CC_normal -> no attribute |
286 | }; |
287 | ASSERT_EQ(found_function_types, expected_function_types); |
288 | } |
289 | |
290 | struct : public testing::Test { |
291 | SubsystemRAII<FileSystem, HostInfo> ; |
292 | clang_utils::TypeSystemClangHolder ; |
293 | TypeSystemClang &; |
294 | |
295 | DWARFASTParserClang ; |
296 | () |
297 | : holder("dummy ASTContext" ), ts(*holder.GetAST()), parser(ts) {} |
298 | |
299 | /// Takes the given integer value, stores it in a DWARFFormValue and then |
300 | /// tries to extract the value back via |
301 | /// DWARFASTParserClang::ExtractIntFromFormValue. |
302 | /// Returns the string representation of the extracted value or the error |
303 | /// that was returned from ExtractIntFromFormValue. |
304 | llvm::Expected<std::string> (clang::QualType qt, uint64_t value) { |
305 | DWARFFormValue form_value; |
306 | form_value.SetUnsigned(value); |
307 | llvm::Expected<llvm::APInt> result = |
308 | parser.ExtractIntFromFormValue(int_type: ts.GetType(qt), form_value); |
309 | if (!result) |
310 | return result.takeError(); |
311 | llvm::SmallString<16> result_str; |
312 | result->toStringUnsigned(Str&: result_str); |
313 | return std::string(result_str.str()); |
314 | } |
315 | |
316 | /// Same as ExtractIntFromFormValueTest::Extract but takes a signed integer |
317 | /// and treats the result as a signed integer. |
318 | llvm::Expected<std::string> (clang::QualType qt, int64_t value) { |
319 | DWARFFormValue form_value; |
320 | form_value.SetSigned(value); |
321 | llvm::Expected<llvm::APInt> result = |
322 | parser.ExtractIntFromFormValue(int_type: ts.GetType(qt), form_value); |
323 | if (!result) |
324 | return result.takeError(); |
325 | llvm::SmallString<16> result_str; |
326 | result->toStringSigned(Str&: result_str); |
327 | return std::string(result_str.str()); |
328 | } |
329 | }; |
330 | |
331 | TEST_F(ExtractIntFromFormValueTest, TestBool) { |
332 | using namespace llvm; |
333 | clang::ASTContext &ast = ts.getASTContext(); |
334 | |
335 | EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 0), HasValue("0" )); |
336 | EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 1), HasValue("1" )); |
337 | EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 2), Failed()); |
338 | EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 3), Failed()); |
339 | } |
340 | |
341 | TEST_F(ExtractIntFromFormValueTest, TestInt) { |
342 | using namespace llvm; |
343 | |
344 | clang::ASTContext &ast = ts.getASTContext(); |
345 | |
346 | // Find the min/max values for 'int' on the current host target. |
347 | constexpr int64_t int_max = std::numeric_limits<int>::max(); |
348 | constexpr int64_t int_min = std::numeric_limits<int>::min(); |
349 | |
350 | // Check that the bit width of int matches the int width in our type system. |
351 | ASSERT_EQ(sizeof(int) * 8, ast.getIntWidth(ast.IntTy)); |
352 | |
353 | // Check values around int_min. |
354 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min - 2), llvm::Failed()); |
355 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min - 1), llvm::Failed()); |
356 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min), |
357 | HasValue(std::to_string(int_min))); |
358 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min + 1), |
359 | HasValue(std::to_string(int_min + 1))); |
360 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min + 2), |
361 | HasValue(std::to_string(int_min + 2))); |
362 | |
363 | // Check values around 0. |
364 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, -128), HasValue("-128" )); |
365 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, -10), HasValue("-10" )); |
366 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, -1), HasValue("-1" )); |
367 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 0), HasValue("0" )); |
368 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 1), HasValue("1" )); |
369 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 10), HasValue("10" )); |
370 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 128), HasValue("128" )); |
371 | |
372 | // Check values around int_max. |
373 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max - 2), |
374 | HasValue(std::to_string(int_max - 2))); |
375 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max - 1), |
376 | HasValue(std::to_string(int_max - 1))); |
377 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max), |
378 | HasValue(std::to_string(int_max))); |
379 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max + 1), llvm::Failed()); |
380 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max + 5), llvm::Failed()); |
381 | |
382 | // Check some values not near an edge case. |
383 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max / 2), |
384 | HasValue(std::to_string(int_max / 2))); |
385 | EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min / 2), |
386 | HasValue(std::to_string(int_min / 2))); |
387 | } |
388 | |
389 | TEST_F(ExtractIntFromFormValueTest, TestUnsignedInt) { |
390 | using namespace llvm; |
391 | |
392 | clang::ASTContext &ast = ts.getASTContext(); |
393 | constexpr uint64_t uint_max = std::numeric_limits<uint32_t>::max(); |
394 | |
395 | // Check values around 0. |
396 | EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, 0), HasValue("0" )); |
397 | EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, 1), HasValue("1" )); |
398 | EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, 1234), HasValue("1234" )); |
399 | |
400 | // Check some values not near an edge case. |
401 | EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max / 2), |
402 | HasValue(std::to_string(uint_max / 2))); |
403 | |
404 | // Check values around uint_max. |
405 | EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max - 2), |
406 | HasValue(std::to_string(uint_max - 2))); |
407 | EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max - 1), |
408 | HasValue(std::to_string(uint_max - 1))); |
409 | EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max), |
410 | HasValue(std::to_string(uint_max))); |
411 | EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max + 1), |
412 | llvm::Failed()); |
413 | EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max + 2), |
414 | llvm::Failed()); |
415 | } |
416 | |
417 | TEST_F(DWARFASTParserClangTests, TestDefaultTemplateParamParsing) { |
418 | // Tests parsing DW_AT_default_value for template parameters. |
419 | auto BufferOrError = llvm::MemoryBuffer::getFile( |
420 | Filename: GetInputFilePath(name: "DW_AT_default_value-test.yaml" ), /*IsText=*/true); |
421 | ASSERT_TRUE(BufferOrError); |
422 | YAMLModuleTester t(BufferOrError.get()->getBuffer()); |
423 | |
424 | DWARFUnit *unit = t.GetDwarfUnit(); |
425 | ASSERT_NE(unit, nullptr); |
426 | const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE(); |
427 | ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit); |
428 | DWARFDIE cu_die(unit, cu_entry); |
429 | |
430 | auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>(args: "ast" ); |
431 | auto &ast_ctx = *holder->GetAST(); |
432 | DWARFASTParserClangStub ast_parser(ast_ctx); |
433 | |
434 | llvm::SmallVector<lldb::TypeSP, 2> types; |
435 | for (DWARFDIE die : cu_die.children()) { |
436 | if (die.Tag() == DW_TAG_class_type) { |
437 | SymbolContext sc; |
438 | bool new_type = false; |
439 | types.push_back(Elt: ast_parser.ParseTypeFromDWARF(sc, die, type_is_new_ptr: &new_type)); |
440 | } |
441 | } |
442 | |
443 | ASSERT_EQ(types.size(), 3U); |
444 | |
445 | auto check_decl = [](auto const *decl) { |
446 | clang::ClassTemplateSpecializationDecl const *ctsd = |
447 | llvm::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(decl); |
448 | ASSERT_NE(ctsd, nullptr); |
449 | |
450 | auto const &args = ctsd->getTemplateArgs(); |
451 | ASSERT_GT(args.size(), 0U); |
452 | |
453 | for (auto const &arg : args.asArray()) { |
454 | EXPECT_TRUE(arg.getIsDefaulted()); |
455 | } |
456 | }; |
457 | |
458 | for (auto const &type_sp : types) { |
459 | ASSERT_NE(type_sp, nullptr); |
460 | auto const *decl = ClangUtil::GetAsTagDecl(type: type_sp->GetFullCompilerType()); |
461 | if (decl->getName() == "bar" || decl->getName() == "baz" ) { |
462 | check_decl(decl); |
463 | } |
464 | } |
465 | } |
466 | |