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/Core/ValueObjectMemory.h" |
10 | #include "lldb/Core/Value.h" |
11 | #include "lldb/Core/ValueObject.h" |
12 | #include "lldb/Symbol/Type.h" |
13 | #include "lldb/Target/ExecutionContext.h" |
14 | #include "lldb/Target/Target.h" |
15 | #include "lldb/Utility/DataExtractor.h" |
16 | #include "lldb/Utility/Scalar.h" |
17 | #include "lldb/Utility/Status.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 | std::optional<uint64_t> ValueObjectMemory::GetByteSize() { |
147 | ExecutionContext exe_ctx(GetExecutionContextRef()); |
148 | if (m_type_sp) |
149 | return m_type_sp->GetByteSize(exe_scope: exe_ctx.GetBestExecutionContextScope()); |
150 | return m_compiler_type.GetByteSize(exe_scope: exe_ctx.GetBestExecutionContextScope()); |
151 | } |
152 | |
153 | lldb::ValueType ValueObjectMemory::GetValueType() const { |
154 | // RETHINK: Should this be inherited from somewhere? |
155 | return lldb::eValueTypeVariableGlobal; |
156 | } |
157 | |
158 | bool ValueObjectMemory::UpdateValue() { |
159 | SetValueIsValid(false); |
160 | m_error.Clear(); |
161 | |
162 | ExecutionContext exe_ctx(GetExecutionContextRef()); |
163 | |
164 | Target *target = exe_ctx.GetTargetPtr(); |
165 | if (target) { |
166 | m_data.SetByteOrder(target->GetArchitecture().GetByteOrder()); |
167 | m_data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); |
168 | } |
169 | |
170 | Value old_value(m_value); |
171 | if (m_address.IsValid()) { |
172 | Value::ValueType value_type = m_value.GetValueType(); |
173 | |
174 | switch (value_type) { |
175 | case Value::ValueType::Invalid: |
176 | m_error.SetErrorString("Invalid value" ); |
177 | return false; |
178 | case Value::ValueType::Scalar: |
179 | // The variable value is in the Scalar value inside the m_value. We can |
180 | // point our m_data right to it. |
181 | m_error = m_value.GetValueAsData(exe_ctx: &exe_ctx, data&: m_data, module: GetModule().get()); |
182 | break; |
183 | |
184 | case Value::ValueType::FileAddress: |
185 | case Value::ValueType::LoadAddress: |
186 | case Value::ValueType::HostAddress: |
187 | // The DWARF expression result was an address in the inferior process. If |
188 | // this variable is an aggregate type, we just need the address as the |
189 | // main value as all child variable objects will rely upon this location |
190 | // and add an offset and then read their own values as needed. If this |
191 | // variable is a simple type, we read all data for it into m_data. Make |
192 | // sure this type has a value before we try and read it |
193 | |
194 | // If we have a file address, convert it to a load address if we can. |
195 | if (value_type == Value::ValueType::FileAddress && |
196 | exe_ctx.GetProcessPtr()) { |
197 | lldb::addr_t load_addr = m_address.GetLoadAddress(target); |
198 | if (load_addr != LLDB_INVALID_ADDRESS) { |
199 | m_value.SetValueType(Value::ValueType::LoadAddress); |
200 | m_value.GetScalar() = load_addr; |
201 | } |
202 | } |
203 | |
204 | if (!CanProvideValue()) { |
205 | // this value object represents an aggregate type whose children have |
206 | // values, but this object does not. So we say we are changed if our |
207 | // location has changed. |
208 | SetValueDidChange(value_type != old_value.GetValueType() || |
209 | m_value.GetScalar() != old_value.GetScalar()); |
210 | } else { |
211 | // Copy the Value and set the context to use our Variable so it can |
212 | // extract read its value into m_data appropriately |
213 | Value value(m_value); |
214 | if (m_type_sp) |
215 | value.SetContext(context_type: Value::ContextType::LLDBType, p: m_type_sp.get()); |
216 | else { |
217 | value.SetCompilerType(m_compiler_type); |
218 | } |
219 | |
220 | m_error = value.GetValueAsData(exe_ctx: &exe_ctx, data&: m_data, module: GetModule().get()); |
221 | } |
222 | break; |
223 | } |
224 | |
225 | SetValueIsValid(m_error.Success()); |
226 | } |
227 | return m_error.Success(); |
228 | } |
229 | |
230 | bool ValueObjectMemory::IsInScope() { |
231 | // FIXME: Maybe try to read the memory address, and if that works, then |
232 | // we are in scope? |
233 | return true; |
234 | } |
235 | |
236 | lldb::ModuleSP ValueObjectMemory::GetModule() { return m_address.GetModule(); } |
237 | |