| //===-- LocateModuleCallbackTest.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/ObjectFile/Breakpad/ObjectFileBreakpad.h" | |
| #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" | |
| #include "Plugins/Platform/Android/PlatformAndroid.h" | |
| #include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h" | |
| #include "Plugins/SymbolFile/Symtab/SymbolFileSymtab.h" | |
| #include "TestingSupport/SubsystemRAII.h" | |
| #include "TestingSupport/TestUtilities.h" | |
| #include "lldb/Core/Debugger.h" | |
| #include "lldb/Core/PluginManager.h" | |
| #include "lldb/Host/HostInfo.h" | |
| #include "lldb/Target/ModuleCache.h" | |
| #include "lldb/Target/Target.h" | |
| #include "gmock/gmock.h" | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| using namespace lldb_private::platform_android; | |
| using namespace lldb_private::platform_linux; | |
| using namespace lldb_private::breakpad; | |
| using namespace testing; | |
| namespace { | |
| constexpr llvm::StringLiteral k_process_plugin("mock-process-plugin"); | |
| constexpr llvm::StringLiteral k_platform_dir("remote-android"); | |
| constexpr llvm::StringLiteral k_cache_dir(".cache"); | |
| constexpr llvm::StringLiteral k_module_file("AndroidModule.so"); | |
| constexpr llvm::StringLiteral k_symbol_file("AndroidModule.unstripped.so"); | |
| constexpr llvm::StringLiteral k_breakpad_symbol_file("AndroidModule.so.sym"); | |
| constexpr llvm::StringLiteral k_arch("aarch64-none-linux"); | |
| constexpr llvm::StringLiteral | |
| k_module_uuid("80008338-82A0-51E5-5922-C905D23890DA-BDDEFECC"); | |
| constexpr llvm::StringLiteral k_function_symbol("boom"); | |
| constexpr llvm::StringLiteral k_hidden_function_symbol("boom_hidden"); | |
| const size_t k_module_size = 3784; | |
| ModuleSpec GetTestModuleSpec(); | |
| class MockProcess : public Process { | |
| public: | |
| MockProcess(TargetSP target_sp, ListenerSP listener_sp) | |
| : Process(target_sp, listener_sp) {} | |
| llvm::StringRef GetPluginName() override { return k_process_plugin; }; | |
| bool CanDebug(TargetSP target, bool plugin_specified_by_name) override { | |
| return true; | |
| } | |
| Status DoDestroy() override { return Status(); } | |
| void RefreshStateAfterStop() override {} | |
| bool DoUpdateThreadList(ThreadList &old_thread_list, | |
| ThreadList &new_thread_list) override { | |
| return false; | |
| } | |
| size_t DoReadMemory(addr_t vm_addr, void *buf, size_t size, | |
| Status &error) override { | |
| return 0; | |
| } | |
| bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, | |
| ModuleSpec &module_spec) override { | |
| module_spec = GetTestModuleSpec(); | |
| return true; | |
| } | |
| }; | |
| FileSpec GetTestDir() { | |
| const auto *info = UnitTest::GetInstance()->current_test_info(); | |
| FileSpec test_dir = HostInfo::GetProcessTempDir(); | |
| test_dir.AppendPathComponent(std::string(info->test_case_name()) + "-" + | |
| info->name()); | |
| std::error_code ec = llvm::sys::fs::create_directory(test_dir.GetPath()); | |
| EXPECT_FALSE(ec); | |
| return test_dir; | |
| } | |
| FileSpec GetRemotePath() { | |
| FileSpec fs("/", FileSpec::Style::posix); | |
| fs.AppendPathComponent("bin"); | |
| fs.AppendPathComponent(k_module_file); | |
| return fs; | |
| } | |
| FileSpec GetUuidView(FileSpec spec) { | |
| spec.AppendPathComponent(k_platform_dir); | |
| spec.AppendPathComponent(k_cache_dir); | |
| spec.AppendPathComponent(k_module_uuid); | |
| spec.AppendPathComponent(k_module_file); | |
| return spec; | |
| } | |
| void BuildEmptyCacheDir(const FileSpec &test_dir) { | |
| FileSpec cache_dir(test_dir); | |
| cache_dir.AppendPathComponent(k_platform_dir); | |
| cache_dir.AppendPathComponent(k_cache_dir); | |
| std::error_code ec = llvm::sys::fs::create_directories(cache_dir.GetPath()); | |
| EXPECT_FALSE(ec); | |
| } | |
| FileSpec BuildCacheDir(const FileSpec &test_dir) { | |
| FileSpec uuid_view = GetUuidView(test_dir); | |
| std::error_code ec = | |
| llvm::sys::fs::create_directories(uuid_view.GetDirectory()); | |
| EXPECT_FALSE(ec); | |
| ec = llvm::sys::fs::copy_file(GetInputFilePath(k_module_file), | |
| uuid_view.GetPath().c_str()); | |
| EXPECT_FALSE(ec); | |
| return uuid_view; | |
| } | |
| FileSpec GetSymFileSpec(const FileSpec &uuid_view) { | |
| return FileSpec(uuid_view.GetPath() + ".sym"); | |
| } | |
| FileSpec BuildCacheDirWithSymbol(const FileSpec &test_dir) { | |
| FileSpec uuid_view = BuildCacheDir(test_dir); | |
| std::error_code ec = | |
| llvm::sys::fs::copy_file(GetInputFilePath(k_symbol_file), | |
| GetSymFileSpec(uuid_view).GetPath().c_str()); | |
| EXPECT_FALSE(ec); | |
| return uuid_view; | |
| } | |
| FileSpec BuildCacheDirWithBreakpadSymbol(const FileSpec &test_dir) { | |
| FileSpec uuid_view = BuildCacheDir(test_dir); | |
| std::error_code ec = | |
| llvm::sys::fs::copy_file(GetInputFilePath(k_breakpad_symbol_file), | |
| GetSymFileSpec(uuid_view).GetPath().c_str()); | |
| EXPECT_FALSE(ec); | |
| return uuid_view; | |
| } | |
| ModuleSpec GetTestModuleSpec() { | |
| ModuleSpec module_spec(GetRemotePath(), ArchSpec(k_arch)); | |
| module_spec.GetUUID().SetFromStringRef(k_module_uuid); | |
| module_spec.SetObjectSize(k_module_size); | |
| return module_spec; | |
| } | |
| void CheckModule(const ModuleSP &module_sp) { | |
| ASSERT_TRUE(module_sp); | |
| ASSERT_EQ(module_sp->GetUUID().GetAsString(), k_module_uuid); | |
| ASSERT_EQ(module_sp->GetObjectOffset(), 0U); | |
| ASSERT_EQ(module_sp->GetPlatformFileSpec(), GetRemotePath()); | |
| } | |
| SymbolContextList FindFunctions(const ModuleSP &module_sp, | |
| const llvm::StringRef &name) { | |
| SymbolContextList sc_list; | |
| ModuleFunctionSearchOptions function_options; | |
| function_options.include_symbols = true; | |
| function_options.include_inlines = true; | |
| FunctionNameType type = static_cast<FunctionNameType>(eSymbolTypeCode); | |
| module_sp->FindFunctions(ConstString(name), CompilerDeclContext(), type, | |
| function_options, sc_list); | |
| return sc_list; | |
| } | |
| void CheckStrippedSymbol(const ModuleSP &module_sp) { | |
| SymbolContextList sc_list = FindFunctions(module_sp, k_function_symbol); | |
| EXPECT_EQ(1U, sc_list.GetSize()); | |
| sc_list = FindFunctions(module_sp, k_hidden_function_symbol); | |
| EXPECT_EQ(0U, sc_list.GetSize()); | |
| } | |
| void CheckUnstrippedSymbol(const ModuleSP &module_sp) { | |
| SymbolContextList sc_list = FindFunctions(module_sp, k_function_symbol); | |
| EXPECT_EQ(1U, sc_list.GetSize()); | |
| sc_list = FindFunctions(module_sp, k_hidden_function_symbol); | |
| EXPECT_EQ(1U, sc_list.GetSize()); | |
| } | |
| ProcessSP MockProcessCreateInstance(TargetSP target_sp, ListenerSP listener_sp, | |
| const FileSpec *crash_file_path, | |
| bool can_connect) { | |
| return std::make_shared<MockProcess>(target_sp, listener_sp); | |
| } | |
| class LocateModuleCallbackTest : public testing::Test { | |
| SubsystemRAII<FileSystem, HostInfo, ObjectFileBreakpad, ObjectFileELF, | |
| PlatformAndroid, PlatformLinux, SymbolFileBreakpad, | |
| SymbolFileSymtab> | |
| subsystems; | |
| public: | |
| void SetUp() override { | |
| m_test_dir = GetTestDir(); | |
| // Set module cache directory for PlatformAndroid. | |
| PlatformAndroid::GetGlobalPlatformProperties().SetModuleCacheDirectory( | |
| m_test_dir); | |
| // Create Debugger. | |
| ArchSpec host_arch("i386-pc-linux"); | |
| Platform::SetHostPlatform( | |
| platform_linux::PlatformLinux::CreateInstance(true, &host_arch)); | |
| m_debugger_sp = Debugger::CreateInstance(); | |
| EXPECT_TRUE(m_debugger_sp); | |
| // Create PlatformAndroid. | |
| ArchSpec arch(k_arch); | |
| m_platform_sp = PlatformAndroid::CreateInstance(true, &arch); | |
| EXPECT_TRUE(m_platform_sp); | |
| // Create Target. | |
| m_debugger_sp->GetTargetList().CreateTarget(*m_debugger_sp, "", arch, | |
| eLoadDependentsNo, | |
| m_platform_sp, m_target_sp); | |
| EXPECT_TRUE(m_target_sp); | |
| // Create MockProcess. | |
| PluginManager::RegisterPlugin(k_process_plugin, "", | |
| MockProcessCreateInstance); | |
| auto unregister_plugin = llvm::scope_exit( | |
| [&]() { PluginManager::UnregisterPlugin(MockProcessCreateInstance); }); | |
| m_process_sp = | |
| m_target_sp->CreateProcess(Listener::MakeListener("test-listener"), | |
| k_process_plugin, /*crash_file=*/nullptr, | |
| /*can_connect=*/true); | |
| EXPECT_TRUE(m_process_sp); | |
| m_module_spec = GetTestModuleSpec(); | |
| m_module_spec_without_uuid = ModuleSpec(GetRemotePath(), ArchSpec(k_arch)); | |
| } | |
| void TearDown() override { | |
| if (m_module_sp) | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| void CheckNoCallback() { | |
| EXPECT_FALSE(m_platform_sp->GetLocateModuleCallback()); | |
| EXPECT_EQ(m_callback_call_count, 0); | |
| } | |
| void CheckCallbackArgs(const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, FileSpec &symbol_file_spec, | |
| const ModuleSpec &expected_module_spec, | |
| int expected_callback_call_count) { | |
| EXPECT_TRUE(expected_module_spec.Matches(module_spec, | |
| /*exact_arch_match=*/true)); | |
| EXPECT_FALSE(module_file_spec); | |
| EXPECT_FALSE(symbol_file_spec); | |
| EXPECT_EQ(++m_callback_call_count, expected_callback_call_count); | |
| } | |
| void CheckCallbackArgsWithUUID(const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec, | |
| int expected_callback_call_count) { | |
| CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec, | |
| m_module_spec, expected_callback_call_count); | |
| EXPECT_TRUE(module_spec.GetUUID().IsValid()); | |
| } | |
| void CheckCallbackArgsWithoutUUID(const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec, | |
| int expected_callback_call_count) { | |
| CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec, | |
| m_module_spec_without_uuid, expected_callback_call_count); | |
| EXPECT_FALSE(module_spec.GetUUID().IsValid()); | |
| } | |
| protected: | |
| FileSpec m_test_dir; | |
| DebuggerSP m_debugger_sp; | |
| PlatformSP m_platform_sp; | |
| TargetSP m_target_sp; | |
| ProcessSP m_process_sp; | |
| ModuleSpec m_module_spec; | |
| ModuleSpec m_module_spec_without_uuid; | |
| ModuleSP m_module_sp; | |
| int m_callback_call_count = 0; | |
| }; | |
| } // namespace | |
| TEST_F(LocateModuleCallbackTest, GetOrCreateModuleWithCachedModule) { | |
| // The module file is cached, and the locate module callback is not set. | |
| // GetOrCreateModule should succeed to return the module from the cache. | |
| FileSpec uuid_view = BuildCacheDir(m_test_dir); | |
| CheckNoCallback(); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ASSERT_FALSE(m_module_sp->GetSymbolFileFileSpec()); | |
| CheckStrippedSymbol(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, GetOrCreateModuleWithCachedModuleAndSymbol) { | |
| // The module and symbol files are cached, and the locate module callback is | |
| // not set. GetOrCreateModule should succeed to return the module from the | |
| // cache with the symbol. | |
| FileSpec uuid_view = BuildCacheDirWithSymbol(m_test_dir); | |
| CheckNoCallback(); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(), GetSymFileSpec(uuid_view)); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleWithCachedModuleAndBreakpadSymbol) { | |
| // The module file and breakpad symbol file are cached, and the locate module | |
| // callback is not set. GetOrCreateModule should succeed to return the module | |
| // from the cache with the symbol. | |
| FileSpec uuid_view = BuildCacheDirWithBreakpadSymbol(m_test_dir); | |
| CheckNoCallback(); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(), GetSymFileSpec(uuid_view)); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, GetOrCreateModuleFailure) { | |
| // The cache dir is empty, and the locate module callback is not set. | |
| // GetOrCreateModule should fail because PlatformAndroid tries to download the | |
| // module and fails. | |
| BuildEmptyCacheDir(m_test_dir); | |
| CheckNoCallback(); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| ASSERT_FALSE(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, GetOrCreateModuleCallbackFailureNoCache) { | |
| // The cache dir is empty, also the locate module callback fails for some | |
| // reason. GetOrCreateModule should fail because PlatformAndroid tries to | |
| // download the module and fails. | |
| BuildEmptyCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| return Status::FromErrorString("The locate module callback failed"); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 3); | |
| ASSERT_FALSE(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, GetOrCreateModuleCallbackFailureCached) { | |
| // The module file is cached, so GetOrCreateModule should succeed to return | |
| // the module from the cache even though the locate module callback fails for | |
| // some reason. | |
| FileSpec uuid_view = BuildCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| return Status::FromErrorString("The locate module callback failed"); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 3); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ASSERT_FALSE(m_module_sp->GetSymbolFileFileSpec()); | |
| CheckStrippedSymbol(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, GetOrCreateModuleCallbackNoFiles) { | |
| // The module file is cached, so GetOrCreateModule should succeed to return | |
| // the module from the cache even though the locate module callback returns | |
| // no files. | |
| FileSpec uuid_view = BuildCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| // The locate module callback succeeds but it does not set | |
| // module_file_spec nor symbol_file_spec. | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 3); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ASSERT_FALSE(m_module_sp->GetSymbolFileFileSpec()); | |
| CheckStrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, GetOrCreateModuleCallbackNonExistentModule) { | |
| // The module file is cached, so GetOrCreateModule should succeed to return | |
| // the module from the cache even though the locate module callback returns | |
| // non-existent module file. | |
| FileSpec uuid_view = BuildCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| module_file_spec.SetPath("/this path does not exist"); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 3); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ASSERT_FALSE(m_module_sp->GetSymbolFileFileSpec()); | |
| CheckStrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, GetOrCreateModuleCallbackNonExistentSymbol) { | |
| // The module file is cached, so GetOrCreateModule should succeed to return | |
| // the module from the cache even though the locate module callback returns | |
| // non-existent symbol file. | |
| FileSpec uuid_view = BuildCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| // The locate module callback returns a right module file. | |
| module_file_spec.SetPath(GetInputFilePath(k_module_file)); | |
| // But it returns non-existent symbols file. | |
| symbol_file_spec.SetPath("/this path does not exist"); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 3); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ASSERT_TRUE(m_module_sp->GetSymbolFileFileSpec().GetPath().empty()); | |
| CheckStrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, GetOrCreateModuleCallbackSuccessWithModule) { | |
| // The locate module callback returns a module file, GetOrCreateModule should | |
| // succeed to return the module from the Inputs directory. | |
| BuildEmptyCacheDir(m_test_dir); | |
| m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, symbol_file_spec, | |
| 1); | |
| module_file_spec.SetPath(GetInputFilePath(k_module_file)); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), | |
| FileSpec(GetInputFilePath(k_module_file))); | |
| ASSERT_FALSE(m_module_sp->GetSymbolFileFileSpec()); | |
| CheckStrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleCallbackSuccessWithSymbolAsModule) { | |
| // The locate module callback returns the symbol file as a module file. It | |
| // should work since the sections and UUID of the symbol file are the exact | |
| // same with the module file, GetOrCreateModule should succeed to return the | |
| // module with the symbol file from Inputs directory. | |
| BuildEmptyCacheDir(m_test_dir); | |
| m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, symbol_file_spec, | |
| 1); | |
| module_file_spec.SetPath(GetInputFilePath(k_symbol_file)); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), | |
| FileSpec(GetInputFilePath(k_symbol_file))); | |
| ASSERT_FALSE(m_module_sp->GetSymbolFileFileSpec()); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleCallbackSuccessWithSymbolAsModuleAndSymbol) { | |
| // The locate module callback returns a symbol file as both a module file and | |
| // a symbol file. It should work since the sections and UUID of the symbol | |
| // file are the exact same with the module file, GetOrCreateModule should | |
| // succeed to return the module with the symbol file from Inputs directory. | |
| BuildEmptyCacheDir(m_test_dir); | |
| m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, symbol_file_spec, | |
| 1); | |
| module_file_spec.SetPath(GetInputFilePath(k_symbol_file)); | |
| symbol_file_spec.SetPath(GetInputFilePath(k_symbol_file)); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), | |
| FileSpec(GetInputFilePath(k_symbol_file))); | |
| ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(), | |
| FileSpec(GetInputFilePath(k_symbol_file))); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleCallbackSuccessWithModuleAndSymbol) { | |
| // The locate module callback returns a module file and a symbol file, | |
| // GetOrCreateModule should succeed to return the module from Inputs | |
| // directory, along with the symbol file. | |
| BuildEmptyCacheDir(m_test_dir); | |
| m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, symbol_file_spec, | |
| 1); | |
| module_file_spec.SetPath(GetInputFilePath(k_module_file)); | |
| symbol_file_spec.SetPath(GetInputFilePath(k_symbol_file)); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), | |
| FileSpec(GetInputFilePath(k_module_file))); | |
| ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(), | |
| FileSpec(GetInputFilePath(k_symbol_file))); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleCallbackSuccessWithModuleAndBreakpadSymbol) { | |
| // The locate module callback returns a module file and a breakpad symbol | |
| // file, GetOrCreateModule should succeed to return the module with the symbol | |
| // file from Inputs directory. | |
| BuildEmptyCacheDir(m_test_dir); | |
| m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, symbol_file_spec, | |
| 1); | |
| module_file_spec.SetPath(GetInputFilePath(k_module_file)); | |
| symbol_file_spec.SetPath(GetInputFilePath(k_breakpad_symbol_file)); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), | |
| FileSpec(GetInputFilePath(k_module_file))); | |
| ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(), | |
| FileSpec(GetInputFilePath(k_breakpad_symbol_file))); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleCallbackSuccessWithOnlySymbol) { | |
| // The get callback returns only a symbol file, and the module is cached, | |
| // GetOrCreateModule should succeed to return the module from the cache | |
| // along with the symbol file from the Inputs directory. | |
| FileSpec uuid_view = BuildCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| symbol_file_spec.SetPath(GetInputFilePath(k_symbol_file)); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 3); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(), | |
| FileSpec(GetInputFilePath(k_symbol_file))); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleCallbackSuccessWithOnlyBreakpadSymbol) { | |
| // The get callback returns only a breakpad symbol file, and the module is | |
| // cached, GetOrCreateModule should succeed to return the module from the | |
| // cache along with the symbol file from the Inputs directory. | |
| FileSpec uuid_view = BuildCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| symbol_file_spec.SetPath(GetInputFilePath(k_breakpad_symbol_file)); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 3); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(), | |
| FileSpec(GetInputFilePath(k_breakpad_symbol_file))); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleCallbackSuccessWithMultipleSymbols) { | |
| // The get callback returns only a symbol file. The first call returns | |
| // a breakpad symbol file and the second call returns a symbol file. | |
| // Also the module is cached, so GetOrCreateModule should succeed to return | |
| // the module from the cache along with the breakpad symbol file from the | |
| // Inputs directory because GetOrCreateModule will use the first symbol file | |
| // from the callback. | |
| FileSpec uuid_view = BuildCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| symbol_file_spec.SetPath(GetInputFilePath( | |
| callback_call_count == 1 ? k_breakpad_symbol_file : k_symbol_file)); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 3); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(), | |
| FileSpec(GetInputFilePath(k_breakpad_symbol_file))); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleNoCacheWithCallbackOnlySymbol) { | |
| // The get callback returns only a symbol file, but the module is not | |
| // cached, GetOrCreateModule should fail because of the missing module. | |
| BuildEmptyCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| symbol_file_spec.SetPath(GetInputFilePath(k_symbol_file)); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 3); | |
| ASSERT_FALSE(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleNoCacheWithCallbackOnlyBreakpadSymbol) { | |
| // The get callback returns only a breakpad symbol file, but the module is not | |
| // cached, GetOrCreateModule should fail because of the missing module. | |
| BuildEmptyCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| symbol_file_spec.SetPath(GetInputFilePath(k_breakpad_symbol_file)); | |
| return Status(); | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 3); | |
| ASSERT_FALSE(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleCallbackSuccessWithModuleByPlatformUUID) { | |
| // This is a simulation for Android remote platform debugging. | |
| // The locate module callback first call fails because module_spec does not | |
| // have UUID. Then, the callback second call returns a module file because the | |
| // platform resolved the module_spec UUID from the target process. | |
| // GetOrCreateModule should succeed to return the module from the Inputs | |
| // directory. | |
| BuildEmptyCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| callback_call_count++; | |
| if (callback_call_count == 1) { | |
| // The module_spec does not have UUID on the first call. | |
| CheckCallbackArgsWithoutUUID(module_spec, module_file_spec, | |
| symbol_file_spec, callback_call_count); | |
| return Status::FromErrorString("Ignored empty UUID"); | |
| } else { | |
| // The module_spec has UUID on the second call. | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, callback_call_count); | |
| module_file_spec.SetPath(GetInputFilePath(k_module_file)); | |
| return Status(); | |
| } | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec_without_uuid, | |
| /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 2); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), | |
| FileSpec(GetInputFilePath(k_module_file))); | |
| ASSERT_FALSE(m_module_sp->GetSymbolFileFileSpec()); | |
| CheckStrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleCallbackSuccessWithSymbolByPlatformUUID) { | |
| // Same as GetOrCreateModuleCallbackSuccessWithModuleByPlatformUUID, | |
| // but with a symbol file. GetOrCreateModule should succeed to return the | |
| // module file and the symbol file from the Inputs directory. | |
| BuildEmptyCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| callback_call_count++; | |
| if (callback_call_count == 1) { | |
| // The module_spec does not have UUID on the first call. | |
| CheckCallbackArgsWithoutUUID(module_spec, module_file_spec, | |
| symbol_file_spec, callback_call_count); | |
| return Status::FromErrorString("Ignored empty UUID"); | |
| } else { | |
| // The module_spec has UUID on the second call. | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, callback_call_count); | |
| module_file_spec.SetPath(GetInputFilePath(k_module_file)); | |
| symbol_file_spec.SetPath(GetInputFilePath(k_symbol_file)); | |
| return Status(); | |
| } | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec_without_uuid, | |
| /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 2); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), | |
| FileSpec(GetInputFilePath(k_module_file))); | |
| ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(), | |
| FileSpec(GetInputFilePath(k_symbol_file))); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleCallbackSuccessWithBreakpadSymbolByPlatformUUID) { | |
| // Same as GetOrCreateModuleCallbackSuccessWithModuleByPlatformUUID, | |
| // but with a breakpad symbol file. GetOrCreateModule should succeed to return | |
| // the module file and the symbol file from the Inputs directory. | |
| BuildEmptyCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| callback_call_count++; | |
| if (callback_call_count == 1) { | |
| // The module_spec does not have UUID on the first call. | |
| CheckCallbackArgsWithoutUUID(module_spec, module_file_spec, | |
| symbol_file_spec, callback_call_count); | |
| return Status::FromErrorString("Ignored empty UUID"); | |
| } else { | |
| // The module_spec has UUID on the second call. | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, callback_call_count); | |
| module_file_spec.SetPath(GetInputFilePath(k_module_file)); | |
| symbol_file_spec.SetPath(GetInputFilePath(k_breakpad_symbol_file)); | |
| return Status(); | |
| } | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec_without_uuid, | |
| /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 2); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), | |
| FileSpec(GetInputFilePath(k_module_file))); | |
| ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(), | |
| FileSpec(GetInputFilePath(k_breakpad_symbol_file))); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetOrCreateModuleCallbackSuccessWithOnlyBreakpadSymbolByPlatformUUID) { | |
| // This is a simulation for Android remote platform debugging. | |
| // The locate module callback first call fails because module_spec does not | |
| // have UUID. Then, the callback second call returns a breakpad symbol file | |
| // for the UUID from the target process. GetOrCreateModule should succeed to | |
| // return the module from the cache along with the symbol file from the Inputs | |
| // directory. | |
| FileSpec uuid_view = BuildCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| callback_call_count++; | |
| if (callback_call_count == 1) { | |
| // The module_spec does not have UUID on the first call. | |
| CheckCallbackArgsWithoutUUID(module_spec, module_file_spec, | |
| symbol_file_spec, callback_call_count); | |
| return Status::FromErrorString("Ignored empty UUID"); | |
| } else { | |
| // The module_spec has UUID on the second call. | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, callback_call_count); | |
| symbol_file_spec.SetPath(GetInputFilePath(k_breakpad_symbol_file)); | |
| return Status(); | |
| } | |
| }); | |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec_without_uuid, | |
| /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 2); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ASSERT_EQ(m_module_sp->GetSymbolFileFileSpec(), | |
| FileSpec(GetInputFilePath(k_breakpad_symbol_file))); | |
| CheckUnstrippedSymbol(m_module_sp); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetSharedModuleWithPlatformFallbackWhenTargetNotAvailable) { | |
| // Test that when a Target is not available but Platform is set on ModuleSpec, | |
| // the locate module callback is still invoked via the Platform fallback. | |
| // This is the key functionality added for launch mode support where the | |
| // Target is not yet created but we need to resolve the main executable. | |
| BuildEmptyCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| module_file_spec.SetPath(GetInputFilePath(k_module_file)); | |
| return Status(); | |
| }); | |
| // Create a ModuleSpec without Target but with Platform set | |
| ModuleSpec module_spec_with_platform = m_module_spec; | |
| module_spec_with_platform.SetPlatform(m_platform_sp); | |
| // Call GetSharedModule directly (simulating the path taken during | |
| // target creation in launch mode) | |
| bool always_create = true; | |
| Status error = ModuleList::GetSharedModule( | |
| module_spec_with_platform, m_module_sp, nullptr, nullptr, always_create); | |
| ASSERT_TRUE(error.Success()); | |
| ASSERT_EQ(callback_call_count, 1); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), | |
| FileSpec(GetInputFilePath(k_module_file))); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetSharedModulePreferTargetPlatformOverModuleSpecPlatform) { | |
| // Test that when both Target and Platform are available on ModuleSpec, | |
| // the Target's platform is preferred. This ensures backward compatibility | |
| // and that the existing behavior is maintained for cases where Target | |
| // is available. | |
| FileSpec uuid_view = BuildCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [this, &callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| CheckCallbackArgsWithUUID(module_spec, module_file_spec, | |
| symbol_file_spec, ++callback_call_count); | |
| return Status(); | |
| }); | |
| // Set both Target and Platform on ModuleSpec | |
| ModuleSpec module_spec_with_both = m_module_spec; | |
| module_spec_with_both.SetTarget(m_target_sp); | |
| module_spec_with_both.SetPlatform(m_platform_sp); | |
| m_module_sp = | |
| m_target_sp->GetOrCreateModule(module_spec_with_both, /*notify=*/false); | |
| ASSERT_EQ(callback_call_count, 3); | |
| CheckModule(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), uuid_view); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, | |
| GetSharedModuleWithPlatformFallbackNoCallback) { | |
| // Test that when Platform is set on ModuleSpec but no callback is | |
| // registered, GetSharedModule still works and finds the module through | |
| // normal resolution (using the file path in ModuleSpec). | |
| // This verifies the no-callback case is handled gracefully and | |
| // doesn't break normal module resolution. | |
| BuildEmptyCacheDir(m_test_dir); | |
| CheckNoCallback(); | |
| // Create a ModuleSpec with an actual file path that exists, and Platform set | |
| ModuleSpec module_spec_with_platform( | |
| FileSpec(GetInputFilePath(k_module_file)), ArchSpec(k_arch)); | |
| module_spec_with_platform.GetUUID().SetFromStringRef(k_module_uuid); | |
| module_spec_with_platform.SetObjectSize(k_module_size); | |
| module_spec_with_platform.SetPlatform(m_platform_sp); | |
| Status error = ModuleList::GetSharedModule( | |
| module_spec_with_platform, m_module_sp, nullptr, nullptr, false); | |
| // Without a callback, the module should still be found via the file path | |
| // in the ModuleSpec. The platform fallback code path is exercised (checking | |
| // for callback) but since there's none, it falls through to normal | |
| // resolution. | |
| ASSERT_TRUE(error.Success()); | |
| ASSERT_TRUE(m_module_sp); | |
| ASSERT_EQ(m_module_sp->GetUUID().GetAsString(), k_module_uuid); | |
| ASSERT_EQ(m_module_sp->GetFileSpec(), | |
| FileSpec(GetInputFilePath(k_module_file))); | |
| ModuleList::RemoveSharedModule(m_module_sp); | |
| } | |
| TEST_F(LocateModuleCallbackTest, ModuleCacheGetDoesNotInvokeCallback) { | |
| // The module file is cached. ModuleCache::Get should succeed to return the | |
| // module from the cache without calling the locate module callback. | |
| FileSpec uuid_view = BuildCacheDir(m_test_dir); | |
| int callback_call_count = 0; | |
| m_platform_sp->SetLocateModuleCallback( | |
| [&callback_call_count](const ModuleSpec &module_spec, | |
| FileSpec &module_file_spec, | |
| FileSpec &symbol_file_spec) { | |
| callback_call_count++; | |
| return Status(); | |
| }); | |
| ModuleCache mc; | |
| ModuleSP cached_module_sp; | |
| bool did_create = false; | |
| FileSpec root_dir_spec(m_test_dir); | |
| root_dir_spec.AppendPathComponent(k_platform_dir); | |
| Status error = mc.GetAndPut( | |
| root_dir_spec, "hostname", m_module_spec, | |
| [](const ModuleSpec &module_spec, | |
| const FileSpec &tmp_download_file_spec) { | |
| return Status::FromErrorString("Should not be called"); | |
| }, | |
| [](const ModuleSP &module_sp, const FileSpec &tmp_download_file_spec) { | |
| return Status::FromErrorString("Should not be called"); | |
| }, | |
| cached_module_sp, &did_create); | |
| ASSERT_TRUE(error.Success()); | |
| ASSERT_TRUE(cached_module_sp); | |
| ASSERT_EQ(cached_module_sp->GetFileSpec(), uuid_view); | |
| // The locate module callback should not be called. | |
| EXPECT_EQ(callback_call_count, 0); | |
| ModuleList::RemoveSharedModule(cached_module_sp); | |
| } |