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 +#include + +namespace lldb_private { +class ScriptedFrameInterface : virtual public ScriptedInterface { +public: + virtual llvm::Expected + 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 GetSymbolContext() { + return std::nullopt; + } + + virtual std::optional GetFunctionName() { return std::nullopt; } + + virtual std::optional 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 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 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 typedef std::shared_ptr ScriptSummaryFormatSP; typedef std::shared_ptr ScriptInterpreterSP; +typedef std::shared_ptr + ScriptedFrameInterfaceSP; typedef std::shared_ptr ScriptedMetadataSP; typedef std::unique_ptr 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> +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 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 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 register_info_sp = + DynamicRegisterInfo::Create( + *reg_info, thread.GetProcess()->GetTarget().GetArchitecture()); + + lldb::RegisterContextSP reg_ctx_sp; + + std::optional reg_data = + scripted_frame_interface->GetRegisterContext(); + if (reg_data) { + DataBufferSP data_sp( + std::make_shared(reg_data->c_str(), reg_data->size())); + + if (!data_sp->GetByteSize()) + return llvm::createStringError("failed to copy raw registers data"); + + std::shared_ptr reg_ctx_memory = + std::make_shared( + 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( + 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 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 function_name = GetInterface()->GetFunctionName(); + if (!function_name) + return nullptr; + return ConstString(function_name->c_str()).AsCString(); +} + +const char *ScriptedFrame::GetDisplayFunctionName() { + CheckInterpreterAndScriptObject(); + std::optional function_name = + GetInterface()->GetDisplayFunctionName(); + if (!function_name) + return nullptr; + return ConstString(function_name->c_str()).AsCString(); +} + +bool ScriptedFrame::IsInlined() { return GetInterface()->IsInlined(); } + +bool ScriptedFrame::IsArtificial() const { + return GetInterface()->IsArtificial(); +} + +bool ScriptedFrame::IsHidden() { return GetInterface()->IsHidden(); } + +lldb::ScriptedFrameInterfaceSP ScriptedFrame::GetInterface() const { + return m_scripted_frame_interface_sp; +} + +std::shared_ptr ScriptedFrame::GetDynamicRegisterInfo() { + CheckInterpreterAndScriptObject(); + + if (!m_register_info_sp) { + StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo(); + + Status error; + if (!reg_info) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr>( + LLVM_PRETTY_FUNCTION, "Failed to get scripted frame registers info.", + error, LLDBLog::Thread); + + ThreadSP thread_sp = m_thread_wp.lock(); + if (!thread_sp || !thread_sp->IsValid()) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr>( + LLVM_PRETTY_FUNCTION, + "Failed to get scripted frame registers info: invalid thread.", error, + LLDBLog::Thread); + + ProcessSP process_sp = thread_sp->GetProcess(); + if (!process_sp || !process_sp->IsValid()) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr>( + LLVM_PRETTY_FUNCTION, + "Failed to get scripted frame registers info: invalid process.", + error, LLDBLog::Thread); + + m_register_info_sp = DynamicRegisterInfo::Create( + *reg_info, process_sp->GetTarget().GetArchitecture()); + } + + return m_register_info_sp; +} diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h new file mode 100644 index 0000000000000..6e01e2fd7653e --- /dev/null +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// 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_SOURCE_PLUGINS_SCRIPTED_FRAME_H +#define LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H + +#include "Plugins/Process/Utility/RegisterContextMemory.h" +#include "ScriptedThread.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/DynamicRegisterInfo.h" +#include "lldb/Target/StackFrame.h" +#include + +namespace lldb_private { +class ScriptedThread; +} + +namespace lldb_private { + +class ScriptedFrame : public lldb_private::StackFrame { + +public: + ScriptedFrame(ScriptedThread &thread, + lldb::ScriptedFrameInterfaceSP interface_sp, + lldb::user_id_t frame_idx, lldb::addr_t pc, + SymbolContext &sym_ctx, lldb::RegisterContextSP reg_ctx_sp, + std::shared_ptr reg_info_sp, + StructuredData::GenericSP script_object_sp = nullptr); + + ~ScriptedFrame() override; + + static llvm::Expected> + Create(ScriptedThread &thread, StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_object = nullptr); + + bool IsInlined() override; + bool IsArtificial() const override; + bool IsHidden() override; + const char *GetFunctionName() override; + const char *GetDisplayFunctionName() override; + +private: + void CheckInterpreterAndScriptObject() const; + lldb::ScriptedFrameInterfaceSP GetInterface() const; + + ScriptedFrame(const ScriptedFrame &) = delete; + const ScriptedFrame &operator=(const ScriptedFrame &) = delete; + + std::shared_ptr GetDynamicRegisterInfo(); + + lldb::ScriptedFrameInterfaceSP m_scripted_frame_interface_sp; + lldb_private::StructuredData::GenericSP m_script_object_sp; + std::shared_ptr m_register_info_sp; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp index 45ad83e4ed671..491efac5aadef 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ScriptedThread.h" +#include "ScriptedFrame.h" #include "Plugins/Process/Utility/RegisterContextThreadMemory.h" #include "Plugins/Process/Utility/StopInfoMachException.h" @@ -173,27 +174,31 @@ bool ScriptedThread::LoadArtificialStackFrames() { .str(), error, LLDBLog::Thread); - StackFrameListSP frames = GetStackFrameList(); - - for (size_t idx = 0; idx < arr_size; idx++) { + auto create_frame_from_dict = + [this, arr_sp](size_t idx) -> llvm::Expected { + Status error; std::optional maybe_dict = arr_sp->GetItemAtIndexAsDictionary(idx); - if (!maybe_dict) - return ScriptedInterface::ErrorWithMessage( + if (!maybe_dict) { + ScriptedInterface::ErrorWithMessage( LLVM_PRETTY_FUNCTION, llvm::Twine( "Couldn't get artificial stackframe dictionary at index (" + llvm::Twine(idx) + llvm::Twine(") from stackframe array.")) .str(), error, LLDBLog::Thread); + return error.ToError(); + } StructuredData::Dictionary *dict = *maybe_dict; lldb::addr_t pc; - if (!dict->GetValueForKeyAsInteger("pc", pc)) - return ScriptedInterface::ErrorWithMessage( + if (!dict->GetValueForKeyAsInteger("pc", pc)) { + ScriptedInterface::ErrorWithMessage( LLVM_PRETTY_FUNCTION, "Couldn't find value for key 'pc' in stackframe dictionary.", error, LLDBLog::Thread); + return error.ToError(); + } Address symbol_addr; symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget()); @@ -205,10 +210,65 @@ bool ScriptedThread::LoadArtificialStackFrames() { SymbolContext sc; symbol_addr.CalculateSymbolContext(&sc); - StackFrameSP synth_frame_sp = std::make_shared( - this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, - StackFrame::Kind::Synthetic, artificial, behaves_like_zeroth_frame, - &sc); + return std::make_shared(this->shared_from_this(), idx, idx, cfa, + cfa_is_valid, pc, + StackFrame::Kind::Synthetic, artificial, + behaves_like_zeroth_frame, &sc); + }; + + auto create_frame_from_script_object = + [this, arr_sp](size_t idx) -> llvm::Expected { + Status error; + StructuredData::ObjectSP object_sp = arr_sp->GetItemAtIndex(idx); + if (!object_sp || !object_sp->GetAsGeneric()) { + ScriptedInterface::ErrorWithMessage( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Couldn't get artificial stackframe object at index (" + + llvm::Twine(idx) + + llvm::Twine(") from stackframe array.")) + .str(), + error, LLDBLog::Thread); + return error.ToError(); + } + + auto frame_or_error = + ScriptedFrame::Create(*this, nullptr, object_sp->GetAsGeneric()); + + if (!frame_or_error) { + ScriptedInterface::ErrorWithMessage( + LLVM_PRETTY_FUNCTION, toString(frame_or_error.takeError()), error); + return error.ToError(); + } + + StackFrameSP frame_sp = frame_or_error.get(); + lldbassert(frame_sp && "Couldn't initialize scripted frame."); + + return frame_sp; + }; + + StackFrameListSP frames = GetStackFrameList(); + + for (size_t idx = 0; idx < arr_size; idx++) { + StackFrameSP synth_frame_sp = nullptr; + + auto frame_from_dict_or_err = create_frame_from_dict(idx); + if (!frame_from_dict_or_err) { + auto frame_from_script_obj_or_err = create_frame_from_script_object(idx); + + if (!frame_from_script_obj_or_err) { + return ScriptedInterface::ErrorWithMessage( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Couldn't add artificial frame (" + llvm::Twine(idx) + + llvm::Twine(") to ScriptedThread StackFrameList.")) + .str(), + error, LLDBLog::Thread); + } else { + llvm::consumeError(frame_from_dict_or_err.takeError()); + synth_frame_sp = *frame_from_script_obj_or_err; + } + } else { + synth_frame_sp = *frame_from_dict_or_err; + } if (!frames->SetFrameAtIndex(static_cast(idx), synth_frame_sp)) return ScriptedInterface::ErrorWithMessage( diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.h b/lldb/source/Plugins/Process/scripted/ScriptedThread.h index cd224d60ceef8..ee5ace4059673 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.h +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.h @@ -15,11 +15,12 @@ #include "Plugins/Process/Utility/RegisterContextMemory.h" #include "lldb/Interpreter/ScriptInterpreter.h" -#include "lldb/Target//DynamicRegisterInfo.h" +#include "lldb/Target/DynamicRegisterInfo.h" #include "lldb/Target/Thread.h" namespace lldb_private { class ScriptedProcess; +class ScriptedFrame; } namespace lldb_private { @@ -61,6 +62,8 @@ class ScriptedThread : public lldb_private::Thread { StructuredData::ObjectSP FetchThreadExtendedInfo() override; private: + friend class ScriptedFrame; + void CheckInterpreterAndScriptObject() const; lldb::ScriptedThreadInterfaceSP GetInterface() const; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt index 04370940423ae..09103573b89c5 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 02dc06507caf2..3814f46615078 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 0000000000000..20ca7a2c01356 --- /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 0000000000000..3aff237ae65d5 --- /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 b49d1d82fe535..8083ccae04026 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 8af89d761764b..fd4d231a747fe 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 1fb23b39c7076..043557a827461 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h @@ -51,6 +51,11 @@ class ScriptedThreadPythonInterface : public ScriptedThreadInterface, std::optional GetRegisterContext() override; StructuredData::ArraySP GetExtendedInfo() override; + + std::optional GetScriptedFramePluginName() override; + +protected: + lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 9330a634489a2..73c5c72932ff1 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -1521,6 +1521,11 @@ ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() { return std::make_shared(*this); } +ScriptedFrameInterfaceSP +ScriptInterpreterPythonImpl::CreateScriptedFrameInterface() { + return std::make_shared(*this); +} + ScriptedThreadPlanInterfaceSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlanInterface() { return std::make_shared(*this); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index 83b64b85faebd..dedac280788f4 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -99,6 +99,8 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override; + lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; + lldb::ScriptedThreadPlanInterfaceSP CreateScriptedThreadPlanInterface() override; diff --git a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py index 2721d961bcb9d..835267221ddb4 100644 --- a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py @@ -5,6 +5,7 @@ import lldb from lldb.plugins.scripted_process import ScriptedProcess from lldb.plugins.scripted_process import ScriptedThread +from lldb.plugins.scripted_process import ScriptedFrame class DummyStopHook: @@ -22,7 +23,7 @@ class DummyScriptedProcess(ScriptedProcess): def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData): super().__init__(exe_ctx, args) - self.threads[0] = DummyScriptedThread(self, None) + self.threads[0] = DummyScriptedThread(self, args) self.memory = {} addr = 0x500000000 debugger = self.target.GetDebugger() @@ -69,6 +70,9 @@ class DummyScriptedThread(ScriptedThread): def __init__(self, process, args): super().__init__(process, args) self.frames.append({"pc": 0x0100001B00}) + self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "baz123")) + self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "bar")) + self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "foo")) def get_thread_id(self) -> int: return 0x19 @@ -109,6 +113,65 @@ def get_register_context(self) -> str: ) +class DummyScriptedFrame(ScriptedFrame): + def __init__(self, thread, args, id, name, sym_ctx=None): + super().__init__(thread, args) + self.id = id + self.name = name + self.sym_ctx = sym_ctx + + def get_id(self): + return self.id + + def get_function_name(self): + return self.name + + def get_register_context(self) -> str: + return struct.pack( + "21Q", + 0x10001, + 0x10002, + 0x10003, + 0x10004, + 0x10005, + 0x10006, + 0x10007, + 0x10008, + 0x10009, + 0x100010, + 0x100011, + 0x100012, + 0x100013, + 0x100014, + 0x100015, + 0x100016, + 0x100017, + 0x100018, + 0x100019, + 0x100020, + 0x100021, + ) + + def get_symbol_context(self): + def get_symbol_context_for_function(func_name): + module = self.target.FindModule(self.target.GetExecutable()) + if not module.IsValid(): + return None + + sym_ctx_list = module.FindFunctions(func_name) + if not sym_ctx_list.IsValid() or sym_ctx_list.GetSize() == 0: + return None + + return sym_ctx_list.GetContextAtIndex(0) + + return ( + self.sym_ctx if self.sym_ctx else get_symbol_context_for_function(self.name) + ) + + def get_scripted_frame_plugin(self): + return DummyScriptedFrame.__module__ + "." + DummyScriptedFrame.__name__ + + def __lldb_init_module(debugger, dict): # This is used when loading the script in an interactive debug session to # automatically, register the stop-hook and launch the scripted process.