[lldb] Add ScriptedPlatform python implementation
authorMed Ismail Bennani <medismail.bennani@gmail.com>
Thu, 12 Jan 2023 07:04:24 +0000 (23:04 -0800)
committerMed Ismail Bennani <medismail.bennani@gmail.com>
Thu, 12 Jan 2023 20:49:05 +0000 (12:49 -0800)
This patch introduces both the Scripted Platform python base
implementation and an example for it.

The base implementation is embedded in lldb python module under
`lldb.plugins.scripted_platform`.

This patch also refactor the various SWIG methods to create scripted
objects into a single method, that is now shared between the Scripted
Platform, Process and Thread. It also replaces the target argument by a
execution context object.

Differential Revision: https://reviews.llvm.org/D139250

Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
13 files changed:
lldb/bindings/python/CMakeLists.txt
lldb/bindings/python/python-wrapper.swig
lldb/examples/python/scripted_process/crashlog_scripted_process.py
lldb/examples/python/scripted_process/scripted_platform.py [new file with mode: 0644]
lldb/examples/python/scripted_process/scripted_process.py
lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp
lldb/source/Plugins/ScriptInterpreter/Python/ScriptedThreadPythonInterface.cpp
lldb/test/API/functionalities/scripted_platform/my_scripted_platform.py [new file with mode: 0644]
lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py
lldb/test/API/functionalities/scripted_process/invalid_scripted_process.py
lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py
lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp

index afad367..4f7941e 100644 (file)
@@ -109,6 +109,13 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar
     FILES
     "${LLDB_SOURCE_DIR}/examples/python/scripted_process/scripted_process.py")
 
+  create_python_package(
+    ${swig_target}
+    ${lldb_python_target_dir}
+    "plugins"
+    FILES
+    "${LLDB_SOURCE_DIR}/examples/python/scripted_process/scripted_platform.py")
+
   if(APPLE)
     create_python_package(
       ${swig_target}
index 91b26ff..9a08c30 100644 (file)
@@ -229,9 +229,9 @@ PythonObject lldb_private::LLDBSwigPythonCreateCommandObject(
   return pfunc(ToSWIGWrapper(std::move(debugger_sp)), dict);
 }
 
-PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(
+PythonObject lldb_private::LLDBSwigPythonCreateScriptedObject(
     const char *python_class_name, const char *session_dictionary_name,
-    const lldb::TargetSP &target_sp,
+    lldb::ExecutionContextRefSP exe_ctx_sp,
     const lldb_private::StructuredDataImpl &args_impl,
     std::string &error_string) {
   if (python_class_name == NULL || python_class_name[0] == '\0' ||
@@ -251,8 +251,6 @@ PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(
     return PythonObject();
   }
 
-  PythonObject target_arg = ToSWIGWrapper(target_sp);
-
   llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo();
   if (!arg_info) {
     llvm::handleAllErrors(
@@ -266,7 +264,7 @@ PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(
 
   PythonObject result = {};
   if (arg_info.get().max_positional_args == 2) {
-    result = pfunc(target_arg, ToSWIGWrapper(args_impl));
+      result = pfunc(ToSWIGWrapper(exe_ctx_sp), ToSWIGWrapper(args_impl));
   } else {
     error_string.assign("wrong number of arguments in __init__, should be 2 "
                         "(not including self)");
@@ -274,46 +272,6 @@ PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(
   return result;
 }
 
-PythonObject lldb_private::LLDBSwigPythonCreateScriptedThread(
-    const char *python_class_name, const char *session_dictionary_name,
-    const lldb::ProcessSP &process_sp, const StructuredDataImpl &args_impl,
-    std::string &error_string) {
-  if (python_class_name == NULL || python_class_name[0] == '\0' ||
-      !session_dictionary_name)
-    return PythonObject();
-
-  PyErr_Cleaner py_err_cleaner(true);
-
-  auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(
-      session_dictionary_name);
-  auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(
-      python_class_name, dict);
-
-  if (!pfunc.IsAllocated()) {
-    error_string.append("could not find script class: ");
-    error_string.append(python_class_name);
-    return PythonObject();
-  }
-
-  llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo();
-  if (!arg_info) {
-    llvm::handleAllErrors(
-        arg_info.takeError(),
-        [&](PythonException &E) { error_string.append(E.ReadBacktrace()); },
-        [&](const llvm::ErrorInfoBase &E) {
-          error_string.append(E.message());
-        });
-    return PythonObject();
-  }
-
-  if (arg_info.get().max_positional_args == 2)
-    return pfunc(ToSWIGWrapper(process_sp), ToSWIGWrapper(args_impl));
-
-  error_string.assign("wrong number of arguments in __init__, should be 2 "
-                      "(not including self)");
-  return PythonObject();
-}
-
 PythonObject lldb_private::LLDBSwigPythonCreateScriptedThreadPlan(
     const char *python_class_name, const char *session_dictionary_name,
     const lldb_private::StructuredDataImpl &args_impl,
index 71cfff2..eddf967 100644 (file)
@@ -62,8 +62,8 @@ class CrashLogScriptedProcess(ScriptedProcess):
                                                                self.addr_mask,
                                                                self.target)
 
-    def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
-        super().__init__(target, args)
+    def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
+        super().__init__(exe_ctx, args)
 
         if not self.target or not self.target.IsValid():
             # Return error
diff --git a/lldb/examples/python/scripted_process/scripted_platform.py b/lldb/examples/python/scripted_process/scripted_platform.py
new file mode 100644 (file)
index 0000000..95b9791
--- /dev/null
@@ -0,0 +1,96 @@
+from abc import ABCMeta, abstractmethod
+
+import lldb
+
+class ScriptedPlatform(metaclass=ABCMeta):
+
+    """
+    The base class for a scripted platform.
+
+    Most of the base class methods are `@abstractmethod` that need to be
+    overwritten by the inheriting class.
+
+    DISCLAIMER: THIS INTERFACE IS STILL UNDER DEVELOPMENT AND NOT STABLE.
+                THE METHODS EXPOSED MIGHT CHANGE IN THE FUTURE.
+    """
+
+    processes = None
+
+    @abstractmethod
+    def __init__(self, exe_ctx, args):
+        """ Construct a scripted platform.
+
+        Args:
+            exe_ctx (lldb.SBExecutionContext): The execution context for the scripted platform
+            args (lldb.SBStructuredData): A Dictionary holding arbitrary
+                key/value pairs used by the scripted platform.
+        """
+        processes = []
+
+    @abstractmethod
+    def list_processes(self):
+        """ Get a list of processes that are running or that can be attached to on the platform.
+
+        processes = {
+            420: {
+                    name: a.out,
+                    arch: aarch64,
+                    pid: 420,
+                    parent_pid: 42 (optional),
+                    uid: 0 (optional),
+                    gid: 0 (optional),
+            },
+        }
+
+        Returns:
+            Dict: The processes represented as a dictionary, with at least the
+                process ID, name, architecture. Optionally, the user can also
+                provide the parent process ID and the user and group IDs.
+                The dictionary can be empty.
+        """
+        pass
+
+    def get_process_info(self, pid):
+        """ Get the dictionary describing the process.
+
+        Returns:
+            Dict: The dictionary of process info that matched process ID.
+            None if the process doesn't exists
+        """
+        pass
+
+    @abstractmethod
+    def attach_to_process(self, attach_info):
+        """ Attach to a process.
+
+        Args:
+            attach_info (lldb.SBAttachInfo): The information related to attach to a process.
+
+        Returns:
+            lldb.SBError: A status object notifying if the attach succeeded.
+        """
+        pass
+
+    @abstractmethod
+    def launch_process(self, launch_info):
+        """ Launch a process.
+
+        Args:
+            launch_info (lldb.SBLaunchInfo): The information related to the process launch.
+
+        Returns:
+            lldb.SBError: A status object notifying if the launch succeeded.
+        """
+        pass
+
+    @abstractmethod
+    def kill_process(self, pid):
+        """ Kill a process.
+
+        Args:
+            pid (int): Process ID for the process to be killed.
+
+        Returns:
+            lldb.SBError: A status object notifying if the shutdown succeeded.
+        """
+        pass
index cc886c6..a84e057 100644 (file)
@@ -20,17 +20,20 @@ class ScriptedProcess(metaclass=ABCMeta):
     metadata = None
 
     @abstractmethod
-    def __init__(self, target, args):
+    def __init__(self, exe_ctx, args):
         """ Construct a scripted process.
 
         Args:
-            target (lldb.SBTarget): The target launching the scripted process.
+            exe_ctx (lldb.SBExecutionContext): The execution context for the scripted process.
             args (lldb.SBStructuredData): A Dictionary holding arbitrary
                 key/value pairs used by the scripted process.
         """
+        target = None
         self.target = None
         self.args = None
         self.arch = None
+        if isinstance(exe_ctx, lldb.SBExecutionContext):
+            target = exe_ctx.target
         if isinstance(target, lldb.SBTarget) and target.IsValid():
             self.target = target
             triple = self.target.triple
index 35c9621..3dc2864 100644 (file)
@@ -96,14 +96,10 @@ void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data);
 // Although these are scripting-language specific, their definition depends on
 // the public API.
 
-python::PythonObject LLDBSwigPythonCreateScriptedProcess(
+python::PythonObject LLDBSwigPythonCreateScriptedObject(
     const char *python_class_name, const char *session_dictionary_name,
-    const lldb::TargetSP &target_sp, const StructuredDataImpl &args_impl,
-    std::string &error_string);
-
-python::PythonObject LLDBSwigPythonCreateScriptedThread(
-    const char *python_class_name, const char *session_dictionary_name,
-    const lldb::ProcessSP &process_sp, const StructuredDataImpl &args_impl,
+    lldb::ExecutionContextRefSP exe_ctx_sp,
+    const lldb_private::StructuredDataImpl &args_impl,
     std::string &error_string);
 
 llvm::Expected<bool> LLDBSwigPythonBreakpointCallbackFunction(
index 5fd085d..6f087e8 100644 (file)
@@ -37,16 +37,18 @@ StructuredData::GenericSP ScriptedProcessPythonInterface::CreatePluginObject(
   if (class_name.empty())
     return {};
 
-  TargetSP target_sp = exe_ctx.GetTargetSP();
   StructuredDataImpl args_impl(args_sp);
   std::string error_string;
 
   Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
                  Locker::FreeLock);
 
-  PythonObject ret_val = LLDBSwigPythonCreateScriptedProcess(
-      class_name.str().c_str(), m_interpreter.GetDictionaryName(), target_sp,
-      args_impl, error_string);
+  lldb::ExecutionContextRefSP exe_ctx_ref_sp =
+      std::make_shared<ExecutionContextRef>(exe_ctx);
+
+  PythonObject ret_val = LLDBSwigPythonCreateScriptedObject(
+      class_name.str().c_str(), m_interpreter.GetDictionaryName(),
+      exe_ctx_ref_sp, args_impl, error_string);
 
   m_object_instance_sp =
       StructuredData::GenericSP(new StructuredPythonObject(std::move(ret_val)));
index 21d2ed3..1b31ed2 100644 (file)
@@ -35,7 +35,6 @@ StructuredData::GenericSP ScriptedThreadPythonInterface::CreatePluginObject(
   if (class_name.empty() && !script_obj)
     return {};
 
-  ProcessSP process_sp = exe_ctx.GetProcessSP();
   StructuredDataImpl args_impl(args_sp);
   std::string error_string;
 
@@ -44,11 +43,13 @@ StructuredData::GenericSP ScriptedThreadPythonInterface::CreatePluginObject(
 
   PythonObject ret_val;
 
-  if (!script_obj)
-    ret_val = LLDBSwigPythonCreateScriptedThread(
-        class_name.str().c_str(), m_interpreter.GetDictionaryName(), process_sp,
-        args_impl, error_string);
-  else
+  if (!script_obj) {
+    lldb::ExecutionContextRefSP exe_ctx_ref_sp =
+        std::make_shared<ExecutionContextRef>(exe_ctx);
+    ret_val = LLDBSwigPythonCreateScriptedObject(
+        class_name.str().c_str(), m_interpreter.GetDictionaryName(),
+        exe_ctx_ref_sp, args_impl, error_string);
+  } else
     ret_val = PythonObject(PyRefType::Borrowed,
                            static_cast<PyObject *>(script_obj->GetValue()));
 
diff --git a/lldb/test/API/functionalities/scripted_platform/my_scripted_platform.py b/lldb/test/API/functionalities/scripted_platform/my_scripted_platform.py
new file mode 100644 (file)
index 0000000..3849f47
--- /dev/null
@@ -0,0 +1,38 @@
+import os
+
+import lldb
+from lldb.plugins.scripted_platform import ScriptedPlatform
+
+class MyScriptedPlatform(ScriptedPlatform):
+
+    def __init__(self, exe_ctx, args):
+        self.processes = {}
+
+        proc = {}
+        proc['name'] = 'a.out'
+        proc['arch'] = 'arm64-apple-macosx'
+        proc['pid'] = 420
+        proc['parent'] = 42
+        proc['uid'] = 501
+        proc['gid'] = 20
+        self.processes[420] = proc
+
+    def list_processes(self):
+        return self.processes
+
+    def get_process_info(self, pid):
+        return self.processes[pid]
+
+    def launch_process(self, launch_info):
+        return lldb.SBError()
+
+    def kill_process(self, pid):
+        return lldb.SBError()
+
+def __lldb_init_module(debugger, dict):
+    if not 'SKIP_SCRIPTED_PLATFORM_SELECT' in os.environ:
+        debugger.HandleCommand(
+            "platform select scripted-platform -C %s.%s" % (__name__, MyScriptedPlatform.__name__))
+    else:
+        print("Name of the class that will manage the scripted platform: '%s.%s'"
+                % (__name__, MyScriptedPlatform.__name__))
index d4f7953..83df1ad 100644 (file)
@@ -7,8 +7,8 @@ from lldb.plugins.scripted_process import ScriptedProcess
 from lldb.plugins.scripted_process import ScriptedThread
 
 class DummyScriptedProcess(ScriptedProcess):
-    def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
-        super().__init__(target, args)
+    def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
+        super().__init__(exe_ctx, args)
         self.threads[0] = DummyScriptedThread(self, None)
 
     def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
index 62db547..5852df9 100644 (file)
@@ -7,8 +7,8 @@ from lldb.plugins.scripted_process import ScriptedProcess
 from lldb.plugins.scripted_process import ScriptedThread
 
 class InvalidScriptedProcess(ScriptedProcess):
-    def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
-        super().__init__(target, args)
+    def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
+        super().__init__(exe_ctx, args)
         self.threads[0] = InvalidScriptedThread(self, None)
 
     def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
index 67a1256..44a2a37 100644 (file)
@@ -13,8 +13,8 @@ class StackCoreScriptedProcess(ScriptedProcess):
                 return module
         return None
 
-    def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
-        super().__init__(target, args)
+    def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
+        super().__init__(exe_ctx, args)
 
         self.corefile_target = None
         self.corefile_process = None
@@ -25,7 +25,7 @@ class StackCoreScriptedProcess(ScriptedProcess):
                 idx = self.backing_target_idx.GetIntegerValue(42)
             if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeString:
                 idx = int(self.backing_target_idx.GetStringValue(100))
-            self.corefile_target = target.GetDebugger().GetTargetAtIndex(idx)
+            self.corefile_target = self.target.GetDebugger().GetTargetAtIndex(idx)
             self.corefile_process = self.corefile_target.GetProcess()
             for corefile_thread in self.corefile_process:
                 structured_data = lldb.SBStructuredData()
index 4d2f27e..75391e2 100644 (file)
@@ -200,16 +200,9 @@ lldb_private::LLDBSWIGPythonCreateOSPlugin(const char *python_class_name,
   return python::PythonObject();
 }
 
-python::PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(
+python::PythonObject lldb_private::LLDBSwigPythonCreateScriptedObject(
     const char *python_class_name, const char *session_dictionary_name,
-    const lldb::TargetSP &target_sp, const StructuredDataImpl &args_impl,
-    std::string &error_string) {
-  return python::PythonObject();
-}
-
-python::PythonObject lldb_private::LLDBSwigPythonCreateScriptedThread(
-    const char *python_class_name, const char *session_dictionary_name,
-    const lldb::ProcessSP &process_sp, const StructuredDataImpl &args_impl,
+    lldb::ExecutionContextRefSP exe_ctx_sp, const StructuredDataImpl &args_impl,
     std::string &error_string) {
   return python::PythonObject();
 }