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

source code of lldb/source/Expression/Materializer.cpp