Skip to content

Commit

Permalink
[lldb] Add ScriptedPlatform python implementation
Browse files Browse the repository at this point in the history
This patch introduces both the Scripted Platform python base
implementation and an example for it.

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

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

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

Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
  • Loading branch information
medismailben committed Jan 12, 2023
1 parent 2d53527 commit bb4ccc6
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 82 deletions.
7 changes: 7 additions & 0 deletions lldb/bindings/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar
FILES
"${LLDB_SOURCE_DIR}/examples/python/scripted_process/scripted_process.py")

create_python_package(
${swig_target}
${lldb_python_target_dir}
"plugins"
FILES
"${LLDB_SOURCE_DIR}/examples/python/scripted_process/scripted_platform.py")

if(APPLE)
create_python_package(
${swig_target}
Expand Down
48 changes: 3 additions & 45 deletions lldb/bindings/python/python-wrapper.swig
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,9 @@ PythonObject lldb_private::LLDBSwigPythonCreateCommandObject(
return pfunc(ToSWIGWrapper(std::move(debugger_sp)), dict);
}

PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(
PythonObject lldb_private::LLDBSwigPythonCreateScriptedObject(
const char *python_class_name, const char *session_dictionary_name,
const lldb::TargetSP &target_sp,
lldb::ExecutionContextRefSP exe_ctx_sp,
const lldb_private::StructuredDataImpl &args_impl,
std::string &error_string) {
if (python_class_name == NULL || python_class_name[0] == '\0' ||
Expand All @@ -251,8 +251,6 @@ PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(
return PythonObject();
}

PythonObject target_arg = ToSWIGWrapper(target_sp);

llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo();
if (!arg_info) {
llvm::handleAllErrors(
Expand All @@ -266,54 +264,14 @@ PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(

PythonObject result = {};
if (arg_info.get().max_positional_args == 2) {
result = pfunc(target_arg, ToSWIGWrapper(args_impl));
result = pfunc(ToSWIGWrapper(exe_ctx_sp), ToSWIGWrapper(args_impl));
} else {
error_string.assign("wrong number of arguments in __init__, should be 2 "
"(not including self)");
}
return result;
}

PythonObject lldb_private::LLDBSwigPythonCreateScriptedThread(
const char *python_class_name, const char *session_dictionary_name,
const lldb::ProcessSP &process_sp, const StructuredDataImpl &args_impl,
std::string &error_string) {
if (python_class_name == NULL || python_class_name[0] == '\0' ||
!session_dictionary_name)
return PythonObject();

PyErr_Cleaner py_err_cleaner(true);

auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(
session_dictionary_name);
auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(
python_class_name, dict);

if (!pfunc.IsAllocated()) {
error_string.append("could not find script class: ");
error_string.append(python_class_name);
return PythonObject();
}

llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo();
if (!arg_info) {
llvm::handleAllErrors(
arg_info.takeError(),
[&](PythonException &E) { error_string.append(E.ReadBacktrace()); },
[&](const llvm::ErrorInfoBase &E) {
error_string.append(E.message());
});
return PythonObject();
}

if (arg_info.get().max_positional_args == 2)
return pfunc(ToSWIGWrapper(process_sp), ToSWIGWrapper(args_impl));

error_string.assign("wrong number of arguments in __init__, should be 2 "
"(not including self)");
return PythonObject();
}

PythonObject lldb_private::LLDBSwigPythonCreateScriptedThreadPlan(
const char *python_class_name, const char *session_dictionary_name,
const lldb_private::StructuredDataImpl &args_impl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ def load_images(self, images):
self.addr_mask,
self.target)

def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
super().__init__(target, args)
def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
super().__init__(exe_ctx, args)

if not self.target or not self.target.IsValid():
# Return error
Expand Down
96 changes: 96 additions & 0 deletions lldb/examples/python/scripted_process/scripted_platform.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from abc import ABCMeta, abstractmethod

import lldb

class ScriptedPlatform(metaclass=ABCMeta):

"""
The base class for a scripted platform.
Most of the base class methods are `@abstractmethod` that need to be
overwritten by the inheriting class.
DISCLAIMER: THIS INTERFACE IS STILL UNDER DEVELOPMENT AND NOT STABLE.
THE METHODS EXPOSED MIGHT CHANGE IN THE FUTURE.
"""

processes = None

@abstractmethod
def __init__(self, exe_ctx, args):
""" Construct a scripted platform.
Args:
exe_ctx (lldb.SBExecutionContext): The execution context for the scripted platform
args (lldb.SBStructuredData): A Dictionary holding arbitrary
key/value pairs used by the scripted platform.
"""
processes = []

@abstractmethod
def list_processes(self):
""" Get a list of processes that are running or that can be attached to on the platform.
processes = {
420: {
name: a.out,
arch: aarch64,
pid: 420,
parent_pid: 42 (optional),
uid: 0 (optional),
gid: 0 (optional),
},
}
Returns:
Dict: The processes represented as a dictionary, with at least the
process ID, name, architecture. Optionally, the user can also
provide the parent process ID and the user and group IDs.
The dictionary can be empty.
"""
pass

def get_process_info(self, pid):
""" Get the dictionary describing the process.
Returns:
Dict: The dictionary of process info that matched process ID.
None if the process doesn't exists
"""
pass

@abstractmethod
def attach_to_process(self, attach_info):
""" Attach to a process.
Args:
attach_info (lldb.SBAttachInfo): The information related to attach to a process.
Returns:
lldb.SBError: A status object notifying if the attach succeeded.
"""
pass

@abstractmethod
def launch_process(self, launch_info):
""" Launch a process.
Args:
launch_info (lldb.SBLaunchInfo): The information related to the process launch.
Returns:
lldb.SBError: A status object notifying if the launch succeeded.
"""
pass

@abstractmethod
def kill_process(self, pid):
""" Kill a process.
Args:
pid (int): Process ID for the process to be killed.
Returns:
lldb.SBError: A status object notifying if the shutdown succeeded.
"""
pass
7 changes: 5 additions & 2 deletions lldb/examples/python/scripted_process/scripted_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@ class ScriptedProcess(metaclass=ABCMeta):
metadata = None

@abstractmethod
def __init__(self, target, args):
def __init__(self, exe_ctx, args):
""" Construct a scripted process.
Args:
target (lldb.SBTarget): The target launching the scripted process.
exe_ctx (lldb.SBExecutionContext): The execution context for the scripted process.
args (lldb.SBStructuredData): A Dictionary holding arbitrary
key/value pairs used by the scripted process.
"""
target = None
self.target = None
self.args = None
self.arch = None
if isinstance(exe_ctx, lldb.SBExecutionContext):
target = exe_ctx.target
if isinstance(target, lldb.SBTarget) and target.IsValid():
self.target = target
triple = self.target.triple
Expand Down
10 changes: 3 additions & 7 deletions lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,10 @@ void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data);
// Although these are scripting-language specific, their definition depends on
// the public API.

python::PythonObject LLDBSwigPythonCreateScriptedProcess(
python::PythonObject LLDBSwigPythonCreateScriptedObject(
const char *python_class_name, const char *session_dictionary_name,
const lldb::TargetSP &target_sp, const StructuredDataImpl &args_impl,
std::string &error_string);

python::PythonObject LLDBSwigPythonCreateScriptedThread(
const char *python_class_name, const char *session_dictionary_name,
const lldb::ProcessSP &process_sp, const StructuredDataImpl &args_impl,
lldb::ExecutionContextRefSP exe_ctx_sp,
const lldb_private::StructuredDataImpl &args_impl,
std::string &error_string);

llvm::Expected<bool> LLDBSwigPythonBreakpointCallbackFunction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,18 @@ StructuredData::GenericSP ScriptedProcessPythonInterface::CreatePluginObject(
if (class_name.empty())
return {};

TargetSP target_sp = exe_ctx.GetTargetSP();
StructuredDataImpl args_impl(args_sp);
std::string error_string;

Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
Locker::FreeLock);

PythonObject ret_val = LLDBSwigPythonCreateScriptedProcess(
class_name.str().c_str(), m_interpreter.GetDictionaryName(), target_sp,
args_impl, error_string);
lldb::ExecutionContextRefSP exe_ctx_ref_sp =
std::make_shared<ExecutionContextRef>(exe_ctx);

PythonObject ret_val = LLDBSwigPythonCreateScriptedObject(
class_name.str().c_str(), m_interpreter.GetDictionaryName(),
exe_ctx_ref_sp, args_impl, error_string);

m_object_instance_sp =
StructuredData::GenericSP(new StructuredPythonObject(std::move(ret_val)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ StructuredData::GenericSP ScriptedThreadPythonInterface::CreatePluginObject(
if (class_name.empty() && !script_obj)
return {};

ProcessSP process_sp = exe_ctx.GetProcessSP();
StructuredDataImpl args_impl(args_sp);
std::string error_string;

Expand All @@ -44,11 +43,13 @@ StructuredData::GenericSP ScriptedThreadPythonInterface::CreatePluginObject(

PythonObject ret_val;

if (!script_obj)
ret_val = LLDBSwigPythonCreateScriptedThread(
class_name.str().c_str(), m_interpreter.GetDictionaryName(), process_sp,
args_impl, error_string);
else
if (!script_obj) {
lldb::ExecutionContextRefSP exe_ctx_ref_sp =
std::make_shared<ExecutionContextRef>(exe_ctx);
ret_val = LLDBSwigPythonCreateScriptedObject(
class_name.str().c_str(), m_interpreter.GetDictionaryName(),
exe_ctx_ref_sp, args_impl, error_string);
} else
ret_val = PythonObject(PyRefType::Borrowed,
static_cast<PyObject *>(script_obj->GetValue()));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import os

import lldb
from lldb.plugins.scripted_platform import ScriptedPlatform

class MyScriptedPlatform(ScriptedPlatform):

def __init__(self, exe_ctx, args):
self.processes = {}

proc = {}
proc['name'] = 'a.out'
proc['arch'] = 'arm64-apple-macosx'
proc['pid'] = 420
proc['parent'] = 42
proc['uid'] = 501
proc['gid'] = 20
self.processes[420] = proc

def list_processes(self):
return self.processes

def get_process_info(self, pid):
return self.processes[pid]

def launch_process(self, launch_info):
return lldb.SBError()

def kill_process(self, pid):
return lldb.SBError()

def __lldb_init_module(debugger, dict):
if not 'SKIP_SCRIPTED_PLATFORM_SELECT' in os.environ:
debugger.HandleCommand(
"platform select scripted-platform -C %s.%s" % (__name__, MyScriptedPlatform.__name__))
else:
print("Name of the class that will manage the scripted platform: '%s.%s'"
% (__name__, MyScriptedPlatform.__name__))
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from lldb.plugins.scripted_process import ScriptedThread

class DummyScriptedProcess(ScriptedProcess):
def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
super().__init__(target, args)
def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
super().__init__(exe_ctx, args)
self.threads[0] = DummyScriptedThread(self, None)

def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from lldb.plugins.scripted_process import ScriptedThread

class InvalidScriptedProcess(ScriptedProcess):
def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
super().__init__(target, args)
def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
super().__init__(exe_ctx, args)
self.threads[0] = InvalidScriptedThread(self, None)

def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ def get_module_with_name(self, target, name):
return module
return None

def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
super().__init__(target, args)
def __init__(self, exe_ctx: lldb.SBExecutionContext, args : lldb.SBStructuredData):
super().__init__(exe_ctx, args)

self.corefile_target = None
self.corefile_process = None
Expand All @@ -25,7 +25,7 @@ def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
idx = self.backing_target_idx.GetIntegerValue(42)
if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeString:
idx = int(self.backing_target_idx.GetStringValue(100))
self.corefile_target = target.GetDebugger().GetTargetAtIndex(idx)
self.corefile_target = self.target.GetDebugger().GetTargetAtIndex(idx)
self.corefile_process = self.corefile_target.GetProcess()
for corefile_thread in self.corefile_process:
structured_data = lldb.SBStructuredData()
Expand Down
11 changes: 2 additions & 9 deletions lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,16 +200,9 @@ lldb_private::LLDBSWIGPythonCreateOSPlugin(const char *python_class_name,
return python::PythonObject();
}

python::PythonObject lldb_private::LLDBSwigPythonCreateScriptedProcess(
python::PythonObject lldb_private::LLDBSwigPythonCreateScriptedObject(
const char *python_class_name, const char *session_dictionary_name,
const lldb::TargetSP &target_sp, const StructuredDataImpl &args_impl,
std::string &error_string) {
return python::PythonObject();
}

python::PythonObject lldb_private::LLDBSwigPythonCreateScriptedThread(
const char *python_class_name, const char *session_dictionary_name,
const lldb::ProcessSP &process_sp, const StructuredDataImpl &args_impl,
lldb::ExecutionContextRefSP exe_ctx_sp, const StructuredDataImpl &args_impl,
std::string &error_string) {
return python::PythonObject();
}
Expand Down

0 comments on commit bb4ccc6

Please sign in to comment.