| //===-- 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/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().GetCString()); |
| 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); |
| 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("The locate module callback failed"); |
| }); |
| |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); |
| ASSERT_EQ(callback_call_count, 2); |
| 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("The locate module callback failed"); |
| }); |
| |
| m_module_sp = m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false); |
| ASSERT_EQ(callback_call_count, 2); |
| 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, 2); |
| 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, 2); |
| 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, 2); |
| 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, 2); |
| 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, 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, |
| 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, 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, |
| 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, 2); |
| 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, 2); |
| 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("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("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("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("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); |
| } |