From 84b56202fbe150e06f92c855107489e08cc17bcd Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani Date: Thu, 4 Sep 2025 15:07:11 -0700 Subject: [lldb] Introduce ScriptedFrame affordance (#149622) This patch introduces a new scripting affordance in lldb: `ScriptedFrame`. This allows user to produce mock stackframes in scripted threads and scripted processes from a python script. With this change, StackFrame can be synthetized from different sources: - Either from a dictionary containing a load address, and a frame index, which is the legacy way. - Or by creating a ScriptedFrame python object. One particularity of synthezising stackframes from the ScriptedFrame python object, is that these frame have an optional PC, meaning that they don't have a report a valid PC and they can act as shells that just contain static information, like the frame function name, the list of variables or registers, etc. It can also provide a symbol context. rdar://157260006 Signed-off-by: Med Ismail Bennani Signed-off-by: Med Ismail Bennani --- .../Python/Interfaces/CMakeLists.txt | 1 + .../Interfaces/ScriptInterpreterPythonInterfaces.h | 1 + .../Interfaces/ScriptedFramePythonInterface.cpp | 157 +++++++++++++++++++++ .../Interfaces/ScriptedFramePythonInterface.h | 59 ++++++++ .../Python/Interfaces/ScriptedPythonInterface.cpp | 3 +- .../Interfaces/ScriptedThreadPythonInterface.cpp | 17 +++ .../Interfaces/ScriptedThreadPythonInterface.h | 5 + 7 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h (limited to 'lldb/source/Plugins/ScriptInterpreter/Python/Interfaces') diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt index 0437094..0910357 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt @@ -22,6 +22,7 @@ endif() add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN OperatingSystemPythonInterface.cpp ScriptInterpreterPythonInterfaces.cpp + ScriptedFramePythonInterface.cpp ScriptedPlatformPythonInterface.cpp ScriptedProcessPythonInterface.cpp ScriptedPythonInterface.cpp diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h index 02dc065..3814f46 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h @@ -17,6 +17,7 @@ #include "OperatingSystemPythonInterface.h" #include "ScriptedBreakpointPythonInterface.h" +#include "ScriptedFramePythonInterface.h" #include "ScriptedPlatformPythonInterface.h" #include "ScriptedProcessPythonInterface.h" #include "ScriptedStopHookPythonInterface.h" diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp new file mode 100644 index 0000000..20ca7a2 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp @@ -0,0 +1,157 @@ +//===----------------------------------------------------------------------===// +// +// 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" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Utility/Log.h" +#include "lldb/lldb-enumerations.h" + +#if LLDB_ENABLE_PYTHON + +// LLDB Python header must be included first +#include "../lldb-python.h" + +#include "../SWIGPythonBridge.h" +#include "../ScriptInterpreterPythonImpl.h" +#include "ScriptedFramePythonInterface.h" +#include + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::python; +using Locker = ScriptInterpreterPythonImpl::Locker; + +ScriptedFramePythonInterface::ScriptedFramePythonInterface( + ScriptInterpreterPythonImpl &interpreter) + : ScriptedFrameInterface(), ScriptedPythonInterface(interpreter) {} + +llvm::Expected +ScriptedFramePythonInterface::CreatePluginObject( + const llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) { + ExecutionContextRefSP exe_ctx_ref_sp = + std::make_shared(exe_ctx); + StructuredDataImpl sd_impl(args_sp); + return ScriptedPythonInterface::CreatePluginObject(class_name, script_obj, + exe_ctx_ref_sp, sd_impl); +} + +lldb::user_id_t ScriptedFramePythonInterface::GetID() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_id", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return LLDB_INVALID_FRAME_ID; + + return obj->GetUnsignedIntegerValue(LLDB_INVALID_FRAME_ID); +} + +lldb::addr_t ScriptedFramePythonInterface::GetPC() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_pc", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return LLDB_INVALID_ADDRESS; + + return obj->GetUnsignedIntegerValue(LLDB_INVALID_ADDRESS); +} + +std::optional ScriptedFramePythonInterface::GetSymbolContext() { + Status error; + auto sym_ctx = Dispatch("get_symbol_context", error); + + if (error.Fail()) { + return ErrorWithMessage(LLVM_PRETTY_FUNCTION, + error.AsCString(), error); + } + + return sym_ctx; +} + +std::optional ScriptedFramePythonInterface::GetFunctionName() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_function_name", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetStringValue().str(); +} + +std::optional +ScriptedFramePythonInterface::GetDisplayFunctionName() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_display_function_name", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetStringValue().str(); +} + +bool ScriptedFramePythonInterface::IsInlined() { + Status error; + StructuredData::ObjectSP obj = Dispatch("is_inlined", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return false; + + return obj->GetBooleanValue(); +} + +bool ScriptedFramePythonInterface::IsArtificial() { + Status error; + StructuredData::ObjectSP obj = Dispatch("is_artificial", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return false; + + return obj->GetBooleanValue(); +} + +bool ScriptedFramePythonInterface::IsHidden() { + Status error; + StructuredData::ObjectSP obj = Dispatch("is_hidden", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return false; + + return obj->GetBooleanValue(); +} + +StructuredData::DictionarySP ScriptedFramePythonInterface::GetRegisterInfo() { + Status error; + StructuredData::DictionarySP dict = + Dispatch("get_register_info", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, + error)) + return {}; + + return dict; +} + +std::optional ScriptedFramePythonInterface::GetRegisterContext() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_register_context", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetAsString()->GetValue().str(); +} + +#endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h new file mode 100644 index 0000000..3aff237 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// 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_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H +#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H + +#include "lldb/Host/Config.h" + +#if LLDB_ENABLE_PYTHON + +#include "ScriptedPythonInterface.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h" +#include + +namespace lldb_private { +class ScriptedFramePythonInterface : public ScriptedFrameInterface, + public ScriptedPythonInterface { +public: + ScriptedFramePythonInterface(ScriptInterpreterPythonImpl &interpreter); + + llvm::Expected + CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) override; + + llvm::SmallVector + GetAbstractMethodRequirements() const override { + return llvm::SmallVector({{"get_id"}}); + } + + lldb::user_id_t GetID() override; + + lldb::addr_t GetPC() override; + + std::optional GetSymbolContext() override; + + std::optional GetFunctionName() override; + + std::optional GetDisplayFunctionName() override; + + bool IsInlined() override; + + bool IsArtificial() override; + + bool IsHidden() override; + + StructuredData::DictionarySP GetRegisterInfo() override; + + std::optional GetRegisterContext() override; +}; +} // namespace lldb_private + +#endif // LLDB_ENABLE_PYTHON +#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index b49d1d8..8083cca 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -167,7 +167,8 @@ ScriptedPythonInterface::ExtractValueFromPythonObject< if (!sb_mem_reg_info) { error = Status::FromErrorStringWithFormat( - "Couldn't cast lldb::SBMemoryRegionInfo to lldb::MemoryRegionInfoSP."); + "Couldn't cast lldb::SBMemoryRegionInfo to " + "lldb_private::MemoryRegionInfo."); return {}; } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp index 8af89d7..fd4d231 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp @@ -144,4 +144,21 @@ StructuredData::ArraySP ScriptedThreadPythonInterface::GetExtendedInfo() { return arr; } +std::optional +ScriptedThreadPythonInterface::GetScriptedFramePluginName() { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_scripted_frame_plugin", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetStringValue().str(); +} + +lldb::ScriptedFrameInterfaceSP +ScriptedThreadPythonInterface::CreateScriptedFrameInterface() { + return m_interpreter.CreateScriptedFrameInterface(); +} + #endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h index 1fb23b3..043557a 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h @@ -51,6 +51,11 @@ public: std::optional GetRegisterContext() override; StructuredData::ArraySP GetExtendedInfo() override; + + std::optional GetScriptedFramePluginName() override; + +protected: + lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; }; } // namespace lldb_private -- cgit v1.1