splhack updated this revision to Diff 538713.
splhack added a comment.

rebase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D153735/new/

https://reviews.llvm.org/D153735

Files:
  lldb/bindings/python/python-typemaps.swig
  lldb/bindings/python/python-wrapper.swig
  lldb/include/lldb/API/SBDefines.h
  lldb/include/lldb/API/SBPlatform.h
  lldb/source/API/SBPlatform.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
  lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
  lldb/test/API/python_api/target/TestGetModuleCallback.py
  lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp

Index: lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
===================================================================
--- lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -321,3 +321,11 @@
 lldb_private::python::SWIGBridge::ToSWIGWrapper(lldb::DataExtractorSP) {
   return python::PythonObject();
 }
+
+Status
+lldb_private::python::SWIGBridge::LLDBSWIGPythonCallTargetGetModuleCallback(
+    void *callback_baton, lldb::DebuggerSP debugger_sp,
+    const ModuleSpec &module_spec, FileSpec &module_file_spec,
+    FileSpec &symbol_file_spec) {
+  return Status();
+}
Index: lldb/test/API/python_api/target/TestGetModuleCallback.py
===================================================================
--- /dev/null
+++ lldb/test/API/python_api/target/TestGetModuleCallback.py
@@ -0,0 +1,303 @@
+"""
+Test Target get 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 GetModuleCallbackTestCase(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, module_spec.GetFileSpec().fullpath)
+
+    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, module.GetPlatformFileSpec().fullpath)
+
+        self.assertEqual(
+            str(self.input_dir / MODULE_FILE),
+            module.GetFileSpec().fullpath,
+        )
+
+        self.assertEqual(
+            str(self.input_dir / symbol_file),
+            module.GetSymbolFileSpec().fullpath,
+        )
+
+        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.SetTargetGetModuleCallback(non_callable)
+
+    def test_set_wrong_args(self):
+        # The callback should accept 4 argument.
+        def test_args3(a, b, c):
+            pass
+
+        with self.assertRaises(TypeError, msg="Expected 4 argument callable object"):
+            self.platform.SetTargetGetModuleCallback(test_args3)
+
+    def test_set_none(self):
+        # SetTargetGetModuleCallback should succeed to clear the callback with None.
+        self.assertTrue(self.platform.SetTargetGetModuleCallback(None).Success())
+
+    def test_return_error(self):
+        # The callback does not return any files, AddModule should fail.
+        def test_get_module(
+            debugger: lldb.SBDebugger,
+            module_spec: lldb.SBModuleSpec,
+            module_file_spec: lldb.SBFileSpec,
+            symbol_file_spec: lldb.SBFileSpec,
+        ):
+            self.check_module_spec(module_spec)
+            return lldb.SBError("get module callback failed")
+
+        self.assertTrue(
+            self.platform.SetTargetGetModuleCallback(test_get_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_get_module(
+            debugger: lldb.SBDebugger,
+            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.SetTargetGetModuleCallback(test_get_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_get_module(
+            debugger: lldb.SBDebugger,
+            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.SetTargetGetModuleCallback(test_get_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_get_module(
+            debugger: lldb.SBDebugger,
+            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.SetTargetGetModuleCallback(test_get_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_get_module(
+            debugger: lldb.SBDebugger,
+            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.SetTargetGetModuleCallback(test_get_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_get_module(
+            debugger: lldb.SBDebugger,
+            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.SetTargetGetModuleCallback(test_get_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_get_module(
+            debugger: lldb.SBDebugger,
+            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.SetTargetGetModuleCallback(test_get_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,
+        )
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -289,6 +289,11 @@
                                     const char *user_input,
                                     bool is_callback) override;
 
+  Status CallTargetGetModuleCallback(void *callback_baton,
+                                     const ModuleSpec &module_spec,
+                                     FileSpec &module_file_spec,
+                                     FileSpec &symbol_file_spec) override;
+
   const char *GetDictionaryName() { return m_dictionary_name.c_str(); }
 
   PyThreadState *GetThreadState() { return m_command_thread_state; }
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1270,6 +1270,16 @@
   }
 }
 
+Status ScriptInterpreterPythonImpl::CallTargetGetModuleCallback(
+    void *callback_baton, const ModuleSpec &module_spec,
+    FileSpec &module_file_spec, FileSpec &symbol_file_spec) {
+  Locker py_lock(this,
+                 Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
+  return SWIGBridge::LLDBSWIGPythonCallTargetGetModuleCallback(
+      callback_baton, Debugger::FindDebuggerWithID(m_debugger.GetID()),
+      module_spec, module_file_spec, symbol_file_spec);
+}
+
 Status ScriptInterpreterPythonImpl::ExportFunctionDefinitionToInterpreter(
     StringList &function_def) {
   // Convert StringList to one long, newline delimited, const char *.
Index: lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -261,6 +261,11 @@
   static void *
   LLDBSWIGPython_GetDynamicSetting(void *module, const char *setting,
                                    const lldb::TargetSP &target_sp);
+
+  static Status LLDBSWIGPythonCallTargetGetModuleCallback(
+      void *callback_baton, lldb::DebuggerSP debugger_sp,
+      const ModuleSpec &module_spec, FileSpec &module_file_spec,
+      FileSpec &symbol_file_spec);
 };
 
 void *LLDBSWIGPython_CastPyObjectToSBData(PyObject *data);
Index: lldb/source/API/SBPlatform.cpp
===================================================================
--- lldb/source/API/SBPlatform.cpp
+++ lldb/source/API/SBPlatform.cpp
@@ -655,3 +655,19 @@
 
   return SBEnvironment();
 }
+
+SBError SBPlatform::SetTargetGetModuleCallback(
+    lldb::SBTargetGetModuleCallback callback, void *callback_baton) {
+  LLDB_INSTRUMENT_VA(this, callback, callback_baton);
+  PlatformSP platform_sp(GetSP());
+  if (platform_sp) {
+    // This does not pass the first 'callback' argument to the Platform
+    // instance, because it is always nullptr that is passed by
+    // python-typemaps.swig, in order to not add the SB* dependencies to
+    // Platform. 'callback_baton' is the actual callback Python callable object.
+    platform_sp->SetTargetGetModuleCallback(callback_baton);
+    return SBError();
+  } else {
+    return SBError("invalid platform");
+  }
+}
Index: lldb/include/lldb/API/SBPlatform.h
===================================================================
--- lldb/include/lldb/API/SBPlatform.h
+++ lldb/include/lldb/API/SBPlatform.h
@@ -169,6 +169,18 @@
   ///     environment.
   SBEnvironment GetEnvironment();
 
+  /// Set a callback as an implementation for getting module in order to
+  /// implement own module cache system. For example, to leverage distributed
+  /// build system, to bypass pulling files from remote platform, or to search
+  /// symbol files from symbol servers. The target will call this callback to
+  /// get a module file and a symbol file, and it will fallback to the LLDB
+  /// implementation when this callback failed or returned non-existent file.
+  /// This callback can set either module_file_spec or symbol_file_spec, or both
+  /// module_file_spec and symbol_file_spec. The callback will be cleared if
+  /// nullptr or None is set.
+  SBError SetTargetGetModuleCallback(lldb::SBTargetGetModuleCallback callback,
+                                     void *callback_baton);
+
 protected:
   friend class SBDebugger;
   friend class SBTarget;
Index: lldb/include/lldb/API/SBDefines.h
===================================================================
--- lldb/include/lldb/API/SBDefines.h
+++ lldb/include/lldb/API/SBDefines.h
@@ -126,6 +126,11 @@
 typedef void (*SBDebuggerDestroyCallback)(lldb::user_id_t debugger_id,
                                           void *baton);
 
+typedef SBError (*SBTargetGetModuleCallback)(SBDebugger debugger,
+                                             SBModuleSpec &module_spec,
+                                             SBFileSpec &module_file_spec,
+                                             SBFileSpec &symbol_file_spec);
+
 typedef void *ScriptedObject;
 }
 
Index: lldb/bindings/python/python-wrapper.swig
===================================================================
--- lldb/bindings/python/python-wrapper.swig
+++ lldb/bindings/python/python-wrapper.swig
@@ -1118,4 +1118,64 @@
     SWIG_PYTHON_THREAD_END_BLOCK;
   }
 }
+
+Status lldb_private::python::SWIGBridge::LLDBSWIGPythonCallTargetGetModuleCallback(
+    void *callback_baton, lldb::DebuggerSP debugger_sp,
+    const ModuleSpec &module_spec, FileSpec &module_file_spec,
+    FileSpec &symbol_file_spec) {
+  // Not possible to use std::make_unique because of the private constructor.
+  std::unique_ptr<lldb::SBModuleSpec>
+      module_spec_sb(new lldb::SBModuleSpec(module_spec));
+  auto module_file_spec_sb = std::make_unique<lldb::SBFileSpec>();
+  auto symbol_file_spec_sb = std::make_unique<lldb::SBFileSpec>();
+
+  PythonObject debugger_arg = SWIGBridge::ToSWIGWrapper(std::move(debugger_sp));
+  PythonObject module_spec_arg =
+      SWIGBridge::ToSWIGWrapper(std::move(module_spec_sb));
+  PythonObject module_file_spec_arg =
+      SWIGBridge::ToSWIGWrapper(std::move(module_file_spec_sb));
+  PythonObject symbol_file_spec_arg =
+      SWIGBridge::ToSWIGWrapper(std::move(symbol_file_spec_sb));
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonCallable callable =
+      Retain<PythonCallable>(reinterpret_cast<PyObject *>(callback_baton));
+  if (!callable.IsValid()) {
+    return Status("The callback callable is not valid.");
+  }
+
+  PythonObject result = callable(debugger_arg, module_spec_arg,
+                                 module_file_spec_arg, symbol_file_spec_arg);
+
+  if (!result.IsAllocated())
+    return Status("No result.");
+  lldb::SBError *sb_error_ptr = nullptr;
+  if (SWIG_ConvertPtr(result.get(), (void **)&sb_error_ptr,
+                      SWIGTYPE_p_lldb__SBError, 0) == -1) {
+    return Status("Result is not SBError.");
+  }
+  Status error = sb_error_ptr->ref();
+  if (error.Success()) {
+    lldb::SBFileSpec *sb_module_file_spec_ptr = nullptr;
+    if (SWIG_ConvertPtr(module_file_spec_arg.get(),
+                        (void **)&sb_module_file_spec_ptr,
+                        SWIGTYPE_p_lldb__SBFileSpec, 0) == -1)
+      return Status("module_file_spec is not SBFileSpec.");
+
+    lldb::SBFileSpec *sb_symbol_file_spec_ptr = nullptr;
+    if (SWIG_ConvertPtr(symbol_file_spec_arg.get(),
+                        (void **)&sb_symbol_file_spec_ptr,
+                        SWIGTYPE_p_lldb__SBFileSpec, 0) == -1)
+      return Status("symbol_file_spec is not SBFileSpec.");
+
+    module_file_spec.SetDirectory(sb_module_file_spec_ptr->GetDirectory());
+    module_file_spec.SetFilename(sb_module_file_spec_ptr->GetFilename());
+
+    symbol_file_spec.SetDirectory(sb_symbol_file_spec_ptr->GetDirectory());
+    symbol_file_spec.SetFilename(sb_symbol_file_spec_ptr->GetFilename());
+  }
+
+  return error;
+}
 %}
Index: lldb/bindings/python/python-typemaps.swig
===================================================================
--- lldb/bindings/python/python-typemaps.swig
+++ lldb/bindings/python/python-typemaps.swig
@@ -576,3 +576,57 @@
     return NULL;
   }
 }
+
+// For lldb::SBTargetGetModuleCallback
+%typemap(in) (lldb::SBTargetGetModuleCallback callback,
+              void *callback_baton) {
+  if (!($input == Py_None ||
+        PyCallable_Check(reinterpret_cast<PyObject *>($input)))) {
+    PyErr_SetString(PyExc_TypeError, "Need a callable object or None!");
+    SWIG_fail;
+  }
+
+  if ($input == Py_None) {
+    $1 = nullptr;
+    $2 = nullptr;
+  } else {
+    PythonCallable callable = Retain<PythonCallable>($input);
+    if (!callable.IsValid()) {
+      PyErr_SetString(PyExc_TypeError, "Need a valid callable object");
+      SWIG_fail;
+    }
+
+    llvm::Expected<PythonCallable::ArgInfo> arg_info = callable.GetArgInfo();
+    if (!arg_info) {
+      PyErr_SetString(PyExc_TypeError,
+                      ("Could not get arguments: " +
+                          llvm::toString(arg_info.takeError())).c_str());
+      SWIG_fail;
+    }
+
+    if (arg_info.get().max_positional_args != 4) {
+      PyErr_SetString(PyExc_TypeError, "Expected 4 argument callable object");
+      SWIG_fail;
+    }
+
+    // Don't lose the callback reference
+    Py_INCREF($input);
+
+    // lldb::SBTarget::SetGetModuleCallback uses lldb::SBTargetGetModuleCallback
+    // type for the Python interface, however lldb_private::Target class does
+    // not use it. This is because we do not want to add the SB* dependencies to
+    // lldb_private::Target. The actual Python callable object is turned into
+    // the second argument as 'void *callback_baton' of
+    // lldb::SBTarget::SetGetModuleCallback by this conversion. Therefore
+    // lldb_private::Target class can retain the callable as 'void *' without
+    // the SB* or Python dependencies.
+    $1 = nullptr;
+    $2 = $input;
+  }
+}
+
+%typemap(typecheck) (lldb::SBTargetGetModuleCallback callback,
+                     void *callback_baton) {
+  $1 = $input == Py_None;
+  $1 = $1 || PyCallable_Check(reinterpret_cast<PyObject *>($input));
+}
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to