diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2020-01-17 20:45:01 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2020-01-17 20:45:01 +0000 |
commit | 706b4fc47bbc608932d3b491ae19a3b9cde9497b (patch) | |
tree | 4adf86a776049cbf7f69a1929c4babcbbef925eb /lldb/source/Plugins/ScriptInterpreter/Python | |
parent | 7cc9cf2bf09f069cb2dd947ead05d0b54301fb71 (diff) |
Notes
Diffstat (limited to 'lldb/source/Plugins/ScriptInterpreter/Python')
8 files changed, 365 insertions, 147 deletions
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index 70d93424fdec..e5a67653e334 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -6,11 +6,9 @@ // //===----------------------------------------------------------------------===// -#ifdef LLDB_DISABLE_PYTHON +#include "lldb/Host/Config.h" -// Python is disabled in this build - -#else +#if LLDB_ENABLE_PYTHON #include "PythonDataObjects.h" #include "ScriptInterpreterPython.h" @@ -802,29 +800,11 @@ bool PythonCallable::Check(PyObject *py_obj) { return PyCallable_Check(py_obj); } -PythonCallable::ArgInfo PythonCallable::GetNumInitArguments() const { - auto arginfo = GetInitArgInfo(); - if (!arginfo) { - llvm::consumeError(arginfo.takeError()); - return ArgInfo{}; - } - return arginfo.get(); -} - -Expected<PythonCallable::ArgInfo> PythonCallable::GetInitArgInfo() const { - if (!IsValid()) - return nullDeref(); - auto init = As<PythonCallable>(GetAttribute("__init__")); - if (!init) - return init.takeError(); - return init.get().GetArgInfo(); -} - #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 static const char get_arg_info_script[] = R"( from inspect import signature, Parameter, ismethod from collections import namedtuple -ArgInfo = namedtuple('ArgInfo', ['count', 'has_varargs', 'is_bound_method']) +ArgInfo = namedtuple('ArgInfo', ['count', 'has_varargs']) def main(f): count = 0 varargs = False @@ -840,7 +820,7 @@ def main(f): pass else: raise Exception(f'unknown parameter kind: {kind}') - return ArgInfo(count, varargs, ismethod(f)) + return ArgInfo(count, varargs) )"; #endif @@ -856,21 +836,27 @@ Expected<PythonCallable::ArgInfo> PythonCallable::GetArgInfo() const { Expected<PythonObject> pyarginfo = get_arg_info(*this); if (!pyarginfo) return pyarginfo.takeError(); - result.count = cantFail(As<long long>(pyarginfo.get().GetAttribute("count"))); - result.has_varargs = + long long count = + cantFail(As<long long>(pyarginfo.get().GetAttribute("count"))); + bool has_varargs = cantFail(As<bool>(pyarginfo.get().GetAttribute("has_varargs"))); - bool is_method = - cantFail(As<bool>(pyarginfo.get().GetAttribute("is_bound_method"))); - result.max_positional_args = - result.has_varargs ? ArgInfo::UNBOUNDED : result.count; - - // FIXME emulate old broken behavior - if (is_method) - result.count++; + result.max_positional_args = has_varargs ? ArgInfo::UNBOUNDED : count; #else + PyObject *py_func_obj; bool is_bound_method = false; - PyObject *py_func_obj = m_py_obj; + bool is_class = false; + + if (PyType_Check(m_py_obj) || PyClass_Check(m_py_obj)) { + auto init = GetAttribute("__init__"); + if (!init) + return init.takeError(); + py_func_obj = init.get().get(); + is_class = true; + } else { + py_func_obj = m_py_obj; + } + if (PyMethod_Check(py_func_obj)) { py_func_obj = PyMethod_GET_FUNCTION(py_func_obj); PythonObject im_self = GetAttributeValue("im_self"); @@ -899,11 +885,11 @@ Expected<PythonCallable::ArgInfo> PythonCallable::GetArgInfo() const { if (!code) return result; - result.count = code->co_argcount; - result.has_varargs = !!(code->co_flags & CO_VARARGS); - result.max_positional_args = result.has_varargs - ? ArgInfo::UNBOUNDED - : (result.count - (int)is_bound_method); + auto count = code->co_argcount; + bool has_varargs = !!(code->co_flags & CO_VARARGS); + result.max_positional_args = + has_varargs ? ArgInfo::UNBOUNDED + : (count - (int)is_bound_method) - (int)is_class; #endif @@ -913,15 +899,6 @@ Expected<PythonCallable::ArgInfo> PythonCallable::GetArgInfo() const { constexpr unsigned PythonCallable::ArgInfo::UNBOUNDED; // FIXME delete after c++17 -PythonCallable::ArgInfo PythonCallable::GetNumArguments() const { - auto arginfo = GetArgInfo(); - if (!arginfo) { - llvm::consumeError(arginfo.takeError()); - return ArgInfo{}; - } - return arginfo.get(); -} - PythonObject PythonCallable::operator()() { return PythonObject(PyRefType::Owned, PyObject_CallObject(m_py_obj, nullptr)); } @@ -1385,11 +1362,13 @@ llvm::Expected<FileSP> PythonFile::ConvertToFile(bool borrowed) { if (!options) return options.takeError(); - // LLDB and python will not share I/O buffers. We should probably - // flush the python buffers now. - auto r = CallMethod("flush"); - if (!r) - return r.takeError(); + if (options.get() & File::eOpenOptionWrite) { + // LLDB and python will not share I/O buffers. We should probably + // flush the python buffers now. + auto r = CallMethod("flush"); + if (!r) + return r.takeError(); + } FileSP file_sp; if (borrowed) { @@ -1498,14 +1477,23 @@ Expected<PythonFile> PythonFile::FromFile(File &file, const char *mode) { PyObject *file_obj; #if PY_MAJOR_VERSION >= 3 file_obj = PyFile_FromFd(file.GetDescriptor(), nullptr, mode, -1, nullptr, - "ignore", nullptr, 0); + "ignore", nullptr, /*closefd=*/0); #else - // Read through the Python source, doesn't seem to modify these strings - char *cmode = const_cast<char *>(mode); - // We pass ::flush instead of ::fclose here so we borrow the FILE* -- - // the lldb_private::File still owns it. - file_obj = - PyFile_FromFile(file.GetStream(), const_cast<char *>(""), cmode, ::fflush); + // I'd like to pass ::fflush here if the file is writable, so that + // when the python side destructs the file object it will be flushed. + // However, this would be dangerous. It can cause fflush to be called + // after fclose if the python program keeps a reference to the file after + // the original lldb_private::File has been destructed. + // + // It's all well and good to ask a python program not to use a closed file + // but asking a python program to make sure objects get released in a + // particular order is not safe. + // + // The tradeoff here is that if a python 2 program wants to make sure this + // file gets flushed, they'll have to do it explicitly or wait untill the + // original lldb File itself gets flushed. + file_obj = PyFile_FromFile(file.GetStream(), py2_const_cast(""), + py2_const_cast(mode), [](FILE *) { return 0; }); #endif if (!file_obj) diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h index 373d3212697d..b75045b239a8 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h @@ -48,7 +48,9 @@ #ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONDATAOBJECTS_H -#ifndef LLDB_DISABLE_PYTHON +#include "lldb/Host/Config.h" + +#if LLDB_ENABLE_PYTHON // LLDB Python header must be included first #include "lldb-python.h" @@ -130,7 +132,7 @@ template <typename T> T Take(PyObject *obj) { assert(!PyErr_Occurred()); T thing(PyRefType::Owned, obj); assert(thing.IsValid()); - return std::move(thing); + return thing; } // Retain a reference you have borrowed, and turn it into @@ -148,7 +150,7 @@ template <typename T> T Retain(PyObject *obj) { assert(!PyErr_Occurred()); T thing(PyRefType::Borrowed, obj); assert(thing.IsValid()); - return std::move(thing); + return thing; } // This class can be used like a utility function to convert from @@ -189,6 +191,14 @@ inline llvm::Error keyError() { "key not in dict"); } +#if PY_MAJOR_VERSION < 3 +// The python 2 API declares some arguments as char* that should +// be const char *, but it doesn't actually modify them. +inline char *py2_const_cast(const char *s) { return const_cast<char *>(s); } +#else +inline const char *py2_const_cast(const char *s) { return s; } +#endif + enum class PyInitialValue { Invalid, Empty }; template <typename T, typename Enable = void> struct PythonFormat; @@ -309,16 +319,6 @@ public: StructuredData::ObjectSP CreateStructuredObject() const; -protected: - -#if PY_MAJOR_VERSION < 3 - // The python 2 API declares some arguments as char* that should - // be const char *, but it doesn't actually modify them. - static char *py2_const_cast(const char *s) { return const_cast<char *>(s); } -#else - static const char *py2_const_cast(const char *s) { return s; } -#endif - public: template <typename... T> llvm::Expected<PythonObject> CallMethod(const char *name, @@ -621,30 +621,12 @@ public: * function and can accept an arbitrary number */ unsigned max_positional_args; static constexpr unsigned UNBOUNDED = UINT_MAX; // FIXME c++17 inline - /* the number of positional arguments, including optional ones, - * and excluding varargs. If this is a bound method, then the - * count will still include a +1 for self. - * - * FIXME. That's crazy. This should be replaced with - * an accurate min and max for positional args. - */ - int count; - /* does the callable have positional varargs? */ - bool has_varargs : 1; // FIXME delete this }; static bool Check(PyObject *py_obj); llvm::Expected<ArgInfo> GetArgInfo() const; - llvm::Expected<ArgInfo> GetInitArgInfo() const; - - ArgInfo GetNumArguments() const; // DEPRECATED - - // If the callable is a Py_Class, then find the number of arguments - // of the __init__ method. - ArgInfo GetNumInitArguments() const; // DEPRECATED - PythonObject operator()(); PythonObject operator()(std::initializer_list<PyObject *> args); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonReadline.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonReadline.cpp new file mode 100644 index 000000000000..5f6429f5cd0e --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonReadline.cpp @@ -0,0 +1,88 @@ +#include "PythonReadline.h" + +#ifdef LLDB_USE_LIBEDIT_READLINE_COMPAT_MODULE + +#include <stdio.h> + +#include <editline/readline.h> + +// Simple implementation of the Python readline module using libedit. +// In the event that libedit is excluded from the build, this turns +// back into a null implementation that blocks the module from pulling +// in the GNU readline shared lib, which causes linkage confusion when +// both readline and libedit's readline compatibility symbols collide. +// +// Currently it only installs a PyOS_ReadlineFunctionPointer, without +// implementing any of the readline module methods. This is meant to +// work around LLVM pr18841 to avoid seg faults in the stock Python +// readline.so linked against GNU readline. +// +// Bug on the cpython side: https://bugs.python.org/issue38634 + +PyDoc_STRVAR(moduleDocumentation, + "Simple readline module implementation based on libedit."); + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef readline_module = { + PyModuleDef_HEAD_INIT, // m_base + "lldb_editline", // m_name + moduleDocumentation, // m_doc + -1, // m_size + nullptr, // m_methods + nullptr, // m_reload + nullptr, // m_traverse + nullptr, // m_clear + nullptr, // m_free +}; +#else +static struct PyMethodDef moduleMethods[] = {{nullptr, nullptr, 0, nullptr}}; +#endif + +static char * +#if PY_MAJOR_VERSION >= 3 +simple_readline(FILE *stdin, FILE *stdout, const char *prompt) +#else +simple_readline(FILE *stdin, FILE *stdout, char *prompt) +#endif +{ + rl_instream = stdin; + rl_outstream = stdout; + char *line = readline(prompt); + if (!line) { +#if PY_MAJOR_VERSION >= 3 + char *ret = (char *)PyMem_RawMalloc(1); +#else + char *ret = (char *)PyMem_Malloc(1); +#endif + if (ret != NULL) + *ret = '\0'; + return ret; + } + if (*line) + add_history(line); + int n = strlen(line); +#if PY_MAJOR_VERSION >= 3 + char *ret = (char *)PyMem_RawMalloc(n + 2); +#else + char *ret = (char *)PyMem_Malloc(n + 2); +#endif + if (ret) { + memcpy(ret, line, n); + free(line); + ret[n] = '\n'; + ret[n + 1] = '\0'; + } + return ret; +} + +PyMODINIT_FUNC initlldb_readline(void) { + PyOS_ReadlineFunctionPointer = simple_readline; + +#if PY_MAJOR_VERSION >= 3 + return PyModule_Create(&readline_module); +#else + Py_InitModule4("readline", moduleMethods, moduleDocumentation, + static_cast<PyObject *>(NULL), PYTHON_API_VERSION); +#endif +} +#endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonReadline.h b/lldb/source/Plugins/ScriptInterpreter/Python/PythonReadline.h new file mode 100644 index 000000000000..c75219eb1a4f --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonReadline.h @@ -0,0 +1,28 @@ +//===-- PythonReadline.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_PYTHONREADLINE_H +#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONREADLINE_H + +#include "lldb/Host/Config.h" + +#if LLDB_ENABLE_LIBEDIT && defined(__linux__) +// NOTE: Since Python may define some pre-processor definitions which affect the +// standard headers on some systems, you must include Python.h before any +// standard headers are included. +#include "Python.h" + +// no need to hack into Python's readline module if libedit isn't used. +// +#define LLDB_USE_LIBEDIT_READLINE_COMPAT_MODULE 1 + +PyMODINIT_FUNC initlldb_readline(void); + +#endif + +#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_PYTHONREADLINE_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 3eee52184142..06e0d5bfa63f 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -6,16 +6,15 @@ // //===----------------------------------------------------------------------===// -#ifdef LLDB_DISABLE_PYTHON +#include "lldb/Host/Config.h" -// Python is disabled in this build - -#else +#if LLDB_ENABLE_PYTHON // LLDB Python header must be included first #include "lldb-python.h" #include "PythonDataObjects.h" +#include "PythonReadline.h" #include "ScriptInterpreterPythonImpl.h" #include "lldb/API/SBFrame.h" @@ -72,10 +71,28 @@ extern "C" void init_lldb(void); // These prototypes are the Pythonic implementations of the required callbacks. // Although these are scripting-language specific, their definition depends on // the public API. -extern "C" bool LLDBSwigPythonBreakpointCallbackFunction( + +#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> LLDBSwigPythonBreakpointCallbackFunction( const char *python_function_name, const char *session_dictionary_name, const lldb::StackFrameSP &sb_frame, - const lldb::BreakpointLocationSP &sb_bp_loc); + const lldb::BreakpointLocationSP &sb_bp_loc, StructuredDataImpl *args_impl); + +#if _MSC_VER +#pragma warning (pop) +#endif + +#pragma clang diagnostic pop extern "C" bool LLDBSwigPythonWatchpointCallbackFunction( const char *python_function_name, const char *session_dictionary_name, @@ -211,6 +228,22 @@ public: InitializePythonHome(); +#ifdef LLDB_USE_LIBEDIT_READLINE_COMPAT_MODULE + // Python's readline is incompatible with libedit being linked into lldb. + // Provide a patched version local to the embedded interpreter. + bool ReadlinePatched = false; + for (auto *p = PyImport_Inittab; p->name != NULL; p++) { + if (strcmp(p->name, "readline") == 0) { + p->initfunc = initlldb_readline; + break; + } + } + if (!ReadlinePatched) { + PyImport_AppendInittab("readline", initlldb_readline); + ReadlinePatched = true; + } +#endif + // Register _lldb as a built-in module. PyImport_AppendInittab("_lldb", LLDBSwigPyInit); @@ -552,8 +585,10 @@ void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler, break; data_up->user_source.SplitIntoLines(data); + StructuredData::ObjectSP empty_args_sp; if (GenerateBreakpointCommandCallbackData(data_up->user_source, - data_up->script_source) + data_up->script_source, + false) .Success()) { auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>( std::move(data_up)); @@ -779,6 +814,32 @@ PythonDictionary &ScriptInterpreterPythonImpl::GetSysModuleDictionary() { return m_sys_module_dict; } +llvm::Expected<unsigned> +ScriptInterpreterPythonImpl::GetMaxPositionalArgumentsForCallable( + const llvm::StringRef &callable_name) { + if (callable_name.empty()) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "called with empty callable name."); + } + Locker py_lock(this, Locker::AcquireLock | + Locker::InitSession | + Locker::NoSTDIN); + auto dict = PythonModule::MainModule() + .ResolveName<PythonDictionary>(m_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>( + callable_name, dict); + if (!pfunc.IsAllocated()) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "can't find callable: %s", callable_name.str().c_str()); + } + llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo(); + if (!arg_info) + return arg_info.takeError(); + return arg_info.get().max_positional_args; +} + static std::string GenerateUniqueName(const char *base_name_wanted, uint32_t &functions_counter, const void *name_token = nullptr) { @@ -1139,6 +1200,7 @@ bool ScriptInterpreterPythonImpl::ExecuteOneLineWithReturn( return true; } } + llvm_unreachable("Fully covered switch!"); } Status ScriptInterpreterPythonImpl::ExecuteMultipleLines( @@ -1186,24 +1248,56 @@ void ScriptInterpreterPythonImpl::CollectDataForBreakpointCommandCallback( CommandReturnObject &result) { m_active_io_handler = eIOHandlerBreakpoint; m_debugger.GetCommandInterpreter().GetPythonCommandsFromIOHandler( - " ", *this, true, &bp_options_vec); + " ", *this, &bp_options_vec); } void ScriptInterpreterPythonImpl::CollectDataForWatchpointCommandCallback( WatchpointOptions *wp_options, CommandReturnObject &result) { m_active_io_handler = eIOHandlerWatchpoint; m_debugger.GetCommandInterpreter().GetPythonCommandsFromIOHandler( - " ", *this, true, wp_options); + " ", *this, wp_options); } -void ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction( - BreakpointOptions *bp_options, const char *function_name) { +Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction( + 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. std::string oneliner("return "); oneliner += function_name; - oneliner += "(frame, bp_loc, internal_dict)"; - m_debugger.GetScriptInterpreter()->SetBreakpointCommandCallback( - bp_options, oneliner.c_str()); + + llvm::Expected<unsigned> maybe_args = + GetMaxPositionalArgumentsForCallable(function_name); + if (!maybe_args) { + error.SetErrorStringWithFormat( + "could not get num args: %s", + llvm::toString(maybe_args.takeError()).c_str()); + return error; + } + size_t max_args = *maybe_args; + + bool uses_extra_args = false; + if (max_args >= 4) { + uses_extra_args = true; + oneliner += "(frame, bp_loc, extra_args, internal_dict)"; + } else if (max_args >= 3) { + if (extra_args_sp) { + error.SetErrorString("cannot pass extra_args to a three argument callback" + ); + return error; + } + uses_extra_args = false; + oneliner += "(frame, bp_loc, internal_dict)"; + } else { + error.SetErrorStringWithFormat("expected 3 or 4 argument " + "function, %s can only take %zu", + function_name, max_args); + return error; + } + + SetBreakpointCommandCallback(bp_options, oneliner.c_str(), extra_args_sp, + uses_extra_args); + return error; } Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( @@ -1211,7 +1305,8 @@ Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( std::unique_ptr<BreakpointOptions::CommandData> &cmd_data_up) { Status error; error = GenerateBreakpointCommandCallbackData(cmd_data_up->user_source, - cmd_data_up->script_source); + cmd_data_up->script_source, + false); if (error.Fail()) { return error; } @@ -1222,11 +1317,17 @@ Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( return error; } -// Set a Python one-liner as the callback for the breakpoint. Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( BreakpointOptions *bp_options, const char *command_body_text) { - auto data_up = std::make_unique<CommandDataPython>(); + 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) { + 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 // auto-generated function, and return the function name in script_source. @@ -1234,7 +1335,8 @@ Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback( data_up->user_source.SplitIntoLines(command_body_text); Status error = GenerateBreakpointCommandCallbackData(data_up->user_source, - data_up->script_source); + data_up->script_source, + uses_extra_args); if (error.Success()) { auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up)); @@ -1771,8 +1873,7 @@ StructuredData::DictionarySP ScriptInterpreterPythonImpl::OSPlugin_CreateThread( StructuredData::ObjectSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlan( const char *class_name, StructuredDataImpl *args_data, - std::string &error_str, - lldb::ThreadPlanSP thread_plan_sp) { + std::string &error_str, lldb::ThreadPlanSP thread_plan_sp) { if (class_name == nullptr || class_name[0] == '\0') return StructuredData::ObjectSP(); @@ -1956,8 +2057,7 @@ ScriptInterpreterPythonImpl::LoadPluginModule(const FileSpec &file_spec, StructuredData::ObjectSP module_sp; - if (LoadScriptingModule(file_spec.GetPath().c_str(), true, true, error, - &module_sp)) + if (LoadScriptingModule(file_spec.GetPath().c_str(), true, error, &module_sp)) return module_sp; return StructuredData::ObjectSP(); @@ -2063,7 +2163,8 @@ bool ScriptInterpreterPythonImpl::GenerateTypeSynthClass( } Status ScriptInterpreterPythonImpl::GenerateBreakpointCommandCallbackData( - StringList &user_input, std::string &output) { + StringList &user_input, std::string &output, + bool has_extra_args) { static uint32_t num_created_functions = 0; user_input.RemoveBlankLines(); StreamString sstr; @@ -2075,8 +2176,12 @@ Status ScriptInterpreterPythonImpl::GenerateBreakpointCommandCallbackData( std::string auto_generated_function_name(GenerateUniqueName( "lldb_autogen_python_bp_callback_func_", num_created_functions)); - sstr.Printf("def %s (frame, bp_loc, internal_dict):", - auto_generated_function_name.c_str()); + if (has_extra_args) + sstr.Printf("def %s (frame, bp_loc, extra_args, internal_dict):", + auto_generated_function_name.c_str()); + else + sstr.Printf("def %s (frame, bp_loc, internal_dict):", + auto_generated_function_name.c_str()); error = GenerateFunction(sstr.GetData(), user_input); if (!error.Success()) @@ -2193,10 +2298,26 @@ bool ScriptInterpreterPythonImpl::BreakpointCallbackFunction( Locker py_lock(python_interpreter, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); - ret_val = LLDBSwigPythonBreakpointCallbackFunction( - python_function_name, - python_interpreter->m_dictionary_name.c_str(), stop_frame_sp, - bp_loc_sp); + Expected<bool> maybe_ret_val = + LLDBSwigPythonBreakpointCallbackFunction( + python_function_name, + python_interpreter->m_dictionary_name.c_str(), stop_frame_sp, + bp_loc_sp, bp_option_data->m_extra_args_up.get()); + + if (!maybe_ret_val) { + + llvm::handleAllErrors( + maybe_ret_val.takeError(), + [&](PythonException &E) { + debugger.GetErrorStream() << E.ReadBacktrace(); + }, + [&](const llvm::ErrorInfoBase &E) { + debugger.GetErrorStream() << E.message(); + }); + + } else { + ret_val = maybe_ret_val.get(); + } } return ret_val; } @@ -2615,8 +2736,8 @@ uint64_t replace_all(std::string &str, const std::string &oldStr, } bool ScriptInterpreterPythonImpl::LoadScriptingModule( - const char *pathname, bool can_reload, bool init_session, - lldb_private::Status &error, StructuredData::ObjectSP *module_sp) { + const char *pathname, bool init_session, lldb_private::Status &error, + StructuredData::ObjectSP *module_sp) { if (!pathname || !pathname[0]) { error.SetErrorString("invalid pathname"); return false; @@ -2716,11 +2837,6 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( bool was_imported = (was_imported_globally || was_imported_locally); - if (was_imported && !can_reload) { - error.SetErrorString("module already imported"); - return false; - } - // now actually do the import command_stream.Clear(); @@ -3171,4 +3287,4 @@ void ScriptInterpreterPythonImpl::AddToSysPath(AddLocation location, // // void ScriptInterpreterPythonImpl::Terminate() { Py_Finalize (); } -#endif // LLDB_DISABLE_PYTHON +#endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h index 33ae308041b2..e59fedbd0971 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h @@ -9,14 +9,13 @@ #ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHON_H #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHON_H -#ifdef LLDB_DISABLE_PYTHON +#include "lldb/Host/Config.h" -// Python is disabled in this build - -#else +#if LLDB_ENABLE_PYTHON #include "lldb/Breakpoint/BreakpointOptions.h" #include "lldb/Core/IOHandler.h" +#include "lldb/Core/StructuredDataImpl.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/lldb-private.h" @@ -34,6 +33,13 @@ public: CommandDataPython() : BreakpointOptions::CommandData() { interpreter = lldb::eScriptLanguagePython; } + CommandDataPython(StructuredData::ObjectSP extra_args_sp) : + BreakpointOptions::CommandData(), + m_extra_args_up(new StructuredDataImpl()) { + interpreter = lldb::eScriptLanguagePython; + m_extra_args_up->SetObjectSP(extra_args_sp); + } + lldb::StructuredDataImplUP m_extra_args_up; }; ScriptInterpreterPython(Debugger &debugger) @@ -52,5 +58,5 @@ protected: }; } // namespace lldb_private -#endif // LLDB_DISABLE_PYTHON +#endif // LLDB_ENABLE_PYTHON #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHON_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index 929567e579d8..1fa198b07e54 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -6,11 +6,9 @@ // //===----------------------------------------------------------------------===// -#ifdef LLDB_DISABLE_PYTHON +#include "lldb/Host/Config.h" -// Python is disabled in this build - -#else +#if LLDB_ENABLE_PYTHON #include "lldb-python.h" @@ -179,8 +177,10 @@ public: Status GenerateFunction(const char *signature, const StringList &input) override; - Status GenerateBreakpointCommandCallbackData(StringList &input, - std::string &output) override; + Status GenerateBreakpointCommandCallbackData( + StringList &input, + std::string &output, + bool has_extra_args) override; bool GenerateWatchpointCommandCallbackData(StringList &input, std::string &output) override; @@ -224,7 +224,7 @@ public: std::string &output, Status &error) override; bool - LoadScriptingModule(const char *filename, bool can_reload, bool init_session, + LoadScriptingModule(const char *filename, bool init_session, lldb_private::Status &error, StructuredData::ObjectSP *module_sp = nullptr) override; @@ -244,14 +244,21 @@ public: Status SetBreakpointCommandCallback(BreakpointOptions *bp_options, const char *callback_body) override; - void SetBreakpointCommandCallbackFunction(BreakpointOptions *bp_options, - const char *function_name) override; + Status SetBreakpointCommandCallbackFunction( + BreakpointOptions *bp_options, + const char *function_name, + StructuredData::ObjectSP extra_args_sp) override; /// This one is for deserialization: Status SetBreakpointCommandCallback( BreakpointOptions *bp_options, std::unique_ptr<BreakpointOptions::CommandData> &data_up) override; + Status SetBreakpointCommandCallback(BreakpointOptions *bp_options, + const char *command_body_text, + StructuredData::ObjectSP extra_args_sp, + bool uses_extra_args); + /// Set a one-liner as the callback for the watchpoint. void SetWatchpointCommandCallback(WatchpointOptions *wp_options, const char *oneliner) override; @@ -369,6 +376,9 @@ public: python::PythonDictionary &GetSysModuleDictionary(); + llvm::Expected<unsigned> GetMaxPositionalArgumentsForCallable( + const llvm::StringRef &callable_name) override; + bool GetEmbeddedInterpreterModuleObjects(); bool SetStdHandle(lldb::FileSP file, const char *py_name, diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h b/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h index 884514da9924..48f27b09b95c 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/lldb-python.h @@ -9,12 +9,12 @@ #ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_LLDB_PYTHON_H #define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_LLDB_PYTHON_H +#include "lldb/Host/Config.h" + // Python.h needs to be included before any system headers in order to avoid // redefinition of macros -#ifdef LLDB_DISABLE_PYTHON -// Python is disabled in this build -#else +#if LLDB_ENABLE_PYTHON #include "llvm/Support/Compiler.h" #if defined(_WIN32) // If anyone #includes Host/PosixApi.h later, it will try to typedef pid_t. We @@ -40,6 +40,6 @@ // Include python for non windows machines #include <Python.h> -#endif // LLDB_DISABLE_PYTHON +#endif #endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_LLDB_PYTHON_H |