//===-- ModuleListTest.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 "lldb/Core/ModuleList.h" #include "TestingSupport/SubsystemRAII.h" #include "TestingSupport/TestUtilities.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/FileSystem.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/UUID.h" #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" #include "gtest/gtest.h" using namespace lldb; using namespace lldb_private; // Test that when we already have a module in the shared_module_list with a // specific UUID, the next call to GetSharedModule with a module_spec with the // same UUID should return the existing module instead of creating a new one. TEST(ModuleListTest, GetSharedModuleReusesExistingModuleWithSameUUID) { SubsystemRAII subsystems; auto ExpectedFile = TestFile::fromYaml(R"( --- !ELF FileHeader: Class: ELFCLASS64 Data: ELFDATA2LSB Type: ET_DYN Machine: EM_X86_64 Sections: - Name: .text Type: SHT_PROGBITS Flags: [ SHF_ALLOC, SHF_EXECINSTR ] AddressAlign: 0x0000000000000010 ... )"); ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); // First, let's verify that calling GetSharedModule twice with the same // module_spec returns the same module pointer ModuleSP first_module; bool first_did_create = false; Status error_first = ModuleList::GetSharedModule( ExpectedFile->moduleSpec(), first_module, nullptr, &first_did_create); // Second call with the same spec ModuleSP second_module; bool second_did_create = false; Status error_second = ModuleList::GetSharedModule( ExpectedFile->moduleSpec(), second_module, nullptr, &second_did_create); if (error_first.Success() && error_second.Success()) { // If both succeeded, verify they're the same module EXPECT_EQ(first_module.get(), second_module.get()) << "GetSharedModule should return the same module for the same spec"; EXPECT_TRUE(first_did_create) << "First call should create the module"; EXPECT_FALSE(second_did_create) << "Second call should reuse the existing module"; } } // Test that UUID-based lookup finds existing modules TEST(ModuleListTest, FindSharedModuleByUUID) { SubsystemRAII subsystems; auto ExpectedFile = TestFile::fromYaml(R"( --- !ELF FileHeader: Class: ELFCLASS64 Data: ELFDATA2LSB Type: ET_DYN Machine: EM_X86_64 Sections: - Name: .text Type: SHT_PROGBITS Flags: [ SHF_ALLOC, SHF_EXECINSTR ] AddressAlign: 0x0000000000000010 ... )"); ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); // Create and add a module to the shared module list using the moduleSpec() ModuleSP created_module; bool did_create = false; Status error = ModuleList::GetSharedModule( ExpectedFile->moduleSpec(), created_module, nullptr, &did_create); if (error.Success() && created_module) { // Get the UUID of the created module UUID module_uuid = created_module->GetUUID(); if (module_uuid.IsValid()) { // Now try to find the module by UUID ModuleSP found_module = ModuleList::FindSharedModule(module_uuid); ASSERT_NE(found_module.get(), nullptr) << "FindSharedModule should find the module by UUID"; EXPECT_EQ(found_module.get(), created_module.get()) << "FindSharedModule should return the same module instance"; EXPECT_EQ(found_module->GetUUID(), module_uuid) << "Found module should have the same UUID"; } } } // Test that GetSharedModule with UUID finds existing module even with different // path TEST(ModuleListTest, GetSharedModuleByUUIDIgnoresPath) { SubsystemRAII subsystems; auto ExpectedFile = TestFile::fromYaml(R"( --- !ELF FileHeader: Class: ELFCLASS64 Data: ELFDATA2LSB Type: ET_DYN Machine: EM_X86_64 Sections: - Name: .text Type: SHT_PROGBITS Flags: [ SHF_ALLOC, SHF_EXECINSTR ] AddressAlign: 0x0000000000000010 ... )"); ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); // Create and add a module to the shared module list ModuleSP first_module; bool first_did_create = false; Status first_error = ModuleList::GetSharedModule( ExpectedFile->moduleSpec(), first_module, nullptr, &first_did_create); if (first_error.Success() && first_module) { UUID module_uuid = first_module->GetUUID(); if (module_uuid.IsValid()) { // Now try to get a module with the same UUID but different path ModuleSpec second_spec; second_spec.GetFileSpec() = FileSpec("/different/path/to/module.so"); second_spec.GetArchitecture() = ArchSpec("x86_64-pc-linux"); second_spec.GetUUID() = module_uuid; ModuleSP second_module; bool second_did_create = false; Status second_error = ModuleList::GetSharedModule( second_spec, second_module, nullptr, &second_did_create); if (second_error.Success() && second_module) { // If we got a module back, check if it's the same one bool is_same_module = (second_module.get() == first_module.get()); // Document the behavior: ideally UUID should take precedence // and return the existing module EXPECT_TRUE(is_same_module) << "GetSharedModule with matching UUID should return existing " "module, " << "even with different path (per PR #160199)"; if (is_same_module) { EXPECT_FALSE(second_did_create) << "Should not create a new module when UUID matches"; } } } } }