llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Med Ismail Bennani (medismailben) <details> <summary>Changes</summary> 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 <ismail@<!-- -->bennani.ma> --- Patch is 47.64 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/149622.diff 25 Files Affected: - (modified) lldb/bindings/python/python-wrapper.swig (+1) - (modified) lldb/examples/python/templates/scripted_process.py (+136) - (modified) lldb/include/lldb/API/SBSymbolContext.h (+1) - (added) lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h (+55) - (modified) lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h (+10) - (modified) lldb/include/lldb/Interpreter/ScriptInterpreter.h (+5) - (modified) lldb/include/lldb/Target/StackFrame.h (+17-17) - (modified) lldb/include/lldb/lldb-forward.h (+3) - (modified) lldb/source/Core/FormatEntity.cpp (+1-1) - (modified) lldb/source/Plugins/Process/scripted/CMakeLists.txt (+1) - (added) lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp (+191) - (added) lldb/source/Plugins/Process/scripted/ScriptedFrame.h (+63) - (modified) lldb/source/Plugins/Process/scripted/ScriptedThread.cpp (+71-11) - (modified) lldb/source/Plugins/Process/scripted/ScriptedThread.h (+4-1) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt (+1) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h (+1) - (added) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp (+157) - (added) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h (+59) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp (+2-1) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp (+17) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h (+5) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp (+5) - (modified) lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h (+2) - (modified) lldb/source/Symbol/LineEntry.cpp (+1-3) - (modified) lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py (+74-1) ``````````diff diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig index 2c30d536a753d..c0ad456bc12e8 100644 --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -519,6 +519,7 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyOb return sb_ptr; } + void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBExecutionContext(PyObject * data) { lldb::SBExecutionContext *sb_ptr = NULL; diff --git a/lldb/examples/python/templates/scripted_process.py b/lldb/examples/python/templates/scripted_process.py index b6360b8519077..49059d533f38a 100644 --- a/lldb/examples/python/templates/scripted_process.py +++ b/lldb/examples/python/templates/scripted_process.py @@ -383,6 +383,142 @@ def get_extended_info(self): """ return self.extended_info + def get_scripted_frame_plugin(self): + """Get scripted frame plugin name. + + Returns: + str: Name of the scripted frame plugin. + """ + return None + + +class ScriptedFrame(metaclass=ABCMeta): + """ + The base class for a scripted frame. + + Most of the base class methods are `@abstractmethod` that need to be + overwritten by the inheriting class. + """ + + @abstractmethod + def __init__(self, thread, args): + """Construct a scripted frame. + + Args: + thread (ScriptedThread): The thread owning this frame. + args (lldb.SBStructuredData): A Dictionary holding arbitrary + key/value pairs used by the scripted frame. + """ + self.target = None + self.originating_thread = None + self.thread = None + self.args = None + self.id = None + self.name = None + self.register_info = None + self.register_ctx = {} + self.variables = [] + + if ( + isinstance(thread, ScriptedThread) + or isinstance(thread, lldb.SBThread) + and thread.IsValid() + ): + self.target = thread.target + self.process = thread.process + self.originating_thread = thread + self.thread = self.process.GetThreadByIndexID(thread.tid) + self.get_register_info() + + @abstractmethod + def get_id(self): + """Get the scripted frame identifier. + + Returns: + int: The identifier of the scripted frame in the scripted thread. + """ + pass + + def get_pc(self): + """Get the scripted frame address. + + Returns: + int: The optional address of the scripted frame in the scripted thread. + """ + return None + + def get_symbol_context(self): + """Get the scripted frame symbol context. + + Returns: + lldb.SBSymbolContext: The symbol context of the scripted frame in the scripted thread. + """ + return None + + def is_inlined(self): + """Check if the scripted frame is inlined. + + Returns: + bool: True if scripted frame is inlined. False otherwise. + """ + return False + + def is_artificial(self): + """Check if the scripted frame is artificial. + + Returns: + bool: True if scripted frame is artificial. False otherwise. + """ + return True + + def is_hidden(self): + """Check if the scripted frame is hidden. + + Returns: + bool: True if scripted frame is hidden. False otherwise. + """ + return False + + def get_function_name(self): + """Get the scripted frame function name. + + Returns: + str: The function name of the scripted frame. + """ + return self.name + + def get_display_function_name(self): + """Get the scripted frame display function name. + + Returns: + str: The display function name of the scripted frame. + """ + return self.get_function_name() + + def get_variables(self, filters): + """Get the scripted thread state type. + + Args: + filter (lldb.SBVariablesOptions): The filter used to resolve the variables + Returns: + lldb.SBValueList: The SBValueList containing the SBValue for each resolved variable. + Returns None by default. + """ + return None + + def get_register_info(self): + if self.register_info is None: + self.register_info = self.originating_thread.get_register_info() + return self.register_info + + @abstractmethod + def get_register_context(self): + """Get the scripted thread register context + + Returns: + str: A byte representing all register's value. + """ + pass class PassthroughScriptedProcess(ScriptedProcess): driving_target = None diff --git a/lldb/include/lldb/API/SBSymbolContext.h b/lldb/include/lldb/API/SBSymbolContext.h index 128b0b65b7860..19f29c629d094 100644 --- a/lldb/include/lldb/API/SBSymbolContext.h +++ b/lldb/include/lldb/API/SBSymbolContext.h @@ -66,6 +66,7 @@ class LLDB_API SBSymbolContext { friend class SBTarget; friend class SBSymbolContextList; + friend class lldb_private::ScriptInterpreter; friend class lldb_private::python::SWIGBridge; SBSymbolContext(const lldb_private::SymbolContext &sc_ptr); diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h new file mode 100644 index 0000000000000..8ef4b37d6ba12 --- /dev/null +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// 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_SCRIPTEDFRAMEINTERFACE_H +#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H + +#include "ScriptedInterface.h" +#include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/lldb-private.h" +#include <optional> +#include <string> + +namespace lldb_private { +class ScriptedFrameInterface : virtual public ScriptedInterface { +public: + virtual llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) = 0; + + virtual lldb::user_id_t GetID() { return LLDB_INVALID_FRAME_ID; } + + virtual lldb::addr_t GetPC() { return LLDB_INVALID_ADDRESS; } + + virtual std::optional<SymbolContext> GetSymbolContext() { + return std::nullopt; + } + + virtual std::optional<std::string> GetFunctionName() { return std::nullopt; } + + virtual std::optional<std::string> GetDisplayFunctionName() { + return std::nullopt; + } + + virtual bool IsInlined() { return false; } + + virtual bool IsArtificial() { return false; } + + virtual bool IsHidden() { return false; } + + virtual StructuredData::DictionarySP GetRegisterInfo() { return {}; } + + virtual std::optional<std::string> GetRegisterContext() { + return std::nullopt; + } +}; +} // namespace lldb_private + +#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h index a7cfc690b67dc..bc58f344d36f8 100644 --- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h @@ -44,6 +44,16 @@ class ScriptedThreadInterface : virtual public ScriptedInterface { } virtual StructuredData::ArraySP GetExtendedInfo() { return {}; } + + virtual std::optional<std::string> GetScriptedFramePluginName() { + return std::nullopt; + } + +protected: + friend class ScriptedFrame; + virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() { + return {}; + } }; } // namespace lldb_private diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index dffb9b82abf3d..024bbc90a9a39 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -26,6 +26,7 @@ #include "lldb/Host/PseudoTerminal.h" #include "lldb/Host/StreamFile.h" #include "lldb/Interpreter/Interfaces/OperatingSystemInterface.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h" @@ -531,6 +532,10 @@ class ScriptInterpreter : public PluginInterface { return {}; } + virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() { + return {}; + } + virtual lldb::ScriptedThreadPlanInterfaceSP CreateScriptedThreadPlanInterface() { return {}; diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h index 4ffbf97ce6e90..cdbe8ae3c6779 100644 --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -398,7 +398,7 @@ class StackFrame : public ExecutionContextScope, /// /// \return /// true if this is an inlined frame. - bool IsInlined(); + virtual bool IsInlined(); /// Query whether this frame is synthetic. bool IsSynthetic() const; @@ -409,12 +409,12 @@ class StackFrame : public ExecutionContextScope, /// Query whether this frame is artificial (e.g a synthesized result of /// inferring missing tail call frames from a backtrace). Artificial frames /// may have limited support for inspecting variables. - bool IsArtificial() const; + virtual bool IsArtificial() const; /// Query whether this frame should be hidden from backtraces. Frame /// recognizers can customize this behavior and hide distracting /// system implementation details this way. - bool IsHidden(); + virtual bool IsHidden(); /// Language plugins can use this API to report language-specific /// runtime information about this compile unit, such as additional @@ -425,13 +425,13 @@ class StackFrame : public ExecutionContextScope, /// /// /// \return /// A C-String containing the function demangled name. Can be null. - const char *GetFunctionName(); + virtual const char *GetFunctionName(); /// Get the frame's demangled display name. /// /// /// \return /// A C-String containing the function demangled display name. Can be null. - const char *GetDisplayFunctionName(); + virtual const char *GetDisplayFunctionName(); /// Query this frame to find what frame it is in this Thread's /// StackFrameList. @@ -543,18 +543,7 @@ class StackFrame : public ExecutionContextScope, bool HasCachedData() const; -private: - /// Private methods, called from GetValueForVariableExpressionPath. - /// See that method for documentation of parameters and return value. - lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath( - llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, - uint32_t options, lldb::VariableSP &var_sp, Status &error); - - lldb::ValueObjectSP DILGetValueForVariableExpressionPath( - llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, - uint32_t options, lldb::VariableSP &var_sp, Status &error); - - /// For StackFrame only. + /// For StackFrame and derived classes only. /// \{ lldb::ThreadWP m_thread_wp; uint32_t m_frame_index; @@ -591,6 +580,17 @@ class StackFrame : public ExecutionContextScope, StreamString m_disassembly; std::recursive_mutex m_mutex; +private: + /// Private methods, called from GetValueForVariableExpressionPath. + /// See that method for documentation of parameters and return value. + lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath( + llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, + uint32_t options, lldb::VariableSP &var_sp, Status &error); + + lldb::ValueObjectSP DILGetValueForVariableExpressionPath( + llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, + uint32_t options, lldb::VariableSP &var_sp, Status &error); + StackFrame(const StackFrame &) = delete; const StackFrame &operator=(const StackFrame &) = delete; }; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 483dce98ea427..af5656b3dcad1 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -187,6 +187,7 @@ class SaveCoreOptions; class Scalar; class ScriptInterpreter; class ScriptInterpreterLocker; +class ScriptedFrameInterface; class ScriptedMetadata; class ScriptedBreakpointInterface; class ScriptedPlatformInterface; @@ -408,6 +409,8 @@ typedef std::shared_ptr<lldb_private::RecognizedStackFrame> typedef std::shared_ptr<lldb_private::ScriptSummaryFormat> ScriptSummaryFormatSP; typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP; +typedef std::shared_ptr<lldb_private::ScriptedFrameInterface> + ScriptedFrameInterfaceSP; typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP; typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface> ScriptedPlatformInterfaceUP; diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 2ff73979e4976..491f5c6320d97 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1681,7 +1681,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { const Address &pc_addr = frame->GetFrameCodeAddress(); - if (pc_addr.IsValid()) { + if (pc_addr.IsValid() || frame->IsSynthetic()) { if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false)) return true; } diff --git a/lldb/source/Plugins/Process/scripted/CMakeLists.txt b/lldb/source/Plugins/Process/scripted/CMakeLists.txt index 590166591a41e..1516ad3132e3b 100644 --- a/lldb/source/Plugins/Process/scripted/CMakeLists.txt +++ b/lldb/source/Plugins/Process/scripted/CMakeLists.txt @@ -1,6 +1,7 @@ add_lldb_library(lldbPluginScriptedProcess PLUGIN ScriptedProcess.cpp ScriptedThread.cpp + ScriptedFrame.cpp LINK_COMPONENTS BinaryFormat diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp new file mode 100644 index 0000000000000..6519df9185df0 --- /dev/null +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp @@ -0,0 +1,191 @@ +//===----------------------------------------------------------------------===// +// +// 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 "ScriptedFrame.h" + +#include "lldb/Utility/DataBufferHeap.h" + +using namespace lldb; +using namespace lldb_private; + +void ScriptedFrame::CheckInterpreterAndScriptObject() const { + lldbassert(m_script_object_sp && "Invalid Script Object."); + lldbassert(GetInterface() && "Invalid Scripted Frame Interface."); +} + +llvm::Expected<std::shared_ptr<ScriptedFrame>> +ScriptedFrame::Create(ScriptedThread &thread, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_object) { + if (!thread.IsValid()) + return llvm::createStringError("Invalid scripted thread."); + + thread.CheckInterpreterAndScriptObject(); + + auto scripted_frame_interface = + thread.GetInterface()->CreateScriptedFrameInterface(); + if (!scripted_frame_interface) + return llvm::createStringError("failed to create scripted frame interface"); + + llvm::StringRef frame_class_name; + if (!script_object) { + std::optional<std::string> class_name = + thread.GetInterface()->GetScriptedFramePluginName(); + if (!class_name || class_name->empty()) + return llvm::createStringError( + "failed to get scripted thread class name"); + frame_class_name = *class_name; + } + + ExecutionContext exe_ctx(thread); + auto obj_or_err = scripted_frame_interface->CreatePluginObject( + frame_class_name, exe_ctx, args_sp, script_object); + + if (!obj_or_err) + return llvm::createStringError( + "failed to create script object: %s", + llvm::toString(obj_or_err.takeError()).c_str()); + + StructuredData::GenericSP owned_script_object_sp = *obj_or_err; + + if (!owned_script_object_sp->IsValid()) + return llvm::createStringError("created script object is invalid"); + + lldb::user_id_t frame_id = scripted_frame_interface->GetID(); + + lldb::addr_t pc = scripted_frame_interface->GetPC(); + SymbolContext sc; + Address symbol_addr; + if (pc != LLDB_INVALID_ADDRESS) { + symbol_addr.SetLoadAddress(pc, &thread.GetProcess()->GetTarget()); + symbol_addr.CalculateSymbolContext(&sc); + } + + std::optional<SymbolContext> maybe_sym_ctx = + scripted_frame_interface->GetSymbolContext(); + if (maybe_sym_ctx) { + sc = *maybe_sym_ctx; + } + + StructuredData::DictionarySP reg_info = + scripted_frame_interface->GetRegisterInfo(); + + if (!reg_info) + return llvm::createStringError( + "failed to get scripted thread registers info"); + + std::shared_ptr<DynamicRegisterInfo> register_info_sp = + DynamicRegisterInfo::Create( + *reg_info, thread.GetProcess()->GetTarget().GetArchitecture()); + + lldb::RegisterContextSP reg_ctx_sp; + + std::optional<std::string> reg_data = + scripted_frame_interface->GetRegisterContext(); + if (reg_data) { + DataBufferSP data_sp( + std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size())); + + if (!data_sp->GetByteSize()) + return llvm::createStringError("failed to copy raw registers data"); + + std::shared_ptr<RegisterContextMemory> reg_ctx_memory = + std::make_shared<RegisterContextMemory>( + thread, frame_id, *register_info_sp, LLDB_INVALID_ADDRESS); + if (!reg_ctx_memory) + return llvm::createStringError("failed to create a register context."); + + reg_ctx_memory->SetAllRegisterData(data_sp); + reg_ctx_sp = reg_ctx_memory; + } + + return std::make_shared<ScriptedFrame>( + thread, scripted_frame_interface, frame_id, pc, sc, reg_ctx_sp, + register_info_sp, owned_script_object_sp); +} + +ScriptedFrame::ScriptedFrame(ScriptedThread &thread, + ScriptedFrameInterfaceSP interface_sp, + lldb::user_id_t id, lldb::addr_t pc, + SymbolContext &sym_ctx, + lldb::RegisterContextSP reg_ctx_sp, + std::shared_ptr<DynamicRegisterInfo> reg_info_sp, + StructuredData::GenericSP script_object_sp) + : StackFrame(thread.shared_from_this(), /*frame_idx=*/id, + /*concrete_frame_idx=*/id, /*reg_context_sp=*/reg_ctx_sp, + /*cfa=*/0, /*pc=*/pc, + /*behaves_like_zeroth_frame=*/!id, /*symbol_ctx=*/&sym_ctx), + m_scripted_frame_interface_sp(interface_sp), + m_script_object_sp(script_object_sp), m_register_info_sp(reg_info_sp) {} + +ScriptedFrame::~ScriptedFrame() {} + +const char *ScriptedFrame::GetFunctionName() { + CheckInterpreterAndScriptObject(); + std::optional<std::string> function_name = GetInterface()->GetFunctionName(); + if (!function_name) + return nullptr; + return ConstString(function_name->c_str()).AsCString(); +} + +const char *ScriptedFrame::GetDisplayFunctionName() { + CheckInterpreterAndScriptObject(); + std::optional<std::string> function_name = + GetInterface()->GetDisplayFunctionName(); + if... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/149622 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits