Skip to content

Commit

Permalink
[lldb/Interpreter] Introduce ScriptedStopHook{,Python}Interface & mak…
Browse files Browse the repository at this point in the history
…e use of it (#109498)

This patch re-lands #105449 and fixes the various test failures.

---------

Signed-off-by: Med Ismail Bennani <[email protected]>
  • Loading branch information
medismailben authored Sep 20, 2024
1 parent cdf2970 commit f732157
Show file tree
Hide file tree
Showing 22 changed files with 304 additions and 224 deletions.
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

0 comments on commit f732157

Please sign in to comment.