[Lldb-commits] [lldb] [lldb/Plugins] Introduce Scripted Platform Plugin (PR #99814)

Med Ismail Bennani via lldb-commits lldb-commits at lists.llvm.org
Mon Jul 22 21:12:19 PDT 2024


https://github.com/medismailben updated https://github.com/llvm/llvm-project/pull/99814

>From 9b7d9a26eabce5f76a99876c14aab619aab934ac Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ismail at bennani.ma>
Date: Mon, 22 Jul 2024 21:11:48 -0700
Subject: [PATCH] [lldb/Plugins] Introduce Scripted Platform Plugin

This patch introduces Scripted Platform, a new platform plugin that can
be customized with a python script.

For now this can list processes described in the python script file but
eventually, it will be used to spawn scripted processes and act as an
interface between them.

This patch is also a less intrusive implementation of 2d53527.

It introduces a new PlatformMetadata held by every platform that
contains various objects that might be used by a platform instance.

In its current form, the PlatformMetadata holds a reference to the
Debugger and a ScriptedMetadata pointer. These are necessary in other to
instanciate the scripted object that the ScriptedPlatform interacts with.

In order to make it less introsive with the rest of lldb's platform
creation code, platform metadata are set after the platform creation,
and requires to platform to reload them (using `Platform::ReloadMetadata`).

This approach has the tradeoff that the ScriptedPlaform instance is
technically invalid and useless right after its creation. However, the user
should never be in that situation, since we reload the platform metadata
everytime with create or select the platform.

This work was previously reviewed in:
- https://reviews.llvm.org/D139252
- https://reviews.llvm.org/D142266

Signed-off-by: Med Ismail Bennani <ismail at bennani.ma>
---
 .../bindings/interface/SBPlatformDocstrings.i |  12 +
 lldb/bindings/python/python-swigsafecast.swig |  10 +
 lldb/bindings/python/python-wrapper.swig      |  36 +++
 .../python/templates/scripted_platform.py     |   9 +-
 lldb/include/lldb/API/SBDebugger.h            |   2 +
 lldb/include/lldb/API/SBPlatform.h            |   4 +-
 lldb/include/lldb/API/SBProcess.h             |   2 +
 lldb/include/lldb/API/SBStructuredData.h      |   1 +
 lldb/include/lldb/API/SBTarget.h              |   2 +
 .../Interfaces/ScriptedPlatformInterface.h    |   7 +-
 .../lldb/Interpreter/OptionGroupPlatform.h    |  10 +-
 .../lldb/Interpreter/ScriptInterpreter.h      |  19 +-
 lldb/include/lldb/Target/Platform.h           |  22 ++
 lldb/include/lldb/Utility/ScriptedMetadata.h  |   4 +-
 lldb/source/API/SBPlatform.cpp                |  42 +++
 lldb/source/API/SBStructuredData.cpp          |   8 +-
 .../source/Commands/CommandObjectPlatform.cpp |  17 +-
 lldb/source/Interpreter/ScriptInterpreter.cpp |  15 +
 lldb/source/Plugins/Platform/CMakeLists.txt   |   1 +
 .../Plugins/Platform/scripted/CMakeLists.txt  |   8 +
 .../Platform/scripted/ScriptedPlatform.cpp    | 279 ++++++++++++++++++
 .../Platform/scripted/ScriptedPlatform.h      |  84 ++++++
 .../Process/scripted/ScriptedProcess.h        |   2 +-
 .../Process/scripted/ScriptedThread.cpp       |   5 -
 .../Plugins/Process/scripted/ScriptedThread.h |   6 +-
 .../ScriptedPlatformPythonInterface.cpp       |  42 +--
 .../ScriptedPlatformPythonInterface.h         |   5 +-
 .../Interfaces/ScriptedPythonInterface.cpp    |  45 +++
 .../Interfaces/ScriptedPythonInterface.h      |  25 +-
 .../Python/SWIGPythonBridge.h                 |   3 +
 .../Python/ScriptInterpreterPython.cpp        |   5 +
 .../Python/ScriptInterpreterPythonImpl.h      |   2 +
 lldb/source/Target/Platform.cpp               |  10 +-
 .../scripted_platform/TestScriptedPlatform.py | 108 +++++++
 .../scripted_platform/my_scripted_platform.py |  27 +-
 .../Python/PythonTestSuite.cpp                |  15 +
 36 files changed, 839 insertions(+), 55 deletions(-)
 create mode 100644 lldb/source/Plugins/Platform/scripted/CMakeLists.txt
 create mode 100644 lldb/source/Plugins/Platform/scripted/ScriptedPlatform.cpp
 create mode 100644 lldb/source/Plugins/Platform/scripted/ScriptedPlatform.h
 create mode 100644 lldb/test/API/functionalities/scripted_platform/TestScriptedPlatform.py

diff --git a/lldb/bindings/interface/SBPlatformDocstrings.i b/lldb/bindings/interface/SBPlatformDocstrings.i
index ef09d5bce13fd..b8cdf21e90f82 100644
--- a/lldb/bindings/interface/SBPlatformDocstrings.i
+++ b/lldb/bindings/interface/SBPlatformDocstrings.i
@@ -29,3 +29,15 @@ and executable type. If the architecture or executable type do not match,
 a suitable platform will be found automatically."
 
 ) lldb::SBPlatform;
+
+%feature("docstring", "
+Create a platform instance using a specific platform plugin name, debugger,
+script name and user-provided dictionary.
+
+:param platform_name: name of the platform plugin to use to create the platform
+:param debugger: debugger instance owning the platform
+:param script_name: name of the script class and module to use to create the platform
+:param dict: user-provided dictionary that can be used when instanciating a platform
+") lldb::SBPlatform (const char *, const lldb::SBDebugger&,
+                    const char *, const lldb::SBStructuredData&);
+
diff --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig
index 34f8c6f0ff8d3..f006101c4ce2d 100644
--- a/lldb/bindings/python/python-swigsafecast.swig
+++ b/lldb/bindings/python/python-swigsafecast.swig
@@ -104,6 +104,16 @@ PythonObject SWIGBridge::ToSWIGWrapper(lldb::DataExtractorSP data_sp) {
                       SWIGTYPE_p_lldb__SBData);
 }
 
+PythonObject ToSWIGWrapper(lldb::ProcessLaunchInfoSP launch_info_sp) {
+  return ToSWIGHelper(new lldb::ProcessLaunchInfoSP(std::move(launch_info_sp)),
+                      SWIGTYPE_p_lldb__SBLaunchInfo);
+}
+
+PythonObject ToSWIGWrapper(lldb::ProcessAttachInfoSP attach_info_sp) {
+  return ToSWIGHelper(new lldb::ProcessAttachInfoSP(std::move(attach_info_sp)),
+                      SWIGTYPE_p_lldb__SBAttachInfo);
+}
+
 ScopedPythonObject<lldb::SBCommandReturnObject>
 SWIGBridge::ToSWIGWrapper(CommandReturnObject &cmd_retobj) {
   return ScopedPythonObject<lldb::SBCommandReturnObject>(
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 8f050643fa68b..24dafc7b3fcd8 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -675,6 +675,42 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyOb
   return sb_ptr;
 }
 
+void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBDebugger(PyObject * data) {
+  lldb::SBDebugger *sb_ptr = nullptr;
+
+  int valid_cast =
+      SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBDebugger, 0);
+
+  if (valid_cast == -1)
+    return NULL;
+
+  return sb_ptr;
+}
+
+void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBTarget(PyObject * data) {
+  lldb::SBTarget *sb_ptr = nullptr;
+
+  int valid_cast =
+      SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBTarget, 0);
+
+  if (valid_cast == -1)
+    return NULL;
+
+  return sb_ptr;
+}
+
+void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBProcess(PyObject * data) {
+  lldb::SBProcess *sb_ptr = nullptr;
+
+  int valid_cast =
+      SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBProcess, 0);
+
+  if (valid_cast == -1)
+    return NULL;
+
+  return sb_ptr;
+}
+
 bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommand(
     const char *python_function_name, const char *session_dictionary_name,
     lldb::DebuggerSP debugger, const char *args,
diff --git a/lldb/examples/python/templates/scripted_platform.py b/lldb/examples/python/templates/scripted_platform.py
index 5805f99dea4ca..250a7d45d1559 100644
--- a/lldb/examples/python/templates/scripted_platform.py
+++ b/lldb/examples/python/templates/scripted_platform.py
@@ -23,7 +23,7 @@ def __init__(self, exe_ctx, args):
             args (lldb.SBStructuredData): A Dictionary holding arbitrary
                 key/value pairs used by the scripted platform.
         """
-        processes = []
+        processes = {}
 
     @abstractmethod
     def list_processes(self):
@@ -60,14 +60,17 @@ def get_process_info(self, pid):
         pass
 
     @abstractmethod
-    def attach_to_process(self, attach_info):
+    def attach_to_process(self, attach_info, target, debugger, error):
         """Attach to a process.
 
         Args:
             attach_info (lldb.SBAttachInfo): The information related to attach to a process.
+            target (lldb.SBTarget): The optional target that we are trying to attach to.
+            debugger (lldb.SBDebugger): The debugger instance.
+            error (lldb.SBError): A status object notifying if the attach succeeded.
 
         Returns:
-            lldb.SBError: A status object notifying if the attach succeeded.
+            lldb.SBProcess: The process that the platform attached to, or None.
         """
         pass
 
diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h
index 84ea9c0f772e1..6cf95698bdb52 100644
--- a/lldb/include/lldb/API/SBDebugger.h
+++ b/lldb/include/lldb/API/SBDebugger.h
@@ -16,6 +16,7 @@
 
 namespace lldb_private {
 class CommandPluginInterfaceImplementation;
+class ScriptInterpreter;
 namespace python {
 class SWIGBridge;
 }
@@ -492,6 +493,7 @@ class LLDB_API SBDebugger {
 
 protected:
   friend class lldb_private::CommandPluginInterfaceImplementation;
+  friend class lldb_private::ScriptInterpreter;
   friend class lldb_private::python::SWIGBridge;
 
   SBDebugger(const lldb::DebuggerSP &debugger_sp);
diff --git a/lldb/include/lldb/API/SBPlatform.h b/lldb/include/lldb/API/SBPlatform.h
index d63d2ed1eaba6..e6e990b08894c 100644
--- a/lldb/include/lldb/API/SBPlatform.h
+++ b/lldb/include/lldb/API/SBPlatform.h
@@ -17,7 +17,6 @@
 
 struct PlatformConnectOptions;
 struct PlatformShellCommand;
-class ProcessInstanceInfoMatch;
 
 namespace lldb {
 
@@ -100,6 +99,9 @@ class LLDB_API SBPlatform {
 
   SBPlatform(const char *platform_name);
 
+  SBPlatform(const char *platform_name, const SBDebugger &debugger,
+             const char *script_name, const SBStructuredData &dict);
+
   SBPlatform(const SBPlatform &rhs);
 
   SBPlatform &operator=(const SBPlatform &rhs);
diff --git a/lldb/include/lldb/API/SBProcess.h b/lldb/include/lldb/API/SBProcess.h
index 778be79583990..dca16748714dd 100644
--- a/lldb/include/lldb/API/SBProcess.h
+++ b/lldb/include/lldb/API/SBProcess.h
@@ -17,6 +17,7 @@
 #include <cstdio>
 
 namespace lldb_private {
+class ScriptInterpreter;
 namespace python {
 class SWIGBridge;
 }
@@ -597,6 +598,7 @@ class LLDB_API SBProcess {
   friend class lldb_private::QueueImpl;
 
   friend class lldb_private::python::SWIGBridge;
+  friend class lldb_private::ScriptInterpreter;
 
   SBProcess(const lldb::ProcessSP &process_sp);
 
diff --git a/lldb/include/lldb/API/SBStructuredData.h b/lldb/include/lldb/API/SBStructuredData.h
index fc6e1ec95c7b8..94f41fdae3f42 100644
--- a/lldb/include/lldb/API/SBStructuredData.h
+++ b/lldb/include/lldb/API/SBStructuredData.h
@@ -111,6 +111,7 @@ class SBStructuredData {
 protected:
   friend class SBAttachInfo;
   friend class SBLaunchInfo;
+  friend class SBPlatform;
   friend class SBDebugger;
   friend class SBTarget;
   friend class SBProcess;
diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h
index 35c2ed9c20a23..038c7586dffe1 100644
--- a/lldb/include/lldb/API/SBTarget.h
+++ b/lldb/include/lldb/API/SBTarget.h
@@ -25,6 +25,7 @@
 #include "lldb/API/SBWatchpointOptions.h"
 
 namespace lldb_private {
+class ScriptInterpreter;
 namespace python {
 class SWIGBridge;
 }
@@ -964,6 +965,7 @@ class LLDB_API SBTarget {
   friend class SBVariablesOptions;
 
   friend class lldb_private::python::SWIGBridge;
+  friend class lldb_private::ScriptInterpreter;
 
   // Constructors are private, use static Target::Create function to create an
   // instance of this class.
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h
index 7feaa01fe89b8..4030196cc6f15 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h
@@ -30,8 +30,11 @@ class ScriptedPlatformInterface : virtual public ScriptedInterface {
     return {};
   }
 
-  virtual Status AttachToProcess(lldb::ProcessAttachInfoSP attach_info) {
-    return Status("ScriptedPlatformInterface cannot attach to a process");
+  virtual lldb::ProcessSP
+  AttachToProcess(lldb::ProcessAttachInfoSP attach_info_sp,
+                  lldb::TargetSP target_sp, lldb::DebuggerSP debugger_sp,
+                  Status &error) {
+    return {};
   }
 
   virtual Status LaunchProcess(lldb::ProcessLaunchInfoSP launch_info) {
diff --git a/lldb/include/lldb/Interpreter/OptionGroupPlatform.h b/lldb/include/lldb/Interpreter/OptionGroupPlatform.h
index 2ffd44f0035de..410b6bafe9af4 100644
--- a/lldb/include/lldb/Interpreter/OptionGroupPlatform.h
+++ b/lldb/include/lldb/Interpreter/OptionGroupPlatform.h
@@ -9,19 +9,20 @@
 #ifndef LLDB_INTERPRETER_OPTIONGROUPPLATFORM_H
 #define LLDB_INTERPRETER_OPTIONGROUPPLATFORM_H
 
+#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
 #include "lldb/Interpreter/Options.h"
 #include "lldb/Utility/ConstString.h"
 #include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
-
 // PlatformOptionGroup
 //
 // Make platform options available to any commands that need the settings.
 class OptionGroupPlatform : public OptionGroup {
 public:
   OptionGroupPlatform(bool include_platform_option)
-      : m_include_platform_option(include_platform_option) {}
+      : m_include_platform_option(include_platform_option),
+        m_class_options("scripted platform", true, 'C', 'K', 'V', 0) {}
 
   ~OptionGroupPlatform() override = default;
 
@@ -61,12 +62,17 @@ class OptionGroupPlatform : public OptionGroup {
 
   bool PlatformMatches(const lldb::PlatformSP &platform_sp) const;
 
+  OptionGroupPythonClassWithDict &GetScriptClassOptions() {
+    return m_class_options;
+  }
+
 protected:
   std::string m_platform_name;
   std::string m_sdk_sysroot;
   std::string m_sdk_build;
   llvm::VersionTuple m_os_version;
   bool m_include_platform_option;
+  OptionGroupPythonClassWithDict m_class_options;
 };
 
 } // namespace lldb_private
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 05f0d7f0955f3..f970cc2b97c81 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -12,11 +12,14 @@
 #include "lldb/API/SBAttachInfo.h"
 #include "lldb/API/SBBreakpoint.h"
 #include "lldb/API/SBData.h"
+#include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBError.h"
 #include "lldb/API/SBEvent.h"
 #include "lldb/API/SBLaunchInfo.h"
 #include "lldb/API/SBMemoryRegionInfo.h"
+#include "lldb/API/SBProcess.h"
 #include "lldb/API/SBStream.h"
+#include "lldb/API/SBTarget.h"
 #include "lldb/Breakpoint/BreakpointOptions.h"
 #include "lldb/Core/PluginInterface.h"
 #include "lldb/Core/SearchFilter.h"
@@ -548,6 +551,10 @@ class ScriptInterpreter : public PluginInterface {
 
   lldb::ScriptLanguage GetLanguage() { return m_script_lang; }
 
+  virtual lldb::ScriptedPlatformInterfaceUP CreateScriptedPlatformInterface() {
+    return {};
+  }
+
   virtual lldb::ScriptedProcessInterfaceUP CreateScriptedProcessInterface() {
     return {};
   }
@@ -565,10 +572,6 @@ class ScriptInterpreter : public PluginInterface {
     return {};
   }
 
-  virtual lldb::ScriptedPlatformInterfaceUP GetScriptedPlatformInterface() {
-    return {};
-  }
-
   virtual StructuredData::ObjectSP
   CreateStructuredDataFromScriptObject(ScriptObject obj) {
     return {};
@@ -586,6 +589,14 @@ class ScriptInterpreter : public PluginInterface {
   lldb::BreakpointSP
   GetOpaqueTypeFromSBBreakpoint(const lldb::SBBreakpoint &breakpoint) const;
 
+  lldb::DebuggerSP
+  GetOpaqueTypeFromSBDebugger(const lldb::SBDebugger &debugger) const;
+
+  lldb::TargetSP GetOpaqueTypeFromSBTarget(const lldb::SBTarget &target) const;
+
+  lldb::ProcessSP
+  GetOpaqueTypeFromSBProcess(const lldb::SBProcess &process) const;
+
   lldb::ProcessAttachInfoSP
   GetOpaqueTypeFromSBAttachInfo(const lldb::SBAttachInfo &attach_info) const;
 
diff --git a/lldb/include/lldb/Target/Platform.h b/lldb/include/lldb/Target/Platform.h
index 5ed2fc33356d9..8168c5a88ae66 100644
--- a/lldb/include/lldb/Target/Platform.h
+++ b/lldb/include/lldb/Target/Platform.h
@@ -24,6 +24,7 @@
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/ConstString.h"
 #include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/ScriptedMetadata.h"
 #include "lldb/Utility/StructuredData.h"
 #include "lldb/Utility/Timeout.h"
 #include "lldb/Utility/UserIDResolver.h"
@@ -35,6 +36,7 @@
 
 namespace lldb_private {
 
+class PlatformMetadata;
 class ProcessInstanceInfo;
 class ProcessInstanceInfoMatch;
 typedef std::vector<ProcessInstanceInfo> ProcessInstanceInfoList;
@@ -632,6 +634,10 @@ class Platform : public PluginInterface {
 
   virtual const char *GetLocalCacheDirectory();
 
+  void SetMetadata(std::unique_ptr<PlatformMetadata> metadata);
+
+  virtual llvm::Error ReloadMetadata() { return llvm::Error::success(); }
+
   virtual std::string GetPlatformSpecificConnectionInformation() { return ""; }
 
   virtual llvm::ErrorOr<llvm::MD5::MD5Result>
@@ -962,6 +968,7 @@ class Platform : public PluginInterface {
   bool m_calculated_trap_handlers;
   const std::unique_ptr<ModuleCache> m_module_cache;
   LocateModuleCallback m_locate_module_callback;
+  std::unique_ptr<PlatformMetadata> m_metadata;
 
   /// Ask the Platform subclass to fill in the list of trap handler names
   ///
@@ -1003,6 +1010,21 @@ class Platform : public PluginInterface {
   FileSpec GetModuleCacheRoot();
 };
 
+class PlatformMetadata {
+public:
+  PlatformMetadata(Debugger &debugger, const ScriptedMetadata metadata);
+  ~PlatformMetadata() = default;
+
+  Debugger &GetDebugger() const { return m_debugger; }
+  const ScriptedMetadata GetScriptedMetadata() const {
+    return m_scripted_metadata;
+  }
+
+protected:
+  Debugger &m_debugger;
+  const ScriptedMetadata m_scripted_metadata;
+};
+
 class PlatformList {
 public:
   PlatformList() = default;
diff --git a/lldb/include/lldb/Utility/ScriptedMetadata.h b/lldb/include/lldb/Utility/ScriptedMetadata.h
index 69c83edce909a..3990309080e87 100644
--- a/lldb/include/lldb/Utility/ScriptedMetadata.h
+++ b/lldb/include/lldb/Utility/ScriptedMetadata.h
@@ -15,8 +15,8 @@
 namespace lldb_private {
 class ScriptedMetadata {
 public:
-  ScriptedMetadata(llvm::StringRef class_name,
-                   StructuredData::DictionarySP dict_sp)
+  ScriptedMetadata(const llvm::StringRef class_name,
+                   const StructuredData::DictionarySP dict_sp)
       : m_class_name(class_name.data()), m_args_sp(dict_sp) {}
 
   ScriptedMetadata(const ProcessInfo &process_info) {
diff --git a/lldb/source/API/SBPlatform.cpp b/lldb/source/API/SBPlatform.cpp
index 3623fd35bcdf1..8e95aa5598aa9 100644
--- a/lldb/source/API/SBPlatform.cpp
+++ b/lldb/source/API/SBPlatform.cpp
@@ -15,6 +15,7 @@
 #include "lldb/API/SBModuleSpec.h"
 #include "lldb/API/SBPlatform.h"
 #include "lldb/API/SBProcessInfoList.h"
+#include "lldb/API/SBStructuredData.h"
 #include "lldb/API/SBTarget.h"
 #include "lldb/API/SBUnixSignals.h"
 #include "lldb/Host/File.h"
@@ -23,8 +24,10 @@
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/Args.h"
 #include "lldb/Utility/Instrumentation.h"
+#include "lldb/Utility/ScriptedMetadata.h"
 #include "lldb/Utility/Status.h"
 
+#include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/FileSystem.h"
 
 #include <functional>
@@ -299,6 +302,45 @@ SBPlatform::SBPlatform(const char *platform_name) {
   m_opaque_sp = Platform::Create(platform_name);
 }
 
+SBPlatform::SBPlatform(const char *platform_name, const SBDebugger &debugger,
+                       const char *script_name, const SBStructuredData &dict)
+    : SBPlatform(platform_name) {
+  LLDB_INSTRUMENT_VA(this, platform_name, debugger, script_name, dict);
+
+  if (!m_opaque_sp)
+    return;
+
+  auto clear_opaque_ptr = llvm::make_scope_exit([this] { Clear(); });
+
+  if (!script_name || !dict.IsValid())
+    return;
+
+  StructuredData::ObjectSP obj_sp = dict.m_impl_up->GetObjectSP();
+
+  if (!obj_sp)
+    return;
+
+  StructuredData::DictionarySP dict_sp =
+      std::make_shared<StructuredData::Dictionary>(obj_sp);
+  if (!dict_sp || dict_sp->GetType() == lldb::eStructuredDataTypeInvalid)
+    return;
+
+  const ScriptedMetadata scripted_metadata(script_name, dict_sp);
+
+  auto platform_metadata =
+      std::make_unique<PlatformMetadata>(debugger.ref(), scripted_metadata);
+  m_opaque_sp->SetMetadata(std::move(platform_metadata));
+  if (llvm::Error e = m_opaque_sp->ReloadMetadata())
+    // If we failed to reload the platform metadata, we should just consume the
+    // error since there is no error reporting in the constructor.
+    // `ReloadMetada` will log the error to the platform logging channel and
+    // `clear_opaque_ptr` scope exist lambda will make this instance invalid by
+    // resetting the opaque pointer.
+    llvm::consumeError(std::move(e));
+  else
+    clear_opaque_ptr.release();
+}
+
 SBPlatform::SBPlatform(const SBPlatform &rhs) {
   LLDB_INSTRUMENT_VA(this, rhs);
 
diff --git a/lldb/source/API/SBStructuredData.cpp b/lldb/source/API/SBStructuredData.cpp
index b18fc5655fc81..32fa5d7bb30f9 100644
--- a/lldb/source/API/SBStructuredData.cpp
+++ b/lldb/source/API/SBStructuredData.cpp
@@ -86,7 +86,11 @@ lldb::SBError SBStructuredData::SetFromJSON(lldb::SBStream &stream) {
       StructuredData::ParseJSON(stream.GetData());
   m_impl_up->SetObjectSP(json_obj);
 
-  if (!json_obj || json_obj->GetType() != eStructuredDataTypeDictionary)
+  static constexpr StructuredDataType structured_data_record_type[] = {
+      eStructuredDataTypeArray, eStructuredDataTypeDictionary};
+
+  if (!json_obj ||
+      !llvm::is_contained(structured_data_record_type, json_obj->GetType()))
     error.SetErrorString("Invalid Syntax");
   return error;
 }
@@ -106,7 +110,7 @@ bool SBStructuredData::IsValid() const {
 SBStructuredData::operator bool() const {
   LLDB_INSTRUMENT_VA(this);
 
-  return m_impl_up->IsValid();
+  return m_impl_up && m_impl_up->IsValid();
 }
 
 void SBStructuredData::Clear() {
diff --git a/lldb/source/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp
index 5b18f2b60e92d..4d2e7f8382744 100644
--- a/lldb/source/Commands/CommandObjectPlatform.cpp
+++ b/lldb/source/Commands/CommandObjectPlatform.cpp
@@ -154,8 +154,11 @@ class CommandObjectPlatformSelect : public CommandObjectParsed {
             false) // Don't include the "--platform" option by passing false
   {
     m_option_group.Append(&m_platform_options, LLDB_OPT_SET_ALL, 1);
+    m_option_group.Append(&m_platform_options.GetScriptClassOptions(),
+                          LLDB_OPT_SET_1 | LLDB_OPT_SET_2, LLDB_OPT_SET_ALL);
     m_option_group.Finalize();
-    AddSimpleArgumentList(eArgTypePlatform);
+    CommandArgumentData platform_arg{eArgTypePlatform, eArgRepeatPlain};
+    m_arguments.push_back({platform_arg});
   }
 
   ~CommandObjectPlatformSelect() override = default;
@@ -180,7 +183,17 @@ class CommandObjectPlatformSelect : public CommandObjectParsed {
             m_interpreter, ArchSpec(), select, error, platform_arch));
         if (platform_sp) {
           GetDebugger().GetPlatformList().SetSelectedPlatform(platform_sp);
-
+          OptionGroupPythonClassWithDict &script_class_opts =
+              m_platform_options.GetScriptClassOptions();
+          const ScriptedMetadata scripted_metadata(
+              script_class_opts.GetName(),
+              script_class_opts.GetStructuredData());
+
+          auto metadata = std::make_unique<PlatformMetadata>(GetDebugger(),
+                                                             scripted_metadata);
+          platform_sp->SetMetadata(std::move(metadata));
+          if (!platform_sp->ReloadMetadata())
+            result.AppendError("platform couldn't reload metadata\n");
           platform_sp->GetStatus(result.GetOutputStream());
           result.SetStatus(eReturnStatusSuccessFinishResult);
         } else {
diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp
index fa23964a52ffe..ded146984f2b0 100644
--- a/lldb/source/Interpreter/ScriptInterpreter.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -82,6 +82,21 @@ lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint(
   return breakpoint.m_opaque_wp.lock();
 }
 
+lldb::DebuggerSP ScriptInterpreter::GetOpaqueTypeFromSBDebugger(
+    const lldb::SBDebugger &debugger) const {
+  return debugger.m_opaque_sp;
+}
+
+lldb::TargetSP ScriptInterpreter::GetOpaqueTypeFromSBTarget(
+    const lldb::SBTarget &target) const {
+  return target.m_opaque_sp;
+}
+
+lldb::ProcessSP ScriptInterpreter::GetOpaqueTypeFromSBProcess(
+    const lldb::SBProcess &process) const {
+  return process.m_opaque_wp.lock();
+}
+
 lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo(
     const lldb::SBAttachInfo &attach_info) const {
   return attach_info.m_opaque_sp;
diff --git a/lldb/source/Plugins/Platform/CMakeLists.txt b/lldb/source/Plugins/Platform/CMakeLists.txt
index 6869587f917eb..094cc019a4821 100644
--- a/lldb/source/Plugins/Platform/CMakeLists.txt
+++ b/lldb/source/Plugins/Platform/CMakeLists.txt
@@ -7,4 +7,5 @@ add_subdirectory(NetBSD)
 add_subdirectory(OpenBSD)
 add_subdirectory(POSIX)
 add_subdirectory(QemuUser)
+add_subdirectory(scripted)
 add_subdirectory(Windows)
diff --git a/lldb/source/Plugins/Platform/scripted/CMakeLists.txt b/lldb/source/Plugins/Platform/scripted/CMakeLists.txt
new file mode 100644
index 0000000000000..96a38fdee1484
--- /dev/null
+++ b/lldb/source/Plugins/Platform/scripted/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_lldb_library(lldbPluginScriptedPlatform PLUGIN
+  ScriptedPlatform.cpp
+
+  LINK_LIBS
+    lldbCore
+    lldbTarget
+    lldbUtility
+)
diff --git a/lldb/source/Plugins/Platform/scripted/ScriptedPlatform.cpp b/lldb/source/Plugins/Platform/scripted/ScriptedPlatform.cpp
new file mode 100644
index 0000000000000..d841d339c1e3c
--- /dev/null
+++ b/lldb/source/Plugins/Platform/scripted/ScriptedPlatform.cpp
@@ -0,0 +1,279 @@
+//===-- ScriptedPlatform.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 "ScriptedPlatform.h"
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/LLDBLog.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(ScriptedPlatform)
+
+static uint32_t g_initialize_count = 0;
+
+static constexpr lldb::ScriptLanguage g_supported_script_languages[] = {
+    ScriptLanguage::eScriptLanguagePython,
+};
+
+bool ScriptedPlatform::IsScriptLanguageSupported(
+    lldb::ScriptLanguage language) {
+  return llvm::is_contained(g_supported_script_languages, language);
+}
+
+ScriptedPlatformInterface &ScriptedPlatform::GetInterface() const {
+  return *m_interface_up;
+}
+
+lldb::PlatformSP ScriptedPlatform::CreateInstance(bool force,
+                                                  const ArchSpec *arch) {
+  Log *log = GetLog(LLDBLog::Platform);
+  if (log) {
+    const char *arch_name;
+    if (arch && arch->GetArchitectureName())
+      arch_name = arch->GetArchitectureName();
+    else
+      arch_name = "<null>";
+
+    const char *triple_cstr =
+        arch ? arch->GetTriple().getTriple().c_str() : "<null>";
+
+    LLDB_LOGF(log, "ScriptedPlatform::%s(force=%s, arch={%s,%s})",
+              __PRETTY_FUNCTION__, force ? "true" : "false", arch_name,
+              triple_cstr);
+  }
+
+  return std::make_shared<ScriptedPlatform>();
+}
+
+ScriptedPlatform::ScriptedPlatform() : Platform(false) {}
+
+llvm::Error ScriptedPlatform::SetupScriptedObject() {
+
+  auto error_with_message = [](llvm::StringRef message) -> llvm::Error {
+    Status error;
+    ScriptedInterface::ErrorWithMessage<bool>(LLVM_PRETTY_FUNCTION, message,
+                                              error, LLDBLog::Platform);
+    return error.ToError();
+  };
+
+  Debugger &debugger = m_metadata->GetDebugger();
+
+  if (!IsScriptLanguageSupported(debugger.GetScriptLanguage()))
+    return error_with_message("Debugger language not supported");
+
+  ScriptInterpreter *interpreter = debugger.GetScriptInterpreter();
+  if (!interpreter)
+    return error_with_message("Debugger has no Script Interpreter");
+
+  // Create platform instance interface
+  m_interface_up = interpreter->CreateScriptedPlatformInterface();
+  if (!m_interface_up)
+    return error_with_message(
+        "Script interpreter couldn't create Scripted Process Interface");
+
+  const ScriptedMetadata scripted_metadata = m_metadata->GetScriptedMetadata();
+
+  ExecutionContext e(&debugger.GetSelectedOrDummyTarget());
+  auto obj_or_err = GetInterface().CreatePluginObject(
+      scripted_metadata.GetClassName(), e, scripted_metadata.GetArgsSP());
+
+  if (!obj_or_err) {
+    llvm::consumeError(obj_or_err.takeError());
+    return error_with_message("Failed to create script object.");
+  }
+
+  StructuredData::GenericSP object_sp = *obj_or_err;
+  if (!object_sp || !object_sp->IsValid())
+    return error_with_message("Failed to create valid script object");
+
+  m_hostname = GetHostPlatform()->GetHostname();
+  return llvm::Error::success();
+}
+
+ScriptedPlatform::~ScriptedPlatform() {}
+
+llvm::Error ScriptedPlatform::ReloadMetadata() {
+  if (!m_metadata)
+    return llvm::createStringError(
+        "Scripted Platform has no platform metadata.");
+  return SetupScriptedObject();
+}
+
+void ScriptedPlatform::Initialize() {
+  if (g_initialize_count++ == 0) {
+    // NOTE: This should probably be using the driving process platform
+    PluginManager::RegisterPlugin(ScriptedPlatform::GetPluginNameStatic(),
+                                  ScriptedPlatform::GetDescriptionStatic(),
+                                  ScriptedPlatform::CreateInstance);
+  }
+}
+
+void ScriptedPlatform::Terminate() {
+  if (g_initialize_count > 0) {
+    if (--g_initialize_count == 0) {
+      PluginManager::UnregisterPlugin(ScriptedPlatform::CreateInstance);
+    }
+  }
+}
+
+std::vector<ArchSpec>
+ScriptedPlatform::GetSupportedArchitectures(const ArchSpec &process_host_arch) {
+  std::vector<ArchSpec> result;
+  result.push_back(process_host_arch);
+  return result;
+}
+
+lldb::ProcessSP
+ScriptedPlatform::Attach(lldb_private::ProcessAttachInfo &attach_info,
+                         lldb_private::Debugger &debugger,
+                         lldb_private::Target *target, // Can be nullptr, if
+                                                       // nullptr create a new
+                                                       // target, else use
+                                                       // existing one
+                         lldb_private::Status &error) {
+  lldb::ProcessAttachInfoSP attach_info_sp =
+      std::make_shared<ProcessAttachInfo>(attach_info);
+
+  if (!target) {
+    target = &debugger.GetSelectedOrDummyTarget();
+  }
+
+  ProcessSP process_sp =
+      GetInterface().AttachToProcess(attach_info_sp, target->shared_from_this(),
+                                     debugger.shared_from_this(), error);
+  if (!process_sp || error.Fail())
+    return {};
+  return process_sp;
+}
+
+llvm::Expected<ProcessInstanceInfo>
+ScriptedPlatform::ParseProcessInfo(StructuredData::Dictionary &dict,
+                                   lldb::pid_t pid) const {
+  if (!dict.HasKey("name"))
+    return llvm::createStringError("No 'name' key in process info dictionary.");
+  if (!dict.HasKey("arch"))
+    return llvm::createStringError("No 'arch' key in process info dictionary.");
+
+  llvm::StringRef result;
+  if (!dict.GetValueForKeyAsString("name", result))
+    return llvm::createStringError(
+        "Couldn't extract 'name' key from process info dictionary.");
+  std::string name = result.str();
+
+  if (!dict.GetValueForKeyAsString("arch", result))
+    return llvm::createStringError(
+        "Couldn't extract 'arch' key from process info dictionary.");
+  const ArchSpec arch(result.data());
+  if (!arch.IsValid())
+    return llvm::createStringError(
+        "Invalid 'arch' key in process info dictionary.");
+
+  ProcessInstanceInfo proc_info = ProcessInstanceInfo(name.c_str(), arch, pid);
+
+  lldb::pid_t parent = LLDB_INVALID_PROCESS_ID;
+  if (dict.GetValueForKeyAsInteger("parent", parent))
+    proc_info.SetParentProcessID(parent);
+
+  uint32_t uid = UINT32_MAX;
+  if (dict.GetValueForKeyAsInteger("uid", uid))
+    proc_info.SetUserID(uid);
+
+  uint32_t gid = UINT32_MAX;
+  if (dict.GetValueForKeyAsInteger("gid", gid))
+    proc_info.SetGroupID(gid);
+
+  return proc_info;
+}
+
+uint32_t
+ScriptedPlatform::FindProcesses(const ProcessInstanceInfoMatch &match_info,
+                                ProcessInstanceInfoList &proc_infos) {
+  CheckInterpreterAndScriptObject();
+  StructuredData::DictionarySP dict_sp = GetInterface().ListProcesses();
+
+  Status error;
+  if (!dict_sp)
+    return ScriptedInterface::ErrorWithMessage<uint32_t>(
+        LLVM_PRETTY_FUNCTION, "Failed to get scripted platform processes.",
+        error, LLDBLog::Platform);
+
+  auto parse_process_info = [this,
+                             &proc_infos](llvm::StringRef key,
+                                          StructuredData::Object *val) -> bool {
+    if (!val)
+      return false;
+
+    StructuredData::Dictionary *dict = val->GetAsDictionary();
+
+    if (!dict || !dict->IsValid())
+      return false;
+
+    lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+    if (!llvm::to_integer(key, pid))
+      return false;
+
+    auto proc_info_or_error = ParseProcessInfo(*dict, pid);
+
+    if (llvm::Error e = proc_info_or_error.takeError()) {
+      LLDB_LOGF(GetLog(LLDBLog::Platform), "%s ERROR = %s",
+                LLVM_PRETTY_FUNCTION, llvm::toString(std::move(e)).c_str());
+      return false;
+    }
+
+    if (!proc_info_or_error) {
+      llvm::consumeError(proc_info_or_error.takeError());
+      return false;
+    }
+
+    proc_infos.push_back(*proc_info_or_error);
+    return true;
+  };
+
+  dict_sp->ForEach(parse_process_info);
+
+  // TODO: Use match_info to filter through processes
+  return proc_infos.size();
+}
+
+bool ScriptedPlatform::GetProcessInfo(lldb::pid_t pid,
+                                      ProcessInstanceInfo &proc_info) {
+  if (pid == LLDB_INVALID_PROCESS_ID)
+    return false;
+
+  StructuredData::DictionarySP dict_sp = GetInterface().GetProcessInfo(pid);
+
+  if (!dict_sp || !dict_sp->IsValid())
+    return false;
+
+  auto proc_info_or_error = ParseProcessInfo(*dict_sp.get(), pid);
+
+  if (!proc_info_or_error) {
+    llvm::consumeError(proc_info_or_error.takeError());
+    return false;
+  }
+
+  proc_info = *proc_info_or_error;
+  return true;
+}
+
+Status ScriptedPlatform::LaunchProcess(ProcessLaunchInfo &launch_info) {
+  lldb::ProcessLaunchInfoSP launch_info_sp =
+      std::make_shared<ProcessLaunchInfo>(launch_info);
+  return GetInterface().LaunchProcess(launch_info_sp);
+}
+
+Status ScriptedPlatform::KillProcess(const lldb::pid_t pid) {
+  return GetInterface().KillProcess(pid);
+}
diff --git a/lldb/source/Plugins/Platform/scripted/ScriptedPlatform.h b/lldb/source/Plugins/Platform/scripted/ScriptedPlatform.h
new file mode 100644
index 0000000000000..eea01534afe91
--- /dev/null
+++ b/lldb/source/Plugins/Platform/scripted/ScriptedPlatform.h
@@ -0,0 +1,84 @@
+//===-- ScriptedPlatform.h --------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_SCRIPTED_PLATFORM_H
+#define LLDB_SOURCE_PLUGINS_SCRIPTED_PLATFORM_H
+
+#include "lldb/Target/Platform.h"
+#include "lldb/Utility/ScriptedMetadata.h"
+
+namespace lldb_private {
+
+class ScriptedPlatform : public Platform {
+public:
+  ScriptedPlatform();
+
+  ~ScriptedPlatform() override;
+
+  llvm::Error SetupScriptedObject();
+
+  static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
+
+  static void Initialize();
+
+  static void Terminate();
+
+  static llvm::StringRef GetPluginNameStatic() { return "scripted-platform"; }
+
+  static llvm::StringRef GetDescriptionStatic() {
+    return "Scripted Platform plug-in.";
+  }
+
+  llvm::StringRef GetDescription() override { return GetDescriptionStatic(); }
+
+  llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+  std::vector<ArchSpec>
+  GetSupportedArchitectures(const ArchSpec &process_host_arch) override;
+
+  bool IsConnected() const override { return true; }
+
+  lldb::ProcessSP Attach(lldb_private::ProcessAttachInfo &attach_info,
+                         lldb_private::Debugger &debugger,
+                         lldb_private::Target *target,
+                         lldb_private::Status &error) override;
+
+  uint32_t FindProcesses(const ProcessInstanceInfoMatch &match_info,
+                         ProcessInstanceInfoList &proc_infos) override;
+
+  bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &proc_info) override;
+
+  Status LaunchProcess(ProcessLaunchInfo &launch_info) override;
+
+  Status KillProcess(const lldb::pid_t pid) override;
+
+  void CalculateTrapHandlerSymbolNames() override {}
+
+  llvm::Error ReloadMetadata() override;
+
+private:
+  inline void CheckInterpreterAndScriptObject() const {
+    assert(m_interface_up && "Invalid Scripted Platform Interface.");
+  }
+
+  ScriptedPlatform(const ScriptedPlatform &) = delete;
+  const ScriptedPlatform &operator=(const ScriptedPlatform &) = delete;
+
+  ScriptedPlatformInterface &GetInterface() const;
+
+  llvm::Expected<ProcessInstanceInfo>
+  ParseProcessInfo(StructuredData::Dictionary &dict, lldb::pid_t pid) const;
+
+  static bool IsScriptLanguageSupported(lldb::ScriptLanguage language);
+
+  lldb::ScriptedPlatformInterfaceUP m_interface_up;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_PLATFORM_H
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
index 0335364b4010b..273e951b2418f 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
@@ -120,7 +120,7 @@ class ScriptedProcess : public Process {
   friend class ScriptedThread;
 
   inline void CheckScriptedInterface() const {
-    lldbassert(m_interface_up && "Invalid scripted process interface.");
+    assert(m_interface_up && "Invalid Scripted Process Interface.");
   }
 
   ScriptedProcessInterface &GetInterface() const;
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
index 88a4ca3b0389f..5ba76096b6d08 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
@@ -23,11 +23,6 @@
 using namespace lldb;
 using namespace lldb_private;
 
-void ScriptedThread::CheckInterpreterAndScriptObject() const {
-  lldbassert(m_script_object_sp && "Invalid Script Object.");
-  lldbassert(GetInterface() && "Invalid Scripted Thread Interface.");
-}
-
 llvm::Expected<std::shared_ptr<ScriptedThread>>
 ScriptedThread::Create(ScriptedProcess &process,
                        StructuredData::Generic *script_object) {
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.h b/lldb/source/Plugins/Process/scripted/ScriptedThread.h
index cd224d60ceef8..17d7c576d5799 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.h
@@ -61,7 +61,11 @@ class ScriptedThread : public lldb_private::Thread {
   StructuredData::ObjectSP FetchThreadExtendedInfo() override;
 
 private:
-  void CheckInterpreterAndScriptObject() const;
+  inline void CheckInterpreterAndScriptObject() const {
+    assert(m_script_object_sp && "Invalid Script Object.");
+    assert(GetInterface() && "Invalid Scripted Thread Interface.");
+  }
+
   lldb::ScriptedThreadInterfaceSP GetInterface() const;
 
   ScriptedThread(const ScriptedThread &) = delete;
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp
index 6e93bec80056e..ca1e33ed2f519 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp
@@ -44,19 +44,14 @@ ScriptedPlatformPythonInterface::CreatePluginObject(
 
 StructuredData::DictionarySP ScriptedPlatformPythonInterface::ListProcesses() {
   Status error;
-  StructuredData::DictionarySP dict_sp =
+  StructuredData::DictionarySP dict =
       Dispatch<StructuredData::DictionarySP>("list_processes", error);
 
-  if (!dict_sp || !dict_sp->IsValid() || error.Fail()) {
-    return ScriptedInterface::ErrorWithMessage<StructuredData::DictionarySP>(
-        LLVM_PRETTY_FUNCTION,
-        llvm::Twine("Null or invalid object (" +
-                    llvm::Twine(error.AsCString()) + llvm::Twine(")."))
-            .str(),
-        error);
-  }
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
+                                                    error))
+    return {};
 
-  return dict_sp;
+  return dict;
 }
 
 StructuredData::DictionarySP
@@ -77,16 +72,29 @@ ScriptedPlatformPythonInterface::GetProcessInfo(lldb::pid_t pid) {
   return dict_sp;
 }
 
-Status ScriptedPlatformPythonInterface::AttachToProcess(
-    ProcessAttachInfoSP attach_info) {
-  // FIXME: Pass `attach_info` to method call
-  return GetStatusFromMethod("attach_to_process");
+lldb::ProcessSP ScriptedPlatformPythonInterface::AttachToProcess(
+    lldb::ProcessAttachInfoSP attach_info_sp, lldb::TargetSP target_sp,
+    lldb::DebuggerSP debugger_sp, Status &error) {
+  Status py_error;
+  ProcessSP process_sp =
+      Dispatch<ProcessSP>("attach_to_process", py_error, attach_info_sp,
+                          target_sp, debugger_sp, error);
+
+  if (!process_sp || error.Fail()) {
+    return ScriptedInterface::ErrorWithMessage<ProcessSP>(
+        LLVM_PRETTY_FUNCTION,
+        llvm::Twine("Null or invalid object (" +
+                    llvm::Twine(error.AsCString()) + llvm::Twine(")."))
+            .str(),
+        error);
+  }
+
+  return process_sp;
 }
 
 Status ScriptedPlatformPythonInterface::LaunchProcess(
-    ProcessLaunchInfoSP launch_info) {
-  // FIXME: Pass `launch_info` to method call
-  return GetStatusFromMethod("launch_process");
+    ProcessLaunchInfoSP launch_info_sp) {
+  return GetStatusFromMethod("launch_process", launch_info_sp);
 }
 
 Status ScriptedPlatformPythonInterface::KillProcess(lldb::pid_t pid) {
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.h
index 0842d3a003429..de33f592585f6 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.h
@@ -38,7 +38,10 @@ class ScriptedPlatformPythonInterface : public ScriptedPlatformInterface,
 
   StructuredData::DictionarySP GetProcessInfo(lldb::pid_t) override;
 
-  Status AttachToProcess(lldb::ProcessAttachInfoSP attach_info) override;
+  lldb::ProcessSP AttachToProcess(lldb::ProcessAttachInfoSP attach_info_sp,
+                                  lldb::TargetSP target_sp,
+                                  lldb::DebuggerSP debugger_sp,
+                                  Status &error) override;
 
   Status LaunchProcess(lldb::ProcessLaunchInfoSP launch_info) override;
 
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
index 699412e437a1a..31df87ee23e2f 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
@@ -92,6 +92,51 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>(
   return m_interpreter.GetDataExtractorFromSBData(*sb_data);
 }
 
+template <>
+lldb::DebuggerSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DebuggerSP>(
+    python::PythonObject &p, Status &error) {
+  lldb::SBDebugger *sb_debugger = reinterpret_cast<lldb::SBDebugger *>(
+      python::LLDBSWIGPython_CastPyObjectToSBDebugger(p.get()));
+
+  if (!sb_debugger) {
+    error.SetErrorString("Couldn't cast lldb::SBDebugger to lldb::DebuggerSP.");
+    return nullptr;
+  }
+
+  return m_interpreter.GetOpaqueTypeFromSBDebugger(*sb_debugger);
+}
+
+template <>
+lldb::TargetSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::TargetSP>(
+    python::PythonObject &p, Status &error) {
+  lldb::SBTarget *sb_target = reinterpret_cast<lldb::SBTarget *>(
+      python::LLDBSWIGPython_CastPyObjectToSBTarget(p.get()));
+
+  if (!sb_target) {
+    error.SetErrorString("Couldn't cast lldb::SBTarget to lldb::TargetSP.");
+    return nullptr;
+  }
+
+  return m_interpreter.GetOpaqueTypeFromSBTarget(*sb_target);
+}
+
+template <>
+lldb::ProcessSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ProcessSP>(
+    python::PythonObject &p, Status &error) {
+  lldb::SBProcess *sb_process = reinterpret_cast<lldb::SBProcess *>(
+      python::LLDBSWIGPython_CastPyObjectToSBProcess(p.get()));
+
+  if (!sb_process) {
+    error.SetErrorString("Couldn't cast lldb::SBProcess to lldb::ProcessSP.");
+    return nullptr;
+  }
+
+  return m_interpreter.GetOpaqueTypeFromSBProcess(*sb_process);
+}
+
 template <>
 lldb::BreakpointSP
 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
index e1a3156d10afd..12fc38fd46e7e 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
@@ -108,7 +108,7 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
               m_interpreter.GetDictionaryName());
       if (!dict.IsAllocated())
         return create_error(
-            llvm::formatv("Could not find interpreter dictionary: %s",
+            llvm::formatv("Could not find interpreter dictionary: {0}",
                           m_interpreter.GetDictionaryName()));
 
       auto init =
@@ -321,6 +321,14 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
     return python::SWIGBridge::ToSWIGWrapper(arg);
   }
 
+  python::PythonObject Transform(lldb::DebuggerSP arg) {
+    return python::SWIGBridge::ToSWIGWrapper(arg);
+  }
+
+  python::PythonObject Transform(lldb::TargetSP arg) {
+    return python::SWIGBridge::ToSWIGWrapper(arg);
+  }
+
   python::PythonObject Transform(lldb::ProcessSP arg) {
     return python::SWIGBridge::ToSWIGWrapper(arg);
   }
@@ -456,6 +464,21 @@ lldb::BreakpointSP
 ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
     python::PythonObject &p, Status &error);
 
+template <>
+lldb::DebuggerSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DebuggerSP>(
+    python::PythonObject &p, Status &error);
+
+template <>
+lldb::TargetSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::TargetSP>(
+    python::PythonObject &p, Status &error);
+
+template <>
+lldb::ProcessSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ProcessSP>(
+    python::PythonObject &p, Status &error);
+
 template <>
 lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
     lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index 3026b6113ae8f..e2e5e21aee6a3 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -255,6 +255,9 @@ class SWIGBridge {
 };
 
 void *LLDBSWIGPython_CastPyObjectToSBData(PyObject *data);
+void *LLDBSWIGPython_CastPyObjectToSBDebugger(PyObject *data);
+void *LLDBSWIGPython_CastPyObjectToSBTarget(PyObject *data);
+void *LLDBSWIGPython_CastPyObjectToSBProcess(PyObject *data);
 void *LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *data);
 void *LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject *data);
 void *LLDBSWIGPython_CastPyObjectToSBLaunchInfo(PyObject *data);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 70fa6d83e306f..7655f408f7096 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1528,6 +1528,11 @@ lldb::ValueObjectListSP ScriptInterpreterPythonImpl::GetRecognizedArguments(
   return ValueObjectListSP();
 }
 
+ScriptedPlatformInterfaceUP
+ScriptInterpreterPythonImpl::CreateScriptedPlatformInterface() {
+  return std::make_unique<ScriptedPlatformPythonInterface>(*this);
+}
+
 ScriptedProcessInterfaceUP
 ScriptInterpreterPythonImpl::CreateScriptedProcessInterface() {
   return std::make_unique<ScriptedProcessPythonInterface>(*this);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index c2024efb395d7..24222fb804bd0 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -107,6 +107,8 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
   GetRecognizedArguments(const StructuredData::ObjectSP &implementor,
                          lldb::StackFrameSP frame_sp) override;
 
+  lldb::ScriptedPlatformInterfaceUP CreateScriptedPlatformInterface() override;
+
   lldb::ScriptedProcessInterfaceUP CreateScriptedProcessInterface() override;
 
   lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override;
diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp
index ab80fe4c8ba24..58db32968836a 100644
--- a/lldb/source/Target/Platform.cpp
+++ b/lldb/source/Target/Platform.cpp
@@ -230,6 +230,10 @@ ArchSpec Platform::GetAugmentedArchSpec(Platform *platform, llvm::StringRef trip
   return HostInfo::GetAugmentedArchSpec(triple);
 }
 
+PlatformMetadata::PlatformMetadata(Debugger &debugger,
+                                   const ScriptedMetadata metadata)
+    : m_debugger(debugger), m_scripted_metadata(metadata) {}
+
 /// Default Constructor
 Platform::Platform(bool is_host)
     : m_is_host(is_host), m_os_version_set_while_connected(false),
@@ -238,7 +242,7 @@ Platform::Platform(bool is_host)
       m_rsync_prefix(), m_supports_ssh(false), m_ssh_opts(),
       m_ignores_remote_hostname(false), m_trap_handlers(),
       m_calculated_trap_handlers(false),
-      m_module_cache(std::make_unique<ModuleCache>()) {
+      m_module_cache(std::make_unique<ModuleCache>()), m_metadata() {
   Log *log = GetLog(LLDBLog::Object);
   LLDB_LOGF(log, "%p Platform::Platform()", static_cast<void *>(this));
 }
@@ -1284,6 +1288,10 @@ const char *Platform::GetLocalCacheDirectory() {
   return m_local_cache_directory.c_str();
 }
 
+void Platform::SetMetadata(std::unique_ptr<PlatformMetadata> metadata) {
+  m_metadata = std::move(metadata);
+}
+
 static constexpr OptionDefinition g_rsync_option_table[] = {
     {LLDB_OPT_SET_ALL, false, "rsync", 'r', OptionParser::eNoArgument, nullptr,
      {}, 0, eArgTypeNone, "Enable rsync."},
diff --git a/lldb/test/API/functionalities/scripted_platform/TestScriptedPlatform.py b/lldb/test/API/functionalities/scripted_platform/TestScriptedPlatform.py
new file mode 100644
index 0000000000000..c2c24c1e4c5d5
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_platform/TestScriptedPlatform.py
@@ -0,0 +1,108 @@
+"""
+Test python scripted platform in lldb
+"""
+
+import os, shutil
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test import lldbtest
+
+
+class ScriptedPlatformTestCase(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @skipUnlessDarwin
+    def test_python_plugin_package(self):
+        """Test that the lldb python module has a `plugins.scripted_platform`
+        package."""
+        self.expect(
+            "script import lldb.plugins",
+            substrs=["ModuleNotFoundError"],
+            matching=False,
+        )
+
+        self.expect("script dir(lldb.plugins)", substrs=["scripted_platform"])
+
+        self.expect(
+            "script import lldb.plugins.scripted_platform",
+            substrs=["ModuleNotFoundError"],
+            matching=False,
+        )
+
+        self.expect(
+            "script dir(lldb.plugins.scripted_platform)", substrs=["ScriptedPlatform"]
+        )
+
+        self.expect(
+            "script from lldb.plugins.scripted_platform import ScriptedPlatform",
+            substrs=["ImportError"],
+            matching=False,
+        )
+
+        self.expect(
+            "script dir(ScriptedPlatform)",
+            substrs=[
+                "attach_to_process",
+                "kill_process",
+                "launch_process",
+                "list_processes",
+            ],
+        )
+
+    @skipUnlessDarwin
+    def test_list_processes(self):
+        """Test that we can load and select an lldb scripted platform using the
+        SBAPI, check its process ID, parent, name & triple.
+        """
+        os.environ["SKIP_SCRIPTED_PLATFORM_SELECT"] = "1"
+
+        def cleanup():
+            del os.environ["SKIP_SCRIPTED_PLATFORM_SELECT"]
+
+        self.addTearDownHook(cleanup)
+
+        scripted_platform_example_relpath = "my_scripted_platform.py"
+        self.runCmd(
+            "command script import "
+            + os.path.join(self.getSourceDir(), scripted_platform_example_relpath)
+        )
+
+        proc_info = {}
+        proc_info["name"] = "a.out"
+        proc_info["arch"] = "arm64-apple-macosx"
+        proc_info["pid"] = 420
+        proc_info["parent"] = 42
+        proc_info["uid"] = 501
+        proc_info["gid"] = 20
+
+        structured_data = lldb.SBStructuredData()
+        structured_data.SetFromJSON(json.dumps({"processes": [proc_info]}))
+
+        platform = lldb.SBPlatform(
+            "scripted-platform",
+            self.dbg,
+            "my_scripted_platform.MyScriptedPlatform",
+            structured_data,
+        )
+
+        self.assertTrue(platform and platform.IsValid())
+        self.assertTrue(platform.IsConnected)
+
+        err = lldb.SBError()
+        proc_list = platform.GetAllProcesses(err)
+        self.assertSuccess(err)
+        self.assertEqual(proc_list.GetSize(), 1)
+
+        sb_proc_info = lldb.SBProcessInfo()
+        self.assertTrue(proc_list.GetProcessInfoAtIndex(0, sb_proc_info))
+        self.assertTrue(sb_proc_info.IsValid())
+
+        self.assertEqual(sb_proc_info.GetName(), proc_info["name"])
+        self.assertEqual(sb_proc_info.GetTriple(), proc_info["arch"])
+        self.assertEqual(sb_proc_info.GetProcessID(), proc_info["pid"])
+        self.assertEqual(sb_proc_info.GetParentProcessID(), proc_info["parent"])
+        self.assertEqual(sb_proc_info.GetUserID(), proc_info["uid"])
+        self.assertEqual(sb_proc_info.GetGroupID(), proc_info["gid"])
diff --git a/lldb/test/API/functionalities/scripted_platform/my_scripted_platform.py b/lldb/test/API/functionalities/scripted_platform/my_scripted_platform.py
index a0e286fbdc467..5d3abe4cdd918 100644
--- a/lldb/test/API/functionalities/scripted_platform/my_scripted_platform.py
+++ b/lldb/test/API/functionalities/scripted_platform/my_scripted_platform.py
@@ -6,20 +6,27 @@
 
 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
+        super.__init__(exe_ctx, args)
+
+        if args and args.GetType() == lldb.eStructuredDataTypeDictionary:
+            processes = args.GetValueForKey("processes")
+            for i in range(0, processes.GetSize()):
+                proc_info = processes.GetItemAtIndex(i)
+                proc = {}
+                proc["name"] = proc_info.GetValueForKey("name").GetStringValue(42)
+                proc["arch"] = proc_info.GetValueForKey("arch").GetStringValue(42)
+                proc["pid"] = proc_info.GetValueForKey("pid").GetIntegerValue()
+                proc["parent"] = proc_info.GetValueForKey("parent").GetIntegerValue()
+                proc["uid"] = proc_info.GetValueForKey("uid").GetIntegerValue()
+                proc["gid"] = proc_info.GetValueForKey("gid").GetIntegerValue()
+                self.processes[proc["pid"]] = proc
 
     def list_processes(self):
         return self.processes
 
+    def attach_to_process(self, attach_info, target, debugger, error):
+        return None
+
     def get_process_info(self, pid):
         return self.processes[pid]
 
diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
index 0edde54d310fd..606c2a6b26a9d 100644
--- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -119,6 +119,21 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpoint(
   return nullptr;
 }
 
+void *
+lldb_private::python::LLDBSWIGPython_CastPyObjectToSBDebugger(PyObject *data) {
+  return nullptr;
+}
+
+void *
+lldb_private::python::LLDBSWIGPython_CastPyObjectToSBTarget(PyObject *data) {
+  return nullptr;
+}
+
+void *
+lldb_private::python::LLDBSWIGPython_CastPyObjectToSBProcess(PyObject *data) {
+  return nullptr;
+}
+
 void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBAttachInfo(
     PyObject *data) {
   return nullptr;



More information about the lldb-commits mailing list