//===----------------------------------------------------------------------===// // // 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 "Plugins/Process/Utility/RegisterContextMemory.h" #include "lldb/Core/Address.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/DynamicRegisterInfo.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StructuredData.h" #include "lldb/ValueObject/ValueObject.h" #include "lldb/ValueObject/ValueObjectList.h" using namespace lldb; using namespace lldb_private; char ScriptedFrame::ID; void ScriptedFrame::CheckInterpreterAndScriptObject() const { lldbassert(m_script_object_sp && "Invalid Script Object."); lldbassert(GetInterface() && "Invalid Scripted Frame Interface."); } llvm::Expected> ScriptedFrame::Create(ThreadSP thread_sp, ScriptedThreadInterfaceSP scripted_thread_interface_sp, StructuredData::DictionarySP args_sp, StructuredData::Generic *script_object) { if (!thread_sp || !thread_sp->IsValid()) return llvm::createStringError("invalid thread"); ProcessSP process_sp = thread_sp->GetProcess(); if (!process_sp || !process_sp->IsValid()) return llvm::createStringError("invalid process"); ScriptInterpreter *script_interp = process_sp->GetTarget().GetDebugger().GetScriptInterpreter(); if (!script_interp) return llvm::createStringError("no script interpreter"); auto scripted_frame_interface = script_interp->CreateScriptedFrameInterface(); if (!scripted_frame_interface) return llvm::createStringError("failed to create scripted frame interface"); llvm::StringRef frame_class_name; if (!script_object) { // If no script object is provided and we have a scripted thread interface, // try to get the frame class name from it. if (scripted_thread_interface_sp) { std::optional class_name = scripted_thread_interface_sp->GetScriptedFramePluginName(); if (!class_name || class_name->empty()) return llvm::createStringError( "failed to get scripted frame class name"); frame_class_name = *class_name; } else { return llvm::createStringError( "no script object provided and no scripted thread interface"); } } ExecutionContext exe_ctx(thread_sp); 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, &process_sp->GetTarget()); symbol_addr.CalculateSymbolContext(&sc); } std::optional maybe_sym_ctx = scripted_frame_interface->GetSymbolContext(); if (maybe_sym_ctx) sc = *maybe_sym_ctx; lldb::RegisterContextSP reg_ctx_sp; auto regs_or_err = CreateRegisterContext(*scripted_frame_interface, *thread_sp, frame_id); if (!regs_or_err) LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), regs_or_err.takeError(), "{0}"); else reg_ctx_sp = *regs_or_err; return std::make_shared(thread_sp, scripted_frame_interface, frame_id, pc, sc, reg_ctx_sp, owned_script_object_sp); } ScriptedFrame::ScriptedFrame(ThreadSP thread_sp, ScriptedFrameInterfaceSP interface_sp, lldb::user_id_t id, lldb::addr_t pc, SymbolContext &sym_ctx, lldb::RegisterContextSP reg_ctx_sp, StructuredData::GenericSP script_object_sp) : StackFrame(thread_sp, /*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) { // FIXME: This should be part of the base class constructor. m_stack_frame_kind = StackFrame::Kind::Synthetic; } ScriptedFrame::~ScriptedFrame() {} const char *ScriptedFrame::GetFunctionName() { CheckInterpreterAndScriptObject(); std::optional function_name = GetInterface()->GetFunctionName(); if (!function_name) return StackFrame::GetFunctionName(); return ConstString(function_name->c_str()).AsCString(); } const char *ScriptedFrame::GetDisplayFunctionName() { CheckInterpreterAndScriptObject(); std::optional function_name = GetInterface()->GetDisplayFunctionName(); if (!function_name) return StackFrame::GetDisplayFunctionName(); 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(); 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); return DynamicRegisterInfo::Create(*reg_info, process_sp->GetTarget().GetArchitecture()); } llvm::Expected ScriptedFrame::CreateRegisterContext(ScriptedFrameInterface &interface, Thread &thread, lldb::user_id_t frame_id) { StructuredData::DictionarySP reg_info = interface.GetRegisterInfo(); if (!reg_info) return llvm::createStringError( "failed to get scripted frame 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 = interface.GetRegisterContext(); if (!reg_data) return llvm::createStringError( "failed to get scripted frame registers 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); reg_ctx_memory->SetAllRegisterData(data_sp); reg_ctx_sp = reg_ctx_memory; return reg_ctx_sp; } lldb::RegisterContextSP ScriptedFrame::GetRegisterContext() { if (!m_reg_context_sp) { Status error; if (!m_scripted_frame_interface_sp) return ScriptedInterface::ErrorWithMessage( LLVM_PRETTY_FUNCTION, "failed to get scripted frame registers context: invalid interface", error, LLDBLog::Thread); ThreadSP thread_sp = GetThread(); if (!thread_sp) return ScriptedInterface::ErrorWithMessage( LLVM_PRETTY_FUNCTION, "failed to get scripted frame registers context: invalid thread", error, LLDBLog::Thread); auto regs_or_err = CreateRegisterContext(*m_scripted_frame_interface_sp, *thread_sp, GetFrameIndex()); if (!regs_or_err) { error = Status::FromError(regs_or_err.takeError()); return ScriptedInterface::ErrorWithMessage( LLVM_PRETTY_FUNCTION, "failed to get scripted frame registers context", error, LLDBLog::Thread); } m_reg_context_sp = *regs_or_err; } return m_reg_context_sp; } VariableList *ScriptedFrame::GetVariableList(bool get_file_globals, Status *error_ptr) { PopulateVariableListFromInterface(); return m_variable_list_sp.get(); } lldb::VariableListSP ScriptedFrame::GetInScopeVariableList(bool get_file_globals, bool must_have_valid_location) { PopulateVariableListFromInterface(); return m_variable_list_sp; } void ScriptedFrame::PopulateVariableListFromInterface() { // Fetch values from the interface. ValueObjectListSP value_list_sp = GetInterface()->GetVariables(); if (!value_list_sp) return; // Convert what we can into a variable. m_variable_list_sp = std::make_shared(); for (uint32_t i = 0, e = value_list_sp->GetSize(); i < e; ++i) { ValueObjectSP v = value_list_sp->GetValueObjectAtIndex(i); if (!v) continue; VariableSP var = v->GetVariable(); // TODO: We could in theory ask the scripted frame to *produce* a // variable for this value object. if (!var) continue; m_variable_list_sp->AddVariable(var); } } lldb::ValueObjectSP ScriptedFrame::GetValueObjectForFrameVariable( const lldb::VariableSP &variable_sp, lldb::DynamicValueType use_dynamic) { // Fetch values from the interface. ValueObjectListSP values = m_scripted_frame_interface_sp->GetVariables(); if (!values) return {}; return values->FindValueObjectByValueName(variable_sp->GetName().AsCString()); } lldb::ValueObjectSP ScriptedFrame::GetValueForVariableExpressionPath( llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, uint32_t options, lldb::VariableSP &var_sp, Status &error) { // Unless the frame implementation knows how to create variables (which it // doesn't), we can't construct anything for the variable. This may seem // somewhat out of place, but it's basically because of how this API is used - // the print command uses this API to fill in var_sp; and this implementation // can't do that! // FIXME: We should make it possible for the frame implementation to create // Variable objects. (void)var_sp; // Otherwise, delegate to the scripted frame interface pointer. return m_scripted_frame_interface_sp->GetValueObjectForVariableExpression( var_expr, options, error); }