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