diff options
Diffstat (limited to 'lldb/source/Plugins/ScriptInterpreter')
15 files changed, 777 insertions, 129 deletions
diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp index f14e2732f6eb..e99b7b88379a 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp @@ -30,6 +30,9 @@ extern "C" llvm::Expected<bool> LLDBSwigLuaBreakpointCallbackFunction( lua_State *L, lldb::StackFrameSP stop_frame_sp, lldb::BreakpointLocationSP bp_loc_sp, StructuredDataImpl *extra_args_impl); +extern "C" llvm::Expected<bool> LLDBSwigLuaWatchpointCallbackFunction( + lua_State *L, lldb::StackFrameSP stop_frame_sp, lldb::WatchpointSP wp_sp); + #if _MSC_VER #pragma warning (pop) #endif @@ -113,6 +116,32 @@ Lua::CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp, bp_loc_sp, extra_args_impl); } +llvm::Error Lua::RegisterWatchpointCallback(void *baton, const char *body) { + lua_pushlightuserdata(m_lua_state, baton); + const char *fmt_str = "return function(frame, wp, ...) {0} end"; + std::string func_str = llvm::formatv(fmt_str, body).str(); + if (luaL_dostring(m_lua_state, func_str.c_str()) != LUA_OK) { + llvm::Error e = llvm::make_error<llvm::StringError>( + llvm::formatv("{0}", lua_tostring(m_lua_state, -1)), + llvm::inconvertibleErrorCode()); + // Pop error message from the stack. + lua_pop(m_lua_state, 2); + return e; + } + lua_settable(m_lua_state, LUA_REGISTRYINDEX); + return llvm::Error::success(); +} + +llvm::Expected<bool> +Lua::CallWatchpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp, + lldb::WatchpointSP wp_sp) { + + lua_pushlightuserdata(m_lua_state, baton); + lua_gettable(m_lua_state, LUA_REGISTRYINDEX); + return LLDBSwigLuaWatchpointCallbackFunction(m_lua_state, stop_frame_sp, + wp_sp); +} + llvm::Error Lua::CheckSyntax(llvm::StringRef buffer) { int error = luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer"); diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h index 873440f0aab3..5daedf835b9b 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h @@ -37,6 +37,10 @@ public: CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp, lldb::BreakpointLocationSP bp_loc_sp, StructuredData::ObjectSP extra_args_sp); + llvm::Error RegisterWatchpointCallback(void *baton, const char *body); + llvm::Expected<bool> CallWatchpointCallback(void *baton, + lldb::StackFrameSP stop_frame_sp, + lldb::WatchpointSP wp_sp); llvm::Error LoadModule(llvm::StringRef filename); llvm::Error CheckSyntax(llvm::StringRef buffer); llvm::Error ChangeIO(FILE *out, FILE *err); diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp index 920e334f51aa..ef46401c8b46 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp @@ -58,7 +58,13 @@ public: const char *instructions = nullptr; switch (m_active_io_handler) { case eIOHandlerNone: + break; case eIOHandlerWatchpoint: + instructions = "Enter your Lua command(s). Type 'quit' to end.\n" + "The commands are compiled as the body of the following " + "Lua function\n" + "function (frame, wp) end\n"; + SetPrompt(llvm::StringRef("..> ")); break; case eIOHandlerBreakpoint: instructions = "Enter your Lua command(s). Type 'quit' to end.\n" @@ -78,7 +84,8 @@ public: StringList &lines) override { size_t last = lines.GetSize() - 1; if (IsQuitCommand(lines.GetStringAtIndex(last))) { - if (m_active_io_handler == eIOHandlerBreakpoint) + if (m_active_io_handler == eIOHandlerBreakpoint || + m_active_io_handler == eIOHandlerWatchpoint) lines.DeleteStringAtIndex(last); return true; } @@ -90,17 +97,19 @@ public: // Lua always errors out to incomplete code with '<eof>' return error_str.find("<eof>") == std::string::npos; } - // The breakpoint handler only exits with a explicit 'quit' - return m_active_io_handler != eIOHandlerBreakpoint; + // The breakpoint and watchpoint handler only exits with a explicit 'quit' + return m_active_io_handler != eIOHandlerBreakpoint && + m_active_io_handler != eIOHandlerWatchpoint; } void IOHandlerInputComplete(IOHandler &io_handler, std::string &data) override { switch (m_active_io_handler) { case eIOHandlerBreakpoint: { - auto *bp_options_vec = static_cast<std::vector<BreakpointOptions *> *>( - io_handler.GetUserData()); - for (auto *bp_options : *bp_options_vec) { + auto *bp_options_vec = + static_cast<std::vector<std::reference_wrapper<BreakpointOptions>> *>( + io_handler.GetUserData()); + for (BreakpointOptions &bp_options : *bp_options_vec) { Status error = m_script_interpreter.SetBreakpointCommandCallback( bp_options, data.c_str()); if (error.Fail()) @@ -108,9 +117,13 @@ public: } io_handler.SetIsDone(true); } break; - case eIOHandlerWatchpoint: + case eIOHandlerWatchpoint: { + auto *wp_options = + static_cast<WatchpointOptions *>(io_handler.GetUserData()); + m_script_interpreter.SetWatchpointCommandCallback(wp_options, + data.c_str()); io_handler.SetIsDone(true); - break; + } break; case eIOHandlerNone: if (IsQuitCommand(data)) { io_handler.SetIsDone(true); @@ -133,7 +146,7 @@ ScriptInterpreterLua::ScriptInterpreterLua(Debugger &debugger) : ScriptInterpreter(debugger, eScriptLanguageLua), m_lua(std::make_unique<Lua>()) {} -ScriptInterpreterLua::~ScriptInterpreterLua() {} +ScriptInterpreterLua::~ScriptInterpreterLua() = default; bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command, CommandReturnObject *result, @@ -194,8 +207,9 @@ void ScriptInterpreterLua::ExecuteInterpreterLoop() { } bool ScriptInterpreterLua::LoadScriptingModule( - const char *filename, bool init_session, lldb_private::Status &error, - StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) { + const char *filename, const LoadScriptOptions &options, + lldb_private::Status &error, StructuredData::ObjectSP *module_sp, + FileSpec extra_search_dir) { FileSystem::Instance().Collect(filename); if (llvm::Error e = m_lua->LoadModule(filename)) { @@ -275,8 +289,35 @@ bool ScriptInterpreterLua::BreakpointCallbackFunction( return *BoolOrErr; } +bool ScriptInterpreterLua::WatchpointCallbackFunction( + void *baton, StoppointCallbackContext *context, user_id_t watch_id) { + assert(context); + + ExecutionContext exe_ctx(context->exe_ctx_ref); + Target *target = exe_ctx.GetTargetPtr(); + if (target == nullptr) + return true; + + StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP()); + WatchpointSP wp_sp = target->GetWatchpointList().FindByID(watch_id); + + Debugger &debugger = target->GetDebugger(); + ScriptInterpreterLua *lua_interpreter = static_cast<ScriptInterpreterLua *>( + debugger.GetScriptInterpreter(true, eScriptLanguageLua)); + Lua &lua = lua_interpreter->GetLua(); + + llvm::Expected<bool> BoolOrErr = + lua.CallWatchpointCallback(baton, stop_frame_sp, wp_sp); + if (llvm::Error E = BoolOrErr.takeError()) { + debugger.GetErrorStream() << toString(std::move(E)); + return true; + } + + return *BoolOrErr; +} + void ScriptInterpreterLua::CollectDataForBreakpointCommandCallback( - std::vector<BreakpointOptions *> &bp_options_vec, + std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, CommandReturnObject &result) { IOHandlerSP io_handler_sp( new IOHandlerLuaInterpreter(m_debugger, *this, eIOHandlerBreakpoint)); @@ -284,8 +325,16 @@ void ScriptInterpreterLua::CollectDataForBreakpointCommandCallback( m_debugger.RunIOHandlerAsync(io_handler_sp); } +void ScriptInterpreterLua::CollectDataForWatchpointCommandCallback( + WatchpointOptions *wp_options, CommandReturnObject &result) { + IOHandlerSP io_handler_sp( + new IOHandlerLuaInterpreter(m_debugger, *this, eIOHandlerWatchpoint)); + io_handler_sp->SetUserData(wp_options); + m_debugger.RunIOHandlerAsync(io_handler_sp); +} + Status ScriptInterpreterLua::SetBreakpointCommandCallbackFunction( - BreakpointOptions *bp_options, const char *function_name, + BreakpointOptions &bp_options, const char *function_name, StructuredData::ObjectSP extra_args_sp) { const char *fmt_str = "return {0}(frame, bp_loc, ...)"; std::string oneliner = llvm::formatv(fmt_str, function_name).str(); @@ -294,12 +343,12 @@ Status ScriptInterpreterLua::SetBreakpointCommandCallbackFunction( } Status ScriptInterpreterLua::SetBreakpointCommandCallback( - BreakpointOptions *bp_options, const char *command_body_text) { + BreakpointOptions &bp_options, const char *command_body_text) { return RegisterBreakpointCallback(bp_options, command_body_text, {}); } Status ScriptInterpreterLua::RegisterBreakpointCallback( - BreakpointOptions *bp_options, const char *command_body_text, + BreakpointOptions &bp_options, const char *command_body_text, StructuredData::ObjectSP extra_args_sp) { Status error; auto data_up = std::make_unique<CommandDataLua>(extra_args_sp); @@ -308,7 +357,27 @@ Status ScriptInterpreterLua::RegisterBreakpointCallback( return error; auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up)); - bp_options->SetCallback(ScriptInterpreterLua::BreakpointCallbackFunction, + bp_options.SetCallback(ScriptInterpreterLua::BreakpointCallbackFunction, + baton_sp); + return error; +} + +void ScriptInterpreterLua::SetWatchpointCommandCallback( + WatchpointOptions *wp_options, const char *command_body_text) { + RegisterWatchpointCallback(wp_options, command_body_text, {}); +} + +Status ScriptInterpreterLua::RegisterWatchpointCallback( + WatchpointOptions *wp_options, const char *command_body_text, + StructuredData::ObjectSP extra_args_sp) { + Status error; + auto data_up = std::make_unique<WatchpointOptions::CommandData>(); + error = m_lua->RegisterWatchpointCallback(data_up.get(), command_body_text); + if (error.Fail()) + return error; + auto baton_sp = + std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up)); + wp_options->SetCallback(ScriptInterpreterLua::WatchpointCallbackFunction, baton_sp); return error; } diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h index 1130ceee3c20..808000b833ec 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h @@ -9,6 +9,9 @@ #ifndef liblldb_ScriptInterpreterLua_h_ #define liblldb_ScriptInterpreterLua_h_ +#include <vector> + +#include "lldb/Breakpoint/WatchpointOptions.h" #include "lldb/Core/StructuredDataImpl.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Utility/Status.h" @@ -40,7 +43,8 @@ public: void ExecuteInterpreterLoop() override; - bool LoadScriptingModule(const char *filename, bool init_session, + bool LoadScriptingModule(const char *filename, + const LoadScriptOptions &options, lldb_private::Status &error, StructuredData::ObjectSP *module_sp = nullptr, FileSpec extra_search_dir = {}) override; @@ -61,6 +65,10 @@ public: lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + static bool WatchpointCallbackFunction(void *baton, + StoppointCallbackContext *context, + lldb::user_id_t watch_id); + // PluginInterface protocol lldb_private::ConstString GetPluginName() override; @@ -72,21 +80,32 @@ public: llvm::Error LeaveSession(); void CollectDataForBreakpointCommandCallback( - std::vector<BreakpointOptions *> &bp_options_vec, + std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, CommandReturnObject &result) override; - Status SetBreakpointCommandCallback(BreakpointOptions *bp_options, + void + CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options, + CommandReturnObject &result) override; + + Status SetBreakpointCommandCallback(BreakpointOptions &bp_options, const char *command_body_text) override; + void SetWatchpointCommandCallback(WatchpointOptions *wp_options, + const char *command_body_text) override; + Status SetBreakpointCommandCallbackFunction( - BreakpointOptions *bp_options, const char *function_name, + BreakpointOptions &bp_options, const char *function_name, StructuredData::ObjectSP extra_args_sp) override; private: std::unique_ptr<Lua> m_lua; bool m_session_is_active = false; - Status RegisterBreakpointCallback(BreakpointOptions *bp_options, + Status RegisterBreakpointCallback(BreakpointOptions &bp_options, + const char *command_body_text, + StructuredData::ObjectSP extra_args_sp); + + Status RegisterWatchpointCallback(WatchpointOptions *wp_options, const char *command_body_text, StructuredData::ObjectSP extra_args_sp); }; diff --git a/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp b/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp index d9c32cc132d4..f8a385240bd9 100644 --- a/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/None/ScriptInterpreterNone.cpp @@ -25,7 +25,7 @@ LLDB_PLUGIN_DEFINE(ScriptInterpreterNone) ScriptInterpreterNone::ScriptInterpreterNone(Debugger &debugger) : ScriptInterpreter(debugger, eScriptLanguageNone) {} -ScriptInterpreterNone::~ScriptInterpreterNone() {} +ScriptInterpreterNone::~ScriptInterpreterNone() = default; bool ScriptInterpreterNone::ExecuteOneLine(llvm::StringRef command, CommandReturnObject *, diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index 7c49502f1b57..f51d9b3a796c 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -24,7 +24,7 @@ #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Errno.h" -#include <stdio.h> +#include <cstdio> using namespace lldb_private; using namespace lldb; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h index 22f6c67eb7a5..4577253227cd 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h @@ -229,7 +229,7 @@ struct PythonFormat< class PythonObject { public: - PythonObject() : m_py_obj(nullptr) {} + PythonObject() = default; PythonObject(PyRefType type, PyObject *py_obj) { m_py_obj = py_obj; @@ -378,7 +378,7 @@ public: } protected: - PyObject *m_py_obj; + PyObject *m_py_obj = nullptr; }; @@ -421,7 +421,7 @@ public: Py_DECREF(py_obj); } - TypedPythonObject() {} + TypedPythonObject() = default; }; class PythonBytes : public TypedPythonObject<PythonBytes> { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonReadline.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonReadline.cpp index 5f6429f5cd0e..95a3365ed983 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonReadline.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonReadline.cpp @@ -2,7 +2,7 @@ #ifdef LLDB_USE_LIBEDIT_READLINE_COMPAT_MODULE -#include <stdio.h> +#include <cstdio> #include <editline/readline.h> diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.cpp new file mode 100644 index 000000000000..7c7c5d73680a --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.cpp @@ -0,0 +1,48 @@ +//===-- SWIGPythonBridge.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" + +using namespace lldb; + +namespace lldb_private { + +template <typename T> const char *GetPythonValueFormatString(T t); +template <> const char *GetPythonValueFormatString(char *) { return "s"; } +template <> const char *GetPythonValueFormatString(char) { return "b"; } +template <> const char *GetPythonValueFormatString(unsigned char) { + return "B"; +} +template <> const char *GetPythonValueFormatString(short) { return "h"; } +template <> const char *GetPythonValueFormatString(unsigned short) { + return "H"; +} +template <> const char *GetPythonValueFormatString(int) { return "i"; } +template <> const char *GetPythonValueFormatString(unsigned int) { return "I"; } +template <> const char *GetPythonValueFormatString(long) { return "l"; } +template <> const char *GetPythonValueFormatString(unsigned long) { + return "k"; +} +template <> const char *GetPythonValueFormatString(long long) { return "L"; } +template <> const char *GetPythonValueFormatString(unsigned long long) { + return "K"; +} +template <> const char *GetPythonValueFormatString(float) { return "f"; } +template <> const char *GetPythonValueFormatString(double) { return "d"; } + +} // namespace lldb_private + +#endif // LLDB_ENABLE_PYTHON diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h new file mode 100644 index 000000000000..1ef792bcf303 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -0,0 +1,56 @@ +//===-- ScriptInterpreterPython.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_PLUGINS_SCRIPTINTERPRETER_PYTHON_SWIGPYTHONBRIDGE_H +#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SWIGPYTHONBRIDGE_H + +#include <string> + +#include "lldb/Host/Config.h" + +#if LLDB_ENABLE_PYTHON + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { + +// GetPythonValueFormatString provides a system independent type safe way to +// convert a variable's type into a python value format. Python value formats +// are defined in terms of builtin C types and could change from system to as +// the underlying typedef for uint* types, size_t, off_t and other values +// change. + +template <typename T> const char *GetPythonValueFormatString(T t); +template <> const char *GetPythonValueFormatString(char *); +template <> const char *GetPythonValueFormatString(char); +template <> const char *GetPythonValueFormatString(unsigned char); +template <> const char *GetPythonValueFormatString(short); +template <> const char *GetPythonValueFormatString(unsigned short); +template <> const char *GetPythonValueFormatString(int); +template <> const char *GetPythonValueFormatString(unsigned int); +template <> const char *GetPythonValueFormatString(long); +template <> const char *GetPythonValueFormatString(unsigned long); +template <> const char *GetPythonValueFormatString(long long); +template <> const char *GetPythonValueFormatString(unsigned long long); +template <> const char *GetPythonValueFormatString(float t); +template <> const char *GetPythonValueFormatString(double t); + +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 *LLDBSWIGPython_CastPyObjectToSBData(void *data); +extern "C" void *LLDBSWIGPython_CastPyObjectToSBError(void *data); +extern "C" void *LLDBSWIGPython_CastPyObjectToSBValue(void *data); + +} // namespace lldb_private + +#endif // LLDB_ENABLE_PYTHON +#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SWIGPYTHONBRIDGE_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 6b53bd3a2edc..7ad63722c31c 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -16,7 +16,11 @@ #include "PythonDataObjects.h" #include "PythonReadline.h" +#include "SWIGPythonBridge.h" #include "ScriptInterpreterPythonImpl.h" +#include "ScriptedProcessPythonInterface.h" + +#include "lldb/API/SBError.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBValue.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" @@ -41,10 +45,10 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatAdapters.h" +#include <cstdio> +#include <cstdlib> #include <memory> #include <mutex> -#include <stdio.h> -#include <stdlib.h> #include <string> using namespace lldb; @@ -148,8 +152,6 @@ extern "C" void *LLDBSwigPython_GetChildAtIndex(void *implementor, extern "C" int LLDBSwigPython_GetIndexOfChildWithName(void *implementor, const char *child_name); -extern "C" void *LLDBSWIGPython_CastPyObjectToSBValue(void *data); - extern lldb::ValueObjectSP LLDBSWIGPython_GetValueObjectSPFromSBValue(void *data); @@ -234,8 +236,7 @@ namespace { // save off initial state at the beginning, and restore it at the end struct InitializePythonRAII { public: - InitializePythonRAII() - : m_gil_state(PyGILState_UNLOCKED), m_was_already_initialized(false) { + InitializePythonRAII() { InitializePythonHome(); #ifdef LLDB_USE_LIBEDIT_READLINE_COMPAT_MODULE @@ -355,8 +356,8 @@ private: } TerminalState m_stdin_tty_state; - PyGILState_STATE m_gil_state; - bool m_was_already_initialized; + PyGILState_STATE m_gil_state = PyGILState_UNLOCKED; + bool m_was_already_initialized = false; }; } // namespace @@ -410,6 +411,32 @@ FileSpec ScriptInterpreterPython::GetPythonDir() { return g_spec; } +void ScriptInterpreterPython::SharedLibraryDirectoryHelper( + FileSpec &this_file) { + // When we're loaded from python, this_file will point to the file inside the + // python package directory. Replace it with the one in the lib directory. +#ifdef _WIN32 + // On windows, we need to manually back out of the python tree, and go into + // the bin directory. This is pretty much the inverse of what ComputePythonDir + // does. + if (this_file.GetFileNameExtension() == ConstString(".pyd")) { + this_file.RemoveLastPathComponent(); // _lldb.pyd or _lldb_d.pyd + this_file.RemoveLastPathComponent(); // lldb + llvm::StringRef libdir = LLDB_PYTHON_RELATIVE_LIBDIR; + for (auto it = llvm::sys::path::begin(libdir), + end = llvm::sys::path::end(libdir); + it != end; ++it) + this_file.RemoveLastPathComponent(); + this_file.AppendPathComponent("bin"); + this_file.AppendPathComponent("liblldb.dll"); + } +#else + // The python file is a symlink, so we can find the real library by resolving + // it. We can do this unconditionally. + FileSystem::Instance().ResolveSymbolicLink(this_file, this_file); +#endif +} + lldb_private::ConstString ScriptInterpreterPython::GetPluginNameStatic() { static ConstString g_name("script-python"); return g_name; @@ -506,6 +533,9 @@ ScriptInterpreterPythonImpl::ScriptInterpreterPythonImpl(Debugger &debugger) m_command_thread_state(nullptr) { InitializePrivate(); + m_scripted_process_interface_up = + std::make_unique<ScriptedProcessPythonInterface>(*this); + m_dictionary_name.append("_dict"); StreamString run_string; run_string.Printf("%s = dict()", m_dictionary_name.c_str()); @@ -605,11 +635,10 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler, case eIOHandlerNone: break; case eIOHandlerBreakpoint: { - std::vector<BreakpointOptions *> *bp_options_vec = - (std::vector<BreakpointOptions *> *)io_handler.GetUserData(); - for (auto bp_options : *bp_options_vec) { - if (!bp_options) - continue; + std::vector<std::reference_wrapper<BreakpointOptions>> *bp_options_vec = + (std::vector<std::reference_wrapper<BreakpointOptions>> *) + io_handler.GetUserData(); + for (BreakpointOptions &bp_options : *bp_options_vec) { auto data_up = std::make_unique<CommandDataPython>(); if (!data_up) @@ -623,7 +652,7 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler, .Success()) { auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>( std::move(data_up)); - bp_options->SetCallback( + bp_options.SetCallback( ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp); } else if (!batch_mode) { StreamFileSP error_sp = io_handler.GetErrorStreamFileSP(); @@ -1048,11 +1077,24 @@ bool ScriptInterpreterPythonImpl::ExecuteOneLineWithReturn( llvm::StringRef in_string, ScriptInterpreter::ScriptReturnType return_type, void *ret_value, const ExecuteScriptOptions &options) { + llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>> + io_redirect_or_error = ScriptInterpreterIORedirect::Create( + options.GetEnableIO(), m_debugger, /*result=*/nullptr); + + if (!io_redirect_or_error) { + llvm::consumeError(io_redirect_or_error.takeError()); + return false; + } + + ScriptInterpreterIORedirect &io_redirect = **io_redirect_or_error; + Locker locker(this, Locker::AcquireLock | Locker::InitSession | (options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) | Locker::NoSTDIN, - Locker::FreeAcquiredLock | Locker::TearDownSession); + Locker::FreeAcquiredLock | Locker::TearDownSession, + io_redirect.GetInputFile(), io_redirect.GetOutputFile(), + io_redirect.GetErrorFile()); PythonModule &main_module = GetMainModule(); PythonDictionary globals = main_module.GetDictionary(); @@ -1161,11 +1203,22 @@ Status ScriptInterpreterPythonImpl::ExecuteMultipleLines( if (in_string == nullptr) return Status(); + llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>> + io_redirect_or_error = ScriptInterpreterIORedirect::Create( + options.GetEnableIO(), m_debugger, /*result=*/nullptr); + + if (!io_redirect_or_error) + return Status(io_redirect_or_error.takeError()); + + ScriptInterpreterIORedirect &io_redirect = **io_redirect_or_error; + Locker locker(this, Locker::AcquireLock | Locker::InitSession | (options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) | Locker::NoSTDIN, - Locker::FreeAcquiredLock | Locker::TearDownSession); + Locker::FreeAcquiredLock | Locker::TearDownSession, + io_redirect.GetInputFile(), io_redirect.GetOutputFile(), + io_redirect.GetErrorFile()); PythonModule &main_module = GetMainModule(); PythonDictionary globals = main_module.GetDictionary(); @@ -1196,7 +1249,7 @@ Status ScriptInterpreterPythonImpl::ExecuteMultipleLines( } void ScriptInterpreterPythonImpl::CollectDataForBreakpointCommandCallback( - std::vector<BreakpointOptions *> &bp_options_vec, + std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, CommandReturnObject &result) { m_active_io_handler = eIOHandlerBreakpoint; m_debugger.GetCommandInterpreter().GetPythonCommandsFromIOHandler( @@ -1211,7 +1264,7 @@ void ScriptInterpreterPythonImpl::CollectDataForWatchpointCommandCallback( } Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction( - BreakpointOptions *bp_options, const char *function_name, + BreakpointOptions &bp_options, const char *function_name, StructuredData::ObjectSP extra_args_sp) { Status error; // For now just cons up a oneliner that calls the provided function. @@ -1253,7 +1306,7 @@ Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction( } Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( - BreakpointOptions *bp_options, + BreakpointOptions &bp_options, std::unique_ptr<BreakpointOptions::CommandData> &cmd_data_up) { Status error; error = GenerateBreakpointCommandCallbackData(cmd_data_up->user_source, @@ -1264,21 +1317,20 @@ Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( } auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>(std::move(cmd_data_up)); - bp_options->SetCallback( + bp_options.SetCallback( ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp); return error; } Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( - BreakpointOptions *bp_options, const char *command_body_text) { + BreakpointOptions &bp_options, const char *command_body_text) { return SetBreakpointCommandCallback(bp_options, command_body_text, {},false); } // Set a Python one-liner as the callback for the breakpoint. Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( - BreakpointOptions *bp_options, const char *command_body_text, - StructuredData::ObjectSP extra_args_sp, - bool uses_extra_args) { + BreakpointOptions &bp_options, const char *command_body_text, + StructuredData::ObjectSP extra_args_sp, bool uses_extra_args) { auto data_up = std::make_unique<CommandDataPython>(extra_args_sp); // Split the command_body_text into lines, and pass that to // GenerateBreakpointCommandCallbackData. That will wrap the body in an @@ -1292,7 +1344,7 @@ Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( if (error.Success()) { auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up)); - bp_options->SetCallback( + bp_options.SetCallback( ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp); return error; } @@ -1330,7 +1382,7 @@ Status ScriptInterpreterPythonImpl::ExportFunctionDefinitionToInterpreter( Status error = ExecuteMultipleLines( function_def_string.c_str(), - ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false)); + ExecuteScriptOptions().SetEnableIO(false)); return error; } @@ -1678,35 +1730,6 @@ StructuredData::ArraySP ScriptInterpreterPythonImpl::OSPlugin_ThreadsInfo( return StructuredData::ArraySP(); } -// GetPythonValueFormatString provides a system independent type safe way to -// convert a variable's type into a python value format. Python value formats -// are defined in terms of builtin C types and could change from system to as -// the underlying typedef for uint* types, size_t, off_t and other values -// change. - -template <typename T> const char *GetPythonValueFormatString(T t); -template <> const char *GetPythonValueFormatString(char *) { return "s"; } -template <> const char *GetPythonValueFormatString(char) { return "b"; } -template <> const char *GetPythonValueFormatString(unsigned char) { - return "B"; -} -template <> const char *GetPythonValueFormatString(short) { return "h"; } -template <> const char *GetPythonValueFormatString(unsigned short) { - return "H"; -} -template <> const char *GetPythonValueFormatString(int) { return "i"; } -template <> const char *GetPythonValueFormatString(unsigned int) { return "I"; } -template <> const char *GetPythonValueFormatString(long) { return "l"; } -template <> const char *GetPythonValueFormatString(unsigned long) { - return "k"; -} -template <> const char *GetPythonValueFormatString(long long) { return "L"; } -template <> const char *GetPythonValueFormatString(unsigned long long) { - return "K"; -} -template <> const char *GetPythonValueFormatString(float t) { return "f"; } -template <> const char *GetPythonValueFormatString(double t) { return "d"; } - StructuredData::StringSP ScriptInterpreterPythonImpl::OSPlugin_RegisterContextData( StructuredData::ObjectSP os_plugin_object_sp, lldb::tid_t tid) { @@ -2058,7 +2081,10 @@ ScriptInterpreterPythonImpl::LoadPluginModule(const FileSpec &file_spec, StructuredData::ObjectSP module_sp; - if (LoadScriptingModule(file_spec.GetPath().c_str(), true, error, &module_sp)) + LoadScriptOptions load_script_options = + LoadScriptOptions().SetInitSession(true).SetSilent(false); + if (LoadScriptingModule(file_spec.GetPath().c_str(), load_script_options, + error, &module_sp)) return module_sp; return StructuredData::ObjectSP(); @@ -2733,26 +2759,44 @@ uint64_t replace_all(std::string &str, const std::string &oldStr, } bool ScriptInterpreterPythonImpl::LoadScriptingModule( - const char *pathname, bool init_session, lldb_private::Status &error, - StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) { + const char *pathname, const LoadScriptOptions &options, + lldb_private::Status &error, StructuredData::ObjectSP *module_sp, + FileSpec extra_search_dir) { namespace fs = llvm::sys::fs; namespace path = llvm::sys::path; + ExecuteScriptOptions exc_options = ExecuteScriptOptions() + .SetEnableIO(!options.GetSilent()) + .SetSetLLDBGlobals(false); + if (!pathname || !pathname[0]) { error.SetErrorString("invalid pathname"); return false; } + llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>> + io_redirect_or_error = ScriptInterpreterIORedirect::Create( + exc_options.GetEnableIO(), m_debugger, /*result=*/nullptr); + + if (!io_redirect_or_error) { + error = io_redirect_or_error.takeError(); + return false; + } + + ScriptInterpreterIORedirect &io_redirect = **io_redirect_or_error; lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this(); // Before executing Python code, lock the GIL. Locker py_lock(this, Locker::AcquireLock | - (init_session ? Locker::InitSession : 0) | Locker::NoSTDIN, + (options.GetInitSession() ? Locker::InitSession : 0) | + Locker::NoSTDIN, Locker::FreeAcquiredLock | - (init_session ? Locker::TearDownSession : 0)); + (options.GetInitSession() ? Locker::TearDownSession : 0), + io_redirect.GetInputFile(), io_redirect.GetOutputFile(), + io_redirect.GetErrorFile()); - auto ExtendSysPath = [this](std::string directory) -> llvm::Error { + auto ExtendSysPath = [&](std::string directory) -> llvm::Error { if (directory.empty()) { return llvm::make_error<llvm::StringError>( "invalid directory name", llvm::inconvertibleErrorCode()); @@ -2767,11 +2811,7 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( "sys.path.insert(1,'%s');\n\n", directory.c_str(), directory.c_str()); bool syspath_retval = - ExecuteMultipleLines(command_stream.GetData(), - ScriptInterpreter::ExecuteScriptOptions() - .SetEnableIO(false) - .SetSetLLDBGlobals(false)) - .Success(); + ExecuteMultipleLines(command_stream.GetData(), exc_options).Success(); if (!syspath_retval) { return llvm::make_error<llvm::StringError>( "Python sys.path handling failed", llvm::inconvertibleErrorCode()); @@ -2781,6 +2821,7 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( }; std::string module_name(pathname); + bool possible_package = false; if (extra_search_dir) { if (llvm::Error e = ExtendSysPath(extra_search_dir.GetPath())) { @@ -2805,6 +2846,7 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( return false; } // Not a filename, probably a package of some sort, let it go through. + possible_package = true; } else if (is_directory(st) || is_regular_file(st)) { if (module_file.GetDirectory().IsEmpty()) { error.SetErrorString("invalid directory name"); @@ -2831,35 +2873,39 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( module_name.resize(module_name.length() - 4); } - // check if the module is already import-ed + if (!possible_package && module_name.find('.') != llvm::StringRef::npos) { + error.SetErrorStringWithFormat( + "Python does not allow dots in module names: %s", module_name.c_str()); + return false; + } + + if (module_name.find('-') != llvm::StringRef::npos) { + error.SetErrorStringWithFormat( + "Python discourages dashes in module names: %s", module_name.c_str()); + return false; + } + + // Check if the module is already imported. StreamString command_stream; command_stream.Clear(); command_stream.Printf("sys.modules.__contains__('%s')", module_name.c_str()); bool does_contain = false; - // this call will succeed if the module was ever imported in any Debugger - // in the lifetime of the process in which this LLDB framework is living - bool was_imported_globally = - (ExecuteOneLineWithReturn( - command_stream.GetData(), - ScriptInterpreterPythonImpl::eScriptReturnTypeBool, &does_contain, - ScriptInterpreter::ExecuteScriptOptions() - .SetEnableIO(false) - .SetSetLLDBGlobals(false)) && - does_contain); - // this call will fail if the module was not imported in this Debugger - // before - command_stream.Clear(); - command_stream.Printf("sys.getrefcount(%s)", module_name.c_str()); - bool was_imported_locally = GetSessionDictionary() - .GetItemForKey(PythonString(module_name)) - .IsAllocated(); - - bool was_imported = (was_imported_globally || was_imported_locally); + // This call will succeed if the module was ever imported in any Debugger in + // the lifetime of the process in which this LLDB framework is living. + const bool does_contain_executed = ExecuteOneLineWithReturn( + command_stream.GetData(), + ScriptInterpreterPythonImpl::eScriptReturnTypeBool, &does_contain, exc_options); + + const bool was_imported_globally = does_contain_executed && does_contain; + const bool was_imported_locally = + GetSessionDictionary() + .GetItemForKey(PythonString(module_name)) + .IsAllocated(); // now actually do the import command_stream.Clear(); - if (was_imported) { + if (was_imported_globally || was_imported_locally) { if (!was_imported_locally) command_stream.Printf("import %s ; reload_module(%s)", module_name.c_str(), module_name.c_str()); @@ -2868,10 +2914,7 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( } else command_stream.Printf("import %s", module_name.c_str()); - error = ExecuteMultipleLines(command_stream.GetData(), - ScriptInterpreter::ExecuteScriptOptions() - .SetEnableIO(false) - .SetSetLLDBGlobals(false)); + error = ExecuteMultipleLines(command_stream.GetData(), exc_options); if (error.Fail()) return false; @@ -2890,7 +2933,8 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( void *module_pyobj = nullptr; if (ExecuteOneLineWithReturn( command_stream.GetData(), - ScriptInterpreter::eScriptReturnTypeOpaqueObject, &module_pyobj) && + ScriptInterpreter::eScriptReturnTypeOpaqueObject, &module_pyobj, + exc_options) && module_pyobj) *module_sp = std::make_shared<StructuredPythonObject>(module_pyobj); } @@ -3047,7 +3091,7 @@ bool ScriptInterpreterPythonImpl::GetDocumentationForItem(const char *item, if (ExecuteOneLineWithReturn( command, ScriptInterpreter::eScriptReturnTypeCharStrOrNone, &result_ptr, - ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false))) { + ExecuteScriptOptions().SetEnableIO(false))) { if (result_ptr) dest.assign(result_ptr); return true; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h index e59fedbd0971..b8b978118218 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h @@ -13,6 +13,8 @@ #if LLDB_ENABLE_PYTHON +#include "ScriptedProcessPythonInterface.h" + #include "lldb/Breakpoint/BreakpointOptions.h" #include "lldb/Core/IOHandler.h" #include "lldb/Core/StructuredDataImpl.h" @@ -51,6 +53,7 @@ public: static lldb_private::ConstString GetPluginNameStatic(); static const char *GetPluginDescriptionStatic(); static FileSpec GetPythonDir(); + static void SharedLibraryDirectoryHelper(FileSpec &this_file); protected: static void ComputePythonDirForApple(llvm::SmallVectorImpl<char> &path); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index 45dad4217005..d1b0b3fda1ef 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -6,6 +6,9 @@ // //===----------------------------------------------------------------------===// +#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHONIMPL_H +#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHONIMPL_H + #include "lldb/Host/Config.h" #if LLDB_ENABLE_PYTHON @@ -231,7 +234,8 @@ public: bool RunScriptFormatKeyword(const char *impl_function, ValueObject *value, std::string &output, Status &error) override; - bool LoadScriptingModule(const char *filename, bool init_session, + bool LoadScriptingModule(const char *filename, + const LoadScriptOptions &options, lldb_private::Status &error, StructuredData::ObjectSP *module_sp = nullptr, FileSpec extra_search_dir = {}) override; @@ -241,7 +245,7 @@ public: std::unique_ptr<ScriptInterpreterLocker> AcquireInterpreterLock() override; void CollectDataForBreakpointCommandCallback( - std::vector<BreakpointOptions *> &bp_options_vec, + std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, CommandReturnObject &result) override; void @@ -249,20 +253,19 @@ public: CommandReturnObject &result) override; /// Set the callback body text into the callback for the breakpoint. - Status SetBreakpointCommandCallback(BreakpointOptions *bp_options, + Status SetBreakpointCommandCallback(BreakpointOptions &bp_options, const char *callback_body) override; Status SetBreakpointCommandCallbackFunction( - BreakpointOptions *bp_options, - const char *function_name, + BreakpointOptions &bp_options, const char *function_name, StructuredData::ObjectSP extra_args_sp) override; /// This one is for deserialization: Status SetBreakpointCommandCallback( - BreakpointOptions *bp_options, + BreakpointOptions &bp_options, std::unique_ptr<BreakpointOptions::CommandData> &data_up) override; - Status SetBreakpointCommandCallback(BreakpointOptions *bp_options, + Status SetBreakpointCommandCallback(BreakpointOptions &bp_options, const char *command_body_text, StructuredData::ObjectSP extra_args_sp, bool uses_extra_args); @@ -416,7 +419,7 @@ public: : IOHandler(debugger, IOHandler::Type::PythonInterpreter), m_python(python) {} - ~IOHandlerPythonInterpreter() override {} + ~IOHandlerPythonInterpreter() override = default; ConstString GetControlSequence(char ch) override { if (ch == 'd') @@ -483,4 +486,5 @@ protected: } // namespace lldb_private -#endif +#endif // LLDB_ENABLE_PYTHON +#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHONIMPL_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp new file mode 100644 index 000000000000..ce262c930f8b --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp @@ -0,0 +1,306 @@ +//===-- 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() { + llvm::Optional<unsigned long long> should_stop = + GetGenericInteger("should_stop"); + + if (!should_stop) + return false; + + return static_cast<bool>(*should_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."); +} + +llvm::Optional<unsigned long long> +ScriptedProcessPythonInterface::GetGenericInteger(llvm::StringRef method_name) { + Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + if (!m_object_instance_sp) + return llvm::None; + + if (!m_object_instance_sp) + return llvm::None; + PythonObject implementor(PyRefType::Borrowed, + (PyObject *)m_object_instance_sp->GetValue()); + + if (!implementor.IsAllocated()) + return llvm::None; + + PythonObject pmeth( + PyRefType::Owned, + PyObject_GetAttrString(implementor.get(), method_name.str().c_str())); + + if (PyErr_Occurred()) + PyErr_Clear(); + + if (!pmeth.IsAllocated()) + return llvm::None; + + if (PyCallable_Check(pmeth.get()) == 0) { + if (PyErr_Occurred()) + PyErr_Clear(); + return llvm::None; + } + + 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()) + return llvm::None; + + llvm::Expected<unsigned long long> size = py_return.AsUnsignedLongLong(); + // FIXME: Handle error. + if (!size) + return llvm::None; + + return *size; +} + +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() { + llvm::Optional<unsigned long long> pid = GetGenericInteger("get_process_id"); + return (!pid) ? LLDB_INVALID_PROCESS_ID : *pid; +} + +bool ScriptedProcessPythonInterface::IsAlive() { + llvm::Optional<unsigned long long> is_alive = GetGenericInteger("is_alive"); + + if (!is_alive) + return false; + + return static_cast<bool>(*is_alive); +} + +#endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h new file mode 100644 index 000000000000..30cb5a882af2 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h @@ -0,0 +1,66 @@ +//===-- ScriptedProcessPythonInterface.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_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPROCESSPYTHONINTERFACE_H +#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPROCESSPYTHONINTERFACE_H + +#include "lldb/Host/Config.h" + +#if LLDB_ENABLE_PYTHON + +#include "lldb/Interpreter/ScriptedProcessInterface.h" + +namespace lldb_private { +class ScriptInterpreterPythonImpl; +class ScriptedProcessPythonInterface : public ScriptedProcessInterface { +public: + ScriptedProcessPythonInterface(ScriptInterpreterPythonImpl &interpreter) + : ScriptedProcessInterface(), m_interpreter(interpreter) {} + + StructuredData::GenericSP + CreatePluginObject(const llvm::StringRef class_name, lldb::TargetSP target_sp, + StructuredData::DictionarySP args_sp) override; + + Status Launch() override; + + Status Resume() override; + + bool ShouldStop() override; + + Status Stop() override; + + lldb::MemoryRegionInfoSP + GetMemoryRegionContainingAddress(lldb::addr_t address) override; + + StructuredData::DictionarySP GetThreadWithID(lldb::tid_t tid) override; + + StructuredData::DictionarySP GetRegistersForThread(lldb::tid_t tid) override; + + lldb::DataExtractorSP ReadMemoryAtAddress(lldb::addr_t address, size_t size, + Status &error) override; + + StructuredData::DictionarySP GetLoadedImages() override; + + lldb::pid_t GetProcessID() override; + + bool IsAlive() override; + +protected: + llvm::Optional<unsigned long long> + GetGenericInteger(llvm::StringRef method_name); + Status GetStatusFromMethod(llvm::StringRef method_name); + +private: + // The lifetime is managed by the ScriptInterpreter + ScriptInterpreterPythonImpl &m_interpreter; + StructuredData::GenericSP m_object_instance_sp; +}; +} // namespace lldb_private + +#endif // LLDB_ENABLE_PYTHON +#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTEDPROCESSPYTHONINTERFACE_H |