Files
clang-p2996/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp
Med Ismail Bennani 312b43da05 [lldb/Plugins] Add ScriptedProcess Process Plugin
This patch introduces Scripted Processes to lldb.

The goal, here, is to be able to attach in the debugger to fake processes
that are backed by script files (in Python, Lua, Swift, etc ...) and
inspect them statically.

Scripted Processes can be used in cooperative multithreading environments
like the XNU Kernel or other real-time operating systems, but it can
also help us improve the debugger testing infrastructure by writting
synthetic tests that simulates hard-to-reproduce process/thread states.

Although ScriptedProcess is not feature-complete at the moment, it has
basic execution capabilities and will improve in the following patches.

rdar://65508855

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

Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2021-07-22 14:47:33 +02:00

295 lines
8.1 KiB
C++

//===-- ScriptedProcessPythonInterface.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 "lldb/Host/Config.h"
#include "lldb/lldb-enumerations.h"
#if LLDB_ENABLE_PYTHON
// LLDB Python header must be included first
#include "lldb-python.h"
#include "SWIGPythonBridge.h"
#include "ScriptInterpreterPythonImpl.h"
#include "ScriptedProcessPythonInterface.h"
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::python;
using Locker = ScriptInterpreterPythonImpl::Locker;
StructuredData::GenericSP ScriptedProcessPythonInterface::CreatePluginObject(
const llvm::StringRef class_name, lldb::TargetSP target_sp,
StructuredData::DictionarySP args_sp) {
if (class_name.empty())
return {};
std::string error_string;
StructuredDataImpl *args_impl = nullptr;
if (args_sp) {
args_impl = new StructuredDataImpl();
args_impl->SetObjectSP(args_sp);
}
void *ret_val;
{
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
Locker::FreeLock);
ret_val = LLDBSwigPythonCreateScriptedProcess(
class_name.str().c_str(), m_interpreter.GetDictionaryName(), target_sp,
args_impl, error_string);
}
m_object_instance_sp =
StructuredData::GenericSP(new StructuredPythonObject(ret_val));
return m_object_instance_sp;
}
Status ScriptedProcessPythonInterface::Launch() {
return GetStatusFromMethod("launch");
}
Status ScriptedProcessPythonInterface::Resume() {
return GetStatusFromMethod("resume");
}
bool ScriptedProcessPythonInterface::ShouldStop() {
return GetGenericInteger("shuold_stop");
}
Status ScriptedProcessPythonInterface::Stop() {
return GetStatusFromMethod("stop");
}
Status ScriptedProcessPythonInterface::GetStatusFromMethod(
llvm::StringRef method_name) {
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
Locker::FreeLock);
if (!m_object_instance_sp)
return Status("Python object ill-formed.");
if (!m_object_instance_sp)
return Status("Cannot convert Python object to StructuredData::Generic.");
PythonObject implementor(PyRefType::Borrowed,
(PyObject *)m_object_instance_sp->GetValue());
if (!implementor.IsAllocated())
return Status("Python implementor not allocated.");
PythonObject pmeth(
PyRefType::Owned,
PyObject_GetAttrString(implementor.get(), method_name.str().c_str()));
if (PyErr_Occurred())
PyErr_Clear();
if (!pmeth.IsAllocated())
return Status("Python method not allocated.");
if (PyCallable_Check(pmeth.get()) == 0) {
if (PyErr_Occurred())
PyErr_Clear();
return Status("Python method not callable.");
}
if (PyErr_Occurred())
PyErr_Clear();
PythonObject py_return(PyRefType::Owned,
PyObject_CallMethod(implementor.get(),
method_name.str().c_str(),
nullptr));
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
return Status("Python method could not be called.");
}
if (PyObject *py_ret_ptr = py_return.get()) {
lldb::SBError *sb_error =
(lldb::SBError *)LLDBSWIGPython_CastPyObjectToSBError(py_ret_ptr);
if (!sb_error)
return Status("Couldn't cast lldb::SBError to lldb::Status.");
Status status = m_interpreter.GetStatusFromSBError(*sb_error);
if (status.Fail())
return Status("error: %s", status.AsCString());
return status;
}
return Status("Returned object is null.");
}
size_t
ScriptedProcessPythonInterface::GetGenericInteger(llvm::StringRef method_name) {
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
Locker::FreeLock);
if (!m_object_instance_sp)
return LLDB_INVALID_ADDRESS;
if (!m_object_instance_sp)
return LLDB_INVALID_ADDRESS;
PythonObject implementor(PyRefType::Borrowed,
(PyObject *)m_object_instance_sp->GetValue());
if (!implementor.IsAllocated())
return LLDB_INVALID_ADDRESS;
PythonObject pmeth(
PyRefType::Owned,
PyObject_GetAttrString(implementor.get(), method_name.str().c_str()));
if (PyErr_Occurred())
PyErr_Clear();
if (!pmeth.IsAllocated())
return LLDB_INVALID_ADDRESS;
if (PyCallable_Check(pmeth.get()) == 0) {
if (PyErr_Occurred())
PyErr_Clear();
return LLDB_INVALID_ADDRESS;
}
if (PyErr_Occurred())
PyErr_Clear();
PythonObject py_return(PyRefType::Owned,
PyObject_CallMethod(implementor.get(),
method_name.str().c_str(),
nullptr));
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
if (py_return.get()) {
auto size = py_return.AsUnsignedLongLong();
return (size) ? *size : LLDB_INVALID_ADDRESS;
}
return LLDB_INVALID_ADDRESS;
}
lldb::MemoryRegionInfoSP
ScriptedProcessPythonInterface::GetMemoryRegionContainingAddress(
lldb::addr_t address) {
// TODO: Implement
return nullptr;
}
StructuredData::DictionarySP
ScriptedProcessPythonInterface::GetThreadWithID(lldb::tid_t tid) {
// TODO: Implement
return nullptr;
}
StructuredData::DictionarySP
ScriptedProcessPythonInterface::GetRegistersForThread(lldb::tid_t tid) {
// TODO: Implement
return nullptr;
}
lldb::DataExtractorSP ScriptedProcessPythonInterface::ReadMemoryAtAddress(
lldb::addr_t address, size_t size, Status &error) {
Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
Locker::FreeLock);
auto error_with_message = [&error](llvm::StringRef message) {
error.SetErrorString(message);
return nullptr;
};
static char callee_name[] = "read_memory_at_address";
std::string param_format = GetPythonValueFormatString(address);
param_format += GetPythonValueFormatString(size);
if (!m_object_instance_sp)
return error_with_message("Python object ill-formed.");
if (!m_object_instance_sp)
return error_with_message("Python method not callable.");
PythonObject implementor(PyRefType::Borrowed,
(PyObject *)m_object_instance_sp->GetValue());
if (!implementor.IsAllocated())
return error_with_message("Python implementor not allocated.");
PythonObject pmeth(PyRefType::Owned,
PyObject_GetAttrString(implementor.get(), callee_name));
if (PyErr_Occurred())
PyErr_Clear();
if (!pmeth.IsAllocated())
return error_with_message("Python method not allocated.");
if (PyCallable_Check(pmeth.get()) == 0) {
if (PyErr_Occurred())
PyErr_Clear();
return error_with_message("Python method not callable.");
}
if (PyErr_Occurred())
PyErr_Clear();
PythonObject py_return(PyRefType::Owned,
PyObject_CallMethod(implementor.get(), callee_name,
param_format.c_str(), address,
size));
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
return error_with_message("Python method could not be called.");
}
if (PyObject *py_ret_ptr = py_return.get()) {
lldb::SBData *sb_data =
(lldb::SBData *)LLDBSWIGPython_CastPyObjectToSBData(py_ret_ptr);
if (!sb_data)
return error_with_message(
"Couldn't cast lldb::SBData to lldb::DataExtractor.");
return m_interpreter.GetDataExtractorFromSBData(*sb_data);
}
return error_with_message("Returned object is null.");
}
StructuredData::DictionarySP ScriptedProcessPythonInterface::GetLoadedImages() {
// TODO: Implement
return nullptr;
}
lldb::pid_t ScriptedProcessPythonInterface::GetProcessID() {
size_t pid = GetGenericInteger("get_process_id");
return (pid >= std::numeric_limits<lldb::pid_t>::max())
? LLDB_INVALID_PROCESS_ID
: pid;
}
bool ScriptedProcessPythonInterface::IsAlive() {
return GetGenericInteger("is_alive");
}
#endif