jimingham a69ecb2420
Add the ability to define a Python based command that uses CommandObjectParsed (#70734)
This allows you to specify options and arguments and their definitions
and then have lldb handle the completions, help, etc. in the same way
that lldb does for its parsed commands internally.

This feature has some design considerations as well as the code, so I've
also set up an RFC, but I did this one first and will put the RFC
address in here once I've pushed it...

Note, the lldb "ParsedCommand interface" doesn't actually do all the
work that it should. For instance, saying the type of an option that has
a completer doesn't automatically hook up the completer, and ditto for
argument values. We also do almost no work to verify that the arguments
match their definition, or do auto-completion for them. This patch
allows you to make a command that's bug-for-bug compatible with built-in
ones, but I didn't want to stall it on getting the auto-command checking
to work all the way correctly.

As an overall design note, my primary goal here was to make an interface
that worked well in the script language. For that I needed, for
instance, to have a property-based way to get all the option values that
were specified. It was much more convenient to do that by making a
fairly bare-bones C interface to define the options and arguments of a
command, and set their values, and then wrap that in a Python class
(installed along with the other bits of the lldb python module) which
you can then derive from to make your new command. This approach will
also make it easier to experiment.

See the file test_commands.py in the test case for examples of how this
works.
2024-02-13 11:09:47 -08:00

280 lines
12 KiB
C++

//===-- ScriptInterpreterPython.h -------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SWIGPYTHONBRIDGE_H
#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SWIGPYTHONBRIDGE_H
#include <optional>
#include <string>
#include "lldb/Host/Config.h"
#if LLDB_ENABLE_PYTHON
// LLDB Python header must be included first
#include "lldb-python.h"
#include "Plugins/ScriptInterpreter/Python/PythonDataObjects.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-types.h"
#include "llvm/Support/Error.h"
namespace lldb {
class SBEvent;
class SBCommandReturnObject;
class SBValue;
class SBStream;
class SBStructuredData;
class SBFileSpec;
class SBModuleSpec;
class SBStringList;
} // namespace lldb
namespace lldb_private {
namespace python {
typedef struct swig_type_info swig_type_info;
python::PythonObject ToSWIGHelper(void *obj, swig_type_info *info);
/// A class that automatically clears an SB object when it goes out of scope.
/// Use for cases where the SB object points to a temporary/unowned entity.
template <typename T> class ScopedPythonObject : PythonObject {
public:
ScopedPythonObject(T *sb, swig_type_info *info)
: PythonObject(ToSWIGHelper(sb, info)), m_sb(sb) {}
~ScopedPythonObject() {
if (m_sb)
*m_sb = T();
}
ScopedPythonObject(ScopedPythonObject &&rhs)
: PythonObject(std::move(rhs)), m_sb(std::exchange(rhs.m_sb, nullptr)) {}
ScopedPythonObject(const ScopedPythonObject &) = delete;
ScopedPythonObject &operator=(const ScopedPythonObject &) = delete;
ScopedPythonObject &operator=(ScopedPythonObject &&) = delete;
const PythonObject &obj() const { return *this; }
private:
T *m_sb;
};
// TODO: We may want to support other languages in the future w/ SWIG (we
// already support Lua right now, for example). We could create a generic
// SWIGBridge class and have this one specialize it, something like this:
//
// <typename T>
// class SWIGBridge {
// static T ToSWIGWrapper(...);
// };
//
// class SWIGPythonBridge : public SWIGBridge<PythonObject> {
// template<> static PythonObject ToSWIGWrapper(...);
// };
//
// And we should be able to more easily support things like Lua
class SWIGBridge {
public:
static PythonObject ToSWIGWrapper(std::unique_ptr<lldb::SBValue> value_sb);
static PythonObject ToSWIGWrapper(lldb::ValueObjectSP value_sp);
static PythonObject ToSWIGWrapper(lldb::TargetSP target_sp);
static PythonObject ToSWIGWrapper(lldb::ProcessSP process_sp);
static PythonObject ToSWIGWrapper(lldb::ThreadPlanSP thread_plan_sp);
static PythonObject ToSWIGWrapper(lldb::BreakpointSP breakpoint_sp);
static PythonObject ToSWIGWrapper(const Status &status);
static PythonObject ToSWIGWrapper(const StructuredDataImpl &data_impl);
static PythonObject ToSWIGWrapper(lldb::ThreadSP thread_sp);
static PythonObject ToSWIGWrapper(lldb::StackFrameSP frame_sp);
static PythonObject ToSWIGWrapper(lldb::DebuggerSP debugger_sp);
static PythonObject ToSWIGWrapper(lldb::WatchpointSP watchpoint_sp);
static PythonObject ToSWIGWrapper(lldb::BreakpointLocationSP bp_loc_sp);
static PythonObject ToSWIGWrapper(lldb::TypeImplSP type_impl_sp);
static PythonObject ToSWIGWrapper(lldb::ExecutionContextRefSP ctx_sp);
static PythonObject ToSWIGWrapper(const TypeSummaryOptions &summary_options);
static PythonObject ToSWIGWrapper(const SymbolContext &sym_ctx);
static PythonObject ToSWIGWrapper(lldb::ProcessAttachInfoSP attach_info_sp);
static PythonObject ToSWIGWrapper(lldb::ProcessLaunchInfoSP launch_info_sp);
static PythonObject ToSWIGWrapper(lldb::DataExtractorSP data_extractor_sp);
static PythonObject ToSWIGWrapper(std::unique_ptr<lldb::SBStream> stream_sb);
static PythonObject
ToSWIGWrapper(std::unique_ptr<lldb::SBStructuredData> data_sb);
static PythonObject
ToSWIGWrapper(std::unique_ptr<lldb::SBFileSpec> file_spec_sb);
static PythonObject
ToSWIGWrapper(std::unique_ptr<lldb::SBModuleSpec> module_spec_sb);
static python::ScopedPythonObject<lldb::SBCommandReturnObject>
ToSWIGWrapper(CommandReturnObject &cmd_retobj);
static python::ScopedPythonObject<lldb::SBEvent> ToSWIGWrapper(Event *event);
// These prototypes are the Pythonic implementations of the required
// callbacks. Although these are scripting-language specific, their definition
// depends on the public API.
static 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_private::StructuredDataImpl &args_impl);
static bool LLDBSwigPythonWatchpointCallbackFunction(
const char *python_function_name, const char *session_dictionary_name,
const lldb::StackFrameSP &sb_frame, const lldb::WatchpointSP &sb_wp);
static bool
LLDBSwigPythonFormatterCallbackFunction(const char *python_function_name,
const char *session_dictionary_name,
lldb::TypeImplSP type_impl_sp);
static bool LLDBSwigPythonCallTypeScript(
const char *python_function_name, const void *session_dictionary,
const lldb::ValueObjectSP &valobj_sp, void **pyfunct_wrapper,
const lldb::TypeSummaryOptionsSP &options_sp, std::string &retval);
static python::PythonObject
LLDBSwigPythonCreateSyntheticProvider(const char *python_class_name,
const char *session_dictionary_name,
const lldb::ValueObjectSP &valobj_sp);
static python::PythonObject
LLDBSwigPythonCreateCommandObject(const char *python_class_name,
const char *session_dictionary_name,
lldb::DebuggerSP debugger_sp);
static python::PythonObject LLDBSwigPythonCreateScriptedThreadPlan(
const char *python_class_name, const char *session_dictionary_name,
const StructuredDataImpl &args_data, std::string &error_string,
const lldb::ThreadPlanSP &thread_plan_sp);
static bool LLDBSWIGPythonCallThreadPlan(void *implementor,
const char *method_name,
lldb_private::Event *event_sp,
bool &got_error);
static bool LLDBSWIGPythonCallThreadPlan(void *implementor,
const char *method_name,
lldb_private::Stream *stream,
bool &got_error);
static python::PythonObject LLDBSwigPythonCreateScriptedBreakpointResolver(
const char *python_class_name, const char *session_dictionary_name,
const StructuredDataImpl &args, const lldb::BreakpointSP &bkpt_sp);
static unsigned int
LLDBSwigPythonCallBreakpointResolver(void *implementor,
const char *method_name,
lldb_private::SymbolContext *sym_ctx);
static python::PythonObject LLDBSwigPythonCreateScriptedStopHook(
lldb::TargetSP target_sp, const char *python_class_name,
const char *session_dictionary_name, const StructuredDataImpl &args,
lldb_private::Status &error);
static bool
LLDBSwigPythonStopHookCallHandleStop(void *implementor,
lldb::ExecutionContextRefSP exc_ctx,
lldb::StreamSP stream);
static size_t LLDBSwigPython_CalculateNumChildren(PyObject *implementor,
uint32_t max);
static PyObject *LLDBSwigPython_GetChildAtIndex(PyObject *implementor,
uint32_t idx);
static int LLDBSwigPython_GetIndexOfChildWithName(PyObject *implementor,
const char *child_name);
static lldb::ValueObjectSP
LLDBSWIGPython_GetValueObjectSPFromSBValue(void *data);
static bool LLDBSwigPython_UpdateSynthProviderInstance(PyObject *implementor);
static bool
LLDBSwigPython_MightHaveChildrenSynthProviderInstance(PyObject *implementor);
static PyObject *
LLDBSwigPython_GetValueSynthProviderInstance(PyObject *implementor);
static bool
LLDBSwigPythonCallCommand(const char *python_function_name,
const char *session_dictionary_name,
lldb::DebuggerSP debugger, const char *args,
lldb_private::CommandReturnObject &cmd_retobj,
lldb::ExecutionContextRefSP exe_ctx_ref_sp);
static bool
LLDBSwigPythonCallCommandObject(PyObject *implementor,
lldb::DebuggerSP debugger, const char *args,
lldb_private::CommandReturnObject &cmd_retobj,
lldb::ExecutionContextRefSP exe_ctx_ref_sp);
static bool
LLDBSwigPythonCallParsedCommandObject(PyObject *implementor,
lldb::DebuggerSP debugger,
StructuredDataImpl &args_impl,
lldb_private::CommandReturnObject &cmd_retobj,
lldb::ExecutionContextRefSP exe_ctx_ref_sp);
static bool LLDBSwigPythonCallModuleInit(const char *python_module_name,
const char *session_dictionary_name,
lldb::DebuggerSP debugger);
static python::PythonObject
LLDBSWIGPythonCreateOSPlugin(const char *python_class_name,
const char *session_dictionary_name,
const lldb::ProcessSP &process_sp);
static python::PythonObject
LLDBSWIGPython_CreateFrameRecognizer(const char *python_class_name,
const char *session_dictionary_name);
static PyObject *
LLDBSwigPython_GetRecognizedArguments(PyObject *implementor,
const lldb::StackFrameSP &frame_sp);
static bool LLDBSWIGPythonRunScriptKeywordProcess(
const char *python_function_name, const char *session_dictionary_name,
const lldb::ProcessSP &process, std::string &output);
static std::optional<std::string>
LLDBSWIGPythonRunScriptKeywordThread(const char *python_function_name,
const char *session_dictionary_name,
lldb::ThreadSP thread);
static bool LLDBSWIGPythonRunScriptKeywordTarget(
const char *python_function_name, const char *session_dictionary_name,
const lldb::TargetSP &target, std::string &output);
static std::optional<std::string>
LLDBSWIGPythonRunScriptKeywordFrame(const char *python_function_name,
const char *session_dictionary_name,
lldb::StackFrameSP frame);
static bool LLDBSWIGPythonRunScriptKeywordValue(
const char *python_function_name, const char *session_dictionary_name,
const lldb::ValueObjectSP &value, std::string &output);
static void *
LLDBSWIGPython_GetDynamicSetting(void *module, const char *setting,
const lldb::TargetSP &target_sp);
};
void *LLDBSWIGPython_CastPyObjectToSBData(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBLaunchInfo(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBError(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBValue(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data);
} // namespace python
} // namespace lldb_private
#endif // LLDB_ENABLE_PYTHON
#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_SWIGPYTHONBRIDGE_H