diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2021-02-16 20:13:02 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2021-02-16 20:13:02 +0000 |
| commit | b60736ec1405bb0a8dd40989f67ef4c93da068ab (patch) | |
| tree | 5c43fbb7c9fc45f0f87e0e6795a86267dbd12f9d /lldb/source/Plugins/ScriptInterpreter | |
| parent | cfca06d7963fa0909f90483b42a6d7d194d01e08 (diff) | |
Diffstat (limited to 'lldb/source/Plugins/ScriptInterpreter')
7 files changed, 526 insertions, 155 deletions
diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp index acd6128d84c5..f14e2732f6eb 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp @@ -9,16 +9,66 @@ #include "Lua.h" #include "lldb/Host/FileSystem.h" #include "lldb/Utility/FileSpec.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" using namespace lldb_private; using namespace lldb; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" + +// Disable warning C4190: 'LLDBSwigPythonBreakpointCallbackFunction' has +// C-linkage specified, but returns UDT 'llvm::Expected<bool>' which is +// incompatible with C +#if _MSC_VER +#pragma warning (push) +#pragma warning (disable : 4190) +#endif + +extern "C" llvm::Expected<bool> LLDBSwigLuaBreakpointCallbackFunction( + lua_State *L, lldb::StackFrameSP stop_frame_sp, + lldb::BreakpointLocationSP bp_loc_sp, StructuredDataImpl *extra_args_impl); + +#if _MSC_VER +#pragma warning (pop) +#endif + +#pragma clang diagnostic pop + +static int lldb_print(lua_State *L) { + int n = lua_gettop(L); + lua_getglobal(L, "io"); + lua_getfield(L, -1, "stdout"); + lua_getfield(L, -1, "write"); + for (int i = 1; i <= n; i++) { + lua_pushvalue(L, -1); // write() + lua_pushvalue(L, -3); // io.stdout + luaL_tolstring(L, i, nullptr); + lua_pushstring(L, i != n ? "\t" : "\n"); + lua_call(L, 3, 0); + } + return 0; +} + +Lua::Lua() : m_lua_state(luaL_newstate()) { + assert(m_lua_state); + luaL_openlibs(m_lua_state); + luaopen_lldb(m_lua_state); + lua_pushcfunction(m_lua_state, lldb_print); + lua_setglobal(m_lua_state, "print"); +} + +Lua::~Lua() { + assert(m_lua_state); + lua_close(m_lua_state); +} + llvm::Error Lua::Run(llvm::StringRef buffer) { int error = luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer") || lua_pcall(m_lua_state, 0, 0, 0); - if (!error) + if (error == LUA_OK) return llvm::Error::success(); llvm::Error e = llvm::make_error<llvm::StringError>( @@ -29,6 +79,57 @@ llvm::Error Lua::Run(llvm::StringRef buffer) { return e; } +llvm::Error Lua::RegisterBreakpointCallback(void *baton, const char *body) { + lua_pushlightuserdata(m_lua_state, baton); + const char *fmt_str = "return function(frame, bp_loc, ...) {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::CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp, + lldb::BreakpointLocationSP bp_loc_sp, + StructuredData::ObjectSP extra_args_sp) { + + lua_pushlightuserdata(m_lua_state, baton); + lua_gettable(m_lua_state, LUA_REGISTRYINDEX); + auto *extra_args_impl = [&]() -> StructuredDataImpl * { + if (extra_args_sp == nullptr) + return nullptr; + auto *extra_args_impl = new StructuredDataImpl(); + extra_args_impl->SetObjectSP(extra_args_sp); + return extra_args_impl; + }(); + return LLDBSwigLuaBreakpointCallbackFunction(m_lua_state, stop_frame_sp, + bp_loc_sp, extra_args_impl); +} + +llvm::Error Lua::CheckSyntax(llvm::StringRef buffer) { + int error = + luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer"); + if (error == LUA_OK) { + // Pop buffer + lua_pop(m_lua_state, 1); + return llvm::Error::success(); + } + + llvm::Error e = llvm::make_error<llvm::StringError>( + llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)), + llvm::inconvertibleErrorCode()); + // Pop error message from the stack. + lua_pop(m_lua_state, 1); + return e; +} + llvm::Error Lua::LoadModule(llvm::StringRef filename) { FileSpec file(filename); if (!FileSystem::Instance().Exists(file)) { @@ -44,7 +145,7 @@ llvm::Error Lua::LoadModule(llvm::StringRef filename) { int error = luaL_loadfile(m_lua_state, filename.data()) || lua_pcall(m_lua_state, 0, 1, 0); - if (error) { + if (error != LUA_OK) { llvm::Error e = llvm::make_error<llvm::StringError>( llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)), llvm::inconvertibleErrorCode()); diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h index 300115aac8a7..873440f0aab3 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h @@ -9,6 +9,9 @@ #ifndef liblldb_Lua_h_ #define liblldb_Lua_h_ +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBFrame.h" +#include "lldb/Core/StructuredDataImpl.h" #include "lldb/lldb-types.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -25,19 +28,17 @@ int luaopen_lldb(lua_State *L); class Lua { public: - Lua() : m_lua_state(luaL_newstate()) { - assert(m_lua_state); - luaL_openlibs(m_lua_state); - luaopen_lldb(m_lua_state); - } - - ~Lua() { - assert(m_lua_state); - luaL_openlibs(m_lua_state); - } + Lua(); + ~Lua(); llvm::Error Run(llvm::StringRef buffer); + llvm::Error RegisterBreakpointCallback(void *baton, const char *body); + llvm::Expected<bool> + CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp, + lldb::BreakpointLocationSP bp_loc_sp, + StructuredData::ObjectSP extra_args_sp); llvm::Error LoadModule(llvm::StringRef filename); + llvm::Error CheckSyntax(llvm::StringRef buffer); llvm::Error ChangeIO(FILE *out, FILE *err); private: diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp index 8cbeac4563c3..920e334f51aa 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp @@ -8,29 +8,42 @@ #include "ScriptInterpreterLua.h" #include "Lua.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamFile.h" #include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/ExecutionContext.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" #include "lldb/Utility/Timer.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/FormatAdapters.h" +#include <memory> +#include <vector> using namespace lldb; using namespace lldb_private; LLDB_PLUGIN_DEFINE(ScriptInterpreterLua) +enum ActiveIOHandler { + eIOHandlerNone, + eIOHandlerBreakpoint, + eIOHandlerWatchpoint +}; + class IOHandlerLuaInterpreter : public IOHandlerDelegate, public IOHandlerEditline { public: IOHandlerLuaInterpreter(Debugger &debugger, - ScriptInterpreterLua &script_interpreter) + ScriptInterpreterLua &script_interpreter, + ActiveIOHandler active_io_handler = eIOHandlerNone) : IOHandlerEditline(debugger, IOHandler::Type::LuaInterpreter, "lua", ">>> ", "..> ", true, debugger.GetUseColor(), 0, *this, nullptr), - m_script_interpreter(script_interpreter) { + m_script_interpreter(script_interpreter), + m_active_io_handler(active_io_handler) { llvm::cantFail(m_script_interpreter.GetLua().ChangeIO( debugger.GetOutputFile().GetStream(), debugger.GetErrorFile().GetStream())); @@ -41,20 +54,79 @@ public: llvm::cantFail(m_script_interpreter.LeaveSession()); } - void IOHandlerInputComplete(IOHandler &io_handler, - std::string &data) override { - if (llvm::StringRef(data).rtrim() == "quit") { - io_handler.SetIsDone(true); + void IOHandlerActivated(IOHandler &io_handler, bool interactive) override { + const char *instructions = nullptr; + switch (m_active_io_handler) { + case eIOHandlerNone: + case eIOHandlerWatchpoint: + break; + case eIOHandlerBreakpoint: + 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, bp_loc, ...) end\n"; + SetPrompt(llvm::StringRef("..> ")); + break; + } + if (instructions == nullptr) return; + if (interactive) + *io_handler.GetOutputStreamFileSP() << instructions; + } + + bool IOHandlerIsInputComplete(IOHandler &io_handler, + StringList &lines) override { + size_t last = lines.GetSize() - 1; + if (IsQuitCommand(lines.GetStringAtIndex(last))) { + if (m_active_io_handler == eIOHandlerBreakpoint) + lines.DeleteStringAtIndex(last); + return true; } + StreamString str; + lines.Join("\n", str); + if (llvm::Error E = + m_script_interpreter.GetLua().CheckSyntax(str.GetString())) { + std::string error_str = toString(std::move(E)); + // 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; + } - if (llvm::Error error = m_script_interpreter.GetLua().Run(data)) { - *GetOutputStreamFileSP() << llvm::toString(std::move(error)); + 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) { + Status error = m_script_interpreter.SetBreakpointCommandCallback( + bp_options, data.c_str()); + if (error.Fail()) + *io_handler.GetErrorStreamFileSP() << error.AsCString() << '\n'; + } + io_handler.SetIsDone(true); + } break; + case eIOHandlerWatchpoint: + io_handler.SetIsDone(true); + break; + case eIOHandlerNone: + if (IsQuitCommand(data)) { + io_handler.SetIsDone(true); + return; + } + if (llvm::Error error = m_script_interpreter.GetLua().Run(data)) + *io_handler.GetErrorStreamFileSP() << toString(std::move(error)); + break; } } private: ScriptInterpreterLua &m_script_interpreter; + ActiveIOHandler m_active_io_handler; + + bool IsQuitCommand(llvm::StringRef cmd) { return cmd.rtrim() == "quit"; } }; ScriptInterpreterLua::ScriptInterpreterLua(Debugger &debugger) @@ -107,8 +179,7 @@ bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command, } void ScriptInterpreterLua::ExecuteInterpreterLoop() { - static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); - Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); + LLDB_SCOPED_TIMER(); // At the moment, the only time the debugger does not have an input file // handle is when this is called directly from lua, in which case it is @@ -124,7 +195,7 @@ void ScriptInterpreterLua::ExecuteInterpreterLoop() { bool ScriptInterpreterLua::LoadScriptingModule( const char *filename, bool init_session, lldb_private::Status &error, - StructuredData::ObjectSP *module_sp) { + StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) { FileSystem::Instance().Collect(filename); if (llvm::Error e = m_lua->LoadModule(filename)) { @@ -174,6 +245,74 @@ llvm::Error ScriptInterpreterLua::LeaveSession() { return m_lua->Run(str); } +bool ScriptInterpreterLua::BreakpointCallbackFunction( + void *baton, StoppointCallbackContext *context, user_id_t break_id, + user_id_t break_loc_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()); + BreakpointSP breakpoint_sp = target->GetBreakpointByID(break_id); + BreakpointLocationSP bp_loc_sp(breakpoint_sp->FindLocationByID(break_loc_id)); + + Debugger &debugger = target->GetDebugger(); + ScriptInterpreterLua *lua_interpreter = static_cast<ScriptInterpreterLua *>( + debugger.GetScriptInterpreter(true, eScriptLanguageLua)); + Lua &lua = lua_interpreter->GetLua(); + + CommandDataLua *bp_option_data = static_cast<CommandDataLua *>(baton); + llvm::Expected<bool> BoolOrErr = lua.CallBreakpointCallback( + baton, stop_frame_sp, bp_loc_sp, bp_option_data->m_extra_args_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, + CommandReturnObject &result) { + IOHandlerSP io_handler_sp( + new IOHandlerLuaInterpreter(m_debugger, *this, eIOHandlerBreakpoint)); + io_handler_sp->SetUserData(&bp_options_vec); + m_debugger.RunIOHandlerAsync(io_handler_sp); +} + +Status ScriptInterpreterLua::SetBreakpointCommandCallbackFunction( + 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(); + return RegisterBreakpointCallback(bp_options, oneliner.c_str(), + extra_args_sp); +} + +Status ScriptInterpreterLua::SetBreakpointCommandCallback( + 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, + StructuredData::ObjectSP extra_args_sp) { + Status error; + auto data_up = std::make_unique<CommandDataLua>(extra_args_sp); + error = m_lua->RegisterBreakpointCallback(data_up.get(), command_body_text); + if (error.Fail()) + return error; + auto baton_sp = + std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up)); + bp_options->SetCallback(ScriptInterpreterLua::BreakpointCallbackFunction, + baton_sp); + return error; +} + lldb::ScriptInterpreterSP ScriptInterpreterLua::CreateInstance(Debugger &debugger) { return std::make_shared<ScriptInterpreterLua>(debugger); diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h index bcc6ab24f6d0..1130ceee3c20 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h @@ -9,12 +9,27 @@ #ifndef liblldb_ScriptInterpreterLua_h_ #define liblldb_ScriptInterpreterLua_h_ +#include "lldb/Core/StructuredDataImpl.h" #include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Utility/Status.h" +#include "lldb/lldb-enumerations.h" namespace lldb_private { class Lua; class ScriptInterpreterLua : public ScriptInterpreter { public: + class CommandDataLua : public BreakpointOptions::CommandData { + public: + CommandDataLua() : BreakpointOptions::CommandData() { + interpreter = lldb::eScriptLanguageLua; + } + CommandDataLua(StructuredData::ObjectSP extra_args_sp) + : BreakpointOptions::CommandData(), m_extra_args_sp(extra_args_sp) { + interpreter = lldb::eScriptLanguageLua; + } + StructuredData::ObjectSP m_extra_args_sp; + }; + ScriptInterpreterLua(Debugger &debugger); ~ScriptInterpreterLua() override; @@ -25,10 +40,10 @@ public: void ExecuteInterpreterLoop() override; - bool - LoadScriptingModule(const char *filename, bool init_session, - lldb_private::Status &error, - StructuredData::ObjectSP *module_sp = nullptr) override; + bool LoadScriptingModule(const char *filename, bool init_session, + lldb_private::Status &error, + StructuredData::ObjectSP *module_sp = nullptr, + FileSpec extra_search_dir = {}) override; // Static Functions static void Initialize(); @@ -41,6 +56,11 @@ public: static const char *GetPluginDescriptionStatic(); + static bool BreakpointCallbackFunction(void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + // PluginInterface protocol lldb_private::ConstString GetPluginName() override; @@ -51,9 +71,24 @@ public: llvm::Error EnterSession(lldb::user_id_t debugger_id); llvm::Error LeaveSession(); + void CollectDataForBreakpointCommandCallback( + std::vector<BreakpointOptions *> &bp_options_vec, + CommandReturnObject &result) override; + + Status SetBreakpointCommandCallback(BreakpointOptions *bp_options, + const char *command_body_text) override; + + Status SetBreakpointCommandCallbackFunction( + 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, + const char *command_body_text, + StructuredData::ObjectSP extra_args_sp); }; } // namespace lldb_private diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index 6f040fdef09b..7c49502f1b57 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -451,7 +451,11 @@ Expected<llvm::StringRef> PythonString::AsUTF8() const { size_t PythonString::GetSize() const { if (IsValid()) { #if PY_MAJOR_VERSION >= 3 +#if PY_MINOR_VERSION >= 3 + return PyUnicode_GetLength(m_py_obj); +#else return PyUnicode_GetSize(m_py_obj); +#endif #else return PyString_Size(m_py_obj); #endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 9f56b4fa60a5..6b53bd3a2edc 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/Config.h" +#include "lldb/lldb-enumerations.h" #if LLDB_ENABLE_PYTHON @@ -32,6 +33,7 @@ #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" +#include "lldb/Utility/ReproducerInstrumentation.h" #include "lldb/Utility/Timer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" @@ -127,6 +129,16 @@ extern "C" unsigned int LLDBSwigPythonCallBreakpointResolver(void *implementor, const char *method_name, lldb_private::SymbolContext *sym_ctx); +extern "C" void *LLDBSwigPythonCreateScriptedStopHook( + TargetSP target_sp, const char *python_class_name, + const char *session_dictionary_name, lldb_private::StructuredDataImpl *args, + lldb_private::Status &error); + +extern "C" bool +LLDBSwigPythonStopHookCallHandleStop(void *implementor, + lldb::ExecutionContextRefSP exc_ctx, + lldb::StreamSP stream); + extern "C" size_t LLDBSwigPython_CalculateNumChildren(void *implementor, uint32_t max); @@ -204,6 +216,12 @@ extern "C" void * LLDBSWIGPython_GetDynamicSetting(void *module, const char *setting, const lldb::TargetSP &target_sp); +static ScriptInterpreterPythonImpl *GetPythonInterpreter(Debugger &debugger) { + ScriptInterpreter *script_interpreter = + debugger.GetScriptInterpreter(true, lldb::eScriptLanguagePython); + return static_cast<ScriptInterpreterPythonImpl *>(script_interpreter); +} + static bool g_initialized = false; namespace { @@ -420,6 +438,7 @@ ScriptInterpreterPythonImpl::Locker::Locker( : ScriptInterpreterLocker(), m_teardown_session((on_leave & TearDownSession) == TearDownSession), m_python_interpreter(py_interpreter) { + repro::Recorder::PrivateThread(); DoAcquireLock(); if ((on_entry & InitSession) == InitSession) { if (!DoInitSession(on_entry, in, out, err)) { @@ -982,8 +1001,7 @@ bool ScriptInterpreterPythonImpl::ExecuteOneLine( } void ScriptInterpreterPythonImpl::ExecuteInterpreterLoop() { - static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); - Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); + LLDB_SCOPED_TIMER(); Debugger &debugger = m_debugger; @@ -1815,11 +1833,10 @@ StructuredData::ObjectSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlan( return {}; Debugger &debugger = thread_plan_sp->GetTarget().GetDebugger(); - ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter(); ScriptInterpreterPythonImpl *python_interpreter = - static_cast<ScriptInterpreterPythonImpl *>(script_interpreter); + GetPythonInterpreter(debugger); - if (!script_interpreter) + if (!python_interpreter) return {}; void *ret_val; @@ -1919,11 +1936,10 @@ ScriptInterpreterPythonImpl::CreateScriptedBreakpointResolver( return StructuredData::GenericSP(); Debugger &debugger = bkpt_sp->GetTarget().GetDebugger(); - ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter(); ScriptInterpreterPythonImpl *python_interpreter = - static_cast<ScriptInterpreterPythonImpl *>(script_interpreter); + GetPythonInterpreter(debugger); - if (!script_interpreter) + if (!python_interpreter) return StructuredData::GenericSP(); void *ret_val; @@ -1979,6 +1995,59 @@ ScriptInterpreterPythonImpl::ScriptedBreakpointResolverSearchDepth( return lldb::eSearchDepthModule; } +StructuredData::GenericSP ScriptInterpreterPythonImpl::CreateScriptedStopHook( + TargetSP target_sp, const char *class_name, StructuredDataImpl *args_data, + Status &error) { + + if (!target_sp) { + error.SetErrorString("No target for scripted stop-hook."); + return StructuredData::GenericSP(); + } + + if (class_name == nullptr || class_name[0] == '\0') { + error.SetErrorString("No class name for scripted stop-hook."); + return StructuredData::GenericSP(); + } + + ScriptInterpreterPythonImpl *python_interpreter = + GetPythonInterpreter(m_debugger); + + if (!python_interpreter) { + error.SetErrorString("No script interpreter for scripted stop-hook."); + return StructuredData::GenericSP(); + } + + void *ret_val; + + { + Locker py_lock(this, + Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); + + ret_val = LLDBSwigPythonCreateScriptedStopHook( + target_sp, class_name, python_interpreter->m_dictionary_name.c_str(), + args_data, error); + } + + return StructuredData::GenericSP(new StructuredPythonObject(ret_val)); +} + +bool ScriptInterpreterPythonImpl::ScriptedStopHookHandleStop( + StructuredData::GenericSP implementor_sp, ExecutionContext &exc_ctx, + lldb::StreamSP stream_sp) { + assert(implementor_sp && + "can't call a stop hook with an invalid implementor"); + assert(stream_sp && "can't call a stop hook with an invalid stream"); + + Locker py_lock(this, + Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); + + lldb::ExecutionContextRefSP exc_ctx_ref_sp(new ExecutionContextRef(exc_ctx)); + + bool ret_val = LLDBSwigPythonStopHookCallHandleStop( + implementor_sp->GetValue(), exc_ctx_ref_sp, stream_sp); + return ret_val; +} + StructuredData::ObjectSP ScriptInterpreterPythonImpl::LoadPluginModule(const FileSpec &file_spec, lldb_private::Status &error) { @@ -2039,11 +2108,10 @@ ScriptInterpreterPythonImpl::CreateSyntheticScriptedProvider( return StructuredData::ObjectSP(); Debugger &debugger = target->GetDebugger(); - ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter(); ScriptInterpreterPythonImpl *python_interpreter = - (ScriptInterpreterPythonImpl *)script_interpreter; + GetPythonInterpreter(debugger); - if (!script_interpreter) + if (!python_interpreter) return StructuredData::ObjectSP(); void *ret_val = nullptr; @@ -2151,8 +2219,7 @@ bool ScriptInterpreterPythonImpl::GetScriptedSummary( StructuredData::ObjectSP &callee_wrapper_sp, const TypeSummaryOptions &options, std::string &retval) { - static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); - Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); + LLDB_SCOPED_TIMER(); if (!valobj.get()) { retval.assign("<no object>"); @@ -2210,11 +2277,10 @@ bool ScriptInterpreterPythonImpl::BreakpointCallbackFunction( return true; Debugger &debugger = target->GetDebugger(); - ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter(); ScriptInterpreterPythonImpl *python_interpreter = - (ScriptInterpreterPythonImpl *)script_interpreter; + GetPythonInterpreter(debugger); - if (!script_interpreter) + if (!python_interpreter) return true; if (python_function_name && python_function_name[0]) { @@ -2276,11 +2342,10 @@ bool ScriptInterpreterPythonImpl::WatchpointCallbackFunction( return true; Debugger &debugger = target->GetDebugger(); - ScriptInterpreter *script_interpreter = debugger.GetScriptInterpreter(); ScriptInterpreterPythonImpl *python_interpreter = - (ScriptInterpreterPythonImpl *)script_interpreter; + GetPythonInterpreter(debugger); - if (!script_interpreter) + if (!python_interpreter) return true; if (python_function_name && python_function_name[0]) { @@ -2669,7 +2734,10 @@ 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) { + StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) { + namespace fs = llvm::sys::fs; + namespace path = llvm::sys::path; + if (!pathname || !pathname[0]) { error.SetErrorString("invalid pathname"); return false; @@ -2677,24 +2745,55 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this(); - { - FileSpec target_file(pathname); - FileSystem::Instance().Resolve(target_file); - FileSystem::Instance().Collect(target_file); - std::string basename(target_file.GetFilename().GetCString()); + // Before executing Python code, lock the GIL. + Locker py_lock(this, + Locker::AcquireLock | + (init_session ? Locker::InitSession : 0) | Locker::NoSTDIN, + Locker::FreeAcquiredLock | + (init_session ? Locker::TearDownSession : 0)); + + auto ExtendSysPath = [this](std::string directory) -> llvm::Error { + if (directory.empty()) { + return llvm::make_error<llvm::StringError>( + "invalid directory name", llvm::inconvertibleErrorCode()); + } + replace_all(directory, "\\", "\\\\"); + replace_all(directory, "'", "\\'"); + + // Make sure that Python has "directory" in the search path. StreamString command_stream; + command_stream.Printf("if not (sys.path.__contains__('%s')):\n " + "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(); + if (!syspath_retval) { + return llvm::make_error<llvm::StringError>( + "Python sys.path handling failed", llvm::inconvertibleErrorCode()); + } + + return llvm::Error::success(); + }; + + std::string module_name(pathname); + + if (extra_search_dir) { + if (llvm::Error e = ExtendSysPath(extra_search_dir.GetPath())) { + error = std::move(e); + return false; + } + } else { + FileSpec module_file(pathname); + FileSystem::Instance().Resolve(module_file); + FileSystem::Instance().Collect(module_file); - // Before executing Python code, lock the GIL. - Locker py_lock(this, - Locker::AcquireLock | - (init_session ? Locker::InitSession : 0) | - Locker::NoSTDIN, - Locker::FreeAcquiredLock | - (init_session ? Locker::TearDownSession : 0)); - namespace fs = llvm::sys::fs; fs::file_status st; - std::error_code ec = status(target_file.GetPath(), st); + std::error_code ec = status(module_file.GetPath(), st); if (ec || st.type() == fs::file_type::status_error || st.type() == fs::file_type::type_unknown || @@ -2705,113 +2804,98 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( error.SetErrorString("invalid pathname"); return false; } - basename = pathname; // not a filename, probably a package of some sort, - // let it go through + // Not a filename, probably a package of some sort, let it go through. } else if (is_directory(st) || is_regular_file(st)) { - if (target_file.GetDirectory().IsEmpty()) { + if (module_file.GetDirectory().IsEmpty()) { error.SetErrorString("invalid directory name"); return false; } - - std::string directory = target_file.GetDirectory().GetCString(); - replace_all(directory, "\\", "\\\\"); - replace_all(directory, "'", "\\'"); - - // now make sure that Python has "directory" in the search path - StreamString command_stream; - command_stream.Printf("if not (sys.path.__contains__('%s')):\n " - "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(); - if (!syspath_retval) { - error.SetErrorString("Python sys.path handling failed"); + if (llvm::Error e = + ExtendSysPath(module_file.GetDirectory().GetCString())) { + error = std::move(e); return false; } - - // strip .py or .pyc extension - ConstString extension = target_file.GetFileNameExtension(); - if (extension) { - if (llvm::StringRef(extension.GetCString()) == ".py") - basename.resize(basename.length() - 3); - else if (llvm::StringRef(extension.GetCString()) == ".pyc") - basename.resize(basename.length() - 4); - } + module_name = module_file.GetFilename().GetCString(); } else { error.SetErrorString("no known way to import this module specification"); return false; } + } - // check if the module is already import-ed - command_stream.Clear(); - command_stream.Printf("sys.modules.__contains__('%s')", basename.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)", basename.c_str()); - bool was_imported_locally = GetSessionDictionary() - .GetItemForKey(PythonString(basename)) - .IsAllocated(); + // Strip .py or .pyc extension + llvm::StringRef extension = llvm::sys::path::extension(module_name); + if (!extension.empty()) { + if (extension == ".py") + module_name.resize(module_name.length() - 3); + else if (extension == ".pyc") + module_name.resize(module_name.length() - 4); + } - bool was_imported = (was_imported_globally || was_imported_locally); + // check if the module is already import-ed + 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(); - // now actually do the import - command_stream.Clear(); + bool was_imported = (was_imported_globally || was_imported_locally); - if (was_imported) { - if (!was_imported_locally) - command_stream.Printf("import %s ; reload_module(%s)", basename.c_str(), - basename.c_str()); - else - command_stream.Printf("reload_module(%s)", basename.c_str()); - } else - command_stream.Printf("import %s", basename.c_str()); + // now actually do the import + command_stream.Clear(); - error = ExecuteMultipleLines(command_stream.GetData(), - ScriptInterpreter::ExecuteScriptOptions() - .SetEnableIO(false) - .SetSetLLDBGlobals(false)); - if (error.Fail()) - return false; + if (was_imported) { + if (!was_imported_locally) + command_stream.Printf("import %s ; reload_module(%s)", + module_name.c_str(), module_name.c_str()); + else + command_stream.Printf("reload_module(%s)", module_name.c_str()); + } else + command_stream.Printf("import %s", module_name.c_str()); - // if we are here, everything worked - // call __lldb_init_module(debugger,dict) - if (!LLDBSwigPythonCallModuleInit(basename.c_str(), - m_dictionary_name.c_str(), debugger_sp)) { - error.SetErrorString("calling __lldb_init_module failed"); - return false; - } + error = ExecuteMultipleLines(command_stream.GetData(), + ScriptInterpreter::ExecuteScriptOptions() + .SetEnableIO(false) + .SetSetLLDBGlobals(false)); + if (error.Fail()) + return false; - if (module_sp) { - // everything went just great, now set the module object - command_stream.Clear(); - command_stream.Printf("%s", basename.c_str()); - void *module_pyobj = nullptr; - if (ExecuteOneLineWithReturn( - command_stream.GetData(), - ScriptInterpreter::eScriptReturnTypeOpaqueObject, - &module_pyobj) && - module_pyobj) - *module_sp = std::make_shared<StructuredPythonObject>(module_pyobj); - } + // if we are here, everything worked + // call __lldb_init_module(debugger,dict) + if (!LLDBSwigPythonCallModuleInit(module_name.c_str(), + m_dictionary_name.c_str(), debugger_sp)) { + error.SetErrorString("calling __lldb_init_module failed"); + return false; + } - return true; + if (module_sp) { + // everything went just great, now set the module object + command_stream.Clear(); + command_stream.Printf("%s", module_name.c_str()); + void *module_pyobj = nullptr; + if (ExecuteOneLineWithReturn( + command_stream.GetData(), + ScriptInterpreter::eScriptReturnTypeOpaqueObject, &module_pyobj) && + module_pyobj) + *module_sp = std::make_shared<StructuredPythonObject>(module_pyobj); } + + return true; } bool ScriptInterpreterPythonImpl::IsReservedWord(const char *word) { @@ -3154,8 +3238,7 @@ void ScriptInterpreterPythonImpl::InitializePrivate() { g_initialized = true; - static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); - Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION); + LLDB_SCOPED_TIMER(); // RAII-based initialization which correctly handles multiple-initialization, // version- specific differences among Python 2 and Python 3, and saving and diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index 22b2c8152eac..45dad4217005 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -106,6 +106,14 @@ public: StructuredData::GenericSP implementor_sp) override; StructuredData::GenericSP + CreateScriptedStopHook(lldb::TargetSP target_sp, const char *class_name, + StructuredDataImpl *args_data, Status &error) override; + + bool ScriptedStopHookHandleStop(StructuredData::GenericSP implementor_sp, + ExecutionContext &exc_ctx, + lldb::StreamSP stream_sp) override; + + StructuredData::GenericSP CreateFrameRecognizer(const char *class_name) override; lldb::ValueObjectListSP @@ -223,10 +231,10 @@ public: bool RunScriptFormatKeyword(const char *impl_function, ValueObject *value, std::string &output, Status &error) override; - bool - LoadScriptingModule(const char *filename, bool init_session, - lldb_private::Status &error, - StructuredData::ObjectSP *module_sp = nullptr) override; + bool LoadScriptingModule(const char *filename, bool init_session, + lldb_private::Status &error, + StructuredData::ObjectSP *module_sp = nullptr, + FileSpec extra_search_dir = {}) override; bool IsReservedWord(const char *word) override; |
