//===----------------------------------------------------------------------===// // // 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 "ScriptedFrameProvider.h" #include "Plugins/Process/scripted/ScriptedFrame.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/PluginManager.h" #include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Target/BorrowedStackFrame.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/ScriptedMetadata.h" #include "lldb/Utility/Status.h" #include "llvm/Support/Error.h" #include using namespace lldb; using namespace lldb_private; void ScriptedFrameProvider::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), "Provides synthetic frames via scripting", nullptr, ScriptedFrameProvider::CreateInstance); } void ScriptedFrameProvider::Terminate() { PluginManager::UnregisterPlugin(ScriptedFrameProvider::CreateInstance); } llvm::Expected ScriptedFrameProvider::CreateInstance( lldb::StackFrameListSP input_frames, const ScriptedFrameProviderDescriptor &descriptor) { if (!input_frames) return llvm::createStringError( "failed to create scripted frame provider: invalid input frames"); Thread &thread = input_frames->GetThread(); ProcessSP process_sp = thread.GetProcess(); if (!process_sp) return nullptr; if (!descriptor.IsValid()) return llvm::createStringError( "failed to create scripted frame provider: invalid scripted metadata"); if (!descriptor.AppliesToThread(thread)) return nullptr; ScriptInterpreter *script_interp = process_sp->GetTarget().GetDebugger().GetScriptInterpreter(); if (!script_interp) return llvm::createStringError("cannot create scripted frame provider: No " "script interpreter installed"); ScriptedFrameProviderInterfaceSP interface_sp = script_interp->CreateScriptedFrameProviderInterface(); if (!interface_sp) return llvm::createStringError( "cannot create scripted frame provider: script interpreter couldn't " "create Scripted Frame Provider Interface"); const ScriptedMetadataSP scripted_metadata = descriptor.scripted_metadata_sp; // If we shouldn't attach a frame provider to this thread, just exit early. if (!interface_sp->AppliesToThread(scripted_metadata->GetClassName(), thread.shared_from_this())) return nullptr; auto obj_or_err = interface_sp->CreatePluginObject( scripted_metadata->GetClassName(), input_frames, scripted_metadata->GetArgsSP()); if (!obj_or_err) return obj_or_err.takeError(); StructuredData::ObjectSP object_sp = *obj_or_err; if (!object_sp || !object_sp->IsValid()) return llvm::createStringError( "cannot create scripted frame provider: failed to create valid scripted" "frame provider object"); return std::make_shared(input_frames, interface_sp, descriptor); } ScriptedFrameProvider::ScriptedFrameProvider( StackFrameListSP input_frames, lldb::ScriptedFrameProviderInterfaceSP interface_sp, const ScriptedFrameProviderDescriptor &descriptor) : SyntheticFrameProvider(input_frames), m_interface_sp(interface_sp), m_descriptor(descriptor) {} ScriptedFrameProvider::~ScriptedFrameProvider() = default; std::string ScriptedFrameProvider::GetDescription() const { if (!m_interface_sp) return {}; return m_interface_sp->GetDescription(m_descriptor.GetName()); } llvm::Expected ScriptedFrameProvider::GetFrameAtIndex(uint32_t idx) { if (!m_interface_sp) return llvm::createStringError( "cannot get stack frame: scripted frame provider not initialized"); auto create_frame_from_dict = [this](StructuredData::Dictionary *dict, uint32_t index) -> llvm::Expected { lldb::addr_t pc; if (!dict->GetValueForKeyAsInteger("pc", pc)) return llvm::createStringError( "missing 'pc' key from scripted frame dictionary"); Address symbol_addr; symbol_addr.SetLoadAddress(pc, &GetThread().GetProcess()->GetTarget()); const lldb::addr_t cfa = LLDB_INVALID_ADDRESS; const bool cfa_is_valid = false; const bool artificial = false; const bool behaves_like_zeroth_frame = false; SymbolContext sc; symbol_addr.CalculateSymbolContext(&sc); ThreadSP thread_sp = GetThread().shared_from_this(); return std::make_shared(thread_sp, index, index, cfa, cfa_is_valid, pc, StackFrame::Kind::Synthetic, artificial, behaves_like_zeroth_frame, &sc); }; auto create_frame_from_script_object = [this]( StructuredData::ObjectSP object_sp) -> llvm::Expected { Status error; if (!object_sp || !object_sp->GetAsGeneric()) return llvm::createStringError("invalid script object"); ThreadSP thread_sp = GetThread().shared_from_this(); auto frame_or_error = ScriptedFrame::Create(thread_sp, nullptr, nullptr, object_sp->GetAsGeneric()); if (!frame_or_error) { ScriptedInterface::ErrorWithMessage( LLVM_PRETTY_FUNCTION, toString(frame_or_error.takeError()), error); return error.ToError(); } return *frame_or_error; }; StructuredData::ObjectSP obj_sp = m_interface_sp->GetFrameAtIndex(idx); // None/null means no more frames or error. if (!obj_sp || !obj_sp->IsValid()) return llvm::createStringError("invalid script object returned for frame " + llvm::Twine(idx)); StackFrameSP synth_frame_sp = nullptr; if (StructuredData::UnsignedInteger *int_obj = obj_sp->GetAsUnsignedInteger()) { uint32_t real_frame_index = int_obj->GetValue(); if (real_frame_index < m_input_frames->GetNumFrames()) { StackFrameSP real_frame_sp = m_input_frames->GetFrameAtIndex(real_frame_index); synth_frame_sp = (real_frame_index == idx) ? real_frame_sp : std::make_shared(real_frame_sp, idx); } } else if (StructuredData::Dictionary *dict = obj_sp->GetAsDictionary()) { // Check if it's a dictionary describing a frame. auto frame_from_dict_or_err = create_frame_from_dict(dict, idx); if (!frame_from_dict_or_err) { return llvm::createStringError(llvm::Twine( "couldn't create frame from dictionary at index " + llvm::Twine(idx) + ": " + toString(frame_from_dict_or_err.takeError()))); } synth_frame_sp = *frame_from_dict_or_err; } else if (obj_sp->GetAsGeneric()) { // It's a ScriptedFrame object. auto frame_from_script_obj_or_err = create_frame_from_script_object(obj_sp); if (!frame_from_script_obj_or_err) { return llvm::createStringError( llvm::Twine("couldn't create frame from script object at index " + llvm::Twine(idx) + ": " + toString(frame_from_script_obj_or_err.takeError()))); } synth_frame_sp = *frame_from_script_obj_or_err; } else { return llvm::createStringError( llvm::Twine("invalid return type from get_frame_at_index at index " + llvm::Twine(idx))); } if (!synth_frame_sp) return llvm::createStringError( llvm::Twine("failed to create frame at index " + llvm::Twine(idx))); synth_frame_sp->SetFrameIndex(idx); return synth_frame_sp; } namespace lldb_private { void lldb_initialize_ScriptedFrameProvider() { ScriptedFrameProvider::Initialize(); } void lldb_terminate_ScriptedFrameProvider() { ScriptedFrameProvider::Terminate(); } } // namespace lldb_private