[Lldb-commits] [lldb] 7f1028e - [lldb][LocateModuleCallback] Call locate module callback
Kazuki Sakamoto via lldb-commits
lldb-commits at lists.llvm.org
Wed Jul 12 11:24:45 PDT 2023
Author: Kazuki Sakamoto
Date: 2023-07-12T11:19:51-07:00
New Revision: 7f1028e9df52b4e7246f189a24684b1ca8c9bfbe
URL: https://github.com/llvm/llvm-project/commit/7f1028e9df52b4e7246f189a24684b1ca8c9bfbe
DIFF: https://github.com/llvm/llvm-project/commit/7f1028e9df52b4e7246f189a24684b1ca8c9bfbe.diff
LOG: [lldb][LocateModuleCallback] Call locate module callback
RFC https://discourse.llvm.org/t/rfc-python-callback-for-target-get-module/71580
Updated Target::GetOrCreateModule to call locate module callback if set.
- include/lldb/Target/Platform.h, source/Target/Platform.cpp
- Implemented SetLocateModuleCallback and GetLocateModuleCallback*
- include/lldb/Target/Target.h, source/Target/Target.cpp
- Implemented CallLocateModuleCallbackIfSet.
- unittests/Target/LocateModuleCallbackTest.cpp
- Added comprehensive GetOrCreateModule tests.
Differential Revision: https://reviews.llvm.org/D153734
Added:
lldb/unittests/Target/Inputs/AndroidModule.c
lldb/unittests/Target/Inputs/AndroidModule.so
lldb/unittests/Target/Inputs/AndroidModule.so.sym
lldb/unittests/Target/Inputs/AndroidModule.unstripped.so
lldb/unittests/Target/LocateModuleCallbackTest.cpp
Modified:
lldb/include/lldb/Target/Platform.h
lldb/include/lldb/Target/Target.h
lldb/source/Target/Platform.cpp
lldb/source/Target/Target.cpp
lldb/unittests/Target/CMakeLists.txt
Removed:
################################################################################
diff --git a/lldb/include/lldb/Target/Platform.h b/lldb/include/lldb/Target/Platform.h
index 35227429b126ae..a5a78fe0f86983 100644
--- a/lldb/include/lldb/Target/Platform.h
+++ b/lldb/include/lldb/Target/Platform.h
@@ -881,6 +881,19 @@ class Platform : public PluginInterface {
virtual Args GetExtraStartupCommands();
+ typedef std::function<Status(const ModuleSpec &module_spec,
+ FileSpec &module_file_spec,
+ FileSpec &symbol_file_spec)>
+ LocateModuleCallback;
+
+ /// Set locate module callback. This allows users to implement their own
+ /// module cache system. For example, to leverage artifacts of build system,
+ /// to bypass pulling files from remote platform, or to search symbol files
+ /// from symbol servers.
+ void SetLocateModuleCallback(LocateModuleCallback callback);
+
+ LocateModuleCallback GetLocateModuleCallback() const;
+
protected:
/// Create a list of ArchSpecs with the given OS and a architectures. The
/// vendor field is left as an "unspecified unknown".
@@ -928,6 +941,7 @@ class Platform : public PluginInterface {
std::vector<ConstString> m_trap_handlers;
bool m_calculated_trap_handlers;
const std::unique_ptr<ModuleCache> m_module_cache;
+ LocateModuleCallback m_locate_module_callback;
/// Ask the Platform subclass to fill in the list of trap handler names
///
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index ed0ecbbddbf814..d5b4b8e18c2d2d 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -1625,6 +1625,12 @@ class Target : public std::enable_shared_from_this<Target>,
Target(const Target &) = delete;
const Target &operator=(const Target &) = delete;
+
+private:
+ void CallLocateModuleCallbackIfSet(const ModuleSpec &module_spec,
+ lldb::ModuleSP &module_sp,
+ FileSpec &symbol_file_spec,
+ bool &did_create_module);
};
} // namespace lldb_private
diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp
index eedf596f1977f3..11a123fb6d583d 100644
--- a/lldb/source/Target/Platform.cpp
+++ b/lldb/source/Target/Platform.cpp
@@ -1971,6 +1971,14 @@ Args Platform::GetExtraStartupCommands() {
return {};
}
+void Platform::SetLocateModuleCallback(LocateModuleCallback callback) {
+ m_locate_module_callback = callback;
+}
+
+Platform::LocateModuleCallback Platform::GetLocateModuleCallback() const {
+ return m_locate_module_callback;
+}
+
PlatformSP PlatformList::GetOrCreate(llvm::StringRef name) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
for (const PlatformSP &platform_sp : m_platforms) {
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 06867d359a3c0f..f0c57ef83c4a14 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -2129,19 +2129,46 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify,
// of the library
bool did_create_module = false;
FileSpecList search_paths = GetExecutableSearchPaths();
- // If there are image search path entries, try to use them first to acquire
- // a suitable image.
- if (m_image_search_paths.GetSize()) {
- ModuleSpec transformed_spec(module_spec);
- ConstString transformed_dir;
- if (m_image_search_paths.RemapPath(
- module_spec.GetFileSpec().GetDirectory(), transformed_dir)) {
- transformed_spec.GetFileSpec().SetDirectory(transformed_dir);
- transformed_spec.GetFileSpec().SetFilename(
- module_spec.GetFileSpec().GetFilename());
- error = ModuleList::GetSharedModule(transformed_spec, module_sp,
- &search_paths, &old_modules,
- &did_create_module);
+ FileSpec symbol_file_spec;
+
+ // Call locate module callback if set. This allows users to implement their
+ // own module cache system. For example, to leverage build system artifacts,
+ // to bypass pulling files from remote platform, or to search symbol files
+ // from symbol servers.
+ CallLocateModuleCallbackIfSet(module_spec, module_sp, symbol_file_spec,
+ did_create_module);
+
+ // The result of this CallLocateModuleCallbackIfSet is one of the following.
+ // 1. module_sp:loaded, symbol_file_spec:set
+ // The callback found a module file and a symbol file for the
+ // module_spec. We will call module_sp->SetSymbolFileFileSpec with
+ // the symbol_file_spec later.
+ // 2. module_sp:loaded, symbol_file_spec:empty
+ // The callback only found a module file for the module_spec.
+ // 3. module_sp:empty, symbol_file_spec:set
+ // The callback only found a symbol file for the module. We continue
+ // to find a module file for this module_spec and we will call
+ // module_sp->SetSymbolFileFileSpec with the symbol_file_spec later.
+ // 4. module_sp:empty, symbol_file_spec:empty
+ // The callback is not set. Or the callback did not find any module
+ // files nor any symbol files. Or the callback failed, or something
+ // went wrong. We continue to find a module file for this module_spec.
+
+ if (!module_sp) {
+ // If there are image search path entries, try to use them to acquire a
+ // suitable image.
+ if (m_image_search_paths.GetSize()) {
+ ModuleSpec transformed_spec(module_spec);
+ ConstString transformed_dir;
+ if (m_image_search_paths.RemapPath(
+ module_spec.GetFileSpec().GetDirectory(), transformed_dir)) {
+ transformed_spec.GetFileSpec().SetDirectory(transformed_dir);
+ transformed_spec.GetFileSpec().SetFilename(
+ module_spec.GetFileSpec().GetFilename());
+ error = ModuleList::GetSharedModule(transformed_spec, module_sp,
+ &search_paths, &old_modules,
+ &did_create_module);
+ }
}
}
@@ -2232,6 +2259,11 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify,
});
}
+ // If the locate module callback had found a symbol file, set it to the
+ // module_sp before preloading symbols.
+ if (symbol_file_spec)
+ module_sp->SetSymbolFileFileSpec(symbol_file_spec);
+
// Preload symbols outside of any lock, so hopefully we can do this for
// each library in parallel.
if (GetPreloadSymbols())
@@ -2306,6 +2338,113 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &module_spec, bool notify,
return module_sp;
}
+void Target::CallLocateModuleCallbackIfSet(const ModuleSpec &module_spec,
+ lldb::ModuleSP &module_sp,
+ FileSpec &symbol_file_spec,
+ bool &did_create_module) {
+ if (!m_platform_sp)
+ return;
+
+ Platform::LocateModuleCallback locate_module_callback =
+ m_platform_sp->GetLocateModuleCallback();
+ if (!locate_module_callback)
+ return;
+
+ FileSpec module_file_spec;
+ Status error =
+ locate_module_callback(module_spec, module_file_spec, symbol_file_spec);
+
+ // Locate module callback is set and called. Check the error.
+ Log *log = GetLog(LLDBLog::Target);
+ if (error.Fail()) {
+ LLDB_LOGF(log, "%s: locate module callback failed: %s",
+ LLVM_PRETTY_FUNCTION, error.AsCString());
+ return;
+ }
+
+ // The locate module callback was succeeded. It should returned
+ // 1. a combination of a module file and a symbol file.
+ // 2. or only a module file.
+ // 3. or only a symbol file. For example, a breakpad symbol text file.
+ //
+ // Check the module_file_spec and symbol_file_spec values.
+ // 1. module:empty symbol:empty -> Invalid
+ // 2. module:exists symbol:exists -> Success
+ // 3. module:exists symbol:empty -> Success
+ // 4. module:empty symbol:exists -> Success
+ if (!module_file_spec && !symbol_file_spec) {
+ // This is '1. module:empty symbol:empty -> Invalid'.
+ LLDB_LOGF(log,
+ "%s: locate module callback did not set both "
+ "module_file_spec and symbol_file_spec",
+ LLVM_PRETTY_FUNCTION);
+ return;
+ }
+
+ // The module file should exist.
+ if (module_file_spec && !FileSystem::Instance().Exists(module_file_spec)) {
+ LLDB_LOGF(log,
+ "%s: locate module callback set a non-existent file to "
+ "module_file_spec: %s",
+ LLVM_PRETTY_FUNCTION, module_file_spec.GetPath().c_str());
+ // Clear symbol_file_spec for the error.
+ symbol_file_spec.Clear();
+ return;
+ }
+
+ // The symbol file should exist.
+ if (symbol_file_spec && !FileSystem::Instance().Exists(symbol_file_spec)) {
+ LLDB_LOGF(log,
+ "%s: locate module callback set a non-existent file to "
+ "symbol_file_spec: %s",
+ LLVM_PRETTY_FUNCTION, symbol_file_spec.GetPath().c_str());
+ // Clear symbol_file_spec for the error.
+ symbol_file_spec.Clear();
+ return;
+ }
+
+ if (!module_file_spec && symbol_file_spec) {
+ // This is '4. module:empty symbol:exists -> Success'.
+ // The locate module callback returned only a symbol file. For example,
+ // a breakpad symbol text file. GetOrCreateModule will use this returned
+ // symbol_file_spec.
+ LLDB_LOGF(log, "%s: locate module callback succeeded: symbol=%s",
+ LLVM_PRETTY_FUNCTION, symbol_file_spec.GetPath().c_str());
+ return;
+ }
+
+ // The locate module callback returned
+ // - '2. module:exists symbol:exists -> Success'
+ // - a combination of a module file and a symbol file.
+ // - Or '3. module:exists symbol:empty -> Success'
+ // - only a module file.
+ // Load the module file.
+ auto cached_module_spec(module_spec);
+ cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5
+ // content hash instead of real UUID.
+ cached_module_spec.GetFileSpec() = module_file_spec;
+ cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec();
+ cached_module_spec.SetObjectOffset(0);
+
+ error = ModuleList::GetSharedModule(cached_module_spec, module_sp, nullptr,
+ nullptr, &did_create_module, false);
+ if (error.Success() && module_sp) {
+ // Succeeded to load the module file.
+ LLDB_LOGF(log, "%s: locate module callback succeeded: module=%s symbol=%s",
+ LLVM_PRETTY_FUNCTION, module_file_spec.GetPath().c_str(),
+ symbol_file_spec.GetPath().c_str());
+ } else {
+ LLDB_LOGF(log,
+ "%s: locate module callback succeeded but failed to load: "
+ "module=%s symbol=%s",
+ LLVM_PRETTY_FUNCTION, module_file_spec.GetPath().c_str(),
+ symbol_file_spec.GetPath().c_str());
+ // Clear module_sp and symbol_file_spec for the error.
+ module_sp.reset();
+ symbol_file_spec.Clear();
+ }
+}
+
TargetSP Target::CalculateTarget() { return shared_from_this(); }
ProcessSP Target::CalculateProcess() { return m_process_sp; }
diff --git a/lldb/unittests/Target/CMakeLists.txt b/lldb/unittests/Target/CMakeLists.txt
index 99431435d29363..d10e93ca6336d6 100644
--- a/lldb/unittests/Target/CMakeLists.txt
+++ b/lldb/unittests/Target/CMakeLists.txt
@@ -2,6 +2,7 @@ add_lldb_unittest(TargetTests
ABITest.cpp
DynamicRegisterInfoTest.cpp
ExecutionContextTest.cpp
+ LocateModuleCallbackTest.cpp
MemoryRegionInfoTest.cpp
MemoryTest.cpp
MemoryTagMapTest.cpp
@@ -15,9 +16,12 @@ add_lldb_unittest(TargetTests
LINK_LIBS
lldbCore
lldbHost
+ lldbPluginObjectFileBreakpad
lldbPluginObjectFileELF
lldbPluginPlatformLinux
lldbPluginPlatformMacOSX
+ lldbPluginPlatformAndroid
+ lldbPluginSymbolFileBreakpad
lldbPluginSymbolFileSymtab
lldbTarget
lldbSymbol
@@ -27,4 +31,10 @@ add_lldb_unittest(TargetTests
Support
)
-add_unittest_inputs(TargetTests TestModule.so)
+set(test_inputs
+ AndroidModule.so
+ AndroidModule.so.sym
+ AndroidModule.unstripped.so
+ TestModule.so
+ )
+add_unittest_inputs(TargetTests "${test_inputs}")
diff --git a/lldb/unittests/Target/Inputs/AndroidModule.c b/lldb/unittests/Target/Inputs/AndroidModule.c
new file mode 100644
index 00000000000000..a898be84522d4d
--- /dev/null
+++ b/lldb/unittests/Target/Inputs/AndroidModule.c
@@ -0,0 +1,13 @@
+// aarch64-linux-android29-clang -shared -Os -glldb -g3 -Wl,--build-id=sha1 \
+// AndroidModule.c -o AndroidModule.so
+// dump_syms AndroidModule.so > AndroidModule.so.sym
+// cp AndroidModule.so AndroidModule.unstripped.so
+// llvm-strip --strip-unneeded AndroidModule.so
+
+int boom(void) {
+ return 47;
+}
+
+__attribute__((visibility("hidden"))) int boom_hidden(void) {
+ return 48;
+}
diff --git a/lldb/unittests/Target/Inputs/AndroidModule.so b/lldb/unittests/Target/Inputs/AndroidModule.so
new file mode 100644
index 00000000000000..89b357dc0360a9
Binary files /dev/null and b/lldb/unittests/Target/Inputs/AndroidModule.so
diff er
diff --git a/lldb/unittests/Target/Inputs/AndroidModule.so.sym b/lldb/unittests/Target/Inputs/AndroidModule.so.sym
new file mode 100644
index 00000000000000..21f23c4df9b275
--- /dev/null
+++ b/lldb/unittests/Target/Inputs/AndroidModule.so.sym
@@ -0,0 +1,21 @@
+MODULE Linux arm64 38830080A082E5515922C905D23890DA0 AndroidModule.so
+INFO CODE_ID 8000833882A051E55922C905D23890DABDDEFECC
+FILE 0 /private/tmp/test/AndroidModule.c
+FUNC 162c 8 0 boom
+162c 8 8 0
+FUNC 1634 8 0 boom_hidden
+1634 8 12 0
+PUBLIC 15cc 0 __on_dlclose
+PUBLIC 15dc 0 __emutls_unregister_key
+PUBLIC 15e4 0 __on_dlclose_late
+PUBLIC 15ec 0 __atexit_handler_wrapper
+PUBLIC 1600 0 atexit
+PUBLIC 161c 0 pthread_atfork
+STACK CFI INIT 15cc 10 .cfa: sp 0 + .ra: x30
+STACK CFI INIT 15dc 8 .cfa: sp 0 + .ra: x30
+STACK CFI INIT 15e4 8 .cfa: sp 0 + .ra: x30
+STACK CFI INIT 15ec 14 .cfa: sp 0 + .ra: x30
+STACK CFI INIT 1600 1c .cfa: sp 0 + .ra: x30
+STACK CFI INIT 161c 10 .cfa: sp 0 + .ra: x30
+STACK CFI INIT 162c 8 .cfa: sp 0 + .ra: x30
+STACK CFI INIT 1634 8 .cfa: sp 0 + .ra: x30
diff --git a/lldb/unittests/Target/Inputs/AndroidModule.unstripped.so b/lldb/unittests/Target/Inputs/AndroidModule.unstripped.so
new file mode 100644
index 00000000000000..459910ca2cdb6e
Binary files /dev/null and b/lldb/unittests/Target/Inputs/AndroidModule.unstripped.so
diff er
diff --git a/lldb/unittests/Target/LocateModuleCallbackTest.cpp b/lldb/unittests/Target/LocateModuleCallbackTest.cpp
new file mode 100644
index 00000000000000..cd5eb4473473bb
--- /dev/null
+++ b/lldb/unittests/Target/LocateModuleCallbackTest.cpp
@@ -0,0 +1,640 @@
+//===-- 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::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, 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.
+ 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();
+ }
+
+ 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) {
+ EXPECT_TRUE(m_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, 0);
+ m_callback_call_count++;
+ }
+
+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;
+ 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();
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(), uuid_view);
+ ASSERT_FALSE(module_sp->GetSymbolFileFileSpec());
+ CheckStrippedSymbol(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();
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(), uuid_view);
+ ASSERT_EQ(module_sp->GetSymbolFileFileSpec(), GetSymFileSpec(uuid_view));
+ CheckUnstrippedSymbol(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();
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(), uuid_view);
+ ASSERT_EQ(module_sp->GetSymbolFileFileSpec(), GetSymFileSpec(uuid_view));
+ CheckUnstrippedSymbol(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();
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ ASSERT_FALSE(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);
+
+ m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec,
+ FileSpec &module_file_spec,
+ FileSpec &symbol_file_spec) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ return Status("The locate module callback failed");
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ ASSERT_FALSE(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);
+
+ m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec,
+ FileSpec &module_file_spec,
+ FileSpec &symbol_file_spec) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ return Status("The locate module callback failed");
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(), uuid_view);
+ ASSERT_FALSE(module_sp->GetSymbolFileFileSpec());
+ CheckStrippedSymbol(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);
+
+ m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec,
+ FileSpec &module_file_spec,
+ FileSpec &symbol_file_spec) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ // The locate module callback succeeds but it does not set
+ // module_file_spec nor symbol_file_spec.
+ return Status();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(), uuid_view);
+ ASSERT_FALSE(module_sp->GetSymbolFileFileSpec());
+ CheckStrippedSymbol(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);
+
+ m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec,
+ FileSpec &module_file_spec,
+ FileSpec &symbol_file_spec) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ module_file_spec.SetPath("/this path does not exist");
+ return Status();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(), uuid_view);
+ ASSERT_FALSE(module_sp->GetSymbolFileFileSpec());
+ CheckStrippedSymbol(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);
+
+ m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec,
+ FileSpec &module_file_spec,
+ FileSpec &symbol_file_spec) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ // 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();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(), uuid_view);
+ ASSERT_TRUE(module_sp->GetSymbolFileFileSpec().GetPath().empty());
+ CheckStrippedSymbol(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) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ module_file_spec.SetPath(GetInputFilePath(k_module_file));
+ return Status();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(),
+ FileSpec(GetInputFilePath(k_module_file)));
+ ASSERT_FALSE(module_sp->GetSymbolFileFileSpec());
+ CheckStrippedSymbol(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) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ module_file_spec.SetPath(GetInputFilePath(k_symbol_file));
+ return Status();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(),
+ FileSpec(GetInputFilePath(k_symbol_file)));
+ ASSERT_FALSE(module_sp->GetSymbolFileFileSpec());
+ CheckUnstrippedSymbol(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) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ module_file_spec.SetPath(GetInputFilePath(k_symbol_file));
+ symbol_file_spec.SetPath(GetInputFilePath(k_symbol_file));
+ return Status();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(),
+ FileSpec(GetInputFilePath(k_symbol_file)));
+ ASSERT_EQ(module_sp->GetSymbolFileFileSpec(),
+ FileSpec(GetInputFilePath(k_symbol_file)));
+ CheckUnstrippedSymbol(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) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ module_file_spec.SetPath(GetInputFilePath(k_module_file));
+ symbol_file_spec.SetPath(GetInputFilePath(k_symbol_file));
+ return Status();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(),
+ FileSpec(GetInputFilePath(k_module_file)));
+ ASSERT_EQ(module_sp->GetSymbolFileFileSpec(),
+ FileSpec(GetInputFilePath(k_symbol_file)));
+ CheckUnstrippedSymbol(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) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ module_file_spec.SetPath(GetInputFilePath(k_module_file));
+ symbol_file_spec.SetPath(GetInputFilePath(k_breakpad_symbol_file));
+ return Status();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(),
+ FileSpec(GetInputFilePath(k_module_file)));
+ ASSERT_EQ(module_sp->GetSymbolFileFileSpec(),
+ FileSpec(GetInputFilePath(k_breakpad_symbol_file)));
+ CheckUnstrippedSymbol(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);
+
+ m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec,
+ FileSpec &module_file_spec,
+ FileSpec &symbol_file_spec) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ symbol_file_spec.SetPath(GetInputFilePath(k_symbol_file));
+ return Status();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(), uuid_view);
+ ASSERT_EQ(module_sp->GetSymbolFileFileSpec(),
+ FileSpec(GetInputFilePath(k_symbol_file)));
+ CheckUnstrippedSymbol(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);
+
+ m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec,
+ FileSpec &module_file_spec,
+ FileSpec &symbol_file_spec) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ symbol_file_spec.SetPath(GetInputFilePath(k_breakpad_symbol_file));
+ return Status();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ CheckModule(module_sp);
+ ASSERT_EQ(module_sp->GetFileSpec(), uuid_view);
+ ASSERT_EQ(module_sp->GetSymbolFileFileSpec(),
+ FileSpec(GetInputFilePath(k_breakpad_symbol_file)));
+ CheckUnstrippedSymbol(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);
+
+ m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec,
+ FileSpec &module_file_spec,
+ FileSpec &symbol_file_spec) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ symbol_file_spec.SetPath(GetInputFilePath(k_symbol_file));
+ return Status();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ ASSERT_FALSE(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);
+
+ m_platform_sp->SetLocateModuleCallback([this](const ModuleSpec &module_spec,
+ FileSpec &module_file_spec,
+ FileSpec &symbol_file_spec) {
+ CheckCallbackArgs(module_spec, module_file_spec, symbol_file_spec);
+ symbol_file_spec.SetPath(GetInputFilePath(k_breakpad_symbol_file));
+ return Status();
+ });
+
+ ModuleSP module_sp =
+ m_target_sp->GetOrCreateModule(m_module_spec, /*notify=*/false);
+ ASSERT_FALSE(module_sp);
+}
More information about the lldb-commits
mailing list