You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
487 lines
17 KiB
487 lines
17 KiB
4 months ago
|
//===-- 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
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "lldb/Host/Config.h"
|
||
|
|
||
|
#if LLDB_ENABLE_PYTHON
|
||
|
|
||
|
#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
|
||
|
CreateScriptedThreadPlan(const char *class_name,
|
||
|
StructuredDataImpl *args_data,
|
||
|
std::string &error_str,
|
||
|
lldb::ThreadPlanSP thread_plan) override;
|
||
|
|
||
|
bool ScriptedThreadPlanExplainsStop(StructuredData::ObjectSP implementor_sp,
|
||
|
Event *event,
|
||
|
bool &script_error) override;
|
||
|
|
||
|
bool ScriptedThreadPlanShouldStop(StructuredData::ObjectSP implementor_sp,
|
||
|
Event *event, bool &script_error) override;
|
||
|
|
||
|
bool ScriptedThreadPlanIsStale(StructuredData::ObjectSP implementor_sp,
|
||
|
bool &script_error) override;
|
||
|
|
||
|
lldb::StateType
|
||
|
ScriptedThreadPlanGetRunState(StructuredData::ObjectSP implementor_sp,
|
||
|
bool &script_error) override;
|
||
|
|
||
|
StructuredData::GenericSP
|
||
|
CreateScriptedBreakpointResolver(const char *class_name,
|
||
|
StructuredDataImpl *args_data,
|
||
|
lldb::BreakpointSP &bkpt_sp) override;
|
||
|
bool ScriptedBreakpointResolverSearchCallback(
|
||
|
StructuredData::GenericSP implementor_sp,
|
||
|
SymbolContext *sym_ctx) override;
|
||
|
|
||
|
lldb::SearchDepth ScriptedBreakpointResolverSearchDepth(
|
||
|
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
|
||
|
GetRecognizedArguments(const StructuredData::ObjectSP &implementor,
|
||
|
lldb::StackFrameSP frame_sp) override;
|
||
|
|
||
|
StructuredData::GenericSP
|
||
|
OSPlugin_CreatePluginObject(const char *class_name,
|
||
|
lldb::ProcessSP process_sp) override;
|
||
|
|
||
|
StructuredData::DictionarySP
|
||
|
OSPlugin_RegisterInfo(StructuredData::ObjectSP os_plugin_object_sp) override;
|
||
|
|
||
|
StructuredData::ArraySP
|
||
|
OSPlugin_ThreadsInfo(StructuredData::ObjectSP os_plugin_object_sp) override;
|
||
|
|
||
|
StructuredData::StringSP
|
||
|
OSPlugin_RegisterContextData(StructuredData::ObjectSP os_plugin_object_sp,
|
||
|
lldb::tid_t thread_id) override;
|
||
|
|
||
|
StructuredData::DictionarySP
|
||
|
OSPlugin_CreateThread(StructuredData::ObjectSP os_plugin_object_sp,
|
||
|
lldb::tid_t tid, lldb::addr_t context) 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;
|
||
|
|
||
|
int 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;
|
||
|
|
||
|
Status GenerateFunction(const char *signature,
|
||
|
const StringList &input) override;
|
||
|
|
||
|
Status GenerateBreakpointCommandCallbackData(
|
||
|
StringList &input,
|
||
|
std::string &output,
|
||
|
bool has_extra_args) override;
|
||
|
|
||
|
bool GenerateWatchpointCommandCallbackData(StringList &input,
|
||
|
std::string &output) override;
|
||
|
|
||
|
bool GetScriptedSummary(const char *function_name, lldb::ValueObjectSP valobj,
|
||
|
StructuredData::ObjectSP &callee_wrapper_sp,
|
||
|
const TypeSummaryOptions &options,
|
||
|
std::string &retval) 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;
|
||
|
|
||
|
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, bool init_session,
|
||
|
lldb_private::Status &error,
|
||
|
StructuredData::ObjectSP *module_sp = nullptr,
|
||
|
FileSpec extra_search_dir = {}) override;
|
||
|
|
||
|
bool IsReservedWord(const char *word) override;
|
||
|
|
||
|
std::unique_ptr<ScriptInterpreterLocker> AcquireInterpreterLock() override;
|
||
|
|
||
|
void CollectDataForBreakpointCommandCallback(
|
||
|
std::vector<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) 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;
|
||
|
|
||
|
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
|
||
|
lldb_private::ConstString GetPluginName() override;
|
||
|
|
||
|
uint32_t GetPluginVersion() override;
|
||
|
|
||
|
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 InitializePrivate();
|
||
|
|
||
|
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() const { return m_lock_count > 0; }
|
||
|
|
||
|
uint32_t IncrementLockCount() { return ++m_lock_count; }
|
||
|
|
||
|
uint32_t DecrementLockCount() {
|
||
|
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;
|
||
|
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 {}
|
||
|
|
||
|
ConstString GetControlSequence(char ch) override {
|
||
|
if (ch == 'd')
|
||
|
return ConstString("quit()\n");
|
||
|
return ConstString();
|
||
|
}
|
||
|
|
||
|
void Run() override {
|
||
|
if (m_python) {
|
||
|
int stdin_fd = GetInputFD();
|
||
|
if (stdin_fd >= 0) {
|
||
|
Terminal terminal(stdin_fd);
|
||
|
TerminalState terminal_state;
|
||
|
const bool is_a_tty = terminal.IsATerminal();
|
||
|
|
||
|
if (is_a_tty) {
|
||
|
terminal_state.Save(stdin_fd, false);
|
||
|
terminal.SetCanonical(false);
|
||
|
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());
|
||
|
PyRun_SimpleString(run_string.GetData());
|
||
|
|
||
|
if (is_a_tty)
|
||
|
terminal_state.Restore();
|
||
|
}
|
||
|
}
|
||
|
SetIsDone(true);
|
||
|
}
|
||
|
|
||
|
void Cancel() override {}
|
||
|
|
||
|
bool Interrupt() override { return m_python->Interrupt(); }
|
||
|
|
||
|
void GotEOF() override {}
|
||
|
|
||
|
protected:
|
||
|
ScriptInterpreterPythonImpl *m_python;
|
||
|
};
|
||
|
|
||
|
} // namespace lldb_private
|
||
|
|
||
|
#endif
|