Skip to content

[lldb/Interpreter] Introduce ScriptedStopHook{,Python}Interface & make use of it #109498

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lldb/bindings/python/python-swigsafecast.swig
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ PythonObject SWIGBridge::ToSWIGWrapper(lldb::BreakpointSP breakpoint_sp) {
SWIGTYPE_p_lldb__SBBreakpoint);
}

PythonObject SWIGBridge::ToSWIGWrapper(Status status) {
PythonObject SWIGBridge::ToSWIGWrapper(Status&& status) {
return ToSWIGHelper(new lldb::SBError(std::move(status)), SWIGTYPE_p_lldb__SBError);
}

Expand Down
111 changes: 13 additions & 98 deletions lldb/bindings/python/python-wrapper.swig
Original file line number Diff line number Diff line change
Expand Up @@ -301,104 +301,6 @@ unsigned int lldb_private::python::SWIGBridge::LLDBSwigPythonCallBreakpointResol
return ret_val;
}

PythonObject lldb_private::python::SWIGBridge::LLDBSwigPythonCreateScriptedStopHook(
lldb::TargetSP target_sp, const char *python_class_name,
const char *session_dictionary_name, const StructuredDataImpl &args_impl,
Status &error) {
if (python_class_name == NULL || python_class_name[0] == '\0') {
error = Status::FromErrorString("Empty class name.");
return PythonObject();
}
if (!session_dictionary_name) {
error = Status::FromErrorString("No session dictionary");
return PythonObject();
}

PyErr_Cleaner py_err_cleaner(true);

auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(
session_dictionary_name);
auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(
python_class_name, dict);

if (!pfunc.IsAllocated()) {
error = Status::FromErrorStringWithFormat("Could not find class: %s.",
python_class_name);
return PythonObject();
}

PythonObject result =
pfunc(SWIGBridge::ToSWIGWrapper(target_sp), SWIGBridge::ToSWIGWrapper(args_impl), dict);

if (result.IsAllocated()) {
// Check that the handle_stop callback is defined:
auto callback_func = result.ResolveName<PythonCallable>("handle_stop");
if (callback_func.IsAllocated()) {
if (auto args_info = callback_func.GetArgInfo()) {
size_t num_args = (*args_info).max_positional_args;
if (num_args != 2) {
error = Status::FromErrorStringWithFormat(
"Wrong number of args for "
"handle_stop callback, should be 2 (excluding self), got: %zu",
num_args);
return PythonObject();
} else
return result;
} else {
error = Status::FromErrorString(
"Couldn't get num arguments for handle_stop "
"callback.");
return PythonObject();
}
return result;
} else {
error = Status::FromErrorStringWithFormat(
"Class \"%s\" is missing the required "
"handle_stop callback.",
python_class_name);
}
}
return PythonObject();
}

bool lldb_private::python::SWIGBridge::LLDBSwigPythonStopHookCallHandleStop(
void *implementor, lldb::ExecutionContextRefSP exc_ctx_sp,
lldb::StreamSP stream) {
// handle_stop will return a bool with the meaning "should_stop"...
// If you return nothing we'll assume we are going to stop.
// Also any errors should return true, since we should stop on error.

PyErr_Cleaner py_err_cleaner(false);
PythonObject self(PyRefType::Borrowed, static_cast<PyObject *>(implementor));
auto pfunc = self.ResolveName<PythonCallable>("handle_stop");

if (!pfunc.IsAllocated())
return true;

std::shared_ptr<lldb::SBStream> sb_stream = std::make_shared<lldb::SBStream>();
PythonObject sb_stream_arg = SWIGBridge::ToSWIGWrapper(sb_stream);
PythonObject result =
pfunc(SWIGBridge::ToSWIGWrapper(std::move(exc_ctx_sp)), sb_stream_arg);

if (PyErr_Occurred()) {
stream->PutCString("Python error occurred handling stop-hook.");
PyErr_Print();
PyErr_Clear();
return true;
}

// Now add the result to the output stream. SBStream only
// makes an internally help StreamString which I can't interpose, so I
// have to copy it over here.
stream->PutCString(sb_stream->GetData());
sb_stream_arg.release();

if (result.get() == Py_False)
return false;
else
return true;
}

// wrapper that calls an optional instance member of an object taking no
// arguments
static PyObject *LLDBSwigPython_CallOptionalMember(
Expand Down Expand Up @@ -677,6 +579,19 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyOb
return sb_ptr;
}

void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBExecutionContext(PyObject *
data) {
lldb::SBExecutionContext *sb_ptr = NULL;

int valid_cast = SWIG_ConvertPtr(data, (void **)&sb_ptr,
SWIGTYPE_p_lldb__SBExecutionContext, 0);

if (valid_cast == -1)
return NULL;

return sb_ptr;
}

bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallCommand(
const char *python_function_name, const char *session_dictionary_name,
lldb::DebuggerSP debugger, const char *args,
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/API/SBExecutionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <vector>

namespace lldb_private {
class ScriptInterpreter;
namespace python {
class SWIGBridge;
}
Expand Down Expand Up @@ -55,6 +56,7 @@ class LLDB_API SBExecutionContext {

protected:
friend class lldb_private::python::SWIGBridge;
friend class lldb_private::ScriptInterpreter;

lldb_private::ExecutionContextRef *get() const;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===-- ScriptedStopHookInterface.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_INTERPRETER_INTERFACES_SCRIPTEDSTOPHOOKINTERFACE_H
#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDSTOPHOOKINTERFACE_H

#include "lldb/lldb-private.h"

#include "ScriptedInterface.h"

namespace lldb_private {
class ScriptedStopHookInterface : public ScriptedInterface {
public:
virtual llvm::Expected<StructuredData::GenericSP>
CreatePluginObject(llvm::StringRef class_name, lldb::TargetSP target_sp,
const StructuredDataImpl &args_sp) = 0;

/// "handle_stop" will return a bool with the meaning "should_stop"...
/// If nothing is returned, we'll assume we are going to stop.
/// Also any errors should return true, since we should stop on error.
virtual llvm::Expected<bool> HandleStop(ExecutionContext &exe_ctx,
lldb::StreamSP &output_sp) {
return true;
}
};
} // namespace lldb_private

#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDSTOPHOOKINTERFACE_H
26 changes: 8 additions & 18 deletions lldb/include/lldb/Interpreter/ScriptInterpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "lldb/API/SBData.h"
#include "lldb/API/SBError.h"
#include "lldb/API/SBEvent.h"
#include "lldb/API/SBExecutionContext.h"
#include "lldb/API/SBLaunchInfo.h"
#include "lldb/API/SBMemoryRegionInfo.h"
#include "lldb/API/SBStream.h"
Expand Down Expand Up @@ -271,24 +272,6 @@ class ScriptInterpreter : public PluginInterface {
return lldb::eSearchDepthModule;
}

virtual StructuredData::GenericSP
CreateScriptedStopHook(lldb::TargetSP target_sp, const char *class_name,
const StructuredDataImpl &args_data, Status &error) {
error =
Status::FromErrorString("Creating scripted stop-hooks with the current "
"script interpreter is not supported.");
return StructuredData::GenericSP();
}

// This dispatches to the handle_stop method of the stop-hook class. It
// returns a "should_stop" bool.
virtual bool
ScriptedStopHookHandleStop(StructuredData::GenericSP implementor_sp,
ExecutionContext &exc_ctx,
lldb::StreamSP stream_sp) {
return true;
}

virtual StructuredData::ObjectSP
LoadPluginModule(const FileSpec &file_spec, lldb_private::Status &error) {
return StructuredData::ObjectSP();
Expand Down Expand Up @@ -561,6 +544,10 @@ class ScriptInterpreter : public PluginInterface {
return {};
}

virtual lldb::ScriptedStopHookInterfaceSP CreateScriptedStopHookInterface() {
return {};
}

virtual StructuredData::ObjectSP
CreateStructuredDataFromScriptObject(ScriptObject obj) {
return {};
Expand All @@ -587,6 +574,9 @@ class ScriptInterpreter : public PluginInterface {
std::optional<MemoryRegionInfo> GetOpaqueTypeFromSBMemoryRegionInfo(
const lldb::SBMemoryRegionInfo &mem_region) const;

lldb::ExecutionContextRefSP GetOpaqueTypeFromSBExecutionContext(
const lldb::SBExecutionContext &exe_ctx) const;

protected:
Debugger &m_debugger;
lldb::ScriptLanguage m_script_lang;
Expand Down
3 changes: 1 addition & 2 deletions lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -1391,8 +1391,7 @@ class Target : public std::enable_shared_from_this<Target>,
/// This holds the dictionary of keys & values that can be used to
/// parametrize any given callback's behavior.
StructuredDataImpl m_extra_args;
/// This holds the python callback object.
StructuredData::GenericSP m_implementation_sp;
lldb::ScriptedStopHookInterfaceSP m_interface_sp;

/// Use CreateStopHook to make a new empty stop hook. The GetCommandPointer
/// and fill it with commands, and SetSpecifier to set the specifier shared
Expand Down
3 changes: 3 additions & 0 deletions lldb/include/lldb/lldb-forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ class ScriptInterpreterLocker;
class ScriptedMetadata;
class ScriptedPlatformInterface;
class ScriptedProcessInterface;
class ScriptedStopHookInterface;
class ScriptedThreadInterface;
class ScriptedThreadPlanInterface;
class ScriptedSyntheticChildren;
Expand Down Expand Up @@ -408,6 +409,8 @@ typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface>
ScriptedPlatformInterfaceUP;
typedef std::unique_ptr<lldb_private::ScriptedProcessInterface>
ScriptedProcessInterfaceUP;
typedef std::shared_ptr<lldb_private::ScriptedStopHookInterface>
ScriptedStopHookInterfaceSP;
typedef std::shared_ptr<lldb_private::ScriptedThreadInterface>
ScriptedThreadInterfaceSP;
typedef std::shared_ptr<lldb_private::ScriptedThreadPlanInterface>
Expand Down
6 changes: 6 additions & 0 deletions lldb/source/Interpreter/ScriptInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo(
return *mem_region.m_opaque_up.get();
}

lldb::ExecutionContextRefSP
ScriptInterpreter::GetOpaqueTypeFromSBExecutionContext(
const lldb::SBExecutionContext &exe_ctx) const {
return exe_ctx.m_exe_ctx_sp;
}

lldb::ScriptLanguage
ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
if (language.equals_insensitive(LanguageToString(eScriptLanguageNone)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN
ScriptedPlatformPythonInterface.cpp
ScriptedProcessPythonInterface.cpp
ScriptedPythonInterface.cpp
ScriptedStopHookPythonInterface.cpp
ScriptedThreadPlanPythonInterface.cpp
ScriptedThreadPythonInterface.cpp

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ void ScriptInterpreterPythonInterfaces::Initialize() {
OperatingSystemPythonInterface::Initialize();
ScriptedPlatformPythonInterface::Initialize();
ScriptedProcessPythonInterface::Initialize();
ScriptedStopHookPythonInterface::Initialize();
ScriptedThreadPlanPythonInterface::Initialize();
}

void ScriptInterpreterPythonInterfaces::Terminate() {
OperatingSystemPythonInterface::Terminate();
ScriptedPlatformPythonInterface::Terminate();
ScriptedProcessPythonInterface::Terminate();
ScriptedStopHookPythonInterface::Terminate();
ScriptedThreadPlanPythonInterface::Terminate();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "OperatingSystemPythonInterface.h"
#include "ScriptedPlatformPythonInterface.h"
#include "ScriptedProcessPythonInterface.h"
#include "ScriptedStopHookPythonInterface.h"
#include "ScriptedThreadPlanPythonInterface.h"

namespace lldb_private {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,23 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<
return m_interpreter.GetOpaqueTypeFromSBMemoryRegionInfo(*sb_mem_reg_info);
}

template <>
lldb::ExecutionContextRefSP
ScriptedPythonInterface::ExtractValueFromPythonObject<
lldb::ExecutionContextRefSP>(python::PythonObject &p, Status &error) {

lldb::SBExecutionContext *sb_exe_ctx =
reinterpret_cast<lldb::SBExecutionContext *>(
python::LLDBSWIGPython_CastPyObjectToSBExecutionContext(p.get()));

if (!sb_exe_ctx) {
error = Status::FromErrorStringWithFormat(
"Couldn't cast lldb::SBExecutionContext to "
"lldb::ExecutionContextRefSP.");
return {};
}

return m_interpreter.GetOpaqueTypeFromSBExecutionContext(*sb_exe_ctx);
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,35 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
llvm::Expected<PythonObject> expected_return_object =
create_error("Resulting object is not initialized.");

std::apply(
[&init, &expected_return_object](auto &&...args) {
llvm::consumeError(expected_return_object.takeError());
expected_return_object = init(args...);
},
transformed_args);
// This relax the requirement on the number of argument for
// initializing scripting extension if the size of the interface
// parameter pack contains 1 less element than the extension maximum
// number of positional arguments for this initializer.
//
// This addresses the cases where the embedded interpreter session
// dictionary is passed to the extension initializer which is not used
// most of the time.
size_t num_args = sizeof...(Args);
if (num_args != arg_info->max_positional_args) {
if (num_args != arg_info->max_positional_args - 1)
return create_error("Passed arguments ({0}) doesn't match the number "
"of expected arguments ({1}).",
num_args, arg_info->max_positional_args);

std::apply(
[&init, &expected_return_object](auto &&...args) {
llvm::consumeError(expected_return_object.takeError());
expected_return_object = init(args...);
},
std::tuple_cat(transformed_args, std::make_tuple(dict)));
} else {
std::apply(
[&init, &expected_return_object](auto &&...args) {
llvm::consumeError(expected_return_object.takeError());
expected_return_object = init(args...);
},
transformed_args);
}

if (!expected_return_object)
return expected_return_object.takeError();
Expand Down Expand Up @@ -405,6 +428,10 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
return python::SWIGBridge::ToSWIGWrapper(arg);
}

python::PythonObject Transform(lldb::TargetSP arg) {
return python::SWIGBridge::ToSWIGWrapper(arg);
}

python::PythonObject Transform(lldb::ProcessSP arg) {
return python::SWIGBridge::ToSWIGWrapper(arg);
}
Expand Down Expand Up @@ -557,6 +584,11 @@ std::optional<MemoryRegionInfo>
ScriptedPythonInterface::ExtractValueFromPythonObject<
std::optional<MemoryRegionInfo>>(python::PythonObject &p, Status &error);

template <>
lldb::ExecutionContextRefSP
ScriptedPythonInterface::ExtractValueFromPythonObject<
lldb::ExecutionContextRefSP>(python::PythonObject &p, Status &error);

} // namespace lldb_private

#endif // LLDB_ENABLE_PYTHON
Expand Down
Loading
Loading