blob: 09b20fafd5e85c19eea967bb5f520cf461309e1b [file]
//===----------------------------------------------------------------------===//
//
// 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/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/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();
SymbolContext sc;
Address symbol_addr;
if (pc != LLDB_INVALID_ADDRESS) {
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, sc,
owned_script_object_sp);
}
ScriptedFrame::ScriptedFrame(ThreadSP thread_sp,
ScriptedFrameInterfaceSP interface_sp,
lldb::user_id_t id, lldb::addr_t pc,
SymbolContext &sym_ctx,
StructuredData::GenericSP script_object_sp)
: StackFrame(thread_sp, /*frame_idx=*/id,
/*concrete_frame_idx=*/id, /*reg_context_sp=*/nullptr,
/*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;
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);
}