diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -258,6 +258,72 @@ Py_RETURN_NONE; } +SWIGEXPORT void* +LLDBSwigPythonCreateScriptedProcess +( + const char *python_class_name, + const char *session_dictionary_name, + const lldb::TargetSP& target_sp, + lldb_private::StructuredDataImpl *args_impl, + std::string &error_string +) +{ + if (python_class_name == NULL || python_class_name[0] == '\0' || !session_dictionary_name) + Py_RETURN_NONE; + + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName(session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary(python_class_name, dict); + + if (!pfunc.IsAllocated()) { + error_string.append("could not find script class: "); + error_string.append(python_class_name); + return nullptr; + } + + // I do not want the SBTarget to be deallocated when going out of scope + // because python has ownership of it and will manage memory for this + // object by itself + PythonObject target_arg(PyRefType::Owned, SBTypeToSWIGWrapper(new lldb::SBTarget(target_sp))); + + if (!target_arg.IsAllocated()) + Py_RETURN_NONE; + + llvm::Expected 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()); + }); + Py_RETURN_NONE; + } + + PythonObject result = {}; + if (arg_info.get().max_positional_args == 2) { + if (args_impl != nullptr) { + error_string.assign("args passed, but __init__ does not take an args dictionary"); + Py_RETURN_NONE; + } + result = pfunc(target_arg, dict); + } else if (arg_info.get().max_positional_args >= 3) { + PythonObject args_arg(PyRefType::Owned, SBTypeToSWIGWrapper(new lldb::SBStructuredData(args_impl))); + result = pfunc(target_arg, args_arg, dict); + } else { + error_string.assign("wrong number of arguments in __init__, should be 2 or 3 (not including self)"); + Py_RETURN_NONE; + } + + if (result.IsAllocated()) + return result.release(); + Py_RETURN_NONE; +} + SWIGEXPORT void* LLDBSwigPythonCreateScriptedThreadPlan ( @@ -785,6 +851,40 @@ return ret_val; } +SWIGEXPORT void* +LLDBSWIGPython_CastPyObjectToSBData +( + PyObject* data +) +{ + lldb::SBData* sb_ptr = nullptr; + + int valid_cast = SWIG_ConvertPtr(data, (void**)&sb_ptr, SWIGTYPE_p_lldb__SBData, 0); + + if (valid_cast == -1) + return NULL; + + return sb_ptr; +} + + +SWIGEXPORT void* +LLDBSWIGPython_CastPyObjectToSBError +( + PyObject* data +) +{ + lldb::SBError* sb_ptr = nullptr; + + int valid_cast = SWIG_ConvertPtr(data, (void**)&sb_ptr, SWIGTYPE_p_lldb__SBError, 0); + + if (valid_cast == -1) + return NULL; + + return sb_ptr; +} + + SWIGEXPORT void* LLDBSWIGPython_CastPyObjectToSBValue ( diff --git a/lldb/include/lldb/API/SBData.h b/lldb/include/lldb/API/SBData.h --- a/lldb/include/lldb/API/SBData.h +++ b/lldb/include/lldb/API/SBData.h @@ -126,6 +126,8 @@ bool SetDataFromDoubleArray(double *array, size_t array_len); + lldb::DataExtractorSP GetOpaque() const; + protected: // Mimic shared pointer... lldb_private::DataExtractor *get() const; diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -528,6 +528,71 @@ lldb::ScriptLanguage GetLanguage() { return m_script_lang; } +#pragma mark ScriptedProcessInterface + + virtual StructuredData::GenericSP + ScriptedProcess_CreatePluginObject(const char *class_name, + lldb::TargetSP target_sp, + StructuredData::DictionarySP args_sp) { + return nullptr; + } + + virtual Status + ScriptedProcess_Launch(StructuredData::ObjectSP scripted_process_object_sp) { + return Status("ScriptedProcess did not launch"); + } + + virtual size_t ScriptedProcess_GetNumMemoryRegions( + StructuredData::ObjectSP scripted_process_object_sp) { + return LLDB_INVALID_ADDRESS; + } + + virtual lldb::MemoryRegionInfoSP ScriptedProcess_GetMemoryRegionAtIndex( + StructuredData::ObjectSP scripted_process_object_sp, size_t index) { + return nullptr; + } + + virtual size_t ScriptedProcess_GetNumThreads( + StructuredData::ObjectSP scripted_process_object_sp) { + return LLDB_INVALID_ADDRESS; + } + + virtual lldb::ThreadSP ScriptedProcess_GetThreadAtIndex( + StructuredData::ObjectSP scripted_process_object_sp, size_t index) { + return nullptr; + } + + virtual StructuredData::DictionarySP ScriptedProcess_GetRegisterForThread( + StructuredData::ObjectSP scripted_process_object_sp) { + return nullptr; + } + + virtual lldb::DataExtractorSP ScriptedProcess_ReadMemoryAtAddress( + StructuredData::ObjectSP scripted_process_object_sp, lldb::addr_t address, + Status &error) { + return nullptr; + } + + virtual StructuredData::DictionarySP ScriptedProcess_GetLoadedImages( + StructuredData::ObjectSP scripted_process_object_sp) { + return nullptr; + } + + virtual lldb::pid_t ScriptedProcess_GetProcessID( + StructuredData::ObjectSP scripted_process_object_sp) { + return LLDB_INVALID_PROCESS_ID; + } + + virtual bool ScriptedProcess_CanDebug( + StructuredData::ObjectSP scripted_process_object_sp) { + return true; + } + + virtual bool + ScriptedProcess_IsAlive(StructuredData::ObjectSP scripted_process_object_sp) { + return true; + } + protected: Debugger &m_debugger; lldb::ScriptLanguage m_script_lang; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -341,6 +341,7 @@ typedef std::weak_ptr ListenerWP; typedef std::shared_ptr MemoryHistorySP; typedef std::unique_ptr MemoryRegionInfoUP; +typedef std::shared_ptr MemoryRegionInfoSP; typedef std::shared_ptr ModuleSP; typedef std::weak_ptr ModuleWP; typedef std::shared_ptr ObjectFileSP; diff --git a/lldb/source/API/SBData.cpp b/lldb/source/API/SBData.cpp --- a/lldb/source/API/SBData.cpp +++ b/lldb/source/API/SBData.cpp @@ -47,6 +47,8 @@ m_opaque_sp = data_sp; } +lldb::DataExtractorSP SBData::GetOpaque() const { return m_opaque_sp; } + lldb_private::DataExtractor *SBData::get() const { return m_opaque_sp.get(); } lldb_private::DataExtractor *SBData::operator->() const { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -17,6 +17,7 @@ #include "PythonDataObjects.h" #include "PythonReadline.h" #include "ScriptInterpreterPythonImpl.h" +#include "lldb/API/SBError.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBValue.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" @@ -111,6 +112,11 @@ const char *session_dictionary_name, const lldb::DebuggerSP debugger_sp); +extern "C" void *LLDBSwigPythonCreateScriptedProcess( + const char *python_class_name, const char *session_dictionary_name, + const lldb::TargetSP &target_sp, StructuredDataImpl *args_impl, + std::string &error_string); + extern "C" void *LLDBSwigPythonCreateScriptedThreadPlan( const char *python_class_name, const char *session_dictionary_name, StructuredDataImpl *args_data, @@ -148,6 +154,10 @@ extern "C" int LLDBSwigPython_GetIndexOfChildWithName(void *implementor, const char *child_name); +extern "C" void *LLDBSWIGPython_CastPyObjectToSBData(void *data); + +extern "C" void *LLDBSWIGPython_CastPyObjectToSBError(void *data); + extern "C" void *LLDBSWIGPython_CastPyObjectToSBValue(void *data); extern lldb::ValueObjectSP @@ -2148,6 +2158,291 @@ return StructuredData::GenericSP(new StructuredPythonObject(ret_val)); } +#pragma mark ScriptedProcessInterface + +StructuredData::GenericSP +ScriptInterpreterPythonImpl::ScriptedProcess_CreatePluginObject( + const char *class_name, lldb::TargetSP target_sp, + StructuredData::DictionarySP args_sp) { + if (class_name == nullptr || class_name[0] == '\0') + 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(this, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + ret_val = LLDBSwigPythonCreateScriptedProcess( + class_name, m_dictionary_name.c_str(), target_sp, args_impl, + error_string); + } + + return StructuredData::GenericSP(new StructuredPythonObject(ret_val)); +} + +Status ScriptInterpreterPythonImpl::ScriptedProcess_Launch( + StructuredData::ObjectSP scripted_process_object_sp) { + Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); + + static char callee_name[] = "launch"; + + if (!scripted_process_object_sp) + return Status("Python object ill-formed."); + + StructuredData::Generic *generic = scripted_process_object_sp->GetAsGeneric(); + if (!generic) + return Status("Cannot convert Python object to StructuredData::Generic."); + PythonObject implementor(PyRefType::Borrowed, + (PyObject *)generic->GetValue()); + + if (!implementor.IsAllocated()) + return Status("Python implementor not allocated."); + + PythonObject pmeth(PyRefType::Owned, + PyObject_GetAttrString(implementor.get(), callee_name)); + + 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(); + + // right now we know this function exists and is callable.. + PythonObject py_return( + PyRefType::Owned, + PyObject_CallMethod(implementor.get(), callee_name, nullptr)); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) { + PyErr_Print(); + PyErr_Clear(); + } + + 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."); + + if (sb_error->Fail()) + return Status("error: %s", sb_error->GetCString()); + + return Status(); + } + + return Status("Returned object is null."); +} + +size_t ScriptInterpreterPythonImpl::ScriptInterpreterPythonImpl:: + ScriptedProcess_GetGenericInteger( + llvm::StringRef method_name, + StructuredData::ObjectSP scripted_process_object_sp) { + Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock); + + if (!scripted_process_object_sp) + return LLDB_INVALID_ADDRESS; + + StructuredData::Generic *generic = scripted_process_object_sp->GetAsGeneric(); + if (!generic) + return LLDB_INVALID_ADDRESS; + PythonObject implementor(PyRefType::Borrowed, + (PyObject *)generic->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(); + + // right now we know this function exists and is callable.. + PythonObject py_return(PyRefType::Owned, + PyObject_CallMethod(implementor.get(), + method_name.str().c_str(), + nullptr)); + + // if it fails, print the error but otherwise go on + 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; +} + +size_t ScriptInterpreterPythonImpl::ScriptedProcess_GetNumMemoryRegions( + StructuredData::ObjectSP scripted_process_object_sp) { + return ScriptedProcess_GetGenericInteger("get_num_memory_regions", + scripted_process_object_sp); +} + +lldb::MemoryRegionInfoSP +ScriptInterpreterPythonImpl::ScriptedProcess_GetMemoryRegionAtIndex( + StructuredData::ObjectSP scripted_process_object_sp, size_t index) { + // TODO: Implement + return nullptr; +} + +size_t ScriptInterpreterPythonImpl::ScriptedProcess_GetNumThreads( + StructuredData::ObjectSP scripted_process_object_sp) { + return ScriptedProcess_GetGenericInteger("get_num_threads", + scripted_process_object_sp); +} + +lldb::ThreadSP ScriptInterpreterPythonImpl::ScriptedProcess_GetThreadAtIndex( + StructuredData::ObjectSP scripted_process_object_sp, size_t index) { + // TODO: Implement + return nullptr; +} + +StructuredData::DictionarySP +ScriptInterpreterPythonImpl::ScriptedProcess_GetRegisterForThread( + StructuredData::ObjectSP scripted_process_object_sp) { + // TODO: Implement + return nullptr; +} + +lldb::DataExtractorSP +ScriptInterpreterPythonImpl::ScriptedProcess_ReadMemoryAtAddress( + StructuredData::ObjectSP scripted_process_object_sp, lldb::addr_t address, + Status &error) { + Locker py_lock(this, 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"; + static char *param_format = + const_cast(GetPythonValueFormatString(address)); + + if (!scripted_process_object_sp) + return error_with_message("Python object ill-formed."); + + StructuredData::Generic *generic = scripted_process_object_sp->GetAsGeneric(); + if (!generic) + return error_with_message("Python method not callable."); + + PythonObject implementor(PyRefType::Borrowed, + (PyObject *)generic->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(); + + // right now we know this function exists and is callable.. + PythonObject py_return(PyRefType::Owned, + PyObject_CallMethod(implementor.get(), callee_name, + param_format, address)); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) { + PyErr_Print(); + PyErr_Clear(); + } + + 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 sb_data->GetOpaque(); + ; + } + + return error_with_message("Returned object is null."); +} + +StructuredData::DictionarySP +ScriptInterpreterPythonImpl::ScriptedProcess_GetLoadedImages( + StructuredData::ObjectSP scripted_process_object_sp) { + // TODO: Implement + return nullptr; +} + +lldb::pid_t ScriptInterpreterPythonImpl::ScriptedProcess_GetProcessID( + StructuredData::ObjectSP scripted_process_object_sp) { + size_t pid = ScriptedProcess_GetGenericInteger("get_process_id", + scripted_process_object_sp); + + return (pid >= std::numeric_limits::max()) + ? LLDB_INVALID_PROCESS_ID + : pid; +} + +bool ScriptInterpreterPythonImpl::ScriptedProcess_CanDebug( + StructuredData::ObjectSP scripted_process_object_sp) { + return ScriptedProcess_GetGenericInteger("can_debug", + scripted_process_object_sp); +} + +bool ScriptInterpreterPythonImpl::ScriptedProcess_IsAlive( + StructuredData::ObjectSP scripted_process_object_sp) { + return ScriptedProcess_GetGenericInteger("is_alive", + scripted_process_object_sp); + ; +} + +#pragma mark ScriptedProcessInterfaceEnd + bool ScriptInterpreterPythonImpl::GenerateTypeScriptFunction( const char *oneliner, std::string &output, const void *name_token) { StringList input; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -74,6 +74,50 @@ StructuredData::GenericSP CreateScriptCommandObject(const char *class_name) override; + StructuredData::GenericSP ScriptedProcess_CreatePluginObject( + const char *class_name, lldb::TargetSP target_sp, + StructuredData::DictionarySP args_sp) override; + + Status ScriptedProcess_Launch( + StructuredData::ObjectSP scripted_process_object_sp) override; + + size_t ScriptedProcess_GetGenericInteger( + llvm::StringRef method_name, + StructuredData::ObjectSP scripted_process_object_sp); + + size_t ScriptedProcess_GetNumMemoryRegions( + StructuredData::ObjectSP scripted_process_object_sp) override; + + lldb::MemoryRegionInfoSP ScriptedProcess_GetMemoryRegionAtIndex( + StructuredData::ObjectSP scripted_process_object_sp, + size_t index) override; + + size_t ScriptedProcess_GetNumThreads( + StructuredData::ObjectSP scripted_process_object_sp) override; + + lldb::ThreadSP ScriptedProcess_GetThreadAtIndex( + StructuredData::ObjectSP scripted_process_object_sp, + size_t index) override; + + StructuredData::DictionarySP ScriptedProcess_GetRegisterForThread( + StructuredData::ObjectSP scripted_process_object_sp) override; + + lldb::DataExtractorSP ScriptedProcess_ReadMemoryAtAddress( + StructuredData::ObjectSP scripted_process_object_sp, lldb::addr_t address, + Status &error) override; + + StructuredData::DictionarySP ScriptedProcess_GetLoadedImages( + StructuredData::ObjectSP scripted_process_object_sp) override; + + lldb::pid_t ScriptedProcess_GetProcessID( + StructuredData::ObjectSP scripted_process_object_sp) override; + + bool ScriptedProcess_CanDebug( + StructuredData::ObjectSP scripted_process_object_sp) override; + + bool ScriptedProcess_IsAlive( + StructuredData::ObjectSP scripted_process_object_sp) override; + StructuredData::ObjectSP CreateScriptedThreadPlan(const char *class_name, StructuredDataImpl *args_data, diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -153,6 +153,14 @@ return 0; } +extern "C" void *LLDBSWIGPython_CastPyObjectToSBData(void *data) { + return nullptr; +} + +extern "C" void *LLDBSWIGPython_CastPyObjectToSBError(void *data) { + return nullptr; +} + extern "C" void *LLDBSWIGPython_CastPyObjectToSBValue(void *data) { return nullptr; }