1 | //===-- Materializer.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/Expression/Materializer.h" |
10 | #include "lldb/Core/DumpDataExtractor.h" |
11 | #include "lldb/Expression/ExpressionVariable.h" |
12 | #include "lldb/Symbol/Symbol.h" |
13 | #include "lldb/Symbol/Type.h" |
14 | #include "lldb/Symbol/Variable.h" |
15 | #include "lldb/Target/ExecutionContext.h" |
16 | #include "lldb/Target/RegisterContext.h" |
17 | #include "lldb/Target/StackFrame.h" |
18 | #include "lldb/Target/Target.h" |
19 | #include "lldb/Target/Thread.h" |
20 | #include "lldb/Utility/LLDBLog.h" |
21 | #include "lldb/Utility/Log.h" |
22 | #include "lldb/Utility/RegisterValue.h" |
23 | #include "lldb/ValueObject/ValueObjectConstResult.h" |
24 | #include "lldb/ValueObject/ValueObjectVariable.h" |
25 | #include "lldb/lldb-forward.h" |
26 | |
27 | #include <memory> |
28 | #include <optional> |
29 | |
30 | using namespace lldb_private; |
31 | |
32 | // FIXME: these should be retrieved from the target |
33 | // instead of being hard-coded. Currently we |
34 | // assume that persistent vars are materialized |
35 | // as references, and thus pick the size of a |
36 | // 64-bit pointer. |
37 | static constexpr uint32_t g_default_var_alignment = 8; |
38 | static constexpr uint32_t g_default_var_byte_size = 8; |
39 | |
40 | uint32_t Materializer::AddStructMember(Entity &entity) { |
41 | uint32_t size = entity.GetSize(); |
42 | uint32_t alignment = entity.GetAlignment(); |
43 | |
44 | uint32_t ret; |
45 | |
46 | if (m_current_offset == 0) |
47 | m_struct_alignment = alignment; |
48 | |
49 | if (m_current_offset % alignment) |
50 | m_current_offset += (alignment - (m_current_offset % alignment)); |
51 | |
52 | ret = m_current_offset; |
53 | |
54 | m_current_offset += size; |
55 | |
56 | return ret; |
57 | } |
58 | |
59 | class EntityPersistentVariable : public Materializer::Entity { |
60 | public: |
61 | EntityPersistentVariable(lldb::ExpressionVariableSP &persistent_variable_sp, |
62 | Materializer::PersistentVariableDelegate *delegate) |
63 | : Entity(), m_persistent_variable_sp(persistent_variable_sp), |
64 | m_delegate(delegate) { |
65 | // Hard-coding to maximum size of a pointer since persistent variables are |
66 | // materialized by reference |
67 | m_size = g_default_var_byte_size; |
68 | m_alignment = g_default_var_alignment; |
69 | } |
70 | |
71 | void MakeAllocation(IRMemoryMap &map, Status &err) { |
72 | Log *log = GetLog(mask: LLDBLog::Expressions); |
73 | |
74 | // Allocate a spare memory area to store the persistent variable's |
75 | // contents. |
76 | |
77 | Status allocate_error; |
78 | const bool zero_memory = false; |
79 | |
80 | lldb::addr_t mem = map.Malloc( |
81 | size: llvm::expectedToOptional(E: m_persistent_variable_sp->GetByteSize()) |
82 | .value_or(u: 0), |
83 | alignment: 8, permissions: lldb::ePermissionsReadable | lldb::ePermissionsWritable, |
84 | policy: IRMemoryMap::eAllocationPolicyMirror, zero_memory, error&: allocate_error); |
85 | |
86 | if (!allocate_error.Success()) { |
87 | err = Status::FromErrorStringWithFormat( |
88 | format: "couldn't allocate a memory area to store %s: %s", |
89 | m_persistent_variable_sp->GetName().GetCString(), |
90 | allocate_error.AsCString()); |
91 | return; |
92 | } |
93 | |
94 | LLDB_LOGF(log, "Allocated %s (0x%"PRIx64 ") successfully", |
95 | m_persistent_variable_sp->GetName().GetCString(), mem); |
96 | |
97 | // Put the location of the spare memory into the live data of the |
98 | // ValueObject. |
99 | |
100 | m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create( |
101 | exe_scope: map.GetBestExecutionContextScope(), |
102 | compiler_type: m_persistent_variable_sp->GetCompilerType(), |
103 | name: m_persistent_variable_sp->GetName(), address: mem, address_type: eAddressTypeLoad, |
104 | addr_byte_size: map.GetAddressByteSize()); |
105 | |
106 | // Clear the flag if the variable will never be deallocated. |
107 | |
108 | if (m_persistent_variable_sp->m_flags & |
109 | ExpressionVariable::EVKeepInTarget) { |
110 | Status leak_error; |
111 | map.Leak(process_address: mem, error&: leak_error); |
112 | m_persistent_variable_sp->m_flags &= |
113 | ~ExpressionVariable::EVNeedsAllocation; |
114 | } |
115 | |
116 | // Write the contents of the variable to the area. |
117 | |
118 | Status write_error; |
119 | |
120 | map.WriteMemory( |
121 | process_address: mem, bytes: m_persistent_variable_sp->GetValueBytes(), |
122 | size: llvm::expectedToOptional(E: m_persistent_variable_sp->GetByteSize()) |
123 | .value_or(u: 0), |
124 | error&: write_error); |
125 | |
126 | if (!write_error.Success()) { |
127 | err = Status::FromErrorStringWithFormat( |
128 | format: "couldn't write %s to the target: %s", |
129 | m_persistent_variable_sp->GetName().AsCString(), |
130 | write_error.AsCString()); |
131 | return; |
132 | } |
133 | } |
134 | |
135 | void DestroyAllocation(IRMemoryMap &map, Status &err) { |
136 | Status deallocate_error; |
137 | |
138 | map.Free(process_address: (lldb::addr_t)m_persistent_variable_sp->m_live_sp->GetValue() |
139 | .GetScalar() |
140 | .ULongLong(), |
141 | error&: deallocate_error); |
142 | |
143 | m_persistent_variable_sp->m_live_sp.reset(); |
144 | |
145 | if (!deallocate_error.Success()) { |
146 | err = Status::FromErrorStringWithFormat( |
147 | format: "couldn't deallocate memory for %s: %s", |
148 | m_persistent_variable_sp->GetName().GetCString(), |
149 | deallocate_error.AsCString()); |
150 | } |
151 | } |
152 | |
153 | void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
154 | lldb::addr_t process_address, Status &err) override { |
155 | Log *log = GetLog(mask: LLDBLog::Expressions); |
156 | |
157 | const lldb::addr_t load_addr = process_address + m_offset; |
158 | |
159 | if (log) { |
160 | LLDB_LOGF(log, |
161 | "EntityPersistentVariable::Materialize [address = 0x%"PRIx64 |
162 | ", m_name = %s, m_flags = 0x%hx]", |
163 | (uint64_t)load_addr, |
164 | m_persistent_variable_sp->GetName().AsCString(), |
165 | m_persistent_variable_sp->m_flags); |
166 | } |
167 | |
168 | if (m_persistent_variable_sp->m_flags & |
169 | ExpressionVariable::EVNeedsAllocation) { |
170 | MakeAllocation(map, err); |
171 | m_persistent_variable_sp->m_flags |= |
172 | ExpressionVariable::EVIsLLDBAllocated; |
173 | |
174 | if (!err.Success()) |
175 | return; |
176 | } |
177 | |
178 | if ((m_persistent_variable_sp->m_flags & |
179 | ExpressionVariable::EVIsProgramReference && |
180 | m_persistent_variable_sp->m_live_sp) || |
181 | m_persistent_variable_sp->m_flags & |
182 | ExpressionVariable::EVIsLLDBAllocated) { |
183 | Status write_error; |
184 | |
185 | map.WriteScalarToMemory( |
186 | process_address: load_addr, |
187 | scalar&: m_persistent_variable_sp->m_live_sp->GetValue().GetScalar(), |
188 | size: map.GetAddressByteSize(), error&: write_error); |
189 | |
190 | if (!write_error.Success()) { |
191 | err = Status::FromErrorStringWithFormat( |
192 | format: "couldn't write the location of %s to memory: %s", |
193 | m_persistent_variable_sp->GetName().AsCString(), |
194 | write_error.AsCString()); |
195 | } |
196 | } else { |
197 | err = Status::FromErrorStringWithFormat( |
198 | format: "no materialization happened for persistent variable %s", |
199 | m_persistent_variable_sp->GetName().AsCString()); |
200 | return; |
201 | } |
202 | } |
203 | |
204 | void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
205 | lldb::addr_t process_address, lldb::addr_t frame_top, |
206 | lldb::addr_t frame_bottom, Status &err) override { |
207 | Log *log = GetLog(mask: LLDBLog::Expressions); |
208 | |
209 | const lldb::addr_t load_addr = process_address + m_offset; |
210 | |
211 | if (log) { |
212 | LLDB_LOGF(log, |
213 | "EntityPersistentVariable::Dematerialize [address = 0x%"PRIx64 |
214 | ", m_name = %s, m_flags = 0x%hx]", |
215 | (uint64_t)process_address + m_offset, |
216 | m_persistent_variable_sp->GetName().AsCString(), |
217 | m_persistent_variable_sp->m_flags); |
218 | } |
219 | |
220 | if (m_delegate) { |
221 | m_delegate->DidDematerialize(variable&: m_persistent_variable_sp); |
222 | } |
223 | |
224 | if ((m_persistent_variable_sp->m_flags & |
225 | ExpressionVariable::EVIsLLDBAllocated) || |
226 | (m_persistent_variable_sp->m_flags & |
227 | ExpressionVariable::EVIsProgramReference)) { |
228 | if (m_persistent_variable_sp->m_flags & |
229 | ExpressionVariable::EVIsProgramReference && |
230 | !m_persistent_variable_sp->m_live_sp) { |
231 | // If the reference comes from the program, then the |
232 | // ClangExpressionVariable's live variable data hasn't been set up yet. |
233 | // Do this now. |
234 | |
235 | lldb::addr_t location; |
236 | Status read_error; |
237 | |
238 | map.ReadPointerFromMemory(address: &location, process_address: load_addr, error&: read_error); |
239 | |
240 | if (!read_error.Success()) { |
241 | err = Status::FromErrorStringWithFormat( |
242 | format: "couldn't read the address of program-allocated variable %s: %s", |
243 | m_persistent_variable_sp->GetName().GetCString(), |
244 | read_error.AsCString()); |
245 | return; |
246 | } |
247 | |
248 | m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create( |
249 | exe_scope: map.GetBestExecutionContextScope(), |
250 | compiler_type: m_persistent_variable_sp.get()->GetCompilerType(), |
251 | name: m_persistent_variable_sp->GetName(), address: location, address_type: eAddressTypeLoad, |
252 | addr_byte_size: llvm::expectedToOptional(E: m_persistent_variable_sp->GetByteSize()) |
253 | .value_or(u: 0)); |
254 | |
255 | if (frame_top != LLDB_INVALID_ADDRESS && |
256 | frame_bottom != LLDB_INVALID_ADDRESS && location >= frame_bottom && |
257 | location <= frame_top) { |
258 | // If the variable is resident in the stack frame created by the |
259 | // expression, then it cannot be relied upon to stay around. We |
260 | // treat it as needing reallocation. |
261 | m_persistent_variable_sp->m_flags |= |
262 | ExpressionVariable::EVIsLLDBAllocated; |
263 | m_persistent_variable_sp->m_flags |= |
264 | ExpressionVariable::EVNeedsAllocation; |
265 | m_persistent_variable_sp->m_flags |= |
266 | ExpressionVariable::EVNeedsFreezeDry; |
267 | m_persistent_variable_sp->m_flags &= |
268 | ~ExpressionVariable::EVIsProgramReference; |
269 | } |
270 | } |
271 | |
272 | lldb::addr_t mem = m_persistent_variable_sp->m_live_sp->GetValue() |
273 | .GetScalar() |
274 | .ULongLong(); |
275 | |
276 | if (!m_persistent_variable_sp->m_live_sp) { |
277 | err = Status::FromErrorStringWithFormat( |
278 | format: "couldn't find the memory area used to store %s", |
279 | m_persistent_variable_sp->GetName().GetCString()); |
280 | return; |
281 | } |
282 | |
283 | if (m_persistent_variable_sp->m_live_sp->GetValue() |
284 | .GetValueAddressType() != eAddressTypeLoad) { |
285 | err = Status::FromErrorStringWithFormat( |
286 | format: "the address of the memory area for %s is in an incorrect format", |
287 | m_persistent_variable_sp->GetName().GetCString()); |
288 | return; |
289 | } |
290 | |
291 | if (m_persistent_variable_sp->m_flags & |
292 | ExpressionVariable::EVNeedsFreezeDry || |
293 | m_persistent_variable_sp->m_flags & |
294 | ExpressionVariable::EVKeepInTarget) { |
295 | LLDB_LOGF(log, "Dematerializing %s from 0x%"PRIx64 " (size = %llu)", |
296 | m_persistent_variable_sp->GetName().GetCString(), |
297 | (uint64_t)mem, |
298 | (unsigned long long)llvm::expectedToOptional( |
299 | m_persistent_variable_sp->GetByteSize()) |
300 | .value_or(0)); |
301 | |
302 | // Read the contents of the spare memory area |
303 | |
304 | m_persistent_variable_sp->ValueUpdated(); |
305 | |
306 | Status read_error; |
307 | |
308 | map.ReadMemory( |
309 | bytes: m_persistent_variable_sp->GetValueBytes(), process_address: mem, |
310 | size: llvm::expectedToOptional(E: m_persistent_variable_sp->GetByteSize()) |
311 | .value_or(u: 0), |
312 | error&: read_error); |
313 | |
314 | if (!read_error.Success()) { |
315 | err = Status::FromErrorStringWithFormat( |
316 | format: "couldn't read the contents of %s from memory: %s", |
317 | m_persistent_variable_sp->GetName().GetCString(), |
318 | read_error.AsCString()); |
319 | return; |
320 | } |
321 | |
322 | m_persistent_variable_sp->m_flags &= |
323 | ~ExpressionVariable::EVNeedsFreezeDry; |
324 | } |
325 | } else { |
326 | err = Status::FromErrorStringWithFormat( |
327 | format: "no dematerialization happened for persistent variable %s", |
328 | m_persistent_variable_sp->GetName().AsCString()); |
329 | return; |
330 | } |
331 | |
332 | lldb::ProcessSP process_sp = |
333 | map.GetBestExecutionContextScope()->CalculateProcess(); |
334 | if (!process_sp || !process_sp->CanJIT()) { |
335 | // Allocations are not persistent so persistent variables cannot stay |
336 | // materialized. |
337 | |
338 | m_persistent_variable_sp->m_flags |= |
339 | ExpressionVariable::EVNeedsAllocation; |
340 | |
341 | DestroyAllocation(map, err); |
342 | if (!err.Success()) |
343 | return; |
344 | } else if (m_persistent_variable_sp->m_flags & |
345 | ExpressionVariable::EVNeedsAllocation && |
346 | !(m_persistent_variable_sp->m_flags & |
347 | ExpressionVariable::EVKeepInTarget)) { |
348 | DestroyAllocation(map, err); |
349 | if (!err.Success()) |
350 | return; |
351 | } |
352 | } |
353 | |
354 | void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, |
355 | Log *log) override { |
356 | StreamString dump_stream; |
357 | |
358 | Status err; |
359 | |
360 | const lldb::addr_t load_addr = process_address + m_offset; |
361 | |
362 | dump_stream.Printf(format: "0x%"PRIx64 ": EntityPersistentVariable (%s)\n", |
363 | load_addr, |
364 | m_persistent_variable_sp->GetName().AsCString()); |
365 | |
366 | { |
367 | dump_stream.Printf(format: "Pointer:\n"); |
368 | |
369 | DataBufferHeap data(m_size, 0); |
370 | |
371 | map.ReadMemory(bytes: data.GetBytes(), process_address: load_addr, size: m_size, error&: err); |
372 | |
373 | if (!err.Success()) { |
374 | dump_stream.Printf(format: " <could not be read>\n"); |
375 | } else { |
376 | DumpHexBytes(s: &dump_stream, src: data.GetBytes(), src_len: data.GetByteSize(), bytes_per_line: 16, |
377 | base_addr: load_addr); |
378 | |
379 | dump_stream.PutChar(ch: '\n'); |
380 | } |
381 | } |
382 | |
383 | { |
384 | dump_stream.Printf(format: "Target:\n"); |
385 | |
386 | lldb::addr_t target_address; |
387 | |
388 | map.ReadPointerFromMemory(address: &target_address, process_address: load_addr, error&: err); |
389 | |
390 | if (!err.Success()) { |
391 | dump_stream.Printf(format: " <could not be read>\n"); |
392 | } else { |
393 | DataBufferHeap data( |
394 | llvm::expectedToOptional(E: m_persistent_variable_sp->GetByteSize()) |
395 | .value_or(u: 0), |
396 | 0); |
397 | |
398 | map.ReadMemory( |
399 | bytes: data.GetBytes(), process_address: target_address, |
400 | size: llvm::expectedToOptional(E: m_persistent_variable_sp->GetByteSize()) |
401 | .value_or(u: 0), |
402 | error&: err); |
403 | |
404 | if (!err.Success()) { |
405 | dump_stream.Printf(format: " <could not be read>\n"); |
406 | } else { |
407 | DumpHexBytes(s: &dump_stream, src: data.GetBytes(), src_len: data.GetByteSize(), bytes_per_line: 16, |
408 | base_addr: target_address); |
409 | |
410 | dump_stream.PutChar(ch: '\n'); |
411 | } |
412 | } |
413 | } |
414 | |
415 | log->PutString(str: dump_stream.GetString()); |
416 | } |
417 | |
418 | void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {} |
419 | |
420 | private: |
421 | lldb::ExpressionVariableSP m_persistent_variable_sp; |
422 | Materializer::PersistentVariableDelegate *m_delegate; |
423 | }; |
424 | |
425 | uint32_t Materializer::AddPersistentVariable( |
426 | lldb::ExpressionVariableSP &persistent_variable_sp, |
427 | PersistentVariableDelegate *delegate, Status &err) { |
428 | EntityVector::iterator iter = m_entities.insert(position: m_entities.end(), x: EntityUP()); |
429 | *iter = std::make_unique<EntityPersistentVariable>(args&: persistent_variable_sp, |
430 | args&: delegate); |
431 | uint32_t ret = AddStructMember(entity&: **iter); |
432 | (*iter)->SetOffset(ret); |
433 | return ret; |
434 | } |
435 | |
436 | /// Base class for materialization of Variables and ValueObjects. |
437 | /// |
438 | /// Subclasses specify how to obtain the Value which is to be |
439 | /// materialized. |
440 | class EntityVariableBase : public Materializer::Entity { |
441 | public: |
442 | virtual ~EntityVariableBase() = default; |
443 | |
444 | EntityVariableBase() { |
445 | // Hard-coding to maximum size of a pointer since all variables are |
446 | // materialized by reference |
447 | m_size = g_default_var_byte_size; |
448 | m_alignment = g_default_var_alignment; |
449 | } |
450 | |
451 | void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
452 | lldb::addr_t process_address, Status &err) override { |
453 | Log *log = GetLog(mask: LLDBLog::Expressions); |
454 | |
455 | const lldb::addr_t load_addr = process_address + m_offset; |
456 | if (log) { |
457 | LLDB_LOGF(log, |
458 | "EntityVariable::Materialize [address = 0x%"PRIx64 |
459 | ", m_variable_sp = %s]", |
460 | (uint64_t)load_addr, GetName().GetCString()); |
461 | } |
462 | |
463 | ExecutionContextScope *scope = frame_sp.get(); |
464 | |
465 | if (!scope) |
466 | scope = map.GetBestExecutionContextScope(); |
467 | |
468 | lldb::ValueObjectSP valobj_sp = SetupValueObject(scope); |
469 | |
470 | if (!valobj_sp) { |
471 | err = Status::FromErrorStringWithFormat( |
472 | format: "couldn't get a value object for variable %s", GetName().AsCString()); |
473 | return; |
474 | } |
475 | |
476 | Status valobj_error = valobj_sp->GetError().Clone(); |
477 | |
478 | if (valobj_error.Fail()) { |
479 | err = Status::FromErrorStringWithFormat( |
480 | format: "couldn't get the value of variable %s: %s", GetName().AsCString(), |
481 | valobj_error.AsCString()); |
482 | return; |
483 | } |
484 | |
485 | if (m_is_reference) { |
486 | DataExtractor valobj_extractor; |
487 | Status extract_error; |
488 | valobj_sp->GetData(data&: valobj_extractor, error&: extract_error); |
489 | |
490 | if (!extract_error.Success()) { |
491 | err = Status::FromErrorStringWithFormat( |
492 | format: "couldn't read contents of reference variable %s: %s", |
493 | GetName().AsCString(), extract_error.AsCString()); |
494 | return; |
495 | } |
496 | |
497 | lldb::offset_t offset = 0; |
498 | lldb::addr_t reference_addr = valobj_extractor.GetAddress(offset_ptr: &offset); |
499 | |
500 | Status write_error; |
501 | map.WritePointerToMemory(process_address: load_addr, address: reference_addr, error&: write_error); |
502 | |
503 | if (!write_error.Success()) { |
504 | err = Status::FromErrorStringWithFormat( |
505 | format: "couldn't write the contents of reference " |
506 | "variable %s to memory: %s", |
507 | GetName().AsCString(), write_error.AsCString()); |
508 | return; |
509 | } |
510 | } else { |
511 | lldb::addr_t addr_of_valobj = |
512 | valobj_sp->GetAddressOf(/*scalar_is_load_address=*/false).address; |
513 | if (addr_of_valobj != LLDB_INVALID_ADDRESS) { |
514 | Status write_error; |
515 | map.WritePointerToMemory(process_address: load_addr, address: addr_of_valobj, error&: write_error); |
516 | |
517 | if (!write_error.Success()) { |
518 | err = Status::FromErrorStringWithFormat( |
519 | format: "couldn't write the address of variable %s to memory: %s", |
520 | GetName().AsCString(), write_error.AsCString()); |
521 | return; |
522 | } |
523 | } else { |
524 | DataExtractor data; |
525 | Status extract_error; |
526 | valobj_sp->GetData(data, error&: extract_error); |
527 | if (!extract_error.Success()) { |
528 | err = Status::FromErrorStringWithFormat( |
529 | format: "couldn't get the value of %s: %s", GetName().AsCString(), |
530 | extract_error.AsCString()); |
531 | return; |
532 | } |
533 | |
534 | if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { |
535 | err = Status::FromErrorStringWithFormat( |
536 | format: "trying to create a temporary region for %s but one exists", |
537 | GetName().AsCString()); |
538 | return; |
539 | } |
540 | |
541 | if (data.GetByteSize() < |
542 | llvm::expectedToOptional(E: GetByteSize(scope)).value_or(u: 0)) { |
543 | if (data.GetByteSize() == 0 && !LocationExpressionIsValid()) { |
544 | err = Status::FromErrorStringWithFormat( |
545 | format: "the variable '%s' has no location, " |
546 | "it may have been optimized out", |
547 | GetName().AsCString()); |
548 | } else { |
549 | err = Status::FromErrorStringWithFormat( |
550 | format: "size of variable %s (%"PRIu64 |
551 | ") is larger than the ValueObject's size (%"PRIu64 ")", |
552 | GetName().AsCString(), |
553 | llvm::expectedToOptional(E: GetByteSize(scope)).value_or(u: 0), |
554 | data.GetByteSize()); |
555 | } |
556 | return; |
557 | } |
558 | |
559 | std::optional<size_t> opt_bit_align = GetTypeBitAlign(scope); |
560 | if (!opt_bit_align) { |
561 | err = Status::FromErrorStringWithFormat( |
562 | format: "can't get the type alignment for %s", GetName().AsCString()); |
563 | return; |
564 | } |
565 | |
566 | size_t byte_align = (*opt_bit_align + 7) / 8; |
567 | |
568 | Status alloc_error; |
569 | const bool zero_memory = false; |
570 | |
571 | m_temporary_allocation = map.Malloc( |
572 | size: data.GetByteSize(), alignment: byte_align, |
573 | permissions: lldb::ePermissionsReadable | lldb::ePermissionsWritable, |
574 | policy: IRMemoryMap::eAllocationPolicyMirror, zero_memory, error&: alloc_error); |
575 | |
576 | m_temporary_allocation_size = data.GetByteSize(); |
577 | |
578 | m_original_data = std::make_shared<DataBufferHeap>(args: data.GetDataStart(), |
579 | args: data.GetByteSize()); |
580 | |
581 | if (!alloc_error.Success()) { |
582 | err = Status::FromErrorStringWithFormat( |
583 | format: "couldn't allocate a temporary region for %s: %s", |
584 | GetName().AsCString(), alloc_error.AsCString()); |
585 | return; |
586 | } |
587 | |
588 | Status write_error; |
589 | |
590 | map.WriteMemory(process_address: m_temporary_allocation, bytes: data.GetDataStart(), |
591 | size: data.GetByteSize(), error&: write_error); |
592 | |
593 | if (!write_error.Success()) { |
594 | err = Status::FromErrorStringWithFormat( |
595 | format: "couldn't write to the temporary region for %s: %s", |
596 | GetName().AsCString(), write_error.AsCString()); |
597 | return; |
598 | } |
599 | |
600 | Status pointer_write_error; |
601 | |
602 | map.WritePointerToMemory(process_address: load_addr, address: m_temporary_allocation, |
603 | error&: pointer_write_error); |
604 | |
605 | if (!pointer_write_error.Success()) { |
606 | err = Status::FromErrorStringWithFormat( |
607 | format: "couldn't write the address of the temporary region for %s: %s", |
608 | GetName().AsCString(), pointer_write_error.AsCString()); |
609 | } |
610 | } |
611 | } |
612 | } |
613 | |
614 | void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
615 | lldb::addr_t process_address, lldb::addr_t frame_top, |
616 | lldb::addr_t frame_bottom, Status &err) override { |
617 | Log *log = GetLog(mask: LLDBLog::Expressions); |
618 | |
619 | const lldb::addr_t load_addr = process_address + m_offset; |
620 | if (log) { |
621 | LLDB_LOGF(log, |
622 | "EntityVariable::Dematerialize [address = 0x%"PRIx64 |
623 | ", m_variable_sp = %s]", |
624 | (uint64_t)load_addr, GetName().AsCString()); |
625 | } |
626 | |
627 | if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { |
628 | ExecutionContextScope *scope = frame_sp.get(); |
629 | |
630 | if (!scope) |
631 | scope = map.GetBestExecutionContextScope(); |
632 | |
633 | lldb::ValueObjectSP valobj_sp = SetupValueObject(scope); |
634 | |
635 | if (!valobj_sp) { |
636 | err = Status::FromErrorStringWithFormat( |
637 | format: "couldn't get a value object for variable %s", |
638 | GetName().AsCString()); |
639 | return; |
640 | } |
641 | |
642 | lldb_private::DataExtractor data; |
643 | |
644 | Status extract_error; |
645 | |
646 | map.GetMemoryData( |
647 | extractor&: data, process_address: m_temporary_allocation, |
648 | size: llvm::expectedToOptional(E: valobj_sp->GetByteSize()).value_or(u: 0), |
649 | error&: extract_error); |
650 | |
651 | if (!extract_error.Success()) { |
652 | err = Status::FromErrorStringWithFormat( |
653 | format: "couldn't get the data for variable %s", GetName().AsCString()); |
654 | return; |
655 | } |
656 | |
657 | bool actually_write = true; |
658 | |
659 | if (m_original_data) { |
660 | if ((data.GetByteSize() == m_original_data->GetByteSize()) && |
661 | !memcmp(s1: m_original_data->GetBytes(), s2: data.GetDataStart(), |
662 | n: data.GetByteSize())) { |
663 | actually_write = false; |
664 | } |
665 | } |
666 | |
667 | Status set_error; |
668 | |
669 | if (actually_write) { |
670 | valobj_sp->SetData(data, error&: set_error); |
671 | |
672 | if (!set_error.Success()) { |
673 | err = Status::FromErrorStringWithFormat( |
674 | format: "couldn't write the new contents of %s back into the variable", |
675 | GetName().AsCString()); |
676 | return; |
677 | } |
678 | } |
679 | |
680 | Status free_error; |
681 | |
682 | map.Free(process_address: m_temporary_allocation, error&: free_error); |
683 | |
684 | if (!free_error.Success()) { |
685 | err = Status::FromErrorStringWithFormat( |
686 | format: "couldn't free the temporary region for %s: %s", |
687 | GetName().AsCString(), free_error.AsCString()); |
688 | return; |
689 | } |
690 | |
691 | m_original_data.reset(); |
692 | m_temporary_allocation = LLDB_INVALID_ADDRESS; |
693 | m_temporary_allocation_size = 0; |
694 | } |
695 | } |
696 | |
697 | void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, |
698 | Log *log) override { |
699 | StreamString dump_stream; |
700 | |
701 | const lldb::addr_t load_addr = process_address + m_offset; |
702 | dump_stream.Printf(format: "0x%"PRIx64 ": EntityVariable\n", load_addr); |
703 | |
704 | Status err; |
705 | |
706 | lldb::addr_t ptr = LLDB_INVALID_ADDRESS; |
707 | |
708 | { |
709 | dump_stream.Printf(format: "Pointer:\n"); |
710 | |
711 | DataBufferHeap data(m_size, 0); |
712 | |
713 | map.ReadMemory(bytes: data.GetBytes(), process_address: load_addr, size: m_size, error&: err); |
714 | |
715 | if (!err.Success()) { |
716 | dump_stream.Printf(format: " <could not be read>\n"); |
717 | } else { |
718 | DataExtractor extractor(data.GetBytes(), data.GetByteSize(), |
719 | map.GetByteOrder(), map.GetAddressByteSize()); |
720 | |
721 | DumpHexBytes(s: &dump_stream, src: data.GetBytes(), src_len: data.GetByteSize(), bytes_per_line: 16, |
722 | base_addr: load_addr); |
723 | |
724 | lldb::offset_t offset = 0; |
725 | |
726 | ptr = extractor.GetAddress(offset_ptr: &offset); |
727 | |
728 | dump_stream.PutChar(ch: '\n'); |
729 | } |
730 | } |
731 | |
732 | if (m_temporary_allocation == LLDB_INVALID_ADDRESS) { |
733 | dump_stream.Printf(format: "Points to process memory:\n"); |
734 | } else { |
735 | dump_stream.Printf(format: "Temporary allocation:\n"); |
736 | } |
737 | |
738 | if (ptr == LLDB_INVALID_ADDRESS) { |
739 | dump_stream.Printf(format: " <could not be be found>\n"); |
740 | } else { |
741 | DataBufferHeap data(m_temporary_allocation_size, 0); |
742 | |
743 | map.ReadMemory(bytes: data.GetBytes(), process_address: m_temporary_allocation, |
744 | size: m_temporary_allocation_size, error&: err); |
745 | |
746 | if (!err.Success()) { |
747 | dump_stream.Printf(format: " <could not be read>\n"); |
748 | } else { |
749 | DumpHexBytes(s: &dump_stream, src: data.GetBytes(), src_len: data.GetByteSize(), bytes_per_line: 16, |
750 | base_addr: load_addr); |
751 | |
752 | dump_stream.PutChar(ch: '\n'); |
753 | } |
754 | } |
755 | |
756 | log->PutString(str: dump_stream.GetString()); |
757 | } |
758 | |
759 | void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override { |
760 | if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { |
761 | Status free_error; |
762 | |
763 | map.Free(process_address: m_temporary_allocation, error&: free_error); |
764 | |
765 | m_temporary_allocation = LLDB_INVALID_ADDRESS; |
766 | m_temporary_allocation_size = 0; |
767 | } |
768 | } |
769 | |
770 | private: |
771 | virtual ConstString GetName() const = 0; |
772 | |
773 | /// Creates and returns ValueObject tied to this variable |
774 | /// and prepares Entity for materialization. |
775 | /// |
776 | /// Called each time the Materializer (de)materializes a |
777 | /// variable. We re-create the ValueObject based on the |
778 | /// current ExecutionContextScope since clients such as |
779 | /// conditional breakpoints may materialize the same |
780 | /// EntityVariable multiple times with different frames. |
781 | /// |
782 | /// Each subsequent use of the EntityVariableBase interface |
783 | /// will query the newly created ValueObject until this |
784 | /// function is called again. |
785 | virtual lldb::ValueObjectSP |
786 | SetupValueObject(ExecutionContextScope *scope) = 0; |
787 | |
788 | /// Returns size in bytes of the type associated with this variable |
789 | /// |
790 | /// \returns On success, returns byte size of the type associated |
791 | /// with this variable. Returns std::nullopt otherwise. |
792 | virtual llvm::Expected<uint64_t> |
793 | GetByteSize(ExecutionContextScope *scope) const = 0; |
794 | |
795 | /// Returns 'true' if the location expression associated with this variable |
796 | /// is valid. |
797 | virtual bool LocationExpressionIsValid() const = 0; |
798 | |
799 | /// Returns alignment of the type associated with this variable in bits. |
800 | /// |
801 | /// \returns On success, returns alignment in bits for the type associated |
802 | /// with this variable. Returns std::nullopt otherwise. |
803 | virtual std::optional<size_t> |
804 | GetTypeBitAlign(ExecutionContextScope *scope) const = 0; |
805 | |
806 | protected: |
807 | bool m_is_reference = false; |
808 | lldb::addr_t m_temporary_allocation = LLDB_INVALID_ADDRESS; |
809 | size_t m_temporary_allocation_size = 0; |
810 | lldb::DataBufferSP m_original_data; |
811 | }; |
812 | |
813 | /// Represents an Entity constructed from a VariableSP. |
814 | /// |
815 | /// This class is used for materialization of variables for which |
816 | /// the user has a VariableSP on hand. The ValueObject is then |
817 | /// derived from the associated DWARF location expression when needed |
818 | /// by the Materializer. |
819 | class EntityVariable : public EntityVariableBase { |
820 | public: |
821 | EntityVariable(lldb::VariableSP &variable_sp) : m_variable_sp(variable_sp) { |
822 | m_is_reference = |
823 | m_variable_sp->GetType()->GetForwardCompilerType().IsReferenceType(); |
824 | } |
825 | |
826 | ConstString GetName() const override { return m_variable_sp->GetName(); } |
827 | |
828 | lldb::ValueObjectSP SetupValueObject(ExecutionContextScope *scope) override { |
829 | assert(m_variable_sp != nullptr); |
830 | return ValueObjectVariable::Create(exe_scope: scope, var_sp: m_variable_sp); |
831 | } |
832 | |
833 | llvm::Expected<uint64_t> |
834 | GetByteSize(ExecutionContextScope *scope) const override { |
835 | return m_variable_sp->GetType()->GetByteSize(exe_scope: scope); |
836 | } |
837 | |
838 | bool LocationExpressionIsValid() const override { |
839 | return m_variable_sp->LocationExpressionList().IsValid(); |
840 | } |
841 | |
842 | std::optional<size_t> |
843 | GetTypeBitAlign(ExecutionContextScope *scope) const override { |
844 | return m_variable_sp->GetType()->GetLayoutCompilerType().GetTypeBitAlign( |
845 | exe_scope: scope); |
846 | } |
847 | |
848 | private: |
849 | lldb::VariableSP m_variable_sp; ///< Variable that this entity is based on. |
850 | }; |
851 | |
852 | /// Represents an Entity constructed from a VariableSP. |
853 | /// |
854 | /// This class is used for materialization of variables for |
855 | /// which the user does not have a VariableSP available (e.g., |
856 | /// when materializing ivars). |
857 | class EntityValueObject : public EntityVariableBase { |
858 | public: |
859 | EntityValueObject(ConstString name, ValueObjectProviderTy provider) |
860 | : m_name(name), m_valobj_provider(std::move(provider)) { |
861 | assert(m_valobj_provider); |
862 | } |
863 | |
864 | ConstString GetName() const override { return m_name; } |
865 | |
866 | lldb::ValueObjectSP SetupValueObject(ExecutionContextScope *scope) override { |
867 | m_valobj_sp = |
868 | m_valobj_provider(GetName(), scope->CalculateStackFrame().get()); |
869 | |
870 | if (m_valobj_sp) |
871 | m_is_reference = m_valobj_sp->GetCompilerType().IsReferenceType(); |
872 | |
873 | return m_valobj_sp; |
874 | } |
875 | |
876 | llvm::Expected<uint64_t> |
877 | GetByteSize(ExecutionContextScope *scope) const override { |
878 | if (m_valobj_sp) |
879 | return m_valobj_sp->GetCompilerType().GetByteSize(exe_scope: scope); |
880 | |
881 | return llvm::createStringError(Fmt: "no value object"); |
882 | } |
883 | |
884 | bool LocationExpressionIsValid() const override { |
885 | if (m_valobj_sp) |
886 | return m_valobj_sp->GetError().Success(); |
887 | |
888 | return false; |
889 | } |
890 | |
891 | std::optional<size_t> |
892 | GetTypeBitAlign(ExecutionContextScope *scope) const override { |
893 | if (m_valobj_sp) |
894 | return m_valobj_sp->GetCompilerType().GetTypeBitAlign(exe_scope: scope); |
895 | |
896 | return {}; |
897 | } |
898 | |
899 | private: |
900 | ConstString m_name; |
901 | lldb::ValueObjectSP m_valobj_sp; |
902 | ValueObjectProviderTy m_valobj_provider; |
903 | }; |
904 | |
905 | uint32_t Materializer::AddVariable(lldb::VariableSP &variable_sp, Status &err) { |
906 | EntityVector::iterator iter = m_entities.insert(position: m_entities.end(), x: EntityUP()); |
907 | *iter = std::make_unique<EntityVariable>(args&: variable_sp); |
908 | uint32_t ret = AddStructMember(entity&: **iter); |
909 | (*iter)->SetOffset(ret); |
910 | return ret; |
911 | } |
912 | |
913 | uint32_t Materializer::AddValueObject(ConstString name, |
914 | ValueObjectProviderTy valobj_provider, |
915 | Status &err) { |
916 | assert(valobj_provider); |
917 | EntityVector::iterator iter = m_entities.insert(position: m_entities.end(), x: EntityUP()); |
918 | *iter = std::make_unique<EntityValueObject>(args&: name, args: std::move(valobj_provider)); |
919 | uint32_t ret = AddStructMember(entity&: **iter); |
920 | (*iter)->SetOffset(ret); |
921 | return ret; |
922 | } |
923 | |
924 | class EntityResultVariable : public Materializer::Entity { |
925 | public: |
926 | EntityResultVariable(const CompilerType &type, bool is_program_reference, |
927 | bool keep_in_memory, |
928 | Materializer::PersistentVariableDelegate *delegate) |
929 | : Entity(), m_type(type), m_is_program_reference(is_program_reference), |
930 | m_keep_in_memory(keep_in_memory), m_delegate(delegate) { |
931 | // Hard-coding to maximum size of a pointer since all results are |
932 | // materialized by reference |
933 | m_size = g_default_var_byte_size; |
934 | m_alignment = g_default_var_alignment; |
935 | } |
936 | |
937 | void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
938 | lldb::addr_t process_address, Status &err) override { |
939 | if (!m_is_program_reference) { |
940 | if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { |
941 | err = Status::FromErrorString( |
942 | str: "Trying to create a temporary region for the result " |
943 | "but one exists"); |
944 | return; |
945 | } |
946 | |
947 | const lldb::addr_t load_addr = process_address + m_offset; |
948 | |
949 | ExecutionContextScope *exe_scope = frame_sp.get(); |
950 | if (!exe_scope) |
951 | exe_scope = map.GetBestExecutionContextScope(); |
952 | |
953 | auto byte_size_or_err = m_type.GetByteSize(exe_scope); |
954 | if (!byte_size_or_err) { |
955 | err = Status::FromError(error: byte_size_or_err.takeError()); |
956 | return; |
957 | } |
958 | auto byte_size = *byte_size_or_err; |
959 | |
960 | std::optional<size_t> opt_bit_align = m_type.GetTypeBitAlign(exe_scope); |
961 | if (!opt_bit_align) { |
962 | err = Status::FromErrorStringWithFormat( |
963 | format: "can't get the alignment of type \"%s\"", |
964 | m_type.GetTypeName().AsCString()); |
965 | return; |
966 | } |
967 | |
968 | size_t byte_align = (*opt_bit_align + 7) / 8; |
969 | |
970 | Status alloc_error; |
971 | const bool zero_memory = true; |
972 | |
973 | m_temporary_allocation = map.Malloc( |
974 | size: byte_size, alignment: byte_align, |
975 | permissions: lldb::ePermissionsReadable | lldb::ePermissionsWritable, |
976 | policy: IRMemoryMap::eAllocationPolicyMirror, zero_memory, error&: alloc_error); |
977 | m_temporary_allocation_size = byte_size; |
978 | |
979 | if (!alloc_error.Success()) { |
980 | err = Status::FromErrorStringWithFormat( |
981 | format: "couldn't allocate a temporary region for the result: %s", |
982 | alloc_error.AsCString()); |
983 | return; |
984 | } |
985 | |
986 | Status pointer_write_error; |
987 | |
988 | map.WritePointerToMemory(process_address: load_addr, address: m_temporary_allocation, |
989 | error&: pointer_write_error); |
990 | |
991 | if (!pointer_write_error.Success()) { |
992 | err = Status::FromErrorStringWithFormat( |
993 | format: "couldn't write the address of the " |
994 | "temporary region for the result: %s", |
995 | pointer_write_error.AsCString()); |
996 | } |
997 | } |
998 | } |
999 | |
1000 | void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
1001 | lldb::addr_t process_address, lldb::addr_t frame_top, |
1002 | lldb::addr_t frame_bottom, Status &err) override { |
1003 | err.Clear(); |
1004 | |
1005 | ExecutionContextScope *exe_scope = frame_sp.get(); |
1006 | if (!exe_scope) |
1007 | exe_scope = map.GetBestExecutionContextScope(); |
1008 | |
1009 | if (!exe_scope) { |
1010 | err = Status::FromErrorString( |
1011 | str: "Couldn't dematerialize a result variable: invalid " |
1012 | "execution context scope"); |
1013 | return; |
1014 | } |
1015 | |
1016 | lldb::addr_t address; |
1017 | Status read_error; |
1018 | const lldb::addr_t load_addr = process_address + m_offset; |
1019 | |
1020 | map.ReadPointerFromMemory(address: &address, process_address: load_addr, error&: read_error); |
1021 | |
1022 | if (!read_error.Success()) { |
1023 | err = Status::FromErrorString( |
1024 | str: "Couldn't dematerialize a result variable: couldn't " |
1025 | "read its address"); |
1026 | return; |
1027 | } |
1028 | |
1029 | lldb::TargetSP target_sp = exe_scope->CalculateTarget(); |
1030 | |
1031 | if (!target_sp) { |
1032 | err = Status::FromErrorString( |
1033 | str: "Couldn't dematerialize a result variable: no target"); |
1034 | return; |
1035 | } |
1036 | |
1037 | auto type_system_or_err = |
1038 | target_sp->GetScratchTypeSystemForLanguage(language: m_type.GetMinimumLanguage()); |
1039 | |
1040 | if (auto error = type_system_or_err.takeError()) { |
1041 | err = Status::FromErrorStringWithFormat( |
1042 | format: "Couldn't dematerialize a result variable: " |
1043 | "couldn't get the corresponding type " |
1044 | "system: %s", |
1045 | llvm::toString(E: std::move(error)).c_str()); |
1046 | return; |
1047 | } |
1048 | auto ts = *type_system_or_err; |
1049 | if (!ts) { |
1050 | err = Status::FromErrorStringWithFormat( |
1051 | format: "Couldn't dematerialize a result variable: " |
1052 | "couldn't corresponding type system is " |
1053 | "no longer live."); |
1054 | return; |
1055 | } |
1056 | PersistentExpressionState *persistent_state = |
1057 | ts->GetPersistentExpressionState(); |
1058 | |
1059 | if (!persistent_state) { |
1060 | err = Status::FromErrorString( |
1061 | str: "Couldn't dematerialize a result variable: " |
1062 | "corresponding type system doesn't handle persistent " |
1063 | "variables"); |
1064 | return; |
1065 | } |
1066 | |
1067 | ConstString name = m_delegate |
1068 | ? m_delegate->GetName() |
1069 | : persistent_state->GetNextPersistentVariableName(); |
1070 | |
1071 | lldb::ExpressionVariableSP ret = persistent_state->CreatePersistentVariable( |
1072 | exe_scope, name, type: m_type, byte_order: map.GetByteOrder(), addr_byte_size: map.GetAddressByteSize()); |
1073 | |
1074 | if (!ret) { |
1075 | err = Status::FromErrorStringWithFormat( |
1076 | format: "couldn't dematerialize a result variable: " |
1077 | "failed to make persistent variable %s", |
1078 | name.AsCString()); |
1079 | return; |
1080 | } |
1081 | |
1082 | lldb::ProcessSP process_sp = |
1083 | map.GetBestExecutionContextScope()->CalculateProcess(); |
1084 | |
1085 | if (m_delegate) { |
1086 | m_delegate->DidDematerialize(variable&: ret); |
1087 | } |
1088 | |
1089 | bool can_persist = |
1090 | (m_is_program_reference && process_sp && process_sp->CanJIT() && |
1091 | !(address >= frame_bottom && address < frame_top)); |
1092 | |
1093 | if (can_persist && m_keep_in_memory) { |
1094 | ret->m_live_sp = ValueObjectConstResult::Create(exe_scope, compiler_type: m_type, name, |
1095 | address, address_type: eAddressTypeLoad, |
1096 | addr_byte_size: map.GetAddressByteSize()); |
1097 | } |
1098 | |
1099 | ret->ValueUpdated(); |
1100 | |
1101 | const size_t pvar_byte_size = |
1102 | llvm::expectedToOptional(E: ret->GetByteSize()).value_or(u: 0); |
1103 | uint8_t *pvar_data = ret->GetValueBytes(); |
1104 | |
1105 | map.ReadMemory(bytes: pvar_data, process_address: address, size: pvar_byte_size, error&: read_error); |
1106 | |
1107 | if (!read_error.Success()) { |
1108 | err = Status::FromErrorString( |
1109 | str: "Couldn't dematerialize a result variable: couldn't read its memory"); |
1110 | return; |
1111 | } |
1112 | |
1113 | if (!can_persist || !m_keep_in_memory) { |
1114 | ret->m_flags |= ExpressionVariable::EVNeedsAllocation; |
1115 | |
1116 | if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { |
1117 | Status free_error; |
1118 | map.Free(process_address: m_temporary_allocation, error&: free_error); |
1119 | } |
1120 | } else { |
1121 | ret->m_flags |= ExpressionVariable::EVIsLLDBAllocated; |
1122 | } |
1123 | |
1124 | m_temporary_allocation = LLDB_INVALID_ADDRESS; |
1125 | m_temporary_allocation_size = 0; |
1126 | } |
1127 | |
1128 | void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, |
1129 | Log *log) override { |
1130 | StreamString dump_stream; |
1131 | |
1132 | const lldb::addr_t load_addr = process_address + m_offset; |
1133 | |
1134 | dump_stream.Printf(format: "0x%"PRIx64 ": EntityResultVariable\n", load_addr); |
1135 | |
1136 | Status err; |
1137 | |
1138 | lldb::addr_t ptr = LLDB_INVALID_ADDRESS; |
1139 | |
1140 | { |
1141 | dump_stream.Printf(format: "Pointer:\n"); |
1142 | |
1143 | DataBufferHeap data(m_size, 0); |
1144 | |
1145 | map.ReadMemory(bytes: data.GetBytes(), process_address: load_addr, size: m_size, error&: err); |
1146 | |
1147 | if (!err.Success()) { |
1148 | dump_stream.Printf(format: " <could not be read>\n"); |
1149 | } else { |
1150 | DataExtractor extractor(data.GetBytes(), data.GetByteSize(), |
1151 | map.GetByteOrder(), map.GetAddressByteSize()); |
1152 | |
1153 | DumpHexBytes(s: &dump_stream, src: data.GetBytes(), src_len: data.GetByteSize(), bytes_per_line: 16, |
1154 | base_addr: load_addr); |
1155 | |
1156 | lldb::offset_t offset = 0; |
1157 | |
1158 | ptr = extractor.GetAddress(offset_ptr: &offset); |
1159 | |
1160 | dump_stream.PutChar(ch: '\n'); |
1161 | } |
1162 | } |
1163 | |
1164 | if (m_temporary_allocation == LLDB_INVALID_ADDRESS) { |
1165 | dump_stream.Printf(format: "Points to process memory:\n"); |
1166 | } else { |
1167 | dump_stream.Printf(format: "Temporary allocation:\n"); |
1168 | } |
1169 | |
1170 | if (ptr == LLDB_INVALID_ADDRESS) { |
1171 | dump_stream.Printf(format: " <could not be be found>\n"); |
1172 | } else { |
1173 | DataBufferHeap data(m_temporary_allocation_size, 0); |
1174 | |
1175 | map.ReadMemory(bytes: data.GetBytes(), process_address: m_temporary_allocation, |
1176 | size: m_temporary_allocation_size, error&: err); |
1177 | |
1178 | if (!err.Success()) { |
1179 | dump_stream.Printf(format: " <could not be read>\n"); |
1180 | } else { |
1181 | DumpHexBytes(s: &dump_stream, src: data.GetBytes(), src_len: data.GetByteSize(), bytes_per_line: 16, |
1182 | base_addr: load_addr); |
1183 | |
1184 | dump_stream.PutChar(ch: '\n'); |
1185 | } |
1186 | } |
1187 | |
1188 | log->PutString(str: dump_stream.GetString()); |
1189 | } |
1190 | |
1191 | void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override { |
1192 | if (!m_keep_in_memory && m_temporary_allocation != LLDB_INVALID_ADDRESS) { |
1193 | Status free_error; |
1194 | |
1195 | map.Free(process_address: m_temporary_allocation, error&: free_error); |
1196 | } |
1197 | |
1198 | m_temporary_allocation = LLDB_INVALID_ADDRESS; |
1199 | m_temporary_allocation_size = 0; |
1200 | } |
1201 | |
1202 | private: |
1203 | CompilerType m_type; |
1204 | /// This is used both to control whether this result entity can (and should) |
1205 | /// track the value in inferior memory, as well as to control whether LLDB |
1206 | /// needs to allocate memory for the variable during materialization. |
1207 | bool m_is_program_reference; |
1208 | bool m_keep_in_memory; |
1209 | |
1210 | lldb::addr_t m_temporary_allocation = LLDB_INVALID_ADDRESS; |
1211 | size_t m_temporary_allocation_size = 0; |
1212 | Materializer::PersistentVariableDelegate *m_delegate; |
1213 | }; |
1214 | |
1215 | uint32_t Materializer::AddResultVariable(const CompilerType &type, |
1216 | bool is_program_reference, |
1217 | bool keep_in_memory, |
1218 | PersistentVariableDelegate *delegate, |
1219 | Status &err) { |
1220 | EntityVector::iterator iter = m_entities.insert(position: m_entities.end(), x: EntityUP()); |
1221 | *iter = std::make_unique<EntityResultVariable>(args: type, args&: is_program_reference, |
1222 | args&: keep_in_memory, args&: delegate); |
1223 | uint32_t ret = AddStructMember(entity&: **iter); |
1224 | (*iter)->SetOffset(ret); |
1225 | return ret; |
1226 | } |
1227 | |
1228 | class EntitySymbol : public Materializer::Entity { |
1229 | public: |
1230 | EntitySymbol(const Symbol &symbol) : Entity(), m_symbol(symbol) { |
1231 | // Hard-coding to maximum size of a symbol |
1232 | m_size = g_default_var_byte_size; |
1233 | m_alignment = g_default_var_alignment; |
1234 | } |
1235 | |
1236 | void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
1237 | lldb::addr_t process_address, Status &err) override { |
1238 | Log *log = GetLog(mask: LLDBLog::Expressions); |
1239 | |
1240 | const lldb::addr_t load_addr = process_address + m_offset; |
1241 | |
1242 | if (log) { |
1243 | LLDB_LOGF(log, |
1244 | "EntitySymbol::Materialize [address = 0x%"PRIx64 |
1245 | ", m_symbol = %s]", |
1246 | (uint64_t)load_addr, m_symbol.GetName().AsCString()); |
1247 | } |
1248 | |
1249 | const Address sym_address = m_symbol.GetAddress(); |
1250 | |
1251 | ExecutionContextScope *exe_scope = frame_sp.get(); |
1252 | if (!exe_scope) |
1253 | exe_scope = map.GetBestExecutionContextScope(); |
1254 | |
1255 | lldb::TargetSP target_sp; |
1256 | |
1257 | if (exe_scope) |
1258 | target_sp = map.GetBestExecutionContextScope()->CalculateTarget(); |
1259 | |
1260 | if (!target_sp) { |
1261 | err = Status::FromErrorStringWithFormat( |
1262 | format: "couldn't resolve symbol %s because there is no target", |
1263 | m_symbol.GetName().AsCString()); |
1264 | return; |
1265 | } |
1266 | |
1267 | lldb::addr_t resolved_address = sym_address.GetLoadAddress(target: target_sp.get()); |
1268 | |
1269 | if (resolved_address == LLDB_INVALID_ADDRESS) |
1270 | resolved_address = sym_address.GetFileAddress(); |
1271 | |
1272 | Status pointer_write_error; |
1273 | |
1274 | map.WritePointerToMemory(process_address: load_addr, address: resolved_address, error&: pointer_write_error); |
1275 | |
1276 | if (!pointer_write_error.Success()) { |
1277 | err = Status::FromErrorStringWithFormat( |
1278 | format: "couldn't write the address of symbol %s: %s", |
1279 | m_symbol.GetName().AsCString(), pointer_write_error.AsCString()); |
1280 | return; |
1281 | } |
1282 | } |
1283 | |
1284 | void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
1285 | lldb::addr_t process_address, lldb::addr_t frame_top, |
1286 | lldb::addr_t frame_bottom, Status &err) override { |
1287 | Log *log = GetLog(mask: LLDBLog::Expressions); |
1288 | |
1289 | const lldb::addr_t load_addr = process_address + m_offset; |
1290 | |
1291 | if (log) { |
1292 | LLDB_LOGF(log, |
1293 | "EntitySymbol::Dematerialize [address = 0x%"PRIx64 |
1294 | ", m_symbol = %s]", |
1295 | (uint64_t)load_addr, m_symbol.GetName().AsCString()); |
1296 | } |
1297 | |
1298 | // no work needs to be done |
1299 | } |
1300 | |
1301 | void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, |
1302 | Log *log) override { |
1303 | StreamString dump_stream; |
1304 | |
1305 | Status err; |
1306 | |
1307 | const lldb::addr_t load_addr = process_address + m_offset; |
1308 | |
1309 | dump_stream.Printf(format: "0x%"PRIx64 ": EntitySymbol (%s)\n", load_addr, |
1310 | m_symbol.GetName().AsCString()); |
1311 | |
1312 | { |
1313 | dump_stream.Printf(format: "Pointer:\n"); |
1314 | |
1315 | DataBufferHeap data(m_size, 0); |
1316 | |
1317 | map.ReadMemory(bytes: data.GetBytes(), process_address: load_addr, size: m_size, error&: err); |
1318 | |
1319 | if (!err.Success()) { |
1320 | dump_stream.Printf(format: " <could not be read>\n"); |
1321 | } else { |
1322 | DumpHexBytes(s: &dump_stream, src: data.GetBytes(), src_len: data.GetByteSize(), bytes_per_line: 16, |
1323 | base_addr: load_addr); |
1324 | |
1325 | dump_stream.PutChar(ch: '\n'); |
1326 | } |
1327 | } |
1328 | |
1329 | log->PutString(str: dump_stream.GetString()); |
1330 | } |
1331 | |
1332 | void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {} |
1333 | |
1334 | private: |
1335 | Symbol m_symbol; |
1336 | }; |
1337 | |
1338 | uint32_t Materializer::AddSymbol(const Symbol &symbol_sp, Status &err) { |
1339 | EntityVector::iterator iter = m_entities.insert(position: m_entities.end(), x: EntityUP()); |
1340 | *iter = std::make_unique<EntitySymbol>(args: symbol_sp); |
1341 | uint32_t ret = AddStructMember(entity&: **iter); |
1342 | (*iter)->SetOffset(ret); |
1343 | return ret; |
1344 | } |
1345 | |
1346 | class EntityRegister : public Materializer::Entity { |
1347 | public: |
1348 | EntityRegister(const RegisterInfo ®ister_info) |
1349 | : Entity(), m_register_info(register_info) { |
1350 | // Hard-coding alignment conservatively |
1351 | m_size = m_register_info.byte_size; |
1352 | m_alignment = m_register_info.byte_size; |
1353 | } |
1354 | |
1355 | void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
1356 | lldb::addr_t process_address, Status &err) override { |
1357 | Log *log = GetLog(mask: LLDBLog::Expressions); |
1358 | |
1359 | const lldb::addr_t load_addr = process_address + m_offset; |
1360 | |
1361 | if (log) { |
1362 | LLDB_LOGF(log, |
1363 | "EntityRegister::Materialize [address = 0x%"PRIx64 |
1364 | ", m_register_info = %s]", |
1365 | (uint64_t)load_addr, m_register_info.name); |
1366 | } |
1367 | |
1368 | RegisterValue reg_value; |
1369 | |
1370 | if (!frame_sp.get()) { |
1371 | err = Status::FromErrorStringWithFormat( |
1372 | format: "couldn't materialize register %s without a stack frame", |
1373 | m_register_info.name); |
1374 | return; |
1375 | } |
1376 | |
1377 | lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext(); |
1378 | |
1379 | if (!reg_context_sp->ReadRegister(reg_info: &m_register_info, reg_value)) { |
1380 | err = Status::FromErrorStringWithFormat( |
1381 | format: "couldn't read the value of register %s", m_register_info.name); |
1382 | return; |
1383 | } |
1384 | |
1385 | DataExtractor register_data; |
1386 | |
1387 | if (!reg_value.GetData(data&: register_data)) { |
1388 | err = Status::FromErrorStringWithFormat( |
1389 | format: "couldn't get the data for register %s", m_register_info.name); |
1390 | return; |
1391 | } |
1392 | |
1393 | if (register_data.GetByteSize() != m_register_info.byte_size) { |
1394 | err = Status::FromErrorStringWithFormat( |
1395 | format: "data for register %s had size %llu but we expected %llu", |
1396 | m_register_info.name, (unsigned long long)register_data.GetByteSize(), |
1397 | (unsigned long long)m_register_info.byte_size); |
1398 | return; |
1399 | } |
1400 | |
1401 | m_register_contents = std::make_shared<DataBufferHeap>( |
1402 | args: register_data.GetDataStart(), args: register_data.GetByteSize()); |
1403 | |
1404 | Status write_error; |
1405 | |
1406 | map.WriteMemory(process_address: load_addr, bytes: register_data.GetDataStart(), |
1407 | size: register_data.GetByteSize(), error&: write_error); |
1408 | |
1409 | if (!write_error.Success()) { |
1410 | err = Status::FromErrorStringWithFormat( |
1411 | format: "couldn't write the contents of register %s: %s", |
1412 | m_register_info.name, write_error.AsCString()); |
1413 | return; |
1414 | } |
1415 | } |
1416 | |
1417 | void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
1418 | lldb::addr_t process_address, lldb::addr_t frame_top, |
1419 | lldb::addr_t frame_bottom, Status &err) override { |
1420 | Log *log = GetLog(mask: LLDBLog::Expressions); |
1421 | |
1422 | const lldb::addr_t load_addr = process_address + m_offset; |
1423 | |
1424 | if (log) { |
1425 | LLDB_LOGF(log, |
1426 | "EntityRegister::Dematerialize [address = 0x%"PRIx64 |
1427 | ", m_register_info = %s]", |
1428 | (uint64_t)load_addr, m_register_info.name); |
1429 | } |
1430 | |
1431 | Status extract_error; |
1432 | |
1433 | DataExtractor register_data; |
1434 | |
1435 | if (!frame_sp.get()) { |
1436 | err = Status::FromErrorStringWithFormat( |
1437 | format: "couldn't dematerialize register %s without a stack frame", |
1438 | m_register_info.name); |
1439 | return; |
1440 | } |
1441 | |
1442 | lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext(); |
1443 | |
1444 | map.GetMemoryData(extractor&: register_data, process_address: load_addr, size: m_register_info.byte_size, |
1445 | error&: extract_error); |
1446 | |
1447 | if (!extract_error.Success()) { |
1448 | err = Status::FromErrorStringWithFormat( |
1449 | format: "couldn't get the data for register %s: %s", m_register_info.name, |
1450 | extract_error.AsCString()); |
1451 | return; |
1452 | } |
1453 | |
1454 | if (!memcmp(s1: register_data.GetDataStart(), s2: m_register_contents->GetBytes(), |
1455 | n: register_data.GetByteSize())) { |
1456 | // No write required, and in particular we avoid errors if the register |
1457 | // wasn't writable |
1458 | |
1459 | m_register_contents.reset(); |
1460 | return; |
1461 | } |
1462 | |
1463 | m_register_contents.reset(); |
1464 | |
1465 | RegisterValue register_value(register_data.GetData(), |
1466 | register_data.GetByteOrder()); |
1467 | |
1468 | if (!reg_context_sp->WriteRegister(reg_info: &m_register_info, reg_value: register_value)) { |
1469 | err = Status::FromErrorStringWithFormat( |
1470 | format: "couldn't write the value of register %s", m_register_info.name); |
1471 | return; |
1472 | } |
1473 | } |
1474 | |
1475 | void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, |
1476 | Log *log) override { |
1477 | StreamString dump_stream; |
1478 | |
1479 | Status err; |
1480 | |
1481 | const lldb::addr_t load_addr = process_address + m_offset; |
1482 | |
1483 | dump_stream.Printf(format: "0x%"PRIx64 ": EntityRegister (%s)\n", load_addr, |
1484 | m_register_info.name); |
1485 | |
1486 | { |
1487 | dump_stream.Printf(format: "Value:\n"); |
1488 | |
1489 | DataBufferHeap data(m_size, 0); |
1490 | |
1491 | map.ReadMemory(bytes: data.GetBytes(), process_address: load_addr, size: m_size, error&: err); |
1492 | |
1493 | if (!err.Success()) { |
1494 | dump_stream.Printf(format: " <could not be read>\n"); |
1495 | } else { |
1496 | DumpHexBytes(s: &dump_stream, src: data.GetBytes(), src_len: data.GetByteSize(), bytes_per_line: 16, |
1497 | base_addr: load_addr); |
1498 | |
1499 | dump_stream.PutChar(ch: '\n'); |
1500 | } |
1501 | } |
1502 | |
1503 | log->PutString(str: dump_stream.GetString()); |
1504 | } |
1505 | |
1506 | void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {} |
1507 | |
1508 | private: |
1509 | RegisterInfo m_register_info; |
1510 | lldb::DataBufferSP m_register_contents; |
1511 | }; |
1512 | |
1513 | uint32_t Materializer::AddRegister(const RegisterInfo ®ister_info, |
1514 | Status &err) { |
1515 | EntityVector::iterator iter = m_entities.insert(position: m_entities.end(), x: EntityUP()); |
1516 | *iter = std::make_unique<EntityRegister>(args: register_info); |
1517 | uint32_t ret = AddStructMember(entity&: **iter); |
1518 | (*iter)->SetOffset(ret); |
1519 | return ret; |
1520 | } |
1521 | |
1522 | Materializer::~Materializer() { |
1523 | DematerializerSP dematerializer_sp = m_dematerializer_wp.lock(); |
1524 | |
1525 | if (dematerializer_sp) |
1526 | dematerializer_sp->Wipe(); |
1527 | } |
1528 | |
1529 | Materializer::DematerializerSP |
1530 | Materializer::Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
1531 | lldb::addr_t process_address, Status &error) { |
1532 | ExecutionContextScope *exe_scope = frame_sp.get(); |
1533 | if (!exe_scope) |
1534 | exe_scope = map.GetBestExecutionContextScope(); |
1535 | |
1536 | DematerializerSP dematerializer_sp = m_dematerializer_wp.lock(); |
1537 | |
1538 | if (dematerializer_sp) { |
1539 | error = |
1540 | Status::FromErrorString(str: "Couldn't materialize: already materialized"); |
1541 | } |
1542 | |
1543 | DematerializerSP ret( |
1544 | new Dematerializer(*this, frame_sp, map, process_address)); |
1545 | |
1546 | if (!exe_scope) { |
1547 | error = |
1548 | Status::FromErrorString(str: "Couldn't materialize: target doesn't exist"); |
1549 | } |
1550 | |
1551 | for (EntityUP &entity_up : m_entities) { |
1552 | entity_up->Materialize(frame_sp, map, process_address, err&: error); |
1553 | |
1554 | if (!error.Success()) |
1555 | return DematerializerSP(); |
1556 | } |
1557 | |
1558 | if (Log *log = GetLog(mask: LLDBLog::Expressions)) { |
1559 | LLDB_LOGF( |
1560 | log, |
1561 | "Materializer::Materialize (frame_sp = %p, process_address = 0x%"PRIx64 |
1562 | ") materialized:", |
1563 | static_cast<void *>(frame_sp.get()), process_address); |
1564 | for (EntityUP &entity_up : m_entities) |
1565 | entity_up->DumpToLog(map, process_address, log); |
1566 | } |
1567 | |
1568 | m_dematerializer_wp = ret; |
1569 | |
1570 | return ret; |
1571 | } |
1572 | |
1573 | void Materializer::Dematerializer::Dematerialize(Status &error, |
1574 | lldb::addr_t frame_bottom, |
1575 | lldb::addr_t frame_top) { |
1576 | lldb::StackFrameSP frame_sp; |
1577 | |
1578 | lldb::ThreadSP thread_sp = m_thread_wp.lock(); |
1579 | if (thread_sp) |
1580 | frame_sp = thread_sp->GetFrameWithStackID(stack_id: m_stack_id); |
1581 | |
1582 | ExecutionContextScope *exe_scope = frame_sp.get(); |
1583 | if (!exe_scope) |
1584 | exe_scope = m_map->GetBestExecutionContextScope(); |
1585 | |
1586 | if (!IsValid()) { |
1587 | error = Status::FromErrorString( |
1588 | str: "Couldn't dematerialize: invalid dematerializer"); |
1589 | } |
1590 | |
1591 | if (!exe_scope) { |
1592 | error = Status::FromErrorString(str: "Couldn't dematerialize: target is gone"); |
1593 | } else { |
1594 | if (Log *log = GetLog(mask: LLDBLog::Expressions)) { |
1595 | LLDB_LOGF(log, |
1596 | "Materializer::Dematerialize (frame_sp = %p, process_address " |
1597 | "= 0x%"PRIx64 ") about to dematerialize:", |
1598 | static_cast<void *>(frame_sp.get()), m_process_address); |
1599 | for (EntityUP &entity_up : m_materializer->m_entities) |
1600 | entity_up->DumpToLog(map&: *m_map, process_address: m_process_address, log); |
1601 | } |
1602 | |
1603 | for (EntityUP &entity_up : m_materializer->m_entities) { |
1604 | entity_up->Dematerialize(frame_sp, map&: *m_map, process_address: m_process_address, frame_top, |
1605 | frame_bottom, err&: error); |
1606 | |
1607 | if (!error.Success()) |
1608 | break; |
1609 | } |
1610 | } |
1611 | |
1612 | Wipe(); |
1613 | } |
1614 | |
1615 | void Materializer::Dematerializer::Wipe() { |
1616 | if (!IsValid()) |
1617 | return; |
1618 | |
1619 | for (EntityUP &entity_up : m_materializer->m_entities) { |
1620 | entity_up->Wipe(map&: *m_map, process_address: m_process_address); |
1621 | } |
1622 | |
1623 | m_materializer = nullptr; |
1624 | m_map = nullptr; |
1625 | m_process_address = LLDB_INVALID_ADDRESS; |
1626 | } |
1627 | |
1628 | Materializer::PersistentVariableDelegate::PersistentVariableDelegate() = |
1629 | default; |
1630 | Materializer::PersistentVariableDelegate::~PersistentVariableDelegate() = |
1631 | default; |
1632 |
Definitions
- g_default_var_alignment
- g_default_var_byte_size
- AddStructMember
- EntityPersistentVariable
- EntityPersistentVariable
- MakeAllocation
- DestroyAllocation
- Materialize
- Dematerialize
- DumpToLog
- Wipe
- AddPersistentVariable
- EntityVariableBase
- ~EntityVariableBase
- EntityVariableBase
- Materialize
- Dematerialize
- DumpToLog
- Wipe
- EntityVariable
- EntityVariable
- GetName
- SetupValueObject
- GetByteSize
- LocationExpressionIsValid
- GetTypeBitAlign
- EntityValueObject
- EntityValueObject
- GetName
- SetupValueObject
- GetByteSize
- LocationExpressionIsValid
- GetTypeBitAlign
- AddVariable
- AddValueObject
- EntityResultVariable
- EntityResultVariable
- Materialize
- Dematerialize
- DumpToLog
- Wipe
- AddResultVariable
- EntitySymbol
- EntitySymbol
- Materialize
- Dematerialize
- DumpToLog
- Wipe
- AddSymbol
- EntityRegister
- EntityRegister
- Materialize
- Dematerialize
- DumpToLog
- Wipe
- AddRegister
- ~Materializer
- Materialize
- Dematerialize
- Wipe
- PersistentVariableDelegate
Learn to use CMake with our Intro Training
Find out more