| 1 | //===-- ValueObjectMemory.cpp ---------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "lldb/ValueObject/ValueObjectMemory.h" |
| 10 | #include "lldb/Core/Value.h" |
| 11 | #include "lldb/Symbol/Type.h" |
| 12 | #include "lldb/Target/ExecutionContext.h" |
| 13 | #include "lldb/Target/Target.h" |
| 14 | #include "lldb/Utility/DataExtractor.h" |
| 15 | #include "lldb/Utility/Scalar.h" |
| 16 | #include "lldb/Utility/Status.h" |
| 17 | #include "lldb/ValueObject/ValueObject.h" |
| 18 | #include "lldb/lldb-types.h" |
| 19 | #include "llvm/Support/ErrorHandling.h" |
| 20 | |
| 21 | #include <cassert> |
| 22 | #include <memory> |
| 23 | #include <optional> |
| 24 | |
| 25 | namespace lldb_private { |
| 26 | class ExecutionContextScope; |
| 27 | } |
| 28 | |
| 29 | using namespace lldb; |
| 30 | using namespace lldb_private; |
| 31 | |
| 32 | ValueObjectSP ValueObjectMemory::Create(ExecutionContextScope *exe_scope, |
| 33 | llvm::StringRef name, |
| 34 | const Address &address, |
| 35 | lldb::TypeSP &type_sp) { |
| 36 | auto manager_sp = ValueObjectManager::Create(); |
| 37 | return (new ValueObjectMemory(exe_scope, *manager_sp, name, address, type_sp)) |
| 38 | ->GetSP(); |
| 39 | } |
| 40 | |
| 41 | ValueObjectSP ValueObjectMemory::Create(ExecutionContextScope *exe_scope, |
| 42 | llvm::StringRef name, |
| 43 | const Address &address, |
| 44 | const CompilerType &ast_type) { |
| 45 | auto manager_sp = ValueObjectManager::Create(); |
| 46 | return (new ValueObjectMemory(exe_scope, *manager_sp, name, address, |
| 47 | ast_type)) |
| 48 | ->GetSP(); |
| 49 | } |
| 50 | |
| 51 | ValueObjectMemory::ValueObjectMemory(ExecutionContextScope *exe_scope, |
| 52 | ValueObjectManager &manager, |
| 53 | llvm::StringRef name, |
| 54 | const Address &address, |
| 55 | lldb::TypeSP &type_sp) |
| 56 | : ValueObject(exe_scope, manager), m_address(address), m_type_sp(type_sp), |
| 57 | m_compiler_type() { |
| 58 | // Do not attempt to construct one of these objects with no variable! |
| 59 | assert(m_type_sp.get() != nullptr); |
| 60 | SetName(ConstString(name)); |
| 61 | m_value.SetContext(context_type: Value::ContextType::LLDBType, p: m_type_sp.get()); |
| 62 | TargetSP target_sp(GetTargetSP()); |
| 63 | lldb::addr_t load_address = m_address.GetLoadAddress(target: target_sp.get()); |
| 64 | if (load_address != LLDB_INVALID_ADDRESS) { |
| 65 | m_value.SetValueType(Value::ValueType::LoadAddress); |
| 66 | m_value.GetScalar() = load_address; |
| 67 | } else { |
| 68 | lldb::addr_t file_address = m_address.GetFileAddress(); |
| 69 | if (file_address != LLDB_INVALID_ADDRESS) { |
| 70 | m_value.SetValueType(Value::ValueType::FileAddress); |
| 71 | m_value.GetScalar() = file_address; |
| 72 | } else { |
| 73 | m_value.GetScalar() = m_address.GetOffset(); |
| 74 | m_value.SetValueType(Value::ValueType::Scalar); |
| 75 | } |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | ValueObjectMemory::ValueObjectMemory(ExecutionContextScope *exe_scope, |
| 80 | ValueObjectManager &manager, |
| 81 | llvm::StringRef name, |
| 82 | const Address &address, |
| 83 | const CompilerType &ast_type) |
| 84 | : ValueObject(exe_scope, manager), m_address(address), m_type_sp(), |
| 85 | m_compiler_type(ast_type) { |
| 86 | // Do not attempt to construct one of these objects with no variable! |
| 87 | assert(m_compiler_type.IsValid()); |
| 88 | |
| 89 | TargetSP target_sp(GetTargetSP()); |
| 90 | |
| 91 | SetName(ConstString(name)); |
| 92 | m_value.SetCompilerType(m_compiler_type); |
| 93 | lldb::addr_t load_address = m_address.GetLoadAddress(target: target_sp.get()); |
| 94 | if (load_address != LLDB_INVALID_ADDRESS) { |
| 95 | m_value.SetValueType(Value::ValueType::LoadAddress); |
| 96 | m_value.GetScalar() = load_address; |
| 97 | } else { |
| 98 | lldb::addr_t file_address = m_address.GetFileAddress(); |
| 99 | if (file_address != LLDB_INVALID_ADDRESS) { |
| 100 | m_value.SetValueType(Value::ValueType::FileAddress); |
| 101 | m_value.GetScalar() = file_address; |
| 102 | } else { |
| 103 | m_value.GetScalar() = m_address.GetOffset(); |
| 104 | m_value.SetValueType(Value::ValueType::Scalar); |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | ValueObjectMemory::~ValueObjectMemory() = default; |
| 110 | |
| 111 | CompilerType ValueObjectMemory::GetCompilerTypeImpl() { |
| 112 | if (m_type_sp) |
| 113 | return m_type_sp->GetForwardCompilerType(); |
| 114 | return m_compiler_type; |
| 115 | } |
| 116 | |
| 117 | ConstString ValueObjectMemory::GetTypeName() { |
| 118 | if (m_type_sp) |
| 119 | return m_type_sp->GetName(); |
| 120 | return m_compiler_type.GetTypeName(); |
| 121 | } |
| 122 | |
| 123 | ConstString ValueObjectMemory::GetDisplayTypeName() { |
| 124 | if (m_type_sp) |
| 125 | return m_type_sp->GetForwardCompilerType().GetDisplayTypeName(); |
| 126 | return m_compiler_type.GetDisplayTypeName(); |
| 127 | } |
| 128 | |
| 129 | llvm::Expected<uint32_t> ValueObjectMemory::CalculateNumChildren(uint32_t max) { |
| 130 | if (m_type_sp) { |
| 131 | auto child_count = m_type_sp->GetNumChildren(omit_empty_base_classes: true); |
| 132 | if (!child_count) |
| 133 | return child_count; |
| 134 | return *child_count <= max ? *child_count : max; |
| 135 | } |
| 136 | |
| 137 | ExecutionContext exe_ctx(GetExecutionContextRef()); |
| 138 | const bool omit_empty_base_classes = true; |
| 139 | auto child_count = |
| 140 | m_compiler_type.GetNumChildren(omit_empty_base_classes, exe_ctx: &exe_ctx); |
| 141 | if (!child_count) |
| 142 | return child_count; |
| 143 | return *child_count <= max ? *child_count : max; |
| 144 | } |
| 145 | |
| 146 | llvm::Expected<uint64_t> ValueObjectMemory::GetByteSize() { |
| 147 | ExecutionContext exe_ctx(GetExecutionContextRef()); |
| 148 | if (m_type_sp) { |
| 149 | if (auto size = |
| 150 | m_type_sp->GetByteSize(exe_scope: exe_ctx.GetBestExecutionContextScope())) |
| 151 | return *size; |
| 152 | return llvm::createStringError(Fmt: "could not get byte size of memory object" ); |
| 153 | } |
| 154 | return m_compiler_type.GetByteSize(exe_scope: exe_ctx.GetBestExecutionContextScope()); |
| 155 | } |
| 156 | |
| 157 | lldb::ValueType ValueObjectMemory::GetValueType() const { |
| 158 | // RETHINK: Should this be inherited from somewhere? |
| 159 | return lldb::eValueTypeVariableGlobal; |
| 160 | } |
| 161 | |
| 162 | bool ValueObjectMemory::UpdateValue() { |
| 163 | SetValueIsValid(false); |
| 164 | m_error.Clear(); |
| 165 | |
| 166 | ExecutionContext exe_ctx(GetExecutionContextRef()); |
| 167 | |
| 168 | Target *target = exe_ctx.GetTargetPtr(); |
| 169 | if (target) { |
| 170 | m_data.SetByteOrder(target->GetArchitecture().GetByteOrder()); |
| 171 | m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); |
| 172 | } |
| 173 | |
| 174 | Value old_value(m_value); |
| 175 | if (m_address.IsValid()) { |
| 176 | Value::ValueType value_type = m_value.GetValueType(); |
| 177 | |
| 178 | switch (value_type) { |
| 179 | case Value::ValueType::Invalid: |
| 180 | m_error = Status::FromErrorString(str: "Invalid value" ); |
| 181 | return false; |
| 182 | case Value::ValueType::Scalar: |
| 183 | // The variable value is in the Scalar value inside the m_value. We can |
| 184 | // point our m_data right to it. |
| 185 | m_error = m_value.GetValueAsData(exe_ctx: &exe_ctx, data&: m_data, module: GetModule().get()); |
| 186 | break; |
| 187 | |
| 188 | case Value::ValueType::FileAddress: |
| 189 | case Value::ValueType::LoadAddress: |
| 190 | case Value::ValueType::HostAddress: |
| 191 | // The DWARF expression result was an address in the inferior process. If |
| 192 | // this variable is an aggregate type, we just need the address as the |
| 193 | // main value as all child variable objects will rely upon this location |
| 194 | // and add an offset and then read their own values as needed. If this |
| 195 | // variable is a simple type, we read all data for it into m_data. Make |
| 196 | // sure this type has a value before we try and read it |
| 197 | |
| 198 | // If we have a file address, convert it to a load address if we can. |
| 199 | if (value_type == Value::ValueType::FileAddress && |
| 200 | exe_ctx.GetProcessPtr()) { |
| 201 | lldb::addr_t load_addr = m_address.GetLoadAddress(target); |
| 202 | if (load_addr != LLDB_INVALID_ADDRESS) { |
| 203 | m_value.SetValueType(Value::ValueType::LoadAddress); |
| 204 | m_value.GetScalar() = load_addr; |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | if (!CanProvideValue()) { |
| 209 | // this value object represents an aggregate type whose children have |
| 210 | // values, but this object does not. So we say we are changed if our |
| 211 | // location has changed. |
| 212 | SetValueDidChange(value_type != old_value.GetValueType() || |
| 213 | m_value.GetScalar() != old_value.GetScalar()); |
| 214 | } else { |
| 215 | // Copy the Value and set the context to use our Variable so it can |
| 216 | // extract read its value into m_data appropriately |
| 217 | Value value(m_value); |
| 218 | if (m_type_sp) |
| 219 | value.SetContext(context_type: Value::ContextType::LLDBType, p: m_type_sp.get()); |
| 220 | else { |
| 221 | value.SetCompilerType(m_compiler_type); |
| 222 | } |
| 223 | |
| 224 | m_error = value.GetValueAsData(exe_ctx: &exe_ctx, data&: m_data, module: GetModule().get()); |
| 225 | } |
| 226 | break; |
| 227 | } |
| 228 | |
| 229 | SetValueIsValid(m_error.Success()); |
| 230 | } |
| 231 | return m_error.Success(); |
| 232 | } |
| 233 | |
| 234 | bool ValueObjectMemory::IsInScope() { |
| 235 | // FIXME: Maybe try to read the memory address, and if that works, then |
| 236 | // we are in scope? |
| 237 | return true; |
| 238 | } |
| 239 | |
| 240 | lldb::ModuleSP ValueObjectMemory::GetModule() { return m_address.GetModule(); } |
| 241 | |