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

Med Ismail Bennani via lldb-commits lldb-commits at lists.llvm.org
Mon Aug 5 00:50:28 PDT 2024


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

>From 4b8ccab40914a9a6ee32939911b66fb701605c96 Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ismail at bennani.ma>
Date: Mon, 5 Aug 2024 00:49:55 -0700
Subject: [PATCH 1/2] [lldb/API] Fix SBStructuredData JSON Array parsing

This patch loosen the parsing requirement to allow parsing not only
JSON dictionaries but also JSON arrays.

Signed-off-by: Med Ismail Bennani <ismail at bennani.ma>
---
 lldb/source/API/SBStructuredData.cpp            |  8 ++++++--
 .../sbstructureddata/TestStructuredDataAPI.py   | 17 +++++++++++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)

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/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py b/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
index b3db3bc61e4dc..01e5a76d1a3a6 100644
--- a/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
+++ b/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
@@ -110,6 +110,23 @@ class MyRandomClass:
         self.assertTrue(my_random_class)
         self.assertEqual(my_random_class.payload, MyRandomClass.payload)
 
+        example_arr = [1, 2.3, "4", {"5": False}]
+        arr_str = json.dumps(example_arr)
+        s.Clear()
+        s.Print(arr_str)
+        example = lldb.SBStructuredData()
+
+        # Check SetFromJSON API for dictionaries, integers, floating point
+        # values, strings and arrays
+        error = example.SetFromJSON(s)
+        if not error.Success():
+            self.fail("FAILED:   " + error.GetCString())
+
+        s.Clear()
+        self.assertSuccess(example.GetAsJSON(s))
+        sb_data = json.loads(s.GetData())
+        self.assertEqual(sb_data, example_arr)
+
     def invalid_struct_test(self, example):
         invalid_struct = lldb.SBStructuredData()
         invalid_struct = example.GetValueForKey("invalid_key")

>From 6f076dc50577938647d00ee895e07e44afe0f0ea Mon Sep 17 00:00:00 2001
From: Med Ismail Bennani <ismail at bennani.ma>
Date: Mon, 5 Aug 2024 00:03:33 -0700
Subject: [PATCH 2/2] [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     |  28 +-
 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    |   9 +-
 .../lldb/Interpreter/OptionGroupPlatform.h    |  10 +-
 .../lldb/Interpreter/ScriptInterpreter.h      |  19 +-
 lldb/include/lldb/Target/Platform.h           |  25 ++
 lldb/include/lldb/Utility/ScriptedMetadata.h  |   4 +-
 lldb/source/API/SBPlatform.cpp                |  42 +++
 .../source/Commands/CommandObjectPlatform.cpp |  20 +-
 lldb/source/Interpreter/ScriptInterpreter.cpp |  15 +
 lldb/source/Plugins/Platform/CMakeLists.txt   |   1 +
 .../Plugins/Platform/scripted/CMakeLists.txt  |   8 +
 .../Platform/scripted/ScriptedPlatform.cpp    | 332 ++++++++++++++++++
 .../Platform/scripted/ScriptedPlatform.h      |  84 +++++
 .../Process/scripted/ScriptedProcess.h        |   2 +-
 .../Process/scripted/ScriptedThread.cpp       |   5 -
 .../Plugins/Process/scripted/ScriptedThread.h |   6 +-
 .../ScriptedPlatformPythonInterface.cpp       |  55 ++-
 .../ScriptedPlatformPythonInterface.h         |  10 +-
 .../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 +
 35 files changed, 927 insertions(+), 57 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..8a65817d450ae 100644
--- a/lldb/examples/python/templates/scripted_platform.py
+++ b/lldb/examples/python/templates/scripted_platform.py
@@ -13,6 +13,7 @@ class ScriptedPlatform(metaclass=ABCMeta):
     """
 
     processes = None
+    supported_archs = None
 
     @abstractmethod
     def __init__(self, exe_ctx, args):
@@ -23,9 +24,23 @@ def __init__(self, exe_ctx, args):
             args (lldb.SBStructuredData): A Dictionary holding arbitrary
                 key/value pairs used by the scripted platform.
         """
-        processes = []
+        self.processes = {}
+        self.supported_archs = []
+
+    def get_supported_architectures(self):
+        """Get a list of supported architectures by this platform.
+
+        .. code-block:: python
+
+            supported_archs = [arch1, arch2, arch3, ...]
+
+        Returns:
+            List: The supported architectures represented as a list.
+                  The list can be empty, in which case the host platform
+                  supported architectures will be used.
+        """
+        return self.supported_archs
 
-    @abstractmethod
     def list_processes(self):
         """Get a list of processes that are running or that can be attached to on the platform.
 
@@ -48,7 +63,7 @@ def list_processes(self):
                 provide the parent process ID and the user and group IDs.
                 The dictionary can be empty.
         """
-        pass
+        return self.processes
 
     def get_process_info(self, pid):
         """Get the dictionary describing the process.
@@ -60,14 +75,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 1624e02070b1b..9e53eeee6fba1 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;
 }
@@ -598,6 +599,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..a4381c42f8ae8 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h
@@ -24,14 +24,19 @@ class ScriptedPlatformInterface : virtual public ScriptedInterface {
                      StructuredData::DictionarySP args_sp,
                      StructuredData::Generic *script_obj = nullptr) = 0;
 
+  virtual StructuredData::ArraySP GetSupportedArchitectures() { return {}; }
+
   virtual StructuredData::DictionarySP ListProcesses() { return {}; }
 
   virtual StructuredData::DictionarySP GetProcessInfo(lldb::pid_t) {
     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..89d1801022083 100644
--- a/lldb/include/lldb/Target/Platform.h
+++ b/lldb/include/lldb/Target/Platform.h
@@ -24,8 +24,10 @@
 #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/UnimplementedError.h"
 #include "lldb/Utility/UserIDResolver.h"
 #include "lldb/lldb-private-forward.h"
 #include "lldb/lldb-public.h"
@@ -35,6 +37,7 @@
 
 namespace lldb_private {
 
+class PlatformMetadata;
 class ProcessInstanceInfo;
 class ProcessInstanceInfoMatch;
 typedef std::vector<ProcessInstanceInfo> ProcessInstanceInfoList;
@@ -632,6 +635,12 @@ class Platform : public PluginInterface {
 
   virtual const char *GetLocalCacheDirectory();
 
+  void SetMetadata(std::unique_ptr<PlatformMetadata> metadata);
+
+  virtual llvm::Error ReloadMetadata() {
+    return llvm::make_error<UnimplementedError>();
+  }
+
   virtual std::string GetPlatformSpecificConnectionInformation() { return ""; }
 
   virtual llvm::ErrorOr<llvm::MD5::MD5Result>
@@ -962,6 +971,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 +1013,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/Commands/CommandObjectPlatform.cpp b/lldb/source/Commands/CommandObjectPlatform.cpp
index 5b18f2b60e92d..8eb84bc106aff 100644
--- a/lldb/source/Commands/CommandObjectPlatform.cpp
+++ b/lldb/source/Commands/CommandObjectPlatform.cpp
@@ -28,6 +28,7 @@
 #include "lldb/Utility/State.h"
 
 #include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FormatAdapters.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -154,8 +155,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 +184,19 @@ 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 (llvm::Error e = platform_sp->ReloadMetadata())
+            result.AppendErrorWithFormatv(
+                "platform couldn't reload metadata: {0}\n",
+                fmt_consume(std::move(e)));
           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..5cc924fbc0ed1
--- /dev/null
+++ b/lldb/source/Plugins/Platform/scripted/ScriptedPlatform.cpp
@@ -0,0 +1,332 @@
+//===-- 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) {
+  CheckInterpreterAndScriptObject();
+  StructuredData::ArraySP archs_sp = GetInterface().GetSupportedArchitectures();
+  if (!archs_sp)
+    return {};
+
+  // If the scripted platform didn't provide any supported architecture, we
+  // should use the host platform support architecture instead.
+  if (!archs_sp->GetSize())
+    return GetHostPlatform()->GetSupportedArchitectures(process_host_arch);
+
+  std::vector<ArchSpec> result;
+  auto extract_arch_specs = [&result](StructuredData::Object *obj) {
+    if (!obj)
+      return false;
+
+    StructuredData::String *arch_str = obj->GetAsString();
+    if (!arch_str)
+      return false;
+
+    ArchSpec arch_spec(arch_str->GetValue());
+    if (!arch_spec.IsValid())
+      return false;
+
+    result.push_back(arch_spec);
+    return true;
+  };
+
+  archs_sp->ForEach(extract_arch_specs);
+
+  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 (!result.empty() && !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 processes_sp = GetInterface().ListProcesses();
+
+  Status error;
+  if (!processes_sp)
+    return ScriptedInterface::ErrorWithMessage<uint32_t>(
+        LLVM_PRETTY_FUNCTION, "Failed to get scripted platform processes.",
+        error, LLDBLog::Platform);
+
+  // Because `StructuredData::Dictionary` uses a `std::map<ConstString,
+  // ObjectSP>` for storage, each item is sorted based on the key alphabetical
+  // order. Since `ListProcesses` provides process ids as the key element,
+  // process info comes ordered alphabetically, instead of numerically, so we
+  // need to sort the process ids before listing them.
+
+  StructuredData::ArraySP keys = processes_sp->GetKeys();
+
+  std::map<lldb::pid_t, StructuredData::ObjectSP> sorted_processes;
+  auto sort_keys = [&sorted_processes,
+                    &processes_sp](StructuredData::Object *item) -> bool {
+    if (!item)
+      return false;
+
+    llvm::StringRef key = item->GetStringValue();
+    size_t idx = 0;
+
+    // Make sure the provided id is actually an integer
+    if (!llvm::to_integer(key, idx))
+      return false;
+
+    sorted_processes[idx] = processes_sp->GetValueForKey(key);
+    return true;
+  };
+
+  size_t process_count = processes_sp->GetSize();
+
+  if (!keys->ForEach(sort_keys) || sorted_processes.size() != process_count)
+    // Might be worth showing the unsorted platform process list instead of
+    // return early.
+    return ScriptedInterface::ErrorWithMessage<bool>(
+        LLVM_PRETTY_FUNCTION, "Couldn't sort platform process list.", error);
+
+  auto parse_process_info =
+      [this, &proc_infos](
+          const std::pair<lldb::pid_t, StructuredData::ObjectSP> pair) -> bool {
+    const lldb::pid_t pid = pair.first;
+    const StructuredData::ObjectSP val = pair.second;
+    if (!val)
+      return false;
+
+    StructuredData::Dictionary *dict = val->GetAsDictionary();
+
+    if (!dict || !dict->IsValid())
+      return false;
+
+    auto proc_info_or_error = ParseProcessInfo(*dict, pid);
+
+    if (llvm::Error e = proc_info_or_error.takeError()) {
+      LLDB_LOG_ERROR(GetLog(LLDBLog::Platform), std::move(e), "{1} ERROR = {0}",
+                     LLVM_PRETTY_FUNCTION);
+      return false;
+    }
+
+    proc_infos.push_back(*proc_info_or_error);
+    return true;
+  };
+
+  llvm::for_each(sorted_processes, 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/ScriptedPlatformPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface/ScriptedPlatformPythonInterface.cpp
index 3586251bd4aac..a1f63c7c000ca 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface/ScriptedPlatformPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface/ScriptedPlatformPythonInterface.cpp
@@ -46,21 +46,29 @@ ScriptedPlatformPythonInterface::CreatePluginObject(
                                                      exe_ctx_ref_sp, sd_impl);
 }
 
+StructuredData::ArraySP
+ScriptedPlatformPythonInterface::GetSupportedArchitectures() {
+  Status error;
+  StructuredData::ArraySP arr =
+      Dispatch<StructuredData::ArraySP>("get_supported_architectures", error);
+
+  if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr,
+                                                    error))
+    return {};
+
+  return arr;
+}
+
 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
@@ -81,16 +89,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/ScriptedPlatformPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface/ScriptedPlatformPythonInterface.h
index 01ee40a5a197c..c7272770f25a4 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface/ScriptedPlatformPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface/ScriptedPlatformPythonInterface.h
@@ -31,15 +31,19 @@ class ScriptedPlatformPythonInterface : public ScriptedPlatformInterface,
 
   llvm::SmallVector<llvm::StringLiteral> GetAbstractMethods() const override {
     return llvm::SmallVector<llvm::StringLiteral>(
-        {"list_processes", "attach_to_process", "launch_process",
-         "kill_process"});
+        {"attach_to_process", "launch_process", "kill_process"});
   }
 
+  StructuredData::ArraySP GetSupportedArchitectures() override;
+
   StructuredData::DictionarySP ListProcesses() override;
 
   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 a78c76b5f94ff..e5d990806776d 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1527,6 +1527,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