| //===-- DWARFASTParserClangTests.cpp --------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Plugins/SymbolFile/DWARF/DWARFASTParserClang.h" |
| #include "Plugins/SymbolFile/DWARF/DWARFCompileUnit.h" |
| #include "Plugins/SymbolFile/DWARF/DWARFDIE.h" |
| #include "TestingSupport/Symbol/ClangTestUtils.h" |
| #include "TestingSupport/Symbol/YAMLModuleTester.h" |
| #include "TestingSupport/TestUtilities.h" |
| #include "lldb/Core/Debugger.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| using namespace lldb_private::plugin::dwarf; |
| using namespace llvm::dwarf; |
| |
| namespace { |
| |
| class DWARFASTParserClangTests : public testing::Test { |
| void SetUp() override { |
| std::call_once(TestUtilities::g_debugger_initialize_flag, |
| []() { Debugger::Initialize(nullptr); }); |
| } |
| }; |
| |
| class DWARFASTParserClangStub : public DWARFASTParserClang { |
| public: |
| using DWARFASTParserClang::DWARFASTParserClang; |
| using DWARFASTParserClang::LinkDeclContextToDIE; |
| |
| std::vector<const clang::DeclContext *> GetDeclContextToDIEMapKeys() { |
| std::vector<const clang::DeclContext *> keys; |
| for (const auto &it : m_decl_ctx_to_die) |
| keys.push_back(it.first); |
| return keys; |
| } |
| }; |
| |
| /// Helper structure for DWARFASTParserClang tests that want to parse DWARF |
| /// generated using yaml2obj. On construction parses the supplied YAML data |
| /// into a DWARF module and thereafter vends a DWARFASTParserClang and |
| /// TypeSystemClang that are guaranteed to live for the duration of this object. |
| class DWARFASTParserClangYAMLTester { |
| public: |
| DWARFASTParserClangYAMLTester(llvm::StringRef yaml_data) |
| : m_module_tester(yaml_data) {} |
| |
| DWARFDIE GetCUDIE() { |
| DWARFUnit *unit = m_module_tester.GetDwarfUnit(); |
| assert(unit); |
| |
| const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE(); |
| assert(cu_entry->Tag() == DW_TAG_compile_unit); |
| |
| return DWARFDIE(unit, cu_entry); |
| } |
| |
| DWARFASTParserClang &GetParser() { |
| auto *parser = GetTypeSystem().GetDWARFParser(); |
| |
| assert(llvm::isa_and_nonnull<DWARFASTParserClang>(parser)); |
| |
| return *llvm::cast<DWARFASTParserClang>(parser); |
| } |
| |
| TypeSystemClang &GetTypeSystem() { |
| ModuleSP module_sp = m_module_tester.GetModule(); |
| assert(module_sp); |
| |
| SymbolFile *symfile = module_sp->GetSymbolFile(); |
| assert(symfile); |
| |
| TypeSystemSP ts_sp = llvm::cantFail( |
| symfile->GetTypeSystemForLanguage(lldb::eLanguageTypeC_plus_plus)); |
| |
| assert(llvm::isa_and_nonnull<TypeSystemClang>(ts_sp.get())); |
| |
| return llvm::cast<TypeSystemClang>(*ts_sp); |
| } |
| |
| private: |
| YAMLModuleTester m_module_tester; |
| }; |
| } // namespace |
| |
| // If your implementation needs to dereference the dummy pointers we are |
| // defining here, causing this test to fail, feel free to delete it. |
| TEST_F(DWARFASTParserClangTests, |
| EnsureAllDIEsInDeclContextHaveBeenParsedParsesOnlyMatchingEntries) { |
| |
| /// Auxiliary debug info. |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_386 |
| DWARF: |
| debug_abbrev: |
| - Table: |
| - Code: 0x00000001 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x00000002 |
| Tag: DW_TAG_base_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_encoding |
| Form: DW_FORM_data1 |
| - Attribute: DW_AT_byte_size |
| Form: DW_FORM_data1 |
| debug_info: |
| - Version: 4 |
| AddrSize: 8 |
| Entries: |
| - AbbrCode: 0x00000001 |
| Values: |
| - Value: 0x000000000000000C |
| - AbbrCode: 0x00000002 |
| Values: |
| - Value: 0x0000000000000007 # DW_ATE_unsigned |
| - Value: 0x0000000000000004 |
| - AbbrCode: 0x00000002 |
| Values: |
| - Value: 0x0000000000000007 # DW_ATE_unsigned |
| - Value: 0x0000000000000008 |
| - AbbrCode: 0x00000002 |
| Values: |
| - Value: 0x0000000000000005 # DW_ATE_signed |
| - Value: 0x0000000000000008 |
| - AbbrCode: 0x00000002 |
| Values: |
| - Value: 0x0000000000000008 # DW_ATE_unsigned_char |
| - Value: 0x0000000000000001 |
| - AbbrCode: 0x00000000 |
| )"; |
| YAMLModuleTester t(yamldata); |
| ASSERT_TRUE((bool)t.GetDwarfUnit()); |
| |
| auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("ast"); |
| auto &ast_ctx = *holder->GetAST(); |
| |
| DWARFASTParserClangStub ast_parser(ast_ctx); |
| |
| DWARFUnit *unit = t.GetDwarfUnit(); |
| const DWARFDebugInfoEntry *die_first = unit->DIE().GetDIE(); |
| const DWARFDebugInfoEntry *die_child0 = die_first->GetFirstChild(); |
| const DWARFDebugInfoEntry *die_child1 = die_child0->GetSibling(); |
| const DWARFDebugInfoEntry *die_child2 = die_child1->GetSibling(); |
| const DWARFDebugInfoEntry *die_child3 = die_child2->GetSibling(); |
| std::vector<DWARFDIE> dies = { |
| DWARFDIE(unit, die_child0), DWARFDIE(unit, die_child1), |
| DWARFDIE(unit, die_child2), DWARFDIE(unit, die_child3)}; |
| std::vector<clang::DeclContext *> decl_ctxs = { |
| (clang::DeclContext *)1LL, (clang::DeclContext *)2LL, |
| (clang::DeclContext *)2LL, (clang::DeclContext *)3LL}; |
| for (int i = 0; i < 4; ++i) |
| ast_parser.LinkDeclContextToDIE(decl_ctxs[i], dies[i]); |
| ast_parser.EnsureAllDIEsInDeclContextHaveBeenParsed( |
| CompilerDeclContext(nullptr, decl_ctxs[1])); |
| |
| EXPECT_THAT(ast_parser.GetDeclContextToDIEMapKeys(), |
| testing::UnorderedElementsAre(decl_ctxs[0], decl_ctxs[3])); |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestCallingConventionParsing) { |
| // Tests parsing DW_AT_calling_convention values. |
| |
| // The DWARF below just declares a list of function types with |
| // DW_AT_calling_convention on them. |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS32 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_386 |
| DWARF: |
| debug_str: |
| - func1 |
| - func2 |
| - func3 |
| - func4 |
| - func5 |
| - func6 |
| - func7 |
| - func8 |
| - func9 |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x1 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x2 |
| Tag: DW_TAG_subprogram |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_low_pc |
| Form: DW_FORM_addr |
| - Attribute: DW_AT_high_pc |
| Form: DW_FORM_data4 |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_calling_convention |
| Form: DW_FORM_data1 |
| - Attribute: DW_AT_external |
| Form: DW_FORM_flag_present |
| debug_info: |
| - Version: 4 |
| AddrSize: 4 |
| Entries: |
| - AbbrCode: 0x1 |
| Values: |
| - Value: 0xC |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x0 |
| - Value: 0x5 |
| - Value: 0x00 |
| - Value: 0xCB |
| - Value: 0x1 |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x10 |
| - Value: 0x5 |
| - Value: 0x06 |
| - Value: 0xB3 |
| - Value: 0x1 |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x20 |
| - Value: 0x5 |
| - Value: 0x0C |
| - Value: 0xB1 |
| - Value: 0x1 |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x30 |
| - Value: 0x5 |
| - Value: 0x12 |
| - Value: 0xC0 |
| - Value: 0x1 |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x40 |
| - Value: 0x5 |
| - Value: 0x18 |
| - Value: 0xB2 |
| - Value: 0x1 |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x50 |
| - Value: 0x5 |
| - Value: 0x1E |
| - Value: 0xC1 |
| - Value: 0x1 |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x60 |
| - Value: 0x5 |
| - Value: 0x24 |
| - Value: 0xC2 |
| - Value: 0x1 |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x70 |
| - Value: 0x5 |
| - Value: 0x2a |
| - Value: 0xEE |
| - Value: 0x1 |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x80 |
| - Value: 0x5 |
| - Value: 0x30 |
| - Value: 0x01 |
| - Value: 0x1 |
| - AbbrCode: 0x0 |
| ... |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| std::vector<std::string> found_function_types; |
| // The DWARF above is just a list of functions. Parse all of them to |
| // extract the function types and their calling convention values. |
| for (DWARFDIE func : cu_die.children()) { |
| ASSERT_EQ(func.Tag(), DW_TAG_subprogram); |
| SymbolContext sc; |
| bool new_type = false; |
| lldb::TypeSP type = |
| tester.GetParser().ParseTypeFromDWARF(sc, func, &new_type); |
| found_function_types.push_back( |
| type->GetForwardCompilerType().GetTypeName().GetString()); |
| } |
| |
| // Compare the parsed function types against the expected list of types. |
| const std::vector<std::string> expected_function_types = { |
| "void () __attribute__((regcall))", |
| "void () __attribute__((fastcall))", |
| "void () __attribute__((stdcall))", |
| "void () __attribute__((vectorcall))", |
| "void () __attribute__((pascal))", |
| "void () __attribute__((ms_abi))", |
| "void () __attribute__((sysv_abi))", |
| "void ()", // invalid calling convention. |
| "void ()", // DW_CC_normal -> no attribute |
| }; |
| ASSERT_EQ(found_function_types, expected_function_types); |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestPtrAuthParsing) { |
| // Tests parsing values with type DW_TAG_LLVM_ptrauth_type corresponding to |
| // explicitly signed raw function pointers |
| |
| // This is Dwarf for the following C code: |
| // ``` |
| // void (*__ptrauth(0, 0, 42) a)(); |
| // ``` |
| |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_AARCH64 |
| DWARF: |
| debug_str: |
| - a |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x01 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x02 |
| Tag: DW_TAG_variable |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| - Attribute: DW_AT_external |
| Form: DW_FORM_flag_present |
| - Code: 0x03 |
| Tag: DW_TAG_LLVM_ptrauth_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| - Attribute: DW_AT_LLVM_ptrauth_key |
| Form: DW_FORM_data1 |
| - Attribute: DW_AT_LLVM_ptrauth_extra_discriminator |
| Form: DW_FORM_data2 |
| - Code: 0x04 |
| Tag: DW_TAG_pointer_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| - Code: 0x05 |
| Tag: DW_TAG_subroutine_type |
| Children: DW_CHILDREN_yes |
| - Code: 0x06 |
| Tag: DW_TAG_unspecified_parameters |
| Children: DW_CHILDREN_no |
| |
| debug_info: |
| - Version: 5 |
| UnitType: DW_UT_compile |
| AddrSize: 8 |
| Entries: |
| # 0x0c: DW_TAG_compile_unit |
| # DW_AT_language [DW_FORM_data2] (DW_LANG_C99) |
| - AbbrCode: 0x01 |
| Values: |
| - Value: 0x0c |
| |
| # 0x0f: DW_TAG_variable |
| # DW_AT_name [DW_FORM_strp] (\"a\") |
| # DW_AT_type [DW_FORM_ref4] (0x00000018 \"void (*__ptrauth(0, 0, 0x02a)\") |
| # DW_AT_external [DW_FORM_flag_present] (true) |
| - AbbrCode: 0x02 |
| Values: |
| - Value: 0x00 |
| - Value: 0x18 |
| |
| # 0x18: DW_TAG_LLVM_ptrauth_type |
| # DW_AT_type [DW_FORM_ref4] (0x00000020 \"void (*)(...)\") |
| # DW_AT_LLVM_ptrauth_key [DW_FORM_data1] (0x00) |
| # DW_AT_LLVM_ptrauth_extra_discriminator [DW_FORM_data2] (0x002a) |
| - AbbrCode: 0x03 |
| Values: |
| - Value: 0x20 |
| - Value: 0x00 |
| - Value: 0x2a |
| |
| # 0x20: DW_TAG_pointer_type |
| # DW_AT_type [DW_AT_type [DW_FORM_ref4] (0x00000025 \"void (...)\") |
| - AbbrCode: 0x04 |
| Values: |
| - Value: 0x25 |
| |
| # 0x25: DW_TAG_subroutine_type |
| - AbbrCode: 0x05 |
| |
| # 0x26: DW_TAG_unspecified_parameters |
| - AbbrCode: 0x06 |
| |
| - AbbrCode: 0x00 # end of child tags of 0x25 |
| - AbbrCode: 0x00 # end of child tags of 0x0c |
| ... |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| DWARFDIE ptrauth_variable = cu_die.GetFirstChild(); |
| ASSERT_EQ(ptrauth_variable.Tag(), DW_TAG_variable); |
| DWARFDIE ptrauth_type = |
| ptrauth_variable.GetAttributeValueAsReferenceDIE(DW_AT_type); |
| ASSERT_EQ(ptrauth_type.Tag(), DW_TAG_LLVM_ptrauth_type); |
| |
| SymbolContext sc; |
| bool new_type = false; |
| lldb::TypeSP type_sp = |
| tester.GetParser().ParseTypeFromDWARF(sc, ptrauth_type, &new_type); |
| CompilerType compiler_type = type_sp->GetForwardCompilerType(); |
| ASSERT_EQ(compiler_type.GetPtrAuthKey(), 0U); |
| ASSERT_EQ(compiler_type.GetPtrAuthAddressDiversity(), false); |
| ASSERT_EQ(compiler_type.GetPtrAuthDiscriminator(), 42U); |
| } |
| |
| struct ExtractIntFromFormValueTest : public testing::Test { |
| SubsystemRAII<FileSystem, HostInfo> subsystems; |
| clang_utils::TypeSystemClangHolder holder; |
| TypeSystemClang &ts; |
| |
| DWARFASTParserClang parser; |
| ExtractIntFromFormValueTest() |
| : holder("dummy ASTContext"), ts(*holder.GetAST()), parser(ts) {} |
| |
| /// Takes the given integer value, stores it in a DWARFFormValue and then |
| /// tries to extract the value back via |
| /// DWARFASTParserClang::ExtractIntFromFormValue. |
| /// Returns the string representation of the extracted value or the error |
| /// that was returned from ExtractIntFromFormValue. |
| llvm::Expected<std::string> Extract(clang::QualType qt, uint64_t value) { |
| DWARFFormValue form_value; |
| form_value.SetUnsigned(value); |
| llvm::Expected<llvm::APInt> result = |
| parser.ExtractIntFromFormValue(ts.GetType(qt), form_value); |
| if (!result) |
| return result.takeError(); |
| llvm::SmallString<16> result_str; |
| result->toStringUnsigned(result_str); |
| return std::string(result_str.str()); |
| } |
| |
| /// Same as ExtractIntFromFormValueTest::Extract but takes a signed integer |
| /// and treats the result as a signed integer. |
| llvm::Expected<std::string> ExtractS(clang::QualType qt, int64_t value) { |
| DWARFFormValue form_value; |
| form_value.SetSigned(value); |
| llvm::Expected<llvm::APInt> result = |
| parser.ExtractIntFromFormValue(ts.GetType(qt), form_value); |
| if (!result) |
| return result.takeError(); |
| llvm::SmallString<16> result_str; |
| result->toStringSigned(result_str); |
| return std::string(result_str.str()); |
| } |
| }; |
| |
| TEST_F(ExtractIntFromFormValueTest, TestBool) { |
| using namespace llvm; |
| clang::ASTContext &ast = ts.getASTContext(); |
| |
| EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 0), HasValue("0")); |
| EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 1), HasValue("1")); |
| EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 2), Failed()); |
| EXPECT_THAT_EXPECTED(Extract(ast.BoolTy, 3), Failed()); |
| } |
| |
| TEST_F(ExtractIntFromFormValueTest, TestInt) { |
| using namespace llvm; |
| |
| clang::ASTContext &ast = ts.getASTContext(); |
| |
| // Find the min/max values for 'int' on the current host target. |
| constexpr int64_t int_max = std::numeric_limits<int>::max(); |
| constexpr int64_t int_min = std::numeric_limits<int>::min(); |
| |
| // Check that the bit width of int matches the int width in our type system. |
| ASSERT_EQ(sizeof(int) * 8, ast.getIntWidth(ast.IntTy)); |
| |
| // Check values around int_min. |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min - 2), llvm::Failed()); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min - 1), llvm::Failed()); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min), |
| HasValue(std::to_string(int_min))); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min + 1), |
| HasValue(std::to_string(int_min + 1))); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min + 2), |
| HasValue(std::to_string(int_min + 2))); |
| |
| // Check values around 0. |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, -128), HasValue("-128")); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, -10), HasValue("-10")); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, -1), HasValue("-1")); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 0), HasValue("0")); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 1), HasValue("1")); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 10), HasValue("10")); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, 128), HasValue("128")); |
| |
| // Check values around int_max. |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max - 2), |
| HasValue(std::to_string(int_max - 2))); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max - 1), |
| HasValue(std::to_string(int_max - 1))); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max), |
| HasValue(std::to_string(int_max))); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max + 1), llvm::Failed()); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max + 5), llvm::Failed()); |
| |
| // Check some values not near an edge case. |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_max / 2), |
| HasValue(std::to_string(int_max / 2))); |
| EXPECT_THAT_EXPECTED(ExtractS(ast.IntTy, int_min / 2), |
| HasValue(std::to_string(int_min / 2))); |
| } |
| |
| TEST_F(ExtractIntFromFormValueTest, TestUnsignedInt) { |
| using namespace llvm; |
| |
| clang::ASTContext &ast = ts.getASTContext(); |
| constexpr uint64_t uint_max = std::numeric_limits<uint32_t>::max(); |
| |
| // Check values around 0. |
| EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, 0), HasValue("0")); |
| EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, 1), HasValue("1")); |
| EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, 1234), HasValue("1234")); |
| |
| // Check some values not near an edge case. |
| EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max / 2), |
| HasValue(std::to_string(uint_max / 2))); |
| |
| // Check values around uint_max. |
| EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max - 2), |
| HasValue(std::to_string(uint_max - 2))); |
| EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max - 1), |
| HasValue(std::to_string(uint_max - 1))); |
| EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max), |
| HasValue(std::to_string(uint_max))); |
| EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max + 1), |
| llvm::Failed()); |
| EXPECT_THAT_EXPECTED(Extract(ast.UnsignedIntTy, uint_max + 2), |
| llvm::Failed()); |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestDefaultTemplateParamParsing) { |
| // Tests parsing DW_AT_default_value for template parameters. |
| auto BufferOrError = llvm::MemoryBuffer::getFile( |
| GetInputFilePath("DW_AT_default_value-test.yaml"), /*IsText=*/true); |
| ASSERT_TRUE(BufferOrError); |
| |
| DWARFASTParserClangYAMLTester tester(BufferOrError.get()->getBuffer()); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| llvm::SmallVector<lldb::TypeSP, 2> types; |
| for (DWARFDIE die : cu_die.children()) { |
| if (die.Tag() == DW_TAG_class_type) { |
| SymbolContext sc; |
| bool new_type = false; |
| types.push_back( |
| tester.GetParser().ParseTypeFromDWARF(sc, die, &new_type)); |
| } |
| } |
| |
| ASSERT_EQ(types.size(), 3U); |
| |
| auto check_decl = [](auto const *decl) { |
| clang::ClassTemplateSpecializationDecl const *ctsd = |
| llvm::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(decl); |
| ASSERT_NE(ctsd, nullptr); |
| |
| auto const &args = ctsd->getTemplateArgs(); |
| ASSERT_GT(args.size(), 0U); |
| |
| for (auto const &arg : args.asArray()) { |
| EXPECT_TRUE(arg.getIsDefaulted()); |
| } |
| }; |
| |
| for (auto const &type_sp : types) { |
| ASSERT_NE(type_sp, nullptr); |
| auto const *decl = ClangUtil::GetAsTagDecl(type_sp->GetFullCompilerType()); |
| if (decl->getName() == "bar" || decl->getName() == "baz") { |
| check_decl(decl); |
| } |
| } |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestSpecDeclExistsError) { |
| // Tests that parsing a ClassTemplateSpecializationDecl that already exists |
| // is handled gracefully. |
| auto BufferOrError = llvm::MemoryBuffer::getFile( |
| GetInputFilePath("DW_AT_spec_decl_exists-test.yaml"), /*IsText=*/true); |
| ASSERT_TRUE(BufferOrError); |
| DWARFASTParserClangYAMLTester tester(BufferOrError.get()->getBuffer()); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| llvm::SmallVector<lldb::TypeSP, 2> specializations; |
| for (DWARFDIE die : cu_die.children()) { |
| SymbolContext sc; |
| bool new_type = false; |
| auto type = tester.GetParser().ParseTypeFromDWARF(sc, die, &new_type); |
| llvm::StringRef die_name = llvm::StringRef(die.GetName()); |
| if (die_name.starts_with("_Optional_payload")) { |
| specializations.push_back(std::move(type)); |
| } |
| } |
| |
| ASSERT_EQ(specializations.size(), 2U); |
| ASSERT_NE(specializations[0], nullptr); |
| ASSERT_EQ(specializations[1], nullptr); |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestUniqueDWARFASTTypeMap_CppInsertMapFind) { |
| // This tests the behaviour of UniqueDWARFASTTypeMap under |
| // following scenario: |
| // 1. DWARFASTParserClang parses a forward declaration and |
| // inserts it into the UniqueDWARFASTTypeMap. |
| // 2. We then MapDeclDIEToDefDIE which updates the map |
| // entry with the line number/file information of the definition. |
| // 3. Parse the definition DIE, which should return the previously |
| // parsed type from the UniqueDWARFASTTypeMap. |
| |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_AARCH64 |
| DWARF: |
| debug_str: |
| - Foo |
| |
| debug_line: |
| - Version: 4 |
| MinInstLength: 1 |
| MaxOpsPerInst: 1 |
| DefaultIsStmt: 1 |
| LineBase: 0 |
| LineRange: 0 |
| Files: |
| - Name: main.cpp |
| DirIdx: 0 |
| ModTime: 0 |
| Length: 0 |
| |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x01 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Attribute: DW_AT_stmt_list |
| Form: DW_FORM_sec_offset |
| - Code: 0x02 |
| Tag: DW_TAG_structure_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_declaration |
| Form: DW_FORM_flag_present |
| - Code: 0x03 |
| Tag: DW_TAG_structure_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_decl_file |
| Form: DW_FORM_data1 |
| - Attribute: DW_AT_decl_line |
| Form: DW_FORM_data1 |
| |
| debug_info: |
| - Version: 5 |
| UnitType: DW_UT_compile |
| AddrSize: 8 |
| Entries: |
| # 0x0c: DW_TAG_compile_unit |
| # DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus) |
| # DW_AT_stmt_list [DW_FORM_sec_offset] |
| - AbbrCode: 0x01 |
| Values: |
| - Value: 0x04 |
| - Value: 0x0000000000000000 |
| |
| # 0x0d: DW_TAG_structure_type |
| # DW_AT_name [DW_FORM_strp] (\"Foo\") |
| # DW_AT_declaration [DW_FORM_flag_present] (true) |
| - AbbrCode: 0x02 |
| Values: |
| - Value: 0x00 |
| |
| # 0x0f: DW_TAG_structure_type |
| # DW_AT_name [DW_FORM_strp] (\"Foo\") |
| # DW_AT_decl_file [DW_FORM_data1] (main.cpp) |
| # DW_AT_decl_line [DW_FORM_data1] (3) |
| - AbbrCode: 0x03 |
| Values: |
| - Value: 0x00 |
| - Value: 0x01 |
| - Value: 0x03 |
| |
| - AbbrCode: 0x00 # end of child tags of 0x0c |
| ... |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| DWARFDIE decl_die; |
| DWARFDIE def_die; |
| for (auto const &die : cu_die.children()) { |
| if (die.Tag() != DW_TAG_structure_type) |
| continue; |
| |
| if (die.GetAttributeValueAsOptionalUnsigned(llvm::dwarf::DW_AT_declaration)) |
| decl_die = die; |
| else |
| def_die = die; |
| } |
| |
| ASSERT_TRUE(decl_die.IsValid()); |
| ASSERT_TRUE(def_die.IsValid()); |
| ASSERT_NE(decl_die, def_die); |
| |
| ParsedDWARFTypeAttributes attrs(def_die); |
| ASSERT_TRUE(attrs.decl.IsValid()); |
| |
| DWARFASTParserClang &ast_parser = tester.GetParser(); |
| |
| SymbolContext sc; |
| bool new_type = false; |
| lldb::TypeSP type_sp = ast_parser.ParseTypeFromDWARF(sc, decl_die, &new_type); |
| ASSERT_NE(type_sp, nullptr); |
| |
| ast_parser.MapDeclDIEToDefDIE(decl_die, def_die); |
| |
| lldb::TypeSP reparsed_type_sp = |
| ast_parser.ParseTypeFromDWARF(sc, def_die, &new_type); |
| ASSERT_NE(reparsed_type_sp, nullptr); |
| |
| ASSERT_EQ(type_sp, reparsed_type_sp); |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestObjectPointer) { |
| // This tests the behaviour of DWARFASTParserClang |
| // for DW_TAG_subprogram definitions which have a DW_AT_object_pointer |
| // *and* a DW_AT_specification that also has a DW_AT_object_pointer. |
| // We don't want the declaration DW_AT_object_pointer to overwrite the |
| // one from the more specific definition's. |
| |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_AARCH64 |
| DWARF: |
| debug_str: |
| - Context |
| - func |
| - this |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x1 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x2 |
| Tag: DW_TAG_structure_type |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Code: 0x3 |
| Tag: DW_TAG_subprogram |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_declaration |
| Form: DW_FORM_flag_present |
| - Attribute: DW_AT_object_pointer |
| Form: DW_FORM_ref4 |
| - Attribute: DW_AT_artificial |
| Form: DW_FORM_flag_present |
| - Attribute: DW_AT_external |
| Form: DW_FORM_flag_present |
| - Code: 0x4 |
| Tag: DW_TAG_formal_parameter |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_artificial |
| Form: DW_FORM_flag_present |
| - Code: 0x5 |
| Tag: DW_TAG_subprogram |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_object_pointer |
| Form: DW_FORM_ref4 |
| - Attribute: DW_AT_specification |
| Form: DW_FORM_ref4 |
| - Code: 0x6 |
| Tag: DW_TAG_formal_parameter |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_artificial |
| Form: DW_FORM_flag_present |
| debug_info: |
| - Version: 5 |
| UnitType: DW_UT_compile |
| AddrSize: 8 |
| Entries: |
| |
| # DW_TAG_compile_unit |
| # DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus) |
| |
| - AbbrCode: 0x1 |
| Values: |
| - Value: 0x04 |
| |
| # DW_TAG_structure_type |
| # DW_AT_name [DW_FORM_strp] ("Context") |
| |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x0 |
| |
| # DW_TAG_subprogram |
| # DW_AT_name [DW_FORM_strp] ("func") |
| # DW_AT_object_pointer [DW_FORM_ref4] |
| - AbbrCode: 0x3 |
| Values: |
| - Value: 0x8 |
| - Value: 0x1 |
| - Value: 0x1d |
| - Value: 0x1 |
| - Value: 0x1 |
| |
| # DW_TAG_formal_parameter |
| # DW_AT_artificial |
| - AbbrCode: 0x4 |
| Values: |
| - Value: 0x1 |
| |
| - AbbrCode: 0x0 |
| - AbbrCode: 0x0 |
| |
| # DW_TAG_subprogram |
| # DW_AT_object_pointer [DW_FORM_ref4] ("this") |
| # DW_AT_specification [DW_FORM_ref4] ("func") |
| - AbbrCode: 0x5 |
| Values: |
| - Value: 0x29 |
| - Value: 0x14 |
| |
| # DW_TAG_formal_parameter |
| # DW_AT_name [DW_FORM_strp] ("this") |
| # DW_AT_artificial |
| - AbbrCode: 0x6 |
| Values: |
| - Value: 0xd |
| - Value: 0x1 |
| |
| - AbbrCode: 0x0 |
| - AbbrCode: 0x0 |
| ... |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| auto context_die = cu_die.GetFirstChild(); |
| ASSERT_TRUE(context_die.IsValid()); |
| ASSERT_EQ(context_die.Tag(), DW_TAG_structure_type); |
| |
| { |
| auto decl_die = context_die.GetFirstChild(); |
| ASSERT_TRUE(decl_die.IsValid()); |
| ASSERT_EQ(decl_die.Tag(), DW_TAG_subprogram); |
| ASSERT_TRUE(decl_die.GetAttributeValueAsOptionalUnsigned(DW_AT_external)); |
| |
| auto param_die = decl_die.GetFirstChild(); |
| ASSERT_TRUE(param_die.IsValid()); |
| |
| EXPECT_EQ(param_die, |
| tester.GetParser().GetObjectParameter(decl_die, context_die)); |
| } |
| |
| { |
| auto subprogram_definition = context_die.GetSibling(); |
| ASSERT_TRUE(subprogram_definition.IsValid()); |
| ASSERT_EQ(subprogram_definition.Tag(), DW_TAG_subprogram); |
| ASSERT_FALSE(subprogram_definition.GetAttributeValueAsOptionalUnsigned( |
| DW_AT_external)); |
| |
| auto param_die = subprogram_definition.GetFirstChild(); |
| ASSERT_TRUE(param_die.IsValid()); |
| |
| EXPECT_EQ(param_die, tester.GetParser().GetObjectParameter( |
| subprogram_definition, context_die)); |
| } |
| } |
| |
| TEST_F(DWARFASTParserClangTests, |
| TestObjectPointer_NoSpecificationOnDefinition) { |
| // This tests the behaviour of DWARFASTParserClang |
| // for DW_TAG_subprogram definitions which have a DW_AT_object_pointer |
| // but no DW_AT_specification that would link back to its declaration. |
| // This is how Objective-C class method definitions are emitted. |
| |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_AARCH64 |
| DWARF: |
| debug_str: |
| - Context |
| - func |
| - this |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x1 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x2 |
| Tag: DW_TAG_structure_type |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Code: 0x3 |
| Tag: DW_TAG_subprogram |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_declaration |
| Form: DW_FORM_flag_present |
| - Attribute: DW_AT_object_pointer |
| Form: DW_FORM_ref4 |
| - Attribute: DW_AT_artificial |
| Form: DW_FORM_flag_present |
| - Attribute: DW_AT_external |
| Form: DW_FORM_flag_present |
| - Code: 0x4 |
| Tag: DW_TAG_formal_parameter |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_artificial |
| Form: DW_FORM_flag_present |
| - Code: 0x5 |
| Tag: DW_TAG_subprogram |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_object_pointer |
| Form: DW_FORM_ref4 |
| - Code: 0x6 |
| Tag: DW_TAG_formal_parameter |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_artificial |
| Form: DW_FORM_flag_present |
| debug_info: |
| - Version: 5 |
| UnitType: DW_UT_compile |
| AddrSize: 8 |
| Entries: |
| |
| # DW_TAG_compile_unit |
| # DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus) |
| |
| - AbbrCode: 0x1 |
| Values: |
| - Value: 0x04 |
| |
| # DW_TAG_structure_type |
| # DW_AT_name [DW_FORM_strp] ("Context") |
| |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x0 |
| |
| # DW_TAG_subprogram |
| # DW_AT_name [DW_FORM_strp] ("func") |
| # DW_AT_object_pointer [DW_FORM_ref4] |
| - AbbrCode: 0x3 |
| Values: |
| - Value: 0x8 |
| - Value: 0x1 |
| - Value: 0x1d |
| - Value: 0x1 |
| - Value: 0x1 |
| |
| # DW_TAG_formal_parameter |
| # DW_AT_artificial |
| - AbbrCode: 0x4 |
| Values: |
| - Value: 0x1 |
| |
| - AbbrCode: 0x0 |
| - AbbrCode: 0x0 |
| |
| # DW_TAG_subprogram |
| # DW_AT_object_pointer [DW_FORM_ref4] ("this") |
| - AbbrCode: 0x5 |
| Values: |
| - Value: 0x25 |
| |
| # DW_TAG_formal_parameter |
| # DW_AT_name [DW_FORM_strp] ("this") |
| # DW_AT_artificial |
| - AbbrCode: 0x6 |
| Values: |
| - Value: 0xd |
| - Value: 0x1 |
| |
| - AbbrCode: 0x0 |
| - AbbrCode: 0x0 |
| ... |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| auto context_die = cu_die.GetFirstChild(); |
| ASSERT_TRUE(context_die.IsValid()); |
| ASSERT_EQ(context_die.Tag(), DW_TAG_structure_type); |
| |
| auto subprogram_definition = context_die.GetSibling(); |
| ASSERT_TRUE(subprogram_definition.IsValid()); |
| ASSERT_EQ(subprogram_definition.Tag(), DW_TAG_subprogram); |
| ASSERT_FALSE(subprogram_definition.GetAttributeValueAsOptionalUnsigned( |
| DW_AT_external)); |
| ASSERT_FALSE( |
| subprogram_definition.GetAttributeValueAsReferenceDIE(DW_AT_specification) |
| .IsValid()); |
| |
| auto param_die = subprogram_definition.GetFirstChild(); |
| ASSERT_TRUE(param_die.IsValid()); |
| EXPECT_EQ(param_die, |
| tester.GetParser().GetObjectParameter(subprogram_definition, {})); |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestParseSubroutine_ExplicitObjectParameter) { |
| // Tests parsing of a C++ non-static member function with an explicit object |
| // parameter that isn't called "this" and is not a pointer (but a CV-qualified |
| // rvalue reference instead). |
| |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_AARCH64 |
| DWARF: |
| debug_str: |
| - Context |
| - func |
| - mySelf |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x1 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x2 |
| Tag: DW_TAG_structure_type |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Code: 0x3 |
| Tag: DW_TAG_subprogram |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_declaration |
| Form: DW_FORM_flag_present |
| - Attribute: DW_AT_object_pointer |
| Form: DW_FORM_ref4 |
| - Attribute: DW_AT_external |
| Form: DW_FORM_flag_present |
| - Code: 0x4 |
| Tag: DW_TAG_formal_parameter |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| - Code: 0x5 |
| Tag: DW_TAG_rvalue_reference_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| - Code: 0x6 |
| Tag: DW_TAG_const_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| - Code: 0x7 |
| Tag: DW_TAG_volatile_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| debug_info: |
| - Version: 5 |
| UnitType: DW_UT_compile |
| AddrSize: 8 |
| Entries: |
| |
| # DW_TAG_compile_unit |
| # DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus) |
| |
| - AbbrCode: 0x1 |
| Values: |
| - Value: 0x04 |
| |
| # DW_TAG_structure_type |
| # DW_AT_name [DW_FORM_strp] ("Context") |
| |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x0 |
| |
| # DW_TAG_subprogram |
| # DW_AT_name [DW_FORM_strp] ("func") |
| # DW_AT_object_pointer [DW_FORM_ref4] |
| - AbbrCode: 0x3 |
| Values: |
| - Value: 0x8 |
| - Value: 0x1 |
| - Value: 0x1d |
| - Value: 0x1 |
| |
| # DW_TAG_formal_parameter |
| # DW_AT_name [DW_FORM_strp] ("mySelf") |
| # DW_AT_type [DW_FORM_ref4] (const volatile Context &&) |
| - AbbrCode: 0x4 |
| Values: |
| - Value: 0xd |
| - Value: 0x28 |
| |
| - AbbrCode: 0x0 |
| - AbbrCode: 0x0 |
| |
| # DW_TAG_rvalue_reference_type |
| # DW_AT_type [DW_FORM_ref4] ("const volatile Context") |
| |
| - AbbrCode: 0x5 |
| Values: |
| - Value: 0x2d |
| |
| # DW_TAG_const_type |
| # DW_AT_type [DW_FORM_ref4] ("volatile Context") |
| |
| - AbbrCode: 0x6 |
| Values: |
| - Value: 0x32 |
| |
| # DW_TAG_volatile_type |
| # DW_AT_type [DW_FORM_ref4] ("Context") |
| |
| - AbbrCode: 0x7 |
| Values: |
| - Value: 0xf |
| |
| - AbbrCode: 0x0 |
| ... |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| auto ts_or_err = |
| cu_die.GetDWARF()->GetTypeSystemForLanguage(eLanguageTypeC_plus_plus); |
| ASSERT_TRUE(static_cast<bool>(ts_or_err)); |
| llvm::consumeError(ts_or_err.takeError()); |
| auto *parser = |
| llvm::cast<DWARFASTParserClang>((*ts_or_err)->GetDWARFParser()); |
| |
| auto context_die = cu_die.GetFirstChild(); |
| ASSERT_TRUE(context_die.IsValid()); |
| ASSERT_EQ(context_die.Tag(), DW_TAG_structure_type); |
| |
| SymbolContext sc; |
| bool new_type; |
| auto context_type_sp = parser->ParseTypeFromDWARF(sc, context_die, &new_type); |
| ASSERT_NE(context_type_sp, nullptr); |
| |
| ASSERT_TRUE( |
| parser->CompleteTypeFromDWARF(context_die, context_type_sp.get(), |
| context_type_sp->GetForwardCompilerType())); |
| |
| auto *record_decl = llvm::dyn_cast_or_null<clang::CXXRecordDecl>( |
| ClangUtil::GetAsTagDecl(context_type_sp->GetForwardCompilerType())); |
| ASSERT_NE(record_decl, nullptr); |
| |
| auto method_it = record_decl->method_begin(); |
| ASSERT_NE(method_it, record_decl->method_end()); |
| |
| // Check that we didn't parse the function as static. |
| EXPECT_FALSE(method_it->isStatic()); |
| |
| // Check that method qualifiers were correctly set. |
| EXPECT_EQ(method_it->getMethodQualifiers(), |
| clang::Qualifiers::fromCVRMask(clang::Qualifiers::Const | |
| clang::Qualifiers::Volatile)); |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestParseSubroutine_ParameterCreation) { |
| // Tests parsing of a C++ free function will create clang::ParmVarDecls with |
| // the correct clang::DeclContext. |
| // |
| // Also ensures we attach names to the ParmVarDecls (even when DWARF contains |
| // a mix of named/unnamed parameters). |
| |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_AARCH64 |
| DWARF: |
| debug_str: |
| - func |
| - int |
| - short |
| - namedParam |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x1 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x2 |
| Tag: DW_TAG_structure_type |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Code: 0x3 |
| Tag: DW_TAG_subprogram |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_declaration |
| Form: DW_FORM_flag_present |
| - Attribute: DW_AT_external |
| Form: DW_FORM_flag_present |
| - Code: 0x4 |
| Tag: DW_TAG_formal_parameter |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| - Code: 0x5 |
| Tag: DW_TAG_formal_parameter |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Code: 0x6 |
| Tag: DW_TAG_base_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_encoding |
| Form: DW_FORM_data1 |
| - Attribute: DW_AT_byte_size |
| Form: DW_FORM_data1 |
| debug_info: |
| - Version: 5 |
| UnitType: DW_UT_compile |
| AddrSize: 8 |
| Entries: |
| |
| # DW_TAG_compile_unit |
| # DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus) |
| |
| - AbbrCode: 0x1 |
| Values: |
| - Value: 0x04 |
| |
| # DW_TAG_subprogram |
| # DW_AT_name [DW_FORM_strp] ("func") |
| - AbbrCode: 0x3 |
| Values: |
| - Value: 0x0 |
| - Value: 0x1 |
| - Value: 0x1 |
| |
| # DW_TAG_formal_parameter |
| # DW_AT_type [DW_FORM_ref4] (int) |
| - AbbrCode: 0x4 |
| Values: |
| - Value: 0x23 |
| |
| # DW_TAG_formal_parameter |
| # DW_AT_type [DW_FORM_ref4] (short) |
| # DW_AT_name [DW_FORM_strp] ("namedParam") |
| - AbbrCode: 0x5 |
| Values: |
| - Value: 0x2a |
| - Value: 0xf |
| |
| - AbbrCode: 0x0 |
| |
| # DW_TAG_base_type |
| # DW_AT_name [DW_FORM_strp] ("int") |
| # DW_AT_encoding [DW_FORM_data1] |
| # DW_AT_byte_size [DW_FORM_data1] |
| |
| - AbbrCode: 0x6 |
| Values: |
| - Value: 0x0000000000000005 |
| - Value: 0x0000000000000005 # DW_ATE_signed |
| - Value: 0x0000000000000004 |
| |
| # DW_TAG_base_type |
| # DW_AT_name [DW_FORM_strp] ("short") |
| # DW_AT_encoding [DW_FORM_data1] |
| # DW_AT_byte_size [DW_FORM_data1] |
| |
| - AbbrCode: 0x6 |
| Values: |
| - Value: 0x0000000000000009 |
| - Value: 0x0000000000000005 # DW_ATE_signed |
| - Value: 0x0000000000000004 |
| |
| - AbbrCode: 0x0 |
| ... |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| auto subprogram = cu_die.GetFirstChild(); |
| ASSERT_TRUE(subprogram.IsValid()); |
| ASSERT_EQ(subprogram.Tag(), DW_TAG_subprogram); |
| |
| SymbolContext sc; |
| bool new_type; |
| auto type_sp = |
| tester.GetParser().ParseTypeFromDWARF(sc, subprogram, &new_type); |
| ASSERT_NE(type_sp, nullptr); |
| |
| TypeSystemClang &ts = tester.GetTypeSystem(); |
| auto result = ts.GetTranslationUnitDecl()->lookup( |
| clang_utils::getDeclarationName(ts, "func")); |
| ASSERT_TRUE(result.isSingleResult()); |
| |
| auto const *func = llvm::cast<clang::FunctionDecl>(result.front()); |
| |
| EXPECT_EQ(func->getNumParams(), 2U); |
| EXPECT_EQ(func->getParamDecl(0)->getDeclContext(), func); |
| EXPECT_TRUE(func->getParamDecl(0)->getName().empty()); |
| EXPECT_EQ(func->getParamDecl(1)->getDeclContext(), func); |
| EXPECT_EQ(func->getParamDecl(1)->getName(), "namedParam"); |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestObjectPointer_IndexEncoding) { |
| // This tests the behaviour of DWARFASTParserClang |
| // for DW_TAG_subprogram definitions which have a DW_AT_object_pointer |
| // that encodes a constant index (instead of a DIE reference). |
| |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_AARCH64 |
| DWARF: |
| debug_str: |
| - Context |
| - func |
| - this |
| - self |
| - arg |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x1 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x2 |
| Tag: DW_TAG_structure_type |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Code: 0x3 |
| Tag: DW_TAG_subprogram |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_declaration |
| Form: DW_FORM_flag_present |
| - Attribute: DW_AT_object_pointer |
| Form: DW_FORM_implicit_const |
| Value: 1 |
| - Attribute: DW_AT_external |
| Form: DW_FORM_flag_present |
| - Code: 0x4 |
| Tag: DW_TAG_subprogram |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_declaration |
| Form: DW_FORM_flag_present |
| - Attribute: DW_AT_object_pointer |
| Form: DW_FORM_implicit_const |
| Value: 0 |
| - Attribute: DW_AT_external |
| Form: DW_FORM_flag_present |
| |
| - Code: 0x5 |
| Tag: DW_TAG_formal_parameter |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| |
| - Code: 0x6 |
| Tag: DW_TAG_formal_parameter |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_artificial |
| Form: DW_FORM_flag_present |
| |
| debug_info: |
| - Version: 5 |
| UnitType: DW_UT_compile |
| AddrSize: 8 |
| Entries: |
| |
| # DW_TAG_compile_unit |
| # DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus) |
| |
| - AbbrCode: 0x1 |
| Values: |
| - Value: 0x04 |
| |
| # DW_TAG_structure_type |
| # DW_AT_name [DW_FORM_strp] ("Context") |
| |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x0 |
| |
| # DW_TAG_subprogram |
| # DW_AT_name [DW_FORM_strp] ("func") |
| # DW_AT_object_pointer [DW_FORM_implicit_const] (1) |
| - AbbrCode: 0x3 |
| Values: |
| - Value: 0x8 |
| - Value: 0x1 |
| - Value: 0x1 |
| - Value: 0x1 |
| |
| # DW_TAG_formal_parameter |
| # DW_AT_name [DW_FORM_strp] ("arg") |
| - AbbrCode: 0x5 |
| Values: |
| - Value: 0x17 |
| |
| # DW_TAG_formal_parameter |
| # DW_AT_name [DW_FORM_strp] ("self") |
| # DW_AT_artificial |
| - AbbrCode: 0x6 |
| Values: |
| - Value: 0x12 |
| - Value: 0x1 |
| |
| - AbbrCode: 0x0 |
| |
| # DW_TAG_subprogram |
| # DW_AT_object_pointer [DW_FORM_implicit_const] (0) |
| # DW_AT_name [DW_FORM_strp] ("func") |
| - AbbrCode: 0x4 |
| Values: |
| - Value: 0x8 |
| - Value: 0x1 |
| - Value: 0x1 |
| - Value: 0x1 |
| |
| # DW_TAG_formal_parameter |
| # DW_AT_name [DW_FORM_strp] ("this") |
| # DW_AT_artificial |
| - AbbrCode: 0x6 |
| Values: |
| - Value: 0xd |
| - Value: 0x1 |
| |
| # DW_TAG_formal_parameter |
| # DW_AT_name [DW_FORM_strp] ("arg") |
| - AbbrCode: 0x5 |
| Values: |
| - Value: 0x17 |
| |
| - AbbrCode: 0x0 |
| - AbbrCode: 0x0 |
| ... |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| auto context_die = cu_die.GetFirstChild(); |
| ASSERT_TRUE(context_die.IsValid()); |
| ASSERT_EQ(context_die.Tag(), DW_TAG_structure_type); |
| |
| auto sub1 = context_die.GetFirstChild(); |
| ASSERT_TRUE(sub1.IsValid()); |
| ASSERT_EQ(sub1.Tag(), DW_TAG_subprogram); |
| |
| auto sub2 = sub1.GetSibling(); |
| ASSERT_TRUE(sub2.IsValid()); |
| ASSERT_EQ(sub2.Tag(), DW_TAG_subprogram); |
| |
| // Object parameter is at constant index 1 |
| { |
| auto param_die = sub1.GetFirstChild().GetSibling(); |
| ASSERT_TRUE(param_die.IsValid()); |
| |
| EXPECT_EQ(param_die, |
| tester.GetParser().GetObjectParameter(sub1, context_die)); |
| } |
| |
| // Object parameter is at constant index 0 |
| { |
| auto param_die = sub2.GetFirstChild(); |
| ASSERT_TRUE(param_die.IsValid()); |
| |
| EXPECT_EQ(param_die, |
| tester.GetParser().GetObjectParameter(sub2, context_die)); |
| } |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestTypeBitSize) { |
| // Tests that we correctly parse DW_AT_bit_size of a DW_AT_base_type. |
| |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_AARCH64 |
| DWARF: |
| debug_str: |
| - _BitInt(2) |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x1 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x2 |
| Tag: DW_TAG_base_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_encoding |
| Form: DW_FORM_data1 |
| - Attribute: DW_AT_byte_size |
| Form: DW_FORM_data1 |
| - Attribute: DW_AT_bit_size |
| Form: DW_FORM_data1 |
| |
| debug_info: |
| - Version: 5 |
| UnitType: DW_UT_compile |
| AddrSize: 8 |
| Entries: |
| |
| # DW_TAG_compile_unit |
| # DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus) |
| |
| - AbbrCode: 0x1 |
| Values: |
| - Value: 0x04 |
| |
| # DW_TAG_base_type |
| # DW_AT_name [DW_FORM_strp] ('_BitInt(2)') |
| |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x0 |
| - Value: 0x05 |
| - Value: 0x01 |
| - Value: 0x02 |
| ... |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| auto type_die = cu_die.GetFirstChild(); |
| ASSERT_TRUE(type_die.IsValid()); |
| ASSERT_EQ(type_die.Tag(), DW_TAG_base_type); |
| |
| ParsedDWARFTypeAttributes attrs(type_die); |
| EXPECT_EQ(attrs.byte_size.value_or(0), 1U); |
| EXPECT_EQ(attrs.data_bit_size.value_or(0), 2U); |
| |
| SymbolContext sc; |
| auto type_sp = tester.GetParser().ParseTypeFromDWARF( |
| sc, type_die, /*type_is_new_ptr=*/nullptr); |
| ASSERT_NE(type_sp, nullptr); |
| |
| EXPECT_EQ(llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0), |
| 1U); |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestBitIntParsing) { |
| // Tests that we correctly parse the DW_AT_base_type for a _BitInt. |
| // Older versions of Clang only emit the `_BitInt` string into the |
| // DW_AT_name (not including the bitsize). Make sure we understand |
| // those too. |
| |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_AARCH64 |
| DWARF: |
| debug_str: |
| - _BitInt(2) |
| - _BitInt |
| - unsigned _BitInt(2) |
| - unsigned _BitInt |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x1 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x2 |
| Tag: DW_TAG_base_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_encoding |
| Form: DW_FORM_data1 |
| - Attribute: DW_AT_byte_size |
| Form: DW_FORM_data1 |
| - Attribute: DW_AT_bit_size |
| Form: DW_FORM_data1 |
| - Code: 0x3 |
| Tag: DW_TAG_base_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_strp |
| - Attribute: DW_AT_encoding |
| Form: DW_FORM_data1 |
| - Attribute: DW_AT_byte_size |
| Form: DW_FORM_data1 |
| |
| debug_info: |
| - Version: 5 |
| UnitType: DW_UT_compile |
| AddrSize: 8 |
| Entries: |
| |
| # DW_TAG_compile_unit |
| # DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus) |
| |
| - AbbrCode: 0x1 |
| Values: |
| - Value: 0x04 |
| |
| # DW_TAG_base_type |
| # DW_AT_name [DW_FORM_strp] ('_BitInt(2)') |
| |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x0 |
| - Value: 0x05 |
| - Value: 0x01 |
| - Value: 0x02 |
| |
| # DW_TAG_base_type |
| # DW_AT_name [DW_FORM_strp] ('_BitInt') |
| |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x0b |
| - Value: 0x05 |
| - Value: 0x08 |
| - Value: 0x34 |
| |
| # DW_TAG_base_type |
| # DW_AT_name [DW_FORM_strp] ('unsigned _BitInt(2)') |
| |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x13 |
| - Value: 0x07 |
| - Value: 0x01 |
| - Value: 0x02 |
| |
| # DW_TAG_base_type |
| # DW_AT_name [DW_FORM_strp] ('unsigned _BitInt') |
| |
| - AbbrCode: 0x2 |
| Values: |
| - Value: 0x27 |
| - Value: 0x07 |
| - Value: 0x08 |
| - Value: 0x34 |
| |
| # DW_TAG_base_type |
| # DW_AT_name [DW_FORM_strp] ('_BitInt') |
| |
| - AbbrCode: 0x3 |
| Values: |
| - Value: 0x0b |
| - Value: 0x05 |
| - Value: 0x08 |
| ... |
| |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| auto type_die = cu_die.GetFirstChild(); |
| ASSERT_TRUE(type_die.IsValid()); |
| |
| { |
| SymbolContext sc; |
| auto type_sp = |
| tester.GetParser().ParseTypeFromDWARF(sc, type_die, |
| /*type_is_new_ptr=*/nullptr); |
| ASSERT_NE(type_sp, nullptr); |
| |
| EXPECT_EQ( |
| llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0), |
| 1U); |
| EXPECT_EQ(type_sp->GetEncoding(), lldb::eEncodingSint); |
| EXPECT_EQ(type_sp->GetName(), "_BitInt(2)"); |
| EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), "_BitInt(2)"); |
| } |
| |
| { |
| type_die = type_die.GetSibling(); |
| SymbolContext sc; |
| auto type_sp = |
| tester.GetParser().ParseTypeFromDWARF(sc, type_die, |
| /*type_is_new_ptr=*/nullptr); |
| ASSERT_NE(type_sp, nullptr); |
| |
| EXPECT_EQ( |
| llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0), |
| 8U); |
| EXPECT_EQ(type_sp->GetEncoding(), lldb::eEncodingSint); |
| EXPECT_EQ(type_sp->GetName(), "_BitInt"); |
| EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), "_BitInt(52)"); |
| } |
| |
| { |
| type_die = type_die.GetSibling(); |
| SymbolContext sc; |
| auto type_sp = |
| tester.GetParser().ParseTypeFromDWARF(sc, type_die, |
| /*type_is_new_ptr=*/nullptr); |
| ASSERT_NE(type_sp, nullptr); |
| |
| EXPECT_EQ( |
| llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0), |
| 1U); |
| EXPECT_EQ(type_sp->GetEncoding(), lldb::eEncodingUint); |
| EXPECT_EQ(type_sp->GetName(), "unsigned _BitInt(2)"); |
| EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), |
| "unsigned _BitInt(2)"); |
| } |
| |
| { |
| type_die = type_die.GetSibling(); |
| SymbolContext sc; |
| auto type_sp = |
| tester.GetParser().ParseTypeFromDWARF(sc, type_die, |
| /*type_is_new_ptr=*/nullptr); |
| ASSERT_NE(type_sp, nullptr); |
| |
| EXPECT_EQ( |
| llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0), |
| 8U); |
| EXPECT_EQ(type_sp->GetEncoding(), lldb::eEncodingUint); |
| EXPECT_EQ(type_sp->GetName(), "unsigned _BitInt"); |
| EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), |
| "unsigned _BitInt(52)"); |
| } |
| |
| { |
| type_die = type_die.GetSibling(); |
| SymbolContext sc; |
| auto type_sp = |
| tester.GetParser().ParseTypeFromDWARF(sc, type_die, |
| /*type_is_new_ptr=*/nullptr); |
| ASSERT_NE(type_sp, nullptr); |
| |
| EXPECT_EQ( |
| llvm::expectedToOptional(type_sp->GetByteSize(nullptr)).value_or(0), |
| 8U); |
| EXPECT_EQ(type_sp->GetEncoding(), lldb::eEncodingSint); |
| EXPECT_EQ(type_sp->GetName(), "_BitInt"); |
| |
| // Older versions of Clang didn't emit a DW_AT_bit_size for _BitInt. In |
| // those cases we would format the CompilerType name using the byte-size. |
| EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), "_BitInt(64)"); |
| } |
| } |
| |
| TEST_F(DWARFASTParserClangTests, TestTemplateAlias_NoSimpleTemplateNames) { |
| // Tests that we correctly parse the DW_TAG_template_alias generated by |
| // -gno-simple-template-names. |
| |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_AARCH64 |
| DWARF: |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x1 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x2 |
| Tag: DW_TAG_base_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_string |
| - Code: 0x3 |
| Tag: DW_TAG_template_alias |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_string |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| - Code: 0x4 |
| Tag: DW_TAG_template_type_parameter |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_string |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| |
| debug_info: |
| - Version: 5 |
| UnitType: DW_UT_compile |
| AddrSize: 8 |
| Entries: |
| |
| # DW_TAG_compile_unit |
| # DW_AT_language (DW_LANG_C_plus_plus) |
| |
| - AbbrCode: 0x1 |
| Values: |
| - Value: 0x04 |
| |
| # DW_TAG_base_type |
| # DW_AT_name ('int') |
| |
| - AbbrCode: 0x2 |
| Values: |
| - CStr: int |
| |
| # DW_TAG_template_alias |
| # DW_AT_name ('Foo<int>') |
| # DW_AT_type ('int') |
| # DW_TAG_template_type_parameter |
| # DW_AT_name ('T') |
| # DW_AT_type ('int') |
| |
| - AbbrCode: 0x3 |
| Values: |
| - CStr: Foo<int> |
| - Value: 0xf |
| |
| - AbbrCode: 0x4 |
| Values: |
| - CStr: T |
| - Value: 0xf |
| |
| - AbbrCode: 0x0 |
| - AbbrCode: 0x0 |
| ... |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| auto alias_die = cu_die.GetFirstChild().GetSibling(); |
| ASSERT_EQ(alias_die.Tag(), DW_TAG_template_alias); |
| |
| SymbolContext sc; |
| auto type_sp = |
| tester.GetParser().ParseTypeFromDWARF(sc, alias_die, |
| /*type_is_new_ptr=*/nullptr); |
| ASSERT_NE(type_sp, nullptr); |
| |
| EXPECT_TRUE(type_sp->IsTypedef()); |
| EXPECT_EQ(type_sp->GetName(), "Foo<int>"); |
| EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), "Foo<int>"); |
| } |
| |
| TEST_F(DWARFASTParserClangTests, |
| TestTemplateAlias_InStruct_NoSimpleTemplateNames) { |
| // Tests that we correctly parse the DW_TAG_template_alias scoped inside a |
| // DW_TAG_structure_type *declaration* generated by |
| // -gno-simple-template-names. This tests the codepath the forcefully |
| // completes the context of the alias via PrepareContextToReceiveMembers. |
| |
| const char *yamldata = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_AARCH64 |
| DWARF: |
| debug_abbrev: |
| - ID: 0 |
| Table: |
| - Code: 0x1 |
| Tag: DW_TAG_compile_unit |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_language |
| Form: DW_FORM_data2 |
| - Code: 0x2 |
| Tag: DW_TAG_base_type |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_string |
| - Code: 0x3 |
| Tag: DW_TAG_structure_type |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_string |
| - Attribute: DW_AT_declaration |
| Form: DW_FORM_flag_present |
| - Code: 0x4 |
| Tag: DW_TAG_template_alias |
| Children: DW_CHILDREN_yes |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_string |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| - Code: 0x5 |
| Tag: DW_TAG_template_type_parameter |
| Children: DW_CHILDREN_no |
| Attributes: |
| - Attribute: DW_AT_name |
| Form: DW_FORM_string |
| - Attribute: DW_AT_type |
| Form: DW_FORM_ref4 |
| |
| debug_info: |
| - Version: 5 |
| UnitType: DW_UT_compile |
| AddrSize: 8 |
| Entries: |
| |
| # DW_TAG_compile_unit |
| # DW_AT_language (DW_LANG_C_plus_plus) |
| |
| - AbbrCode: 0x1 |
| Values: |
| - Value: 0x04 |
| |
| # DW_TAG_base_type |
| # DW_AT_name ('int') |
| |
| - AbbrCode: 0x2 |
| Values: |
| - CStr: int |
| |
| # DW_TAG_structure_type |
| # DW_AT_name ('Foo') |
| |
| - AbbrCode: 0x3 |
| Values: |
| - CStr: Foo |
| |
| # DW_TAG_template_alias |
| # DW_AT_name ('Bar<int>') |
| # DW_AT_type ('int') |
| # DW_TAG_template_type_parameter |
| # DW_AT_name ('T') |
| # DW_AT_type ('int') |
| |
| - AbbrCode: 0x4 |
| Values: |
| - CStr: Bar<int> |
| - Value: 0xf |
| |
| - AbbrCode: 0x5 |
| Values: |
| - CStr: T |
| - Value: 0xf |
| |
| - AbbrCode: 0x0 |
| - AbbrCode: 0x0 |
| - AbbrCode: 0x0 |
| ... |
| )"; |
| DWARFASTParserClangYAMLTester tester(yamldata); |
| DWARFDIE cu_die = tester.GetCUDIE(); |
| |
| auto alias_die = cu_die.GetFirstChild().GetSibling().GetFirstChild(); |
| ASSERT_EQ(alias_die.Tag(), DW_TAG_template_alias); |
| |
| SymbolContext sc; |
| auto type_sp = |
| tester.GetParser().ParseTypeFromDWARF(sc, alias_die, |
| /*type_is_new_ptr=*/nullptr); |
| ASSERT_NE(type_sp, nullptr); |
| |
| EXPECT_TRUE(type_sp->IsTypedef()); |
| EXPECT_EQ(type_sp->GetName(), "Bar<int>"); |
| EXPECT_EQ(type_sp->GetForwardCompilerType().GetTypeName(), "Foo::Bar<int>"); |
| } |