| """ |
| Test platform locate module callback functionality |
| """ |
| |
| import ctypes |
| from lldbsuite.test.decorators import * |
| from lldbsuite.test.lldbtest import * |
| from pathlib import Path |
| |
| import lldb |
| |
| UNITTESTS_TARGET_INPUTS_PATH = "../../../../unittests/Target/Inputs" |
| MODULE_PLATFORM_PATH = "/system/lib64/AndroidModule.so" |
| MODULE_TRIPLE = "aarch64-none-linux" |
| MODULE_RESOLVED_TRIPLE = "aarch64--linux-android" |
| MODULE_UUID = "80008338-82A0-51E5-5922-C905D23890DA-BDDEFECC" |
| MODULE_FUNCTION = "boom" |
| MODULE_HIDDEN_FUNCTION = "boom_hidden" |
| MODULE_FILE = "AndroidModule.so" |
| MODULE_NON_EXISTENT_FILE = "non-existent-file" |
| SYMBOL_FILE = "AndroidModule.unstripped.so" |
| BREAKPAD_SYMBOL_FILE = "AndroidModule.so.sym" |
| SYMBOL_STRIPPED = "stripped" |
| SYMBOL_UNSTRIPPED = "unstripped" |
| |
| |
| class LocateModuleCallbackTestCase(TestBase): |
| def setUp(self): |
| TestBase.setUp(self) |
| self.platform = self.dbg.GetSelectedPlatform() |
| self.target = self.dbg.CreateTarget("") |
| self.assertTrue(self.target) |
| |
| self.input_dir = ( |
| Path(self.getSourceDir()) / UNITTESTS_TARGET_INPUTS_PATH |
| ).resolve() |
| self.assertTrue(self.input_dir.is_dir()) |
| |
| def check_module_spec(self, module_spec: lldb.SBModuleSpec): |
| self.assertEqual( |
| MODULE_UUID.replace("-", ""), |
| ctypes.string_at( |
| int(module_spec.GetUUIDBytes()), |
| module_spec.GetUUIDLength(), |
| ) |
| .hex() |
| .upper(), |
| ) |
| |
| self.assertEqual(MODULE_TRIPLE, module_spec.GetTriple()) |
| |
| self.assertEqual( |
| MODULE_PLATFORM_PATH, Path(module_spec.GetFileSpec().fullpath).as_posix() |
| ) |
| |
| def check_module(self, module: lldb.SBModule, symbol_file: str, symbol_kind: str): |
| self.assertTrue(module.IsValid()) |
| |
| self.assertEqual( |
| MODULE_UUID, |
| module.GetUUIDString(), |
| ) |
| |
| self.assertEqual(MODULE_RESOLVED_TRIPLE, module.GetTriple()) |
| |
| self.assertEqual( |
| MODULE_PLATFORM_PATH, Path(module.GetPlatformFileSpec().fullpath).as_posix() |
| ) |
| |
| self.assertTrue( |
| (self.input_dir / MODULE_FILE) |
| .resolve() |
| .samefile(Path(module.GetFileSpec().fullpath).resolve()) |
| ) |
| |
| self.assertTrue( |
| (self.input_dir / symbol_file) |
| .resolve() |
| .samefile(Path(module.GetSymbolFileSpec().fullpath).resolve()) |
| ) |
| |
| sc_list = module.FindFunctions(MODULE_FUNCTION, lldb.eSymbolTypeCode) |
| self.assertEqual(1, sc_list.GetSize()) |
| sc_list = module.FindFunctions(MODULE_HIDDEN_FUNCTION, lldb.eSymbolTypeCode) |
| self.assertEqual(0 if symbol_kind == SYMBOL_STRIPPED else 1, sc_list.GetSize()) |
| |
| def test_set_non_callable(self): |
| # The callback should be callable. |
| non_callable = "a" |
| |
| with self.assertRaises(TypeError, msg="Need a callable object or None!"): |
| self.platform.SetLocateModuleCallback(non_callable) |
| |
| def test_set_wrong_args(self): |
| # The callback should accept 3 argument. |
| def test_args2(a, b): |
| pass |
| |
| with self.assertRaises(TypeError, msg="Expected 3 argument callable object"): |
| self.platform.SetLocateModuleCallback(test_args2) |
| |
| def test_default(self): |
| # The default behavior is to locate the module with LLDB implementation |
| # and AddModule should fail. |
| module = self.target.AddModule( |
| MODULE_PLATFORM_PATH, |
| MODULE_TRIPLE, |
| MODULE_UUID, |
| ) |
| |
| self.assertFalse(module) |
| |
| def test_set_none(self): |
| # SetLocateModuleCallback should succeed to clear the callback with None. |
| # and AddModule should fail. |
| self.assertTrue(self.platform.SetLocateModuleCallback(None).Success()) |
| |
| module = self.target.AddModule( |
| MODULE_PLATFORM_PATH, |
| MODULE_TRIPLE, |
| MODULE_UUID, |
| ) |
| |
| self.assertFalse(module) |
| |
| def test_return_error(self): |
| # The callback fails, AddModule should fail. |
| def test_locate_module( |
| module_spec: lldb.SBModuleSpec, |
| module_file_spec: lldb.SBFileSpec, |
| symbol_file_spec: lldb.SBFileSpec, |
| ): |
| self.check_module_spec(module_spec) |
| return lldb.SBError("locate module callback failed") |
| |
| self.assertTrue( |
| self.platform.SetLocateModuleCallback(test_locate_module).Success() |
| ) |
| |
| module = self.target.AddModule( |
| MODULE_PLATFORM_PATH, |
| MODULE_TRIPLE, |
| MODULE_UUID, |
| ) |
| |
| self.assertFalse(module) |
| |
| def test_return_no_files(self): |
| # The callback succeeds but not return any files, AddModule should fail. |
| def test_locate_module( |
| module_spec: lldb.SBModuleSpec, |
| module_file_spec: lldb.SBFileSpec, |
| symbol_file_spec: lldb.SBFileSpec, |
| ): |
| self.check_module_spec(module_spec) |
| return lldb.SBError() |
| |
| self.assertTrue( |
| self.platform.SetLocateModuleCallback(test_locate_module).Success() |
| ) |
| |
| module = self.target.AddModule( |
| MODULE_PLATFORM_PATH, |
| MODULE_TRIPLE, |
| MODULE_UUID, |
| ) |
| |
| self.assertFalse(module) |
| |
| def test_return_non_existent_module(self): |
| # The callback returns non-existent module file, AddModule should fail. |
| def test_locate_module( |
| module_spec: lldb.SBModuleSpec, |
| module_file_spec: lldb.SBFileSpec, |
| symbol_file_spec: lldb.SBFileSpec, |
| ): |
| self.check_module_spec(module_spec) |
| |
| module_file_spec.SetDirectory(str(self.input_dir)) |
| module_file_spec.SetFilename(MODULE_NON_EXISTENT_FILE) |
| |
| return lldb.SBError() |
| |
| self.assertTrue( |
| self.platform.SetLocateModuleCallback(test_locate_module).Success() |
| ) |
| |
| module = self.target.AddModule( |
| MODULE_PLATFORM_PATH, |
| MODULE_TRIPLE, |
| MODULE_UUID, |
| ) |
| |
| self.assertFalse(module) |
| |
| def test_return_module_with_non_existent_symbol(self): |
| # The callback returns a module and non-existent symbol file, |
| # AddModule should fail. |
| def test_locate_module( |
| module_spec: lldb.SBModuleSpec, |
| module_file_spec: lldb.SBFileSpec, |
| symbol_file_spec: lldb.SBFileSpec, |
| ): |
| self.check_module_spec(module_spec) |
| |
| module_file_spec.SetDirectory(str(self.input_dir)) |
| module_file_spec.SetFilename(MODULE_FILE) |
| |
| symbol_file_spec.SetDirectory(str(self.input_dir)) |
| symbol_file_spec.SetFilename(MODULE_NON_EXISTENT_FILE) |
| |
| return lldb.SBError() |
| |
| self.assertTrue( |
| self.platform.SetLocateModuleCallback(test_locate_module).Success() |
| ) |
| |
| module = self.target.AddModule( |
| MODULE_PLATFORM_PATH, |
| MODULE_TRIPLE, |
| MODULE_UUID, |
| ) |
| |
| self.assertFalse(module) |
| |
| def test_return_non_existent_symbol(self): |
| # The callback returns non-existent symbol file, AddModule should fail. |
| def test_locate_module( |
| module_spec: lldb.SBModuleSpec, |
| module_file_spec: lldb.SBFileSpec, |
| symbol_file_spec: lldb.SBFileSpec, |
| ): |
| self.check_module_spec(module_spec) |
| |
| symbol_file_spec.SetDirectory(str(self.input_dir)) |
| symbol_file_spec.SetFilename(MODULE_NON_EXISTENT_FILE) |
| |
| return lldb.SBError() |
| |
| self.assertTrue( |
| self.platform.SetLocateModuleCallback(test_locate_module).Success() |
| ) |
| |
| module = self.target.AddModule( |
| MODULE_PLATFORM_PATH, |
| MODULE_TRIPLE, |
| MODULE_UUID, |
| ) |
| |
| self.assertFalse(module) |
| |
| def test_return_module(self): |
| # The callback returns the module file, AddModule should succeed. |
| def test_locate_module( |
| module_spec: lldb.SBModuleSpec, |
| module_file_spec: lldb.SBFileSpec, |
| symbol_file_spec: lldb.SBFileSpec, |
| ): |
| self.check_module_spec(module_spec) |
| |
| module_file_spec.SetDirectory(str(self.input_dir)) |
| module_file_spec.SetFilename(MODULE_FILE) |
| |
| return lldb.SBError() |
| |
| self.assertTrue( |
| self.platform.SetLocateModuleCallback(test_locate_module).Success() |
| ) |
| |
| module = self.target.AddModule( |
| MODULE_PLATFORM_PATH, |
| MODULE_TRIPLE, |
| MODULE_UUID, |
| ) |
| |
| self.check_module( |
| module=module, symbol_file=MODULE_FILE, symbol_kind=SYMBOL_STRIPPED |
| ) |
| |
| def test_return_module_with_symbol(self): |
| # The callback returns the module file and the symbol file, |
| # AddModule should succeed. |
| def test_locate_module( |
| module_spec: lldb.SBModuleSpec, |
| module_file_spec: lldb.SBFileSpec, |
| symbol_file_spec: lldb.SBFileSpec, |
| ): |
| self.check_module_spec(module_spec) |
| |
| module_file_spec.SetDirectory(str(self.input_dir)) |
| module_file_spec.SetFilename(MODULE_FILE) |
| |
| symbol_file_spec.SetDirectory(str(self.input_dir)) |
| symbol_file_spec.SetFilename(SYMBOL_FILE) |
| |
| return lldb.SBError() |
| |
| self.assertTrue( |
| self.platform.SetLocateModuleCallback(test_locate_module).Success() |
| ) |
| |
| module = self.target.AddModule( |
| MODULE_PLATFORM_PATH, |
| MODULE_TRIPLE, |
| MODULE_UUID, |
| ) |
| |
| self.check_module( |
| module=module, symbol_file=SYMBOL_FILE, symbol_kind=SYMBOL_UNSTRIPPED |
| ) |
| |
| def test_return_module_with_breakpad_symbol(self): |
| # The callback returns the module file and the breakpad symbol file, |
| # AddModule should succeed. |
| def test_locate_module( |
| module_spec: lldb.SBModuleSpec, |
| module_file_spec: lldb.SBFileSpec, |
| symbol_file_spec: lldb.SBFileSpec, |
| ): |
| self.check_module_spec(module_spec) |
| |
| module_file_spec.SetDirectory(str(self.input_dir)) |
| module_file_spec.SetFilename(MODULE_FILE) |
| |
| symbol_file_spec.SetDirectory(str(self.input_dir)) |
| symbol_file_spec.SetFilename(BREAKPAD_SYMBOL_FILE) |
| |
| return lldb.SBError() |
| |
| self.assertTrue( |
| self.platform.SetLocateModuleCallback(test_locate_module).Success() |
| ) |
| |
| module = self.target.AddModule( |
| MODULE_PLATFORM_PATH, |
| MODULE_TRIPLE, |
| MODULE_UUID, |
| ) |
| |
| self.check_module( |
| module=module, |
| symbol_file=BREAKPAD_SYMBOL_FILE, |
| symbol_kind=SYMBOL_UNSTRIPPED, |
| ) |