| //===-- ScriptInterpreterPythonImpl.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_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHONIMPL_H | |
| #define LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHONIMPL_H | |
| #include "lldb-python.h" | |
| #include "PythonDataObjects.h" | |
| #include "ScriptInterpreterPython.h" | |
| #include "lldb/Host/Terminal.h" | |
| #include "lldb/Utility/StreamString.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/StringRef.h" | |
| namespace lldb_private { | |
| class IOHandlerPythonInterpreter; | |
| class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { | |
| public: | |
| friend class IOHandlerPythonInterpreter; | |
| ScriptInterpreterPythonImpl(Debugger &debugger); | |
| ~ScriptInterpreterPythonImpl() override; | |
| bool Interrupt() override; | |
| bool ExecuteOneLine( | |
| llvm::StringRef command, CommandReturnObject *result, | |
| const ExecuteScriptOptions &options = ExecuteScriptOptions()) override; | |
| void ExecuteInterpreterLoop() override; | |
| bool ExecuteOneLineWithReturn( | |
| llvm::StringRef in_string, | |
| ScriptInterpreter::ScriptReturnType return_type, void *ret_value, | |
| const ExecuteScriptOptions &options = ExecuteScriptOptions()) override; | |
| lldb_private::Status ExecuteMultipleLines( | |
| const char *in_string, | |
| const ExecuteScriptOptions &options = ExecuteScriptOptions()) override; | |
| Status | |
| ExportFunctionDefinitionToInterpreter(StringList &function_def) override; | |
| bool GenerateTypeScriptFunction(StringList &input, std::string &output, | |
| const void *name_token = nullptr) override; | |
| bool GenerateTypeSynthClass(StringList &input, std::string &output, | |
| const void *name_token = nullptr) override; | |
| bool GenerateTypeSynthClass(const char *oneliner, std::string &output, | |
| const void *name_token = nullptr) override; | |
| // use this if the function code is just a one-liner script | |
| bool GenerateTypeScriptFunction(const char *oneliner, std::string &output, | |
| const void *name_token = nullptr) override; | |
| bool GenerateScriptAliasFunction(StringList &input, | |
| std::string &output) override; | |
| StructuredData::ObjectSP | |
| CreateSyntheticScriptedProvider(const char *class_name, | |
| lldb::ValueObjectSP valobj) override; | |
| StructuredData::GenericSP | |
| CreateScriptCommandObject(const char *class_name) override; | |
| StructuredData::ObjectSP | |
| CreateStructuredDataFromScriptObject(ScriptObject obj) override; | |
| StructuredData::GenericSP | |
| CreateFrameRecognizer(const char *class_name) override; | |
| lldb::ValueObjectListSP | |
| GetRecognizedArguments(const StructuredData::ObjectSP &implementor, | |
| lldb::StackFrameSP frame_sp) override; | |
| bool ShouldHide(const StructuredData::ObjectSP &implementor, | |
| lldb::StackFrameSP frame_sp) override; | |
| lldb::ScriptedProcessInterfaceUP CreateScriptedProcessInterface() override; | |
| lldb::ScriptedStopHookInterfaceSP CreateScriptedStopHookInterface() override; | |
| lldb::ScriptedHookInterfaceSP CreateScriptedHookInterface() override; | |
| lldb::ScriptedBreakpointInterfaceSP | |
| CreateScriptedBreakpointInterface() override; | |
| lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override; | |
| lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; | |
| lldb::ScriptedFrameProviderInterfaceSP | |
| CreateScriptedFrameProviderInterface() override; | |
| lldb::ScriptedThreadPlanInterfaceSP | |
| CreateScriptedThreadPlanInterface() override; | |
| lldb::OperatingSystemInterfaceSP CreateOperatingSystemInterface() override; | |
| StructuredData::ObjectSP | |
| LoadPluginModule(const FileSpec &file_spec, | |
| lldb_private::Status &error) override; | |
| StructuredData::DictionarySP | |
| GetDynamicSettings(StructuredData::ObjectSP plugin_module_sp, Target *target, | |
| const char *setting_name, | |
| lldb_private::Status &error) override; | |
| size_t CalculateNumChildren(const StructuredData::ObjectSP &implementor, | |
| uint32_t max) override; | |
| lldb::ValueObjectSP | |
| GetChildAtIndex(const StructuredData::ObjectSP &implementor, | |
| uint32_t idx) override; | |
| llvm::Expected<uint32_t> | |
| GetIndexOfChildWithName(const StructuredData::ObjectSP &implementor, | |
| const char *child_name) override; | |
| bool UpdateSynthProviderInstance( | |
| const StructuredData::ObjectSP &implementor) override; | |
| bool MightHaveChildrenSynthProviderInstance( | |
| const StructuredData::ObjectSP &implementor) override; | |
| lldb::ValueObjectSP | |
| GetSyntheticValue(const StructuredData::ObjectSP &implementor) override; | |
| ConstString | |
| GetSyntheticTypeName(const StructuredData::ObjectSP &implementor) override; | |
| bool | |
| RunScriptBasedCommand(const char *impl_function, llvm::StringRef args, | |
| ScriptedCommandSynchronicity synchronicity, | |
| lldb_private::CommandReturnObject &cmd_retobj, | |
| Status &error, | |
| const lldb_private::ExecutionContext &exe_ctx) override; | |
| bool RunScriptBasedCommand( | |
| StructuredData::GenericSP impl_obj_sp, llvm::StringRef args, | |
| ScriptedCommandSynchronicity synchronicity, | |
| lldb_private::CommandReturnObject &cmd_retobj, Status &error, | |
| const lldb_private::ExecutionContext &exe_ctx) override; | |
| bool RunScriptBasedParsedCommand( | |
| StructuredData::GenericSP impl_obj_sp, Args &args, | |
| ScriptedCommandSynchronicity synchronicity, | |
| lldb_private::CommandReturnObject &cmd_retobj, Status &error, | |
| const lldb_private::ExecutionContext &exe_ctx) override; | |
| std::optional<std::string> | |
| GetRepeatCommandForScriptedCommand(StructuredData::GenericSP impl_obj_sp, | |
| Args &args) override; | |
| StructuredData::DictionarySP HandleArgumentCompletionForScriptedCommand( | |
| StructuredData::GenericSP impl_obj_sp, std::vector<llvm::StringRef> &args, | |
| size_t args_pos, size_t char_in_arg) override; | |
| StructuredData::DictionarySP HandleOptionArgumentCompletionForScriptedCommand( | |
| StructuredData::GenericSP impl_obj_sp, llvm::StringRef &long_options, | |
| size_t char_in_arg) override; | |
| Status GenerateFunction(const char *signature, const StringList &input, | |
| bool is_callback) override; | |
| Status GenerateBreakpointCommandCallbackData(StringList &input, | |
| std::string &output, | |
| bool has_extra_args, | |
| bool is_callback) override; | |
| bool GenerateWatchpointCommandCallbackData(StringList &input, | |
| std::string &output, | |
| bool is_callback) override; | |
| bool GetScriptedSummary(const char *function_name, lldb::ValueObjectSP valobj, | |
| StructuredData::ObjectSP &callee_wrapper_sp, | |
| const TypeSummaryOptions &options, | |
| std::string &retval) override; | |
| bool FormatterCallbackFunction(const char *function_name, | |
| lldb::TypeImplSP type_impl_sp) override; | |
| bool GetDocumentationForItem(const char *item, std::string &dest) override; | |
| bool GetShortHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp, | |
| std::string &dest) override; | |
| uint32_t | |
| GetFlagsForCommandObject(StructuredData::GenericSP cmd_obj_sp) override; | |
| bool GetLongHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp, | |
| std::string &dest) override; | |
| StructuredData::ObjectSP | |
| GetOptionsForCommandObject(StructuredData::GenericSP cmd_obj_sp) override; | |
| StructuredData::ObjectSP | |
| GetArgumentsForCommandObject(StructuredData::GenericSP cmd_obj_sp) override; | |
| bool SetOptionValueForCommandObject(StructuredData::GenericSP cmd_obj_sp, | |
| ExecutionContext *exe_ctx, | |
| llvm::StringRef long_option, | |
| llvm::StringRef value) override; | |
| void OptionParsingStartedForCommandObject( | |
| StructuredData::GenericSP cmd_obj_sp) override; | |
| bool CheckObjectExists(const char *name) override { | |
| if (!name || !name[0]) | |
| return false; | |
| std::string temp; | |
| return GetDocumentationForItem(name, temp); | |
| } | |
| bool RunScriptFormatKeyword(const char *impl_function, Process *process, | |
| std::string &output, Status &error) override; | |
| bool RunScriptFormatKeyword(const char *impl_function, Thread *thread, | |
| std::string &output, Status &error) override; | |
| bool RunScriptFormatKeyword(const char *impl_function, Target *target, | |
| std::string &output, Status &error) override; | |
| bool RunScriptFormatKeyword(const char *impl_function, StackFrame *frame, | |
| std::string &output, Status &error) override; | |
| bool RunScriptFormatKeyword(const char *impl_function, ValueObject *value, | |
| std::string &output, Status &error) override; | |
| bool LoadScriptingModule(const char *filename, | |
| const LoadScriptOptions &options, | |
| lldb_private::Status &error, | |
| StructuredData::ObjectSP *module_sp = nullptr, | |
| FileSpec extra_search_dir = {}, | |
| lldb::TargetSP loaded_into_target_sp = {}) override; | |
| bool IsReservedWord(const char *word) override; | |
| std::unique_ptr<ScriptInterpreterLocker> AcquireInterpreterLock() override; | |
| void CollectDataForBreakpointCommandCallback( | |
| std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec, | |
| CommandReturnObject &result) override; | |
| void | |
| CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options, | |
| CommandReturnObject &result) override; | |
| /// Set the callback body text into the callback for the breakpoint. | |
| Status SetBreakpointCommandCallback(BreakpointOptions &bp_options, | |
| const char *callback_body, | |
| bool is_callback) 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, | |
| bool is_callback); | |
| /// Set a one-liner as the callback for the watchpoint. | |
| void SetWatchpointCommandCallback(WatchpointOptions *wp_options, | |
| const char *user_input, | |
| bool is_callback) override; | |
| const char *GetDictionaryName() { return m_dictionary_name.c_str(); } | |
| PyThreadState *GetThreadState() { return m_command_thread_state; } | |
| void SetThreadState(PyThreadState *s) { | |
| if (s) | |
| m_command_thread_state = s; | |
| } | |
| // IOHandlerDelegate | |
| void IOHandlerActivated(IOHandler &io_handler, bool interactive) override; | |
| void IOHandlerInputComplete(IOHandler &io_handler, | |
| std::string &data) override; | |
| static lldb::ScriptInterpreterSP CreateInstance(Debugger &debugger); | |
| // PluginInterface protocol | |
| llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } | |
| class Locker : public ScriptInterpreterLocker { | |
| public: | |
| enum OnEntry { | |
| AcquireLock = 0x0001, | |
| InitSession = 0x0002, | |
| InitGlobals = 0x0004, | |
| NoSTDIN = 0x0008 | |
| }; | |
| enum OnLeave { | |
| FreeLock = 0x0001, | |
| FreeAcquiredLock = 0x0002, // do not free the lock if we already held it | |
| // when calling constructor | |
| TearDownSession = 0x0004 | |
| }; | |
| Locker(ScriptInterpreterPythonImpl *py_interpreter, | |
| uint16_t on_entry = AcquireLock | InitSession, | |
| uint16_t on_leave = FreeLock | TearDownSession, | |
| lldb::FileSP in = nullptr, lldb::FileSP out = nullptr, | |
| lldb::FileSP err = nullptr); | |
| ~Locker() override; | |
| private: | |
| bool DoAcquireLock(); | |
| bool DoInitSession(uint16_t on_entry_flags, lldb::FileSP in, | |
| lldb::FileSP out, lldb::FileSP err); | |
| bool DoFreeLock(); | |
| bool DoTearDownSession(); | |
| bool m_teardown_session; | |
| ScriptInterpreterPythonImpl *m_python_interpreter; | |
| PyGILState_STATE m_GILState; | |
| }; | |
| static bool BreakpointCallbackFunction(void *baton, | |
| StoppointCallbackContext *context, | |
| 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); | |
| static void Initialize(); | |
| class SynchronicityHandler { | |
| private: | |
| lldb::DebuggerSP m_debugger_sp; | |
| ScriptedCommandSynchronicity m_synch_wanted; | |
| bool m_old_asynch; | |
| public: | |
| SynchronicityHandler(lldb::DebuggerSP, ScriptedCommandSynchronicity); | |
| ~SynchronicityHandler(); | |
| }; | |
| enum class AddLocation { Beginning, End }; | |
| static void AddToSysPath(AddLocation location, std::string path); | |
| bool EnterSession(uint16_t on_entry_flags, lldb::FileSP in, lldb::FileSP out, | |
| lldb::FileSP err); | |
| void LeaveSession(); | |
| uint32_t IsExecutingPython() { | |
| std::lock_guard<std::mutex> guard(m_mutex); | |
| return m_lock_count > 0; | |
| } | |
| uint32_t IncrementLockCount() { | |
| std::lock_guard<std::mutex> guard(m_mutex); | |
| return ++m_lock_count; | |
| } | |
| uint32_t DecrementLockCount() { | |
| std::lock_guard<std::mutex> guard(m_mutex); | |
| if (m_lock_count > 0) | |
| --m_lock_count; | |
| return m_lock_count; | |
| } | |
| enum ActiveIOHandler { | |
| eIOHandlerNone, | |
| eIOHandlerBreakpoint, | |
| eIOHandlerWatchpoint | |
| }; | |
| python::PythonModule &GetMainModule(); | |
| python::PythonDictionary &GetSessionDictionary(); | |
| python::PythonDictionary &GetSysModuleDictionary(); | |
| llvm::Expected<unsigned> GetMaxPositionalArgumentsForCallable( | |
| const llvm::StringRef &callable_name) override; | |
| bool GetEmbeddedInterpreterModuleObjects(); | |
| bool SetStdHandle(lldb::FileSP file, const char *py_name, | |
| python::PythonObject &save_file, const char *mode); | |
| python::PythonObject m_saved_stdin; | |
| python::PythonObject m_saved_stdout; | |
| python::PythonObject m_saved_stderr; | |
| python::PythonModule m_main_module; | |
| python::PythonDictionary m_session_dict; | |
| python::PythonDictionary m_sys_module_dict; | |
| python::PythonObject m_run_one_line_function; | |
| python::PythonObject m_run_one_line_str_global; | |
| std::string m_dictionary_name; | |
| ActiveIOHandler m_active_io_handler; | |
| bool m_session_is_active; | |
| bool m_pty_secondary_is_open; | |
| bool m_valid_session; | |
| uint32_t m_lock_count; | |
| std::mutex m_mutex; | |
| PyThreadState *m_command_thread_state; | |
| }; | |
| class IOHandlerPythonInterpreter : public IOHandler { | |
| public: | |
| IOHandlerPythonInterpreter(Debugger &debugger, | |
| ScriptInterpreterPythonImpl *python) | |
| : IOHandler(debugger, IOHandler::Type::PythonInterpreter), | |
| m_python(python) {} | |
| ~IOHandlerPythonInterpreter() override = default; | |
| llvm::StringRef GetControlSequence(char ch) override { | |
| static constexpr llvm::StringLiteral control_sequence("quit()\n"); | |
| if (ch == 'd') | |
| return control_sequence; | |
| return {}; | |
| } | |
| void Run() override { | |
| if (m_python) { | |
| int stdin_fd = GetInputFD(); | |
| if (stdin_fd >= 0) { | |
| Terminal terminal(stdin_fd); | |
| TerminalState terminal_state(terminal); | |
| if (terminal.IsATerminal()) { | |
| // FIXME: error handling? | |
| llvm::consumeError(terminal.SetCanonical(false)); | |
| llvm::consumeError(terminal.SetEcho(true)); | |
| } | |
| ScriptInterpreterPythonImpl::Locker locker( | |
| m_python, | |
| ScriptInterpreterPythonImpl::Locker::AcquireLock | | |
| ScriptInterpreterPythonImpl::Locker::InitSession | | |
| ScriptInterpreterPythonImpl::Locker::InitGlobals, | |
| ScriptInterpreterPythonImpl::Locker::FreeAcquiredLock | | |
| ScriptInterpreterPythonImpl::Locker::TearDownSession); | |
| // The following call drops into the embedded interpreter loop and | |
| // stays there until the user chooses to exit from the Python | |
| // interpreter. This embedded interpreter will, as any Python code that | |
| // performs I/O, unlock the GIL before a system call that can hang, and | |
| // lock it when the syscall has returned. | |
| // We need to surround the call to the embedded interpreter with calls | |
| // to PyGILState_Ensure and PyGILState_Release (using the Locker | |
| // above). This is because Python has a global lock which must be held | |
| // whenever we want to touch any Python objects. Otherwise, if the user | |
| // calls Python code, the interpreter state will be off, and things | |
| // could hang (it's happened before). | |
| StreamString run_string; | |
| run_string.Printf("run_python_interpreter (%s)", | |
| m_python->GetDictionaryName()); | |
| python::RunSimpleString(run_string.GetData()); | |
| } | |
| } | |
| SetIsDone(true); | |
| } | |
| void Cancel() override {} | |
| bool Interrupt() override { return m_python->Interrupt(); } | |
| void GotEOF() override {} | |
| protected: | |
| ScriptInterpreterPythonImpl *m_python; | |
| }; | |
| } // namespace lldb_private | |
| #endif // LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_PYTHON_SCRIPTINTERPRETERPYTHONIMPL_H |