| //===----------------------------------------------------------------------===// | |
| // | |
| // 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/Expression/DWARFExpressionList.h" | |
| #include "lldb/Host/FileSystem.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/ScriptedThreadPlan.h" | |
| #include "lldb/Target/StackFrame.h" | |
| #include "lldb/Target/Thread.h" | |
| #include "lldb/Target/ThreadPlan.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/Utility/ValueType.h" | |
| #include "lldb/ValueObject/ValueObject.h" | |
| #include "lldb/ValueObject/ValueObjectList.h" | |
| #include "lldb/ValueObject/ValueObjectSynthesizedValue.h" | |
| #include "lldb/lldb-enumerations.h" | |
| #include "lldb/lldb-forward.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include <memory> | |
| 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<std::shared_ptr<ScriptedFrame>> | |
| 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<std::string> 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(); | |
| lldb::addr_t cfa = scripted_frame_interface->GetCFA(); | |
| SymbolContext sc; | |
| Address symbol_addr; | |
| if (pc != LLDB_INVALID_ADDRESS) { | |
| pc = process_sp->FixCodeAddress(pc); | |
| symbol_addr.SetLoadAddress(pc, &process_sp->GetTarget()); | |
| symbol_addr.CalculateSymbolContext(&sc); | |
| } | |
| std::optional<SymbolContext> maybe_sym_ctx = | |
| scripted_frame_interface->GetSymbolContext(); | |
| if (maybe_sym_ctx) | |
| sc = *maybe_sym_ctx; | |
| return std::make_shared<ScriptedFrame>(thread_sp, scripted_frame_interface, | |
| frame_id, pc, cfa, sc, | |
| owned_script_object_sp); | |
| } | |
| ScriptedFrame::ScriptedFrame(ThreadSP thread_sp, | |
| ScriptedFrameInterfaceSP interface_sp, | |
| lldb::user_id_t id, lldb::addr_t pc, | |
| lldb::addr_t cfa, SymbolContext &sym_ctx, | |
| StructuredData::GenericSP script_object_sp) | |
| : StackFrame(thread_sp, /*frame_idx=*/id, | |
| /*concrete_frame_idx=*/id, /*reg_context_sp=*/nullptr, | |
| /*cfa=*/cfa, /*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; | |
| llvm::Expected<lldb::RegisterContextSP> reg_ctx_or_err = | |
| CreateRegisterContext(); | |
| if (!reg_ctx_or_err) { | |
| std::optional<lldb::user_id_t> debugger_id; | |
| if (ProcessSP process_sp = thread_sp->GetProcess()) | |
| debugger_id = process_sp->GetTarget().GetDebugger().GetID(); | |
| Debugger::ReportError("failed to create scripted frame register context: " + | |
| llvm::toString(reg_ctx_or_err.takeError()), | |
| debugger_id); | |
| return; | |
| } | |
| m_reg_context_sp = *reg_ctx_or_err; | |
| } | |
| ScriptedFrame::~ScriptedFrame() {} | |
| const char *ScriptedFrame::GetFunctionName() { | |
| CheckInterpreterAndScriptObject(); | |
| std::optional<std::string> function_name = GetInterface()->GetFunctionName(); | |
| if (!function_name) | |
| return StackFrame::GetFunctionName(); | |
| return ConstString(*function_name).AsCString(nullptr); | |
| } | |
| const char *ScriptedFrame::GetDisplayFunctionName() { | |
| CheckInterpreterAndScriptObject(); | |
| std::optional<std::string> function_name = | |
| GetInterface()->GetDisplayFunctionName(); | |
| if (!function_name) | |
| return StackFrame::GetDisplayFunctionName(); | |
| return ConstString(*function_name).AsCString(nullptr); | |
| } | |
| 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; | |
| } | |
| llvm::Expected<DynamicRegisterInfoSP> ScriptedFrame::GetDynamicRegisterInfo() { | |
| CheckInterpreterAndScriptObject(); | |
| StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo(); | |
| if (!reg_info) | |
| return llvm::createStringError( | |
| "failed to get scripted frame registers info"); | |
| ThreadSP thread_sp = m_thread_wp.lock(); | |
| 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"); | |
| DynamicRegisterInfoSP register_info_sp = DynamicRegisterInfo::Create( | |
| *reg_info, process_sp->GetTarget().GetArchitecture()); | |
| if (!register_info_sp) | |
| return llvm::createStringError( | |
| "failed to create scripted frame registers info"); | |
| return register_info_sp; | |
| } | |
| llvm::Expected<lldb::RegisterContextSP> ScriptedFrame::CreateRegisterContext() { | |
| if (!m_scripted_frame_interface_sp) | |
| return llvm::createStringError("invalid scripted frame interface"); | |
| ThreadSP thread_sp = GetThread(); | |
| if (!thread_sp) | |
| return llvm::createStringError("invalid thread"); | |
| // A frame that reports no register data has no register context. That is a | |
| // valid state, not a failure: only frames that expose registers implement it. | |
| std::optional<std::string> reg_data = | |
| m_scripted_frame_interface_sp->GetRegisterContext(); | |
| if (!reg_data) | |
| return lldb::RegisterContextSP(); | |
| 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"); | |
| llvm::Expected<DynamicRegisterInfoSP> register_info_or_err = | |
| GetDynamicRegisterInfo(); | |
| if (!register_info_or_err) | |
| return register_info_or_err.takeError(); | |
| std::shared_ptr<RegisterContextMemory> reg_ctx_memory = | |
| std::make_shared<RegisterContextMemory>(*thread_sp, GetFrameIndex(), | |
| std::move(*register_info_or_err), | |
| LLDB_INVALID_ADDRESS); | |
| reg_ctx_memory->SetAllRegisterData(data_sp); | |
| return reg_ctx_memory; | |
| } | |
| lldb::RegisterContextSP ScriptedFrame::GetRegisterContext() { | |
| return m_reg_context_sp; | |
| } | |
| VariableList *ScriptedFrame::GetVariableList(bool get_file_globals, | |
| bool include_synthetic_vars, | |
| Status *error_ptr) { | |
| PopulateVariableListFromInterface(include_synthetic_vars); | |
| return m_variable_list_sp.get(); | |
| } | |
| lldb::VariableListSP | |
| ScriptedFrame::GetInScopeVariableList(bool get_file_globals, | |
| bool include_synthetic_vars, | |
| bool must_have_valid_location) { | |
| PopulateVariableListFromInterface(include_synthetic_vars); | |
| return m_variable_list_sp; | |
| } | |
| void ScriptedFrame::PopulateVariableListFromInterface( | |
| bool include_synthetic_vars) { | |
| // 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<VariableList>(); | |
| for (uint32_t i = 0, e = value_list_sp->GetSize(); i < e; ++i) { | |
| ValueObjectSP v = value_list_sp->GetValueObjectAtIndex(i); | |
| if (!v) | |
| continue; | |
| // Ask the interface about the value type of this variable. If it doesn't | |
| // specify any, use the original value type. | |
| lldb::ValueType vt = GetInterface()->GetValueTypeForVariable(v).value_or( | |
| GetSyntheticValueType(v->GetValueType())); | |
| if (IsSyntheticValueType(vt) && !include_synthetic_vars) | |
| continue; | |
| // Just make up a variable - the frame variable dumper just passes it | |
| // back in to GetValueObjectForFrameVariable, so we really just need to | |
| // make sure the name and type are correct. We create IDs based on | |
| // value_list_sp in order to make sure they're unique. | |
| m_variable_list_sp->AddVariable(std::make_shared<lldb_private::Variable>( | |
| (lldb::user_id_t)value_list_sp->GetSize() + i, | |
| v->GetName().GetCString(), v->GetName().GetCString(), nullptr, vt, | |
| /*owner_scope=*/nullptr, | |
| /*scope_range=*/Variable::RangeList{}, | |
| /*decl=*/nullptr, DWARFExpressionList{}, /*external=*/false, | |
| /*artificial=*/true, /*location_is_constant_data=*/false)); | |
| } | |
| } | |
| 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 {}; | |
| lldb::ValueObjectSP valobj_sp = values->FindValueObjectByValueName( | |
| variable_sp->GetName().AsCString(nullptr)); | |
| if (!valobj_sp) | |
| return {}; | |
| return ValueObjectSynthesizedValue::Create(*valobj_sp, | |
| variable_sp->GetScope()); | |
| } | |
| lldb::ValueObjectSP ScriptedFrame::FindVariable(ConstString name) { | |
| // Fetch values from the interface. | |
| ValueObjectListSP values = m_scripted_frame_interface_sp->GetVariables(); | |
| if (!values) | |
| return {}; | |
| lldb::ValueObjectSP valobj_sp = | |
| values->FindValueObjectByValueName(name.AsCString(nullptr)); | |
| if (!valobj_sp) | |
| return {}; | |
| // Present the ValueObject under the same ValueType the frame's variable list | |
| // would, so reaching a variable by name and by enumeration agree. | |
| if (m_variable_list_sp) { | |
| if (VariableSP variable_sp = m_variable_list_sp->FindVariable(name)) | |
| return ValueObjectSynthesizedValue::Create(*valobj_sp, | |
| variable_sp->GetScope()); | |
| } | |
| return valobj_sp; | |
| } | |
| lldb::ValueObjectSP ScriptedFrame::GetValueForVariableExpressionPath( | |
| llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, | |
| uint32_t options, lldb::VariableSP &var_sp, Status &error, | |
| lldb::DILMode mode) { | |
| // 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); | |
| } | |
| llvm::Expected<lldb::ThreadPlanSP> | |
| ScriptedFrame::GetThreadPlanForStepType(lldb::StepType step_type) { | |
| llvm::Expected<ScriptedMetadata> metadata = | |
| m_scripted_frame_interface_sp->GetThreadPlanMetadataForStepType( | |
| step_type); | |
| if (!metadata) | |
| return metadata.takeError(); | |
| // Returning a ScriptedMetadata with an empty class name means that the | |
| // scripted frame doesn't know how to step. That's not an error, but don't | |
| // try to make a thread plan in this case. The bool operator of | |
| // ScriptedMetadata checks the class name... | |
| ThreadSP thread_sp = GetThread(); | |
| if (thread_sp && !metadata->GetClassName().empty()) { | |
| lldb::ThreadPlanSP new_plan_sp( | |
| new ScriptedThreadPlan(*thread_sp.get(), *metadata)); | |
| return new_plan_sp; | |
| } | |
| return lldb::ThreadPlanSP(); | |
| } |