1dda28197Spatrick //===-- Materializer.cpp --------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/Expression/Materializer.h"
10061da546Spatrick #include "lldb/Core/DumpDataExtractor.h"
11061da546Spatrick #include "lldb/Core/ValueObjectConstResult.h"
12061da546Spatrick #include "lldb/Core/ValueObjectVariable.h"
13061da546Spatrick #include "lldb/Expression/ExpressionVariable.h"
14061da546Spatrick #include "lldb/Symbol/Symbol.h"
15061da546Spatrick #include "lldb/Symbol/Type.h"
16061da546Spatrick #include "lldb/Symbol/Variable.h"
17061da546Spatrick #include "lldb/Target/ExecutionContext.h"
18061da546Spatrick #include "lldb/Target/RegisterContext.h"
19061da546Spatrick #include "lldb/Target/StackFrame.h"
20061da546Spatrick #include "lldb/Target/Target.h"
21061da546Spatrick #include "lldb/Target/Thread.h"
22*f6aab3d8Srobert #include "lldb/Utility/LLDBLog.h"
23061da546Spatrick #include "lldb/Utility/Log.h"
24061da546Spatrick #include "lldb/Utility/RegisterValue.h"
25*f6aab3d8Srobert #include "lldb/lldb-forward.h"
26061da546Spatrick 
27061da546Spatrick #include <memory>
28*f6aab3d8Srobert #include <optional>
29061da546Spatrick 
30061da546Spatrick using namespace lldb_private;
31061da546Spatrick 
32*f6aab3d8Srobert // FIXME: these should be retrieved from the target
33*f6aab3d8Srobert //        instead of being hard-coded. Currently we
34*f6aab3d8Srobert //        assume that persistent vars are materialized
35*f6aab3d8Srobert //        as references, and thus pick the size of a
36*f6aab3d8Srobert //        64-bit pointer.
37*f6aab3d8Srobert static constexpr uint32_t g_default_var_alignment = 8;
38*f6aab3d8Srobert static constexpr uint32_t g_default_var_byte_size = 8;
39*f6aab3d8Srobert 
AddStructMember(Entity & entity)40061da546Spatrick uint32_t Materializer::AddStructMember(Entity &entity) {
41061da546Spatrick   uint32_t size = entity.GetSize();
42061da546Spatrick   uint32_t alignment = entity.GetAlignment();
43061da546Spatrick 
44061da546Spatrick   uint32_t ret;
45061da546Spatrick 
46061da546Spatrick   if (m_current_offset == 0)
47061da546Spatrick     m_struct_alignment = alignment;
48061da546Spatrick 
49061da546Spatrick   if (m_current_offset % alignment)
50061da546Spatrick     m_current_offset += (alignment - (m_current_offset % alignment));
51061da546Spatrick 
52061da546Spatrick   ret = m_current_offset;
53061da546Spatrick 
54061da546Spatrick   m_current_offset += size;
55061da546Spatrick 
56061da546Spatrick   return ret;
57061da546Spatrick }
58061da546Spatrick 
59061da546Spatrick class EntityPersistentVariable : public Materializer::Entity {
60061da546Spatrick public:
EntityPersistentVariable(lldb::ExpressionVariableSP & persistent_variable_sp,Materializer::PersistentVariableDelegate * delegate)61061da546Spatrick   EntityPersistentVariable(lldb::ExpressionVariableSP &persistent_variable_sp,
62061da546Spatrick                            Materializer::PersistentVariableDelegate *delegate)
63061da546Spatrick       : Entity(), m_persistent_variable_sp(persistent_variable_sp),
64061da546Spatrick         m_delegate(delegate) {
65061da546Spatrick     // Hard-coding to maximum size of a pointer since persistent variables are
66061da546Spatrick     // materialized by reference
67*f6aab3d8Srobert     m_size = g_default_var_byte_size;
68*f6aab3d8Srobert     m_alignment = g_default_var_alignment;
69061da546Spatrick   }
70061da546Spatrick 
MakeAllocation(IRMemoryMap & map,Status & err)71061da546Spatrick   void MakeAllocation(IRMemoryMap &map, Status &err) {
72*f6aab3d8Srobert     Log *log = GetLog(LLDBLog::Expressions);
73061da546Spatrick 
74061da546Spatrick     // Allocate a spare memory area to store the persistent variable's
75061da546Spatrick     // contents.
76061da546Spatrick 
77061da546Spatrick     Status allocate_error;
78061da546Spatrick     const bool zero_memory = false;
79061da546Spatrick 
80061da546Spatrick     lldb::addr_t mem = map.Malloc(
81*f6aab3d8Srobert         m_persistent_variable_sp->GetByteSize().value_or(0), 8,
82061da546Spatrick         lldb::ePermissionsReadable | lldb::ePermissionsWritable,
83061da546Spatrick         IRMemoryMap::eAllocationPolicyMirror, zero_memory, allocate_error);
84061da546Spatrick 
85061da546Spatrick     if (!allocate_error.Success()) {
86061da546Spatrick       err.SetErrorStringWithFormat(
87061da546Spatrick           "couldn't allocate a memory area to store %s: %s",
88061da546Spatrick           m_persistent_variable_sp->GetName().GetCString(),
89061da546Spatrick           allocate_error.AsCString());
90061da546Spatrick       return;
91061da546Spatrick     }
92061da546Spatrick 
93061da546Spatrick     LLDB_LOGF(log, "Allocated %s (0x%" PRIx64 ") successfully",
94061da546Spatrick               m_persistent_variable_sp->GetName().GetCString(), mem);
95061da546Spatrick 
96061da546Spatrick     // Put the location of the spare memory into the live data of the
97061da546Spatrick     // ValueObject.
98061da546Spatrick 
99061da546Spatrick     m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create(
100061da546Spatrick         map.GetBestExecutionContextScope(),
101061da546Spatrick         m_persistent_variable_sp->GetCompilerType(),
102061da546Spatrick         m_persistent_variable_sp->GetName(), mem, eAddressTypeLoad,
103061da546Spatrick         map.GetAddressByteSize());
104061da546Spatrick 
105061da546Spatrick     // Clear the flag if the variable will never be deallocated.
106061da546Spatrick 
107061da546Spatrick     if (m_persistent_variable_sp->m_flags &
108061da546Spatrick         ExpressionVariable::EVKeepInTarget) {
109061da546Spatrick       Status leak_error;
110061da546Spatrick       map.Leak(mem, leak_error);
111061da546Spatrick       m_persistent_variable_sp->m_flags &=
112061da546Spatrick           ~ExpressionVariable::EVNeedsAllocation;
113061da546Spatrick     }
114061da546Spatrick 
115061da546Spatrick     // Write the contents of the variable to the area.
116061da546Spatrick 
117061da546Spatrick     Status write_error;
118061da546Spatrick 
119061da546Spatrick     map.WriteMemory(mem, m_persistent_variable_sp->GetValueBytes(),
120*f6aab3d8Srobert                     m_persistent_variable_sp->GetByteSize().value_or(0),
121be691f3bSpatrick                     write_error);
122061da546Spatrick 
123061da546Spatrick     if (!write_error.Success()) {
124061da546Spatrick       err.SetErrorStringWithFormat(
125061da546Spatrick           "couldn't write %s to the target: %s",
126061da546Spatrick           m_persistent_variable_sp->GetName().AsCString(),
127061da546Spatrick           write_error.AsCString());
128061da546Spatrick       return;
129061da546Spatrick     }
130061da546Spatrick   }
131061da546Spatrick 
DestroyAllocation(IRMemoryMap & map,Status & err)132061da546Spatrick   void DestroyAllocation(IRMemoryMap &map, Status &err) {
133061da546Spatrick     Status deallocate_error;
134061da546Spatrick 
135061da546Spatrick     map.Free((lldb::addr_t)m_persistent_variable_sp->m_live_sp->GetValue()
136061da546Spatrick                  .GetScalar()
137061da546Spatrick                  .ULongLong(),
138061da546Spatrick              deallocate_error);
139061da546Spatrick 
140061da546Spatrick     m_persistent_variable_sp->m_live_sp.reset();
141061da546Spatrick 
142061da546Spatrick     if (!deallocate_error.Success()) {
143061da546Spatrick       err.SetErrorStringWithFormat(
144061da546Spatrick           "couldn't deallocate memory for %s: %s",
145061da546Spatrick           m_persistent_variable_sp->GetName().GetCString(),
146061da546Spatrick           deallocate_error.AsCString());
147061da546Spatrick     }
148061da546Spatrick   }
149061da546Spatrick 
Materialize(lldb::StackFrameSP & frame_sp,IRMemoryMap & map,lldb::addr_t process_address,Status & err)150061da546Spatrick   void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
151061da546Spatrick                    lldb::addr_t process_address, Status &err) override {
152*f6aab3d8Srobert     Log *log = GetLog(LLDBLog::Expressions);
153061da546Spatrick 
154061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
155061da546Spatrick 
156061da546Spatrick     if (log) {
157061da546Spatrick       LLDB_LOGF(log,
158061da546Spatrick                 "EntityPersistentVariable::Materialize [address = 0x%" PRIx64
159061da546Spatrick                 ", m_name = %s, m_flags = 0x%hx]",
160061da546Spatrick                 (uint64_t)load_addr,
161061da546Spatrick                 m_persistent_variable_sp->GetName().AsCString(),
162061da546Spatrick                 m_persistent_variable_sp->m_flags);
163061da546Spatrick     }
164061da546Spatrick 
165061da546Spatrick     if (m_persistent_variable_sp->m_flags &
166061da546Spatrick         ExpressionVariable::EVNeedsAllocation) {
167061da546Spatrick       MakeAllocation(map, err);
168061da546Spatrick       m_persistent_variable_sp->m_flags |=
169061da546Spatrick           ExpressionVariable::EVIsLLDBAllocated;
170061da546Spatrick 
171061da546Spatrick       if (!err.Success())
172061da546Spatrick         return;
173061da546Spatrick     }
174061da546Spatrick 
175061da546Spatrick     if ((m_persistent_variable_sp->m_flags &
176061da546Spatrick              ExpressionVariable::EVIsProgramReference &&
177061da546Spatrick          m_persistent_variable_sp->m_live_sp) ||
178061da546Spatrick         m_persistent_variable_sp->m_flags &
179061da546Spatrick             ExpressionVariable::EVIsLLDBAllocated) {
180061da546Spatrick       Status write_error;
181061da546Spatrick 
182061da546Spatrick       map.WriteScalarToMemory(
183061da546Spatrick           load_addr,
184061da546Spatrick           m_persistent_variable_sp->m_live_sp->GetValue().GetScalar(),
185061da546Spatrick           map.GetAddressByteSize(), write_error);
186061da546Spatrick 
187061da546Spatrick       if (!write_error.Success()) {
188061da546Spatrick         err.SetErrorStringWithFormat(
189061da546Spatrick             "couldn't write the location of %s to memory: %s",
190061da546Spatrick             m_persistent_variable_sp->GetName().AsCString(),
191061da546Spatrick             write_error.AsCString());
192061da546Spatrick       }
193061da546Spatrick     } else {
194061da546Spatrick       err.SetErrorStringWithFormat(
195061da546Spatrick           "no materialization happened for persistent variable %s",
196061da546Spatrick           m_persistent_variable_sp->GetName().AsCString());
197061da546Spatrick       return;
198061da546Spatrick     }
199061da546Spatrick   }
200061da546Spatrick 
Dematerialize(lldb::StackFrameSP & frame_sp,IRMemoryMap & map,lldb::addr_t process_address,lldb::addr_t frame_top,lldb::addr_t frame_bottom,Status & err)201061da546Spatrick   void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
202061da546Spatrick                      lldb::addr_t process_address, lldb::addr_t frame_top,
203061da546Spatrick                      lldb::addr_t frame_bottom, Status &err) override {
204*f6aab3d8Srobert     Log *log = GetLog(LLDBLog::Expressions);
205061da546Spatrick 
206061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
207061da546Spatrick 
208061da546Spatrick     if (log) {
209061da546Spatrick       LLDB_LOGF(log,
210061da546Spatrick                 "EntityPersistentVariable::Dematerialize [address = 0x%" PRIx64
211061da546Spatrick                 ", m_name = %s, m_flags = 0x%hx]",
212061da546Spatrick                 (uint64_t)process_address + m_offset,
213061da546Spatrick                 m_persistent_variable_sp->GetName().AsCString(),
214061da546Spatrick                 m_persistent_variable_sp->m_flags);
215061da546Spatrick     }
216061da546Spatrick 
217061da546Spatrick     if (m_delegate) {
218061da546Spatrick       m_delegate->DidDematerialize(m_persistent_variable_sp);
219061da546Spatrick     }
220061da546Spatrick 
221061da546Spatrick     if ((m_persistent_variable_sp->m_flags &
222061da546Spatrick          ExpressionVariable::EVIsLLDBAllocated) ||
223061da546Spatrick         (m_persistent_variable_sp->m_flags &
224061da546Spatrick          ExpressionVariable::EVIsProgramReference)) {
225061da546Spatrick       if (m_persistent_variable_sp->m_flags &
226061da546Spatrick               ExpressionVariable::EVIsProgramReference &&
227061da546Spatrick           !m_persistent_variable_sp->m_live_sp) {
228061da546Spatrick         // If the reference comes from the program, then the
229061da546Spatrick         // ClangExpressionVariable's live variable data hasn't been set up yet.
230061da546Spatrick         // Do this now.
231061da546Spatrick 
232061da546Spatrick         lldb::addr_t location;
233061da546Spatrick         Status read_error;
234061da546Spatrick 
235061da546Spatrick         map.ReadPointerFromMemory(&location, load_addr, read_error);
236061da546Spatrick 
237061da546Spatrick         if (!read_error.Success()) {
238061da546Spatrick           err.SetErrorStringWithFormat(
239061da546Spatrick               "couldn't read the address of program-allocated variable %s: %s",
240061da546Spatrick               m_persistent_variable_sp->GetName().GetCString(),
241061da546Spatrick               read_error.AsCString());
242061da546Spatrick           return;
243061da546Spatrick         }
244061da546Spatrick 
245061da546Spatrick         m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create(
246061da546Spatrick             map.GetBestExecutionContextScope(),
247061da546Spatrick             m_persistent_variable_sp.get()->GetCompilerType(),
248061da546Spatrick             m_persistent_variable_sp->GetName(), location, eAddressTypeLoad,
249*f6aab3d8Srobert             m_persistent_variable_sp->GetByteSize().value_or(0));
250061da546Spatrick 
251061da546Spatrick         if (frame_top != LLDB_INVALID_ADDRESS &&
252061da546Spatrick             frame_bottom != LLDB_INVALID_ADDRESS && location >= frame_bottom &&
253061da546Spatrick             location <= frame_top) {
254061da546Spatrick           // If the variable is resident in the stack frame created by the
255061da546Spatrick           // expression, then it cannot be relied upon to stay around.  We
256061da546Spatrick           // treat it as needing reallocation.
257061da546Spatrick           m_persistent_variable_sp->m_flags |=
258061da546Spatrick               ExpressionVariable::EVIsLLDBAllocated;
259061da546Spatrick           m_persistent_variable_sp->m_flags |=
260061da546Spatrick               ExpressionVariable::EVNeedsAllocation;
261061da546Spatrick           m_persistent_variable_sp->m_flags |=
262061da546Spatrick               ExpressionVariable::EVNeedsFreezeDry;
263061da546Spatrick           m_persistent_variable_sp->m_flags &=
264061da546Spatrick               ~ExpressionVariable::EVIsProgramReference;
265061da546Spatrick         }
266061da546Spatrick       }
267061da546Spatrick 
268061da546Spatrick       lldb::addr_t mem = m_persistent_variable_sp->m_live_sp->GetValue()
269061da546Spatrick                              .GetScalar()
270061da546Spatrick                              .ULongLong();
271061da546Spatrick 
272061da546Spatrick       if (!m_persistent_variable_sp->m_live_sp) {
273061da546Spatrick         err.SetErrorStringWithFormat(
274061da546Spatrick             "couldn't find the memory area used to store %s",
275061da546Spatrick             m_persistent_variable_sp->GetName().GetCString());
276061da546Spatrick         return;
277061da546Spatrick       }
278061da546Spatrick 
279061da546Spatrick       if (m_persistent_variable_sp->m_live_sp->GetValue()
280061da546Spatrick               .GetValueAddressType() != eAddressTypeLoad) {
281061da546Spatrick         err.SetErrorStringWithFormat(
282061da546Spatrick             "the address of the memory area for %s is in an incorrect format",
283061da546Spatrick             m_persistent_variable_sp->GetName().GetCString());
284061da546Spatrick         return;
285061da546Spatrick       }
286061da546Spatrick 
287061da546Spatrick       if (m_persistent_variable_sp->m_flags &
288061da546Spatrick               ExpressionVariable::EVNeedsFreezeDry ||
289061da546Spatrick           m_persistent_variable_sp->m_flags &
290061da546Spatrick               ExpressionVariable::EVKeepInTarget) {
291061da546Spatrick         LLDB_LOGF(log, "Dematerializing %s from 0x%" PRIx64 " (size = %llu)",
292061da546Spatrick                   m_persistent_variable_sp->GetName().GetCString(),
293061da546Spatrick                   (uint64_t)mem,
294be691f3bSpatrick                   (unsigned long long)m_persistent_variable_sp->GetByteSize()
295*f6aab3d8Srobert                       .value_or(0));
296061da546Spatrick 
297061da546Spatrick         // Read the contents of the spare memory area
298061da546Spatrick 
299061da546Spatrick         m_persistent_variable_sp->ValueUpdated();
300061da546Spatrick 
301061da546Spatrick         Status read_error;
302061da546Spatrick 
303061da546Spatrick         map.ReadMemory(m_persistent_variable_sp->GetValueBytes(), mem,
304*f6aab3d8Srobert                        m_persistent_variable_sp->GetByteSize().value_or(0),
305*f6aab3d8Srobert                        read_error);
306061da546Spatrick 
307061da546Spatrick         if (!read_error.Success()) {
308061da546Spatrick           err.SetErrorStringWithFormat(
309061da546Spatrick               "couldn't read the contents of %s from memory: %s",
310061da546Spatrick               m_persistent_variable_sp->GetName().GetCString(),
311061da546Spatrick               read_error.AsCString());
312061da546Spatrick           return;
313061da546Spatrick         }
314061da546Spatrick 
315061da546Spatrick         m_persistent_variable_sp->m_flags &=
316061da546Spatrick             ~ExpressionVariable::EVNeedsFreezeDry;
317061da546Spatrick       }
318061da546Spatrick     } else {
319061da546Spatrick       err.SetErrorStringWithFormat(
320061da546Spatrick           "no dematerialization happened for persistent variable %s",
321061da546Spatrick           m_persistent_variable_sp->GetName().AsCString());
322061da546Spatrick       return;
323061da546Spatrick     }
324061da546Spatrick 
325061da546Spatrick     lldb::ProcessSP process_sp =
326061da546Spatrick         map.GetBestExecutionContextScope()->CalculateProcess();
327061da546Spatrick     if (!process_sp || !process_sp->CanJIT()) {
328061da546Spatrick       // Allocations are not persistent so persistent variables cannot stay
329061da546Spatrick       // materialized.
330061da546Spatrick 
331061da546Spatrick       m_persistent_variable_sp->m_flags |=
332061da546Spatrick           ExpressionVariable::EVNeedsAllocation;
333061da546Spatrick 
334061da546Spatrick       DestroyAllocation(map, err);
335061da546Spatrick       if (!err.Success())
336061da546Spatrick         return;
337061da546Spatrick     } else if (m_persistent_variable_sp->m_flags &
338061da546Spatrick                    ExpressionVariable::EVNeedsAllocation &&
339061da546Spatrick                !(m_persistent_variable_sp->m_flags &
340061da546Spatrick                  ExpressionVariable::EVKeepInTarget)) {
341061da546Spatrick       DestroyAllocation(map, err);
342061da546Spatrick       if (!err.Success())
343061da546Spatrick         return;
344061da546Spatrick     }
345061da546Spatrick   }
346061da546Spatrick 
DumpToLog(IRMemoryMap & map,lldb::addr_t process_address,Log * log)347061da546Spatrick   void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address,
348061da546Spatrick                  Log *log) override {
349061da546Spatrick     StreamString dump_stream;
350061da546Spatrick 
351061da546Spatrick     Status err;
352061da546Spatrick 
353061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
354061da546Spatrick 
355061da546Spatrick     dump_stream.Printf("0x%" PRIx64 ": EntityPersistentVariable (%s)\n",
356061da546Spatrick                        load_addr,
357061da546Spatrick                        m_persistent_variable_sp->GetName().AsCString());
358061da546Spatrick 
359061da546Spatrick     {
360061da546Spatrick       dump_stream.Printf("Pointer:\n");
361061da546Spatrick 
362061da546Spatrick       DataBufferHeap data(m_size, 0);
363061da546Spatrick 
364061da546Spatrick       map.ReadMemory(data.GetBytes(), load_addr, m_size, err);
365061da546Spatrick 
366061da546Spatrick       if (!err.Success()) {
367061da546Spatrick         dump_stream.Printf("  <could not be read>\n");
368061da546Spatrick       } else {
369061da546Spatrick         DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
370061da546Spatrick                      load_addr);
371061da546Spatrick 
372061da546Spatrick         dump_stream.PutChar('\n');
373061da546Spatrick       }
374061da546Spatrick     }
375061da546Spatrick 
376061da546Spatrick     {
377061da546Spatrick       dump_stream.Printf("Target:\n");
378061da546Spatrick 
379061da546Spatrick       lldb::addr_t target_address;
380061da546Spatrick 
381061da546Spatrick       map.ReadPointerFromMemory(&target_address, load_addr, err);
382061da546Spatrick 
383061da546Spatrick       if (!err.Success()) {
384061da546Spatrick         dump_stream.Printf("  <could not be read>\n");
385061da546Spatrick       } else {
386*f6aab3d8Srobert         DataBufferHeap data(m_persistent_variable_sp->GetByteSize().value_or(0),
387*f6aab3d8Srobert                             0);
388061da546Spatrick 
389061da546Spatrick         map.ReadMemory(data.GetBytes(), target_address,
390*f6aab3d8Srobert                        m_persistent_variable_sp->GetByteSize().value_or(0),
391*f6aab3d8Srobert                        err);
392061da546Spatrick 
393061da546Spatrick         if (!err.Success()) {
394061da546Spatrick           dump_stream.Printf("  <could not be read>\n");
395061da546Spatrick         } else {
396061da546Spatrick           DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
397061da546Spatrick                        target_address);
398061da546Spatrick 
399061da546Spatrick           dump_stream.PutChar('\n');
400061da546Spatrick         }
401061da546Spatrick       }
402061da546Spatrick     }
403061da546Spatrick 
404061da546Spatrick     log->PutString(dump_stream.GetString());
405061da546Spatrick   }
406061da546Spatrick 
Wipe(IRMemoryMap & map,lldb::addr_t process_address)407061da546Spatrick   void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {}
408061da546Spatrick 
409061da546Spatrick private:
410061da546Spatrick   lldb::ExpressionVariableSP m_persistent_variable_sp;
411061da546Spatrick   Materializer::PersistentVariableDelegate *m_delegate;
412061da546Spatrick };
413061da546Spatrick 
AddPersistentVariable(lldb::ExpressionVariableSP & persistent_variable_sp,PersistentVariableDelegate * delegate,Status & err)414061da546Spatrick uint32_t Materializer::AddPersistentVariable(
415061da546Spatrick     lldb::ExpressionVariableSP &persistent_variable_sp,
416061da546Spatrick     PersistentVariableDelegate *delegate, Status &err) {
417061da546Spatrick   EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
418dda28197Spatrick   *iter = std::make_unique<EntityPersistentVariable>(persistent_variable_sp,
419dda28197Spatrick                                                      delegate);
420061da546Spatrick   uint32_t ret = AddStructMember(**iter);
421061da546Spatrick   (*iter)->SetOffset(ret);
422061da546Spatrick   return ret;
423061da546Spatrick }
424061da546Spatrick 
425*f6aab3d8Srobert /// Base class for materialization of Variables and ValueObjects.
426*f6aab3d8Srobert ///
427*f6aab3d8Srobert /// Subclasses specify how to obtain the Value which is to be
428*f6aab3d8Srobert /// materialized.
429*f6aab3d8Srobert class EntityVariableBase : public Materializer::Entity {
430061da546Spatrick public:
431*f6aab3d8Srobert   virtual ~EntityVariableBase() = default;
432*f6aab3d8Srobert 
EntityVariableBase()433*f6aab3d8Srobert   EntityVariableBase() {
434061da546Spatrick     // Hard-coding to maximum size of a pointer since all variables are
435061da546Spatrick     // materialized by reference
436*f6aab3d8Srobert     m_size = g_default_var_byte_size;
437*f6aab3d8Srobert     m_alignment = g_default_var_alignment;
438061da546Spatrick   }
439061da546Spatrick 
Materialize(lldb::StackFrameSP & frame_sp,IRMemoryMap & map,lldb::addr_t process_address,Status & err)440061da546Spatrick   void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
441061da546Spatrick                    lldb::addr_t process_address, Status &err) override {
442*f6aab3d8Srobert     Log *log = GetLog(LLDBLog::Expressions);
443061da546Spatrick 
444061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
445061da546Spatrick     if (log) {
446061da546Spatrick       LLDB_LOGF(log,
447061da546Spatrick                 "EntityVariable::Materialize [address = 0x%" PRIx64
448061da546Spatrick                 ", m_variable_sp = %s]",
449*f6aab3d8Srobert                 (uint64_t)load_addr, GetName().GetCString());
450061da546Spatrick     }
451061da546Spatrick 
452061da546Spatrick     ExecutionContextScope *scope = frame_sp.get();
453061da546Spatrick 
454061da546Spatrick     if (!scope)
455061da546Spatrick       scope = map.GetBestExecutionContextScope();
456061da546Spatrick 
457*f6aab3d8Srobert     lldb::ValueObjectSP valobj_sp = SetupValueObject(scope);
458061da546Spatrick 
459061da546Spatrick     if (!valobj_sp) {
460061da546Spatrick       err.SetErrorStringWithFormat(
461*f6aab3d8Srobert           "couldn't get a value object for variable %s", GetName().AsCString());
462061da546Spatrick       return;
463061da546Spatrick     }
464061da546Spatrick 
465061da546Spatrick     Status valobj_error = valobj_sp->GetError();
466061da546Spatrick 
467061da546Spatrick     if (valobj_error.Fail()) {
468061da546Spatrick       err.SetErrorStringWithFormat("couldn't get the value of variable %s: %s",
469*f6aab3d8Srobert                                    GetName().AsCString(),
470061da546Spatrick                                    valobj_error.AsCString());
471061da546Spatrick       return;
472061da546Spatrick     }
473061da546Spatrick 
474061da546Spatrick     if (m_is_reference) {
475061da546Spatrick       DataExtractor valobj_extractor;
476061da546Spatrick       Status extract_error;
477061da546Spatrick       valobj_sp->GetData(valobj_extractor, extract_error);
478061da546Spatrick 
479061da546Spatrick       if (!extract_error.Success()) {
480061da546Spatrick         err.SetErrorStringWithFormat(
481061da546Spatrick             "couldn't read contents of reference variable %s: %s",
482*f6aab3d8Srobert             GetName().AsCString(), extract_error.AsCString());
483061da546Spatrick         return;
484061da546Spatrick       }
485061da546Spatrick 
486061da546Spatrick       lldb::offset_t offset = 0;
487061da546Spatrick       lldb::addr_t reference_addr = valobj_extractor.GetAddress(&offset);
488061da546Spatrick 
489061da546Spatrick       Status write_error;
490061da546Spatrick       map.WritePointerToMemory(load_addr, reference_addr, write_error);
491061da546Spatrick 
492061da546Spatrick       if (!write_error.Success()) {
493061da546Spatrick         err.SetErrorStringWithFormat("couldn't write the contents of reference "
494061da546Spatrick                                      "variable %s to memory: %s",
495*f6aab3d8Srobert                                      GetName().AsCString(),
496061da546Spatrick                                      write_error.AsCString());
497061da546Spatrick         return;
498061da546Spatrick       }
499061da546Spatrick     } else {
500061da546Spatrick       AddressType address_type = eAddressTypeInvalid;
501061da546Spatrick       const bool scalar_is_load_address = false;
502061da546Spatrick       lldb::addr_t addr_of_valobj =
503061da546Spatrick           valobj_sp->GetAddressOf(scalar_is_load_address, &address_type);
504061da546Spatrick       if (addr_of_valobj != LLDB_INVALID_ADDRESS) {
505061da546Spatrick         Status write_error;
506061da546Spatrick         map.WritePointerToMemory(load_addr, addr_of_valobj, write_error);
507061da546Spatrick 
508061da546Spatrick         if (!write_error.Success()) {
509061da546Spatrick           err.SetErrorStringWithFormat(
510061da546Spatrick               "couldn't write the address of variable %s to memory: %s",
511*f6aab3d8Srobert               GetName().AsCString(), write_error.AsCString());
512061da546Spatrick           return;
513061da546Spatrick         }
514061da546Spatrick       } else {
515061da546Spatrick         DataExtractor data;
516061da546Spatrick         Status extract_error;
517061da546Spatrick         valobj_sp->GetData(data, extract_error);
518061da546Spatrick         if (!extract_error.Success()) {
519061da546Spatrick           err.SetErrorStringWithFormat("couldn't get the value of %s: %s",
520*f6aab3d8Srobert                                        GetName().AsCString(),
521061da546Spatrick                                        extract_error.AsCString());
522061da546Spatrick           return;
523061da546Spatrick         }
524061da546Spatrick 
525061da546Spatrick         if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
526061da546Spatrick           err.SetErrorStringWithFormat(
527061da546Spatrick               "trying to create a temporary region for %s but one exists",
528*f6aab3d8Srobert               GetName().AsCString());
529061da546Spatrick           return;
530061da546Spatrick         }
531061da546Spatrick 
532*f6aab3d8Srobert         if (data.GetByteSize() < GetByteSize(scope)) {
533*f6aab3d8Srobert           if (data.GetByteSize() == 0 && !LocationExpressionIsValid()) {
534061da546Spatrick             err.SetErrorStringWithFormat("the variable '%s' has no location, "
535061da546Spatrick                                          "it may have been optimized out",
536*f6aab3d8Srobert                                          GetName().AsCString());
537061da546Spatrick           } else {
538061da546Spatrick             err.SetErrorStringWithFormat(
539061da546Spatrick                 "size of variable %s (%" PRIu64
540061da546Spatrick                 ") is larger than the ValueObject's size (%" PRIu64 ")",
541*f6aab3d8Srobert                 GetName().AsCString(), GetByteSize(scope).value_or(0),
542061da546Spatrick                 data.GetByteSize());
543061da546Spatrick           }
544061da546Spatrick           return;
545061da546Spatrick         }
546061da546Spatrick 
547*f6aab3d8Srobert         std::optional<size_t> opt_bit_align = GetTypeBitAlign(scope);
548061da546Spatrick         if (!opt_bit_align) {
549061da546Spatrick           err.SetErrorStringWithFormat("can't get the type alignment for %s",
550*f6aab3d8Srobert                                        GetName().AsCString());
551061da546Spatrick           return;
552061da546Spatrick         }
553061da546Spatrick 
554061da546Spatrick         size_t byte_align = (*opt_bit_align + 7) / 8;
555061da546Spatrick 
556061da546Spatrick         Status alloc_error;
557061da546Spatrick         const bool zero_memory = false;
558061da546Spatrick 
559061da546Spatrick         m_temporary_allocation = map.Malloc(
560061da546Spatrick             data.GetByteSize(), byte_align,
561061da546Spatrick             lldb::ePermissionsReadable | lldb::ePermissionsWritable,
562061da546Spatrick             IRMemoryMap::eAllocationPolicyMirror, zero_memory, alloc_error);
563061da546Spatrick 
564061da546Spatrick         m_temporary_allocation_size = data.GetByteSize();
565061da546Spatrick 
566061da546Spatrick         m_original_data = std::make_shared<DataBufferHeap>(data.GetDataStart(),
567061da546Spatrick                                                            data.GetByteSize());
568061da546Spatrick 
569061da546Spatrick         if (!alloc_error.Success()) {
570061da546Spatrick           err.SetErrorStringWithFormat(
571061da546Spatrick               "couldn't allocate a temporary region for %s: %s",
572*f6aab3d8Srobert               GetName().AsCString(), alloc_error.AsCString());
573061da546Spatrick           return;
574061da546Spatrick         }
575061da546Spatrick 
576061da546Spatrick         Status write_error;
577061da546Spatrick 
578061da546Spatrick         map.WriteMemory(m_temporary_allocation, data.GetDataStart(),
579061da546Spatrick                         data.GetByteSize(), write_error);
580061da546Spatrick 
581061da546Spatrick         if (!write_error.Success()) {
582061da546Spatrick           err.SetErrorStringWithFormat(
583061da546Spatrick               "couldn't write to the temporary region for %s: %s",
584*f6aab3d8Srobert               GetName().AsCString(), write_error.AsCString());
585061da546Spatrick           return;
586061da546Spatrick         }
587061da546Spatrick 
588061da546Spatrick         Status pointer_write_error;
589061da546Spatrick 
590061da546Spatrick         map.WritePointerToMemory(load_addr, m_temporary_allocation,
591061da546Spatrick                                  pointer_write_error);
592061da546Spatrick 
593061da546Spatrick         if (!pointer_write_error.Success()) {
594061da546Spatrick           err.SetErrorStringWithFormat(
595061da546Spatrick               "couldn't write the address of the temporary region for %s: %s",
596*f6aab3d8Srobert               GetName().AsCString(), pointer_write_error.AsCString());
597061da546Spatrick         }
598061da546Spatrick       }
599061da546Spatrick     }
600061da546Spatrick   }
601061da546Spatrick 
Dematerialize(lldb::StackFrameSP & frame_sp,IRMemoryMap & map,lldb::addr_t process_address,lldb::addr_t frame_top,lldb::addr_t frame_bottom,Status & err)602061da546Spatrick   void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
603061da546Spatrick                      lldb::addr_t process_address, lldb::addr_t frame_top,
604061da546Spatrick                      lldb::addr_t frame_bottom, Status &err) override {
605*f6aab3d8Srobert     Log *log = GetLog(LLDBLog::Expressions);
606061da546Spatrick 
607061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
608061da546Spatrick     if (log) {
609061da546Spatrick       LLDB_LOGF(log,
610061da546Spatrick                 "EntityVariable::Dematerialize [address = 0x%" PRIx64
611061da546Spatrick                 ", m_variable_sp = %s]",
612*f6aab3d8Srobert                 (uint64_t)load_addr, GetName().AsCString());
613061da546Spatrick     }
614061da546Spatrick 
615061da546Spatrick     if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
616061da546Spatrick       ExecutionContextScope *scope = frame_sp.get();
617061da546Spatrick 
618061da546Spatrick       if (!scope)
619061da546Spatrick         scope = map.GetBestExecutionContextScope();
620061da546Spatrick 
621*f6aab3d8Srobert       lldb::ValueObjectSP valobj_sp = SetupValueObject(scope);
622061da546Spatrick 
623061da546Spatrick       if (!valobj_sp) {
624061da546Spatrick         err.SetErrorStringWithFormat(
625061da546Spatrick             "couldn't get a value object for variable %s",
626*f6aab3d8Srobert             GetName().AsCString());
627061da546Spatrick         return;
628061da546Spatrick       }
629061da546Spatrick 
630061da546Spatrick       lldb_private::DataExtractor data;
631061da546Spatrick 
632061da546Spatrick       Status extract_error;
633061da546Spatrick 
634be691f3bSpatrick       map.GetMemoryData(data, m_temporary_allocation,
635*f6aab3d8Srobert                         valobj_sp->GetByteSize().value_or(0), extract_error);
636061da546Spatrick 
637061da546Spatrick       if (!extract_error.Success()) {
638061da546Spatrick         err.SetErrorStringWithFormat("couldn't get the data for variable %s",
639*f6aab3d8Srobert                                      GetName().AsCString());
640061da546Spatrick         return;
641061da546Spatrick       }
642061da546Spatrick 
643061da546Spatrick       bool actually_write = true;
644061da546Spatrick 
645061da546Spatrick       if (m_original_data) {
646061da546Spatrick         if ((data.GetByteSize() == m_original_data->GetByteSize()) &&
647061da546Spatrick             !memcmp(m_original_data->GetBytes(), data.GetDataStart(),
648061da546Spatrick                     data.GetByteSize())) {
649061da546Spatrick           actually_write = false;
650061da546Spatrick         }
651061da546Spatrick       }
652061da546Spatrick 
653061da546Spatrick       Status set_error;
654061da546Spatrick 
655061da546Spatrick       if (actually_write) {
656061da546Spatrick         valobj_sp->SetData(data, set_error);
657061da546Spatrick 
658061da546Spatrick         if (!set_error.Success()) {
659061da546Spatrick           err.SetErrorStringWithFormat(
660061da546Spatrick               "couldn't write the new contents of %s back into the variable",
661*f6aab3d8Srobert               GetName().AsCString());
662061da546Spatrick           return;
663061da546Spatrick         }
664061da546Spatrick       }
665061da546Spatrick 
666061da546Spatrick       Status free_error;
667061da546Spatrick 
668061da546Spatrick       map.Free(m_temporary_allocation, free_error);
669061da546Spatrick 
670061da546Spatrick       if (!free_error.Success()) {
671061da546Spatrick         err.SetErrorStringWithFormat(
672061da546Spatrick             "couldn't free the temporary region for %s: %s",
673*f6aab3d8Srobert             GetName().AsCString(), free_error.AsCString());
674061da546Spatrick         return;
675061da546Spatrick       }
676061da546Spatrick 
677061da546Spatrick       m_original_data.reset();
678061da546Spatrick       m_temporary_allocation = LLDB_INVALID_ADDRESS;
679061da546Spatrick       m_temporary_allocation_size = 0;
680061da546Spatrick     }
681061da546Spatrick   }
682061da546Spatrick 
DumpToLog(IRMemoryMap & map,lldb::addr_t process_address,Log * log)683061da546Spatrick   void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address,
684061da546Spatrick                  Log *log) override {
685061da546Spatrick     StreamString dump_stream;
686061da546Spatrick 
687061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
688061da546Spatrick     dump_stream.Printf("0x%" PRIx64 ": EntityVariable\n", load_addr);
689061da546Spatrick 
690061da546Spatrick     Status err;
691061da546Spatrick 
692061da546Spatrick     lldb::addr_t ptr = LLDB_INVALID_ADDRESS;
693061da546Spatrick 
694061da546Spatrick     {
695061da546Spatrick       dump_stream.Printf("Pointer:\n");
696061da546Spatrick 
697061da546Spatrick       DataBufferHeap data(m_size, 0);
698061da546Spatrick 
699061da546Spatrick       map.ReadMemory(data.GetBytes(), load_addr, m_size, err);
700061da546Spatrick 
701061da546Spatrick       if (!err.Success()) {
702061da546Spatrick         dump_stream.Printf("  <could not be read>\n");
703061da546Spatrick       } else {
704061da546Spatrick         DataExtractor extractor(data.GetBytes(), data.GetByteSize(),
705061da546Spatrick                                 map.GetByteOrder(), map.GetAddressByteSize());
706061da546Spatrick 
707061da546Spatrick         DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
708061da546Spatrick                      load_addr);
709061da546Spatrick 
710*f6aab3d8Srobert         lldb::offset_t offset = 0;
711061da546Spatrick 
712dda28197Spatrick         ptr = extractor.GetAddress(&offset);
713061da546Spatrick 
714061da546Spatrick         dump_stream.PutChar('\n');
715061da546Spatrick       }
716061da546Spatrick     }
717061da546Spatrick 
718061da546Spatrick     if (m_temporary_allocation == LLDB_INVALID_ADDRESS) {
719061da546Spatrick       dump_stream.Printf("Points to process memory:\n");
720061da546Spatrick     } else {
721061da546Spatrick       dump_stream.Printf("Temporary allocation:\n");
722061da546Spatrick     }
723061da546Spatrick 
724061da546Spatrick     if (ptr == LLDB_INVALID_ADDRESS) {
725061da546Spatrick       dump_stream.Printf("  <could not be be found>\n");
726061da546Spatrick     } else {
727061da546Spatrick       DataBufferHeap data(m_temporary_allocation_size, 0);
728061da546Spatrick 
729061da546Spatrick       map.ReadMemory(data.GetBytes(), m_temporary_allocation,
730061da546Spatrick                      m_temporary_allocation_size, err);
731061da546Spatrick 
732061da546Spatrick       if (!err.Success()) {
733061da546Spatrick         dump_stream.Printf("  <could not be read>\n");
734061da546Spatrick       } else {
735061da546Spatrick         DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
736061da546Spatrick                      load_addr);
737061da546Spatrick 
738061da546Spatrick         dump_stream.PutChar('\n');
739061da546Spatrick       }
740061da546Spatrick     }
741061da546Spatrick 
742061da546Spatrick     log->PutString(dump_stream.GetString());
743061da546Spatrick   }
744061da546Spatrick 
Wipe(IRMemoryMap & map,lldb::addr_t process_address)745061da546Spatrick   void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {
746061da546Spatrick     if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
747061da546Spatrick       Status free_error;
748061da546Spatrick 
749061da546Spatrick       map.Free(m_temporary_allocation, free_error);
750061da546Spatrick 
751061da546Spatrick       m_temporary_allocation = LLDB_INVALID_ADDRESS;
752061da546Spatrick       m_temporary_allocation_size = 0;
753061da546Spatrick     }
754061da546Spatrick   }
755061da546Spatrick 
756061da546Spatrick private:
757*f6aab3d8Srobert   virtual ConstString GetName() const = 0;
758*f6aab3d8Srobert 
759*f6aab3d8Srobert   /// Creates and returns ValueObject tied to this variable
760*f6aab3d8Srobert   /// and prepares Entity for materialization.
761*f6aab3d8Srobert   ///
762*f6aab3d8Srobert   /// Called each time the Materializer (de)materializes a
763*f6aab3d8Srobert   /// variable. We re-create the ValueObject based on the
764*f6aab3d8Srobert   /// current ExecutionContextScope since clients such as
765*f6aab3d8Srobert   /// conditional breakpoints may materialize the same
766*f6aab3d8Srobert   /// EntityVariable multiple times with different frames.
767*f6aab3d8Srobert   ///
768*f6aab3d8Srobert   /// Each subsequent use of the EntityVariableBase interface
769*f6aab3d8Srobert   /// will query the newly created ValueObject until this
770*f6aab3d8Srobert   /// function is called again.
771*f6aab3d8Srobert   virtual lldb::ValueObjectSP
772*f6aab3d8Srobert   SetupValueObject(ExecutionContextScope *scope) = 0;
773*f6aab3d8Srobert 
774*f6aab3d8Srobert   /// Returns size in bytes of the type associated with this variable
775*f6aab3d8Srobert   ///
776*f6aab3d8Srobert   /// \returns On success, returns byte size of the type associated
777*f6aab3d8Srobert   ///          with this variable. Returns std::nullopt otherwise.
778*f6aab3d8Srobert   virtual std::optional<uint64_t>
779*f6aab3d8Srobert   GetByteSize(ExecutionContextScope *scope) const = 0;
780*f6aab3d8Srobert 
781*f6aab3d8Srobert   /// Returns 'true' if the location expression associated with this variable
782*f6aab3d8Srobert   /// is valid.
783*f6aab3d8Srobert   virtual bool LocationExpressionIsValid() const = 0;
784*f6aab3d8Srobert 
785*f6aab3d8Srobert   /// Returns alignment of the type associated with this variable in bits.
786*f6aab3d8Srobert   ///
787*f6aab3d8Srobert   /// \returns On success, returns alignment in bits for the type associated
788*f6aab3d8Srobert   ///          with this variable. Returns std::nullopt otherwise.
789*f6aab3d8Srobert   virtual std::optional<size_t>
790*f6aab3d8Srobert   GetTypeBitAlign(ExecutionContextScope *scope) const = 0;
791*f6aab3d8Srobert 
792*f6aab3d8Srobert protected:
793*f6aab3d8Srobert   bool m_is_reference = false;
794*f6aab3d8Srobert   lldb::addr_t m_temporary_allocation = LLDB_INVALID_ADDRESS;
795*f6aab3d8Srobert   size_t m_temporary_allocation_size = 0;
796061da546Spatrick   lldb::DataBufferSP m_original_data;
797061da546Spatrick };
798061da546Spatrick 
799*f6aab3d8Srobert /// Represents an Entity constructed from a VariableSP.
800*f6aab3d8Srobert ///
801*f6aab3d8Srobert /// This class is used for materialization of variables for which
802*f6aab3d8Srobert /// the user has a VariableSP on hand. The ValueObject is then
803*f6aab3d8Srobert /// derived from the associated DWARF location expression when needed
804*f6aab3d8Srobert /// by the Materializer.
805*f6aab3d8Srobert class EntityVariable : public EntityVariableBase {
806*f6aab3d8Srobert public:
EntityVariable(lldb::VariableSP & variable_sp)807*f6aab3d8Srobert   EntityVariable(lldb::VariableSP &variable_sp) : m_variable_sp(variable_sp) {
808*f6aab3d8Srobert     m_is_reference =
809*f6aab3d8Srobert         m_variable_sp->GetType()->GetForwardCompilerType().IsReferenceType();
810*f6aab3d8Srobert   }
811*f6aab3d8Srobert 
GetName() const812*f6aab3d8Srobert   ConstString GetName() const override { return m_variable_sp->GetName(); }
813*f6aab3d8Srobert 
SetupValueObject(ExecutionContextScope * scope)814*f6aab3d8Srobert   lldb::ValueObjectSP SetupValueObject(ExecutionContextScope *scope) override {
815*f6aab3d8Srobert     assert(m_variable_sp != nullptr);
816*f6aab3d8Srobert     return ValueObjectVariable::Create(scope, m_variable_sp);
817*f6aab3d8Srobert   }
818*f6aab3d8Srobert 
819*f6aab3d8Srobert   std::optional<uint64_t>
GetByteSize(ExecutionContextScope * scope) const820*f6aab3d8Srobert   GetByteSize(ExecutionContextScope *scope) const override {
821*f6aab3d8Srobert     return m_variable_sp->GetType()->GetByteSize(scope);
822*f6aab3d8Srobert   }
823*f6aab3d8Srobert 
LocationExpressionIsValid() const824*f6aab3d8Srobert   bool LocationExpressionIsValid() const override {
825*f6aab3d8Srobert     return m_variable_sp->LocationExpressionList().IsValid();
826*f6aab3d8Srobert   }
827*f6aab3d8Srobert 
828*f6aab3d8Srobert   std::optional<size_t>
GetTypeBitAlign(ExecutionContextScope * scope) const829*f6aab3d8Srobert   GetTypeBitAlign(ExecutionContextScope *scope) const override {
830*f6aab3d8Srobert     return m_variable_sp->GetType()->GetLayoutCompilerType().GetTypeBitAlign(
831*f6aab3d8Srobert         scope);
832*f6aab3d8Srobert   }
833*f6aab3d8Srobert 
834*f6aab3d8Srobert private:
835*f6aab3d8Srobert   lldb::VariableSP m_variable_sp; ///< Variable that this entity is based on.
836*f6aab3d8Srobert };
837*f6aab3d8Srobert 
838*f6aab3d8Srobert /// Represents an Entity constructed from a VariableSP.
839*f6aab3d8Srobert ///
840*f6aab3d8Srobert /// This class is used for materialization of variables for
841*f6aab3d8Srobert /// which the user does not have a VariableSP available (e.g.,
842*f6aab3d8Srobert /// when materializing ivars).
843*f6aab3d8Srobert class EntityValueObject : public EntityVariableBase {
844*f6aab3d8Srobert public:
EntityValueObject(ConstString name,ValueObjectProviderTy provider)845*f6aab3d8Srobert   EntityValueObject(ConstString name, ValueObjectProviderTy provider)
846*f6aab3d8Srobert       : m_name(name), m_valobj_provider(std::move(provider)) {
847*f6aab3d8Srobert     assert(m_valobj_provider);
848*f6aab3d8Srobert   }
849*f6aab3d8Srobert 
GetName() const850*f6aab3d8Srobert   ConstString GetName() const override { return m_name; }
851*f6aab3d8Srobert 
SetupValueObject(ExecutionContextScope * scope)852*f6aab3d8Srobert   lldb::ValueObjectSP SetupValueObject(ExecutionContextScope *scope) override {
853*f6aab3d8Srobert     m_valobj_sp =
854*f6aab3d8Srobert         m_valobj_provider(GetName(), scope->CalculateStackFrame().get());
855*f6aab3d8Srobert 
856*f6aab3d8Srobert     if (m_valobj_sp)
857*f6aab3d8Srobert       m_is_reference = m_valobj_sp->GetCompilerType().IsReferenceType();
858*f6aab3d8Srobert 
859*f6aab3d8Srobert     return m_valobj_sp;
860*f6aab3d8Srobert   }
861*f6aab3d8Srobert 
862*f6aab3d8Srobert   std::optional<uint64_t>
GetByteSize(ExecutionContextScope * scope) const863*f6aab3d8Srobert   GetByteSize(ExecutionContextScope *scope) const override {
864*f6aab3d8Srobert     if (m_valobj_sp)
865*f6aab3d8Srobert       return m_valobj_sp->GetCompilerType().GetByteSize(scope);
866*f6aab3d8Srobert 
867*f6aab3d8Srobert     return {};
868*f6aab3d8Srobert   }
869*f6aab3d8Srobert 
LocationExpressionIsValid() const870*f6aab3d8Srobert   bool LocationExpressionIsValid() const override {
871*f6aab3d8Srobert     if (m_valobj_sp)
872*f6aab3d8Srobert       return m_valobj_sp->GetError().Success();
873*f6aab3d8Srobert 
874*f6aab3d8Srobert     return false;
875*f6aab3d8Srobert   }
876*f6aab3d8Srobert 
877*f6aab3d8Srobert   std::optional<size_t>
GetTypeBitAlign(ExecutionContextScope * scope) const878*f6aab3d8Srobert   GetTypeBitAlign(ExecutionContextScope *scope) const override {
879*f6aab3d8Srobert     if (m_valobj_sp)
880*f6aab3d8Srobert       return m_valobj_sp->GetCompilerType().GetTypeBitAlign(scope);
881*f6aab3d8Srobert 
882*f6aab3d8Srobert     return {};
883*f6aab3d8Srobert   }
884*f6aab3d8Srobert 
885*f6aab3d8Srobert private:
886*f6aab3d8Srobert   ConstString m_name;
887*f6aab3d8Srobert   lldb::ValueObjectSP m_valobj_sp;
888*f6aab3d8Srobert   ValueObjectProviderTy m_valobj_provider;
889*f6aab3d8Srobert };
890*f6aab3d8Srobert 
AddVariable(lldb::VariableSP & variable_sp,Status & err)891061da546Spatrick uint32_t Materializer::AddVariable(lldb::VariableSP &variable_sp, Status &err) {
892061da546Spatrick   EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
893dda28197Spatrick   *iter = std::make_unique<EntityVariable>(variable_sp);
894061da546Spatrick   uint32_t ret = AddStructMember(**iter);
895061da546Spatrick   (*iter)->SetOffset(ret);
896061da546Spatrick   return ret;
897061da546Spatrick }
898061da546Spatrick 
AddValueObject(ConstString name,ValueObjectProviderTy valobj_provider,Status & err)899*f6aab3d8Srobert uint32_t Materializer::AddValueObject(ConstString name,
900*f6aab3d8Srobert                                       ValueObjectProviderTy valobj_provider,
901*f6aab3d8Srobert                                       Status &err) {
902*f6aab3d8Srobert   assert(valobj_provider);
903*f6aab3d8Srobert   EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
904*f6aab3d8Srobert   *iter = std::make_unique<EntityValueObject>(name, std::move(valobj_provider));
905*f6aab3d8Srobert   uint32_t ret = AddStructMember(**iter);
906*f6aab3d8Srobert   (*iter)->SetOffset(ret);
907*f6aab3d8Srobert   return ret;
908*f6aab3d8Srobert }
909*f6aab3d8Srobert 
910061da546Spatrick class EntityResultVariable : public Materializer::Entity {
911061da546Spatrick public:
EntityResultVariable(const CompilerType & type,bool is_program_reference,bool keep_in_memory,Materializer::PersistentVariableDelegate * delegate)912061da546Spatrick   EntityResultVariable(const CompilerType &type, bool is_program_reference,
913061da546Spatrick                        bool keep_in_memory,
914061da546Spatrick                        Materializer::PersistentVariableDelegate *delegate)
915061da546Spatrick       : Entity(), m_type(type), m_is_program_reference(is_program_reference),
916*f6aab3d8Srobert         m_keep_in_memory(keep_in_memory), m_delegate(delegate) {
917061da546Spatrick     // Hard-coding to maximum size of a pointer since all results are
918061da546Spatrick     // materialized by reference
919*f6aab3d8Srobert     m_size = g_default_var_byte_size;
920*f6aab3d8Srobert     m_alignment = g_default_var_alignment;
921061da546Spatrick   }
922061da546Spatrick 
Materialize(lldb::StackFrameSP & frame_sp,IRMemoryMap & map,lldb::addr_t process_address,Status & err)923061da546Spatrick   void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
924061da546Spatrick                    lldb::addr_t process_address, Status &err) override {
925061da546Spatrick     if (!m_is_program_reference) {
926061da546Spatrick       if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
927061da546Spatrick         err.SetErrorString("Trying to create a temporary region for the result "
928061da546Spatrick                            "but one exists");
929061da546Spatrick         return;
930061da546Spatrick       }
931061da546Spatrick 
932061da546Spatrick       const lldb::addr_t load_addr = process_address + m_offset;
933061da546Spatrick 
934dda28197Spatrick       ExecutionContextScope *exe_scope = frame_sp.get();
935dda28197Spatrick       if (!exe_scope)
936dda28197Spatrick         exe_scope = map.GetBestExecutionContextScope();
937061da546Spatrick 
938*f6aab3d8Srobert       std::optional<uint64_t> byte_size = m_type.GetByteSize(exe_scope);
939061da546Spatrick       if (!byte_size) {
940be691f3bSpatrick         err.SetErrorStringWithFormat("can't get size of type \"%s\"",
941be691f3bSpatrick                                      m_type.GetTypeName().AsCString());
942061da546Spatrick         return;
943061da546Spatrick       }
944061da546Spatrick 
945*f6aab3d8Srobert       std::optional<size_t> opt_bit_align = m_type.GetTypeBitAlign(exe_scope);
946061da546Spatrick       if (!opt_bit_align) {
947be691f3bSpatrick         err.SetErrorStringWithFormat("can't get the alignment of type  \"%s\"",
948be691f3bSpatrick                                      m_type.GetTypeName().AsCString());
949061da546Spatrick         return;
950061da546Spatrick       }
951061da546Spatrick 
952061da546Spatrick       size_t byte_align = (*opt_bit_align + 7) / 8;
953061da546Spatrick 
954061da546Spatrick       Status alloc_error;
955061da546Spatrick       const bool zero_memory = true;
956061da546Spatrick 
957061da546Spatrick       m_temporary_allocation = map.Malloc(
958061da546Spatrick           *byte_size, byte_align,
959061da546Spatrick           lldb::ePermissionsReadable | lldb::ePermissionsWritable,
960061da546Spatrick           IRMemoryMap::eAllocationPolicyMirror, zero_memory, alloc_error);
961061da546Spatrick       m_temporary_allocation_size = *byte_size;
962061da546Spatrick 
963061da546Spatrick       if (!alloc_error.Success()) {
964061da546Spatrick         err.SetErrorStringWithFormat(
965061da546Spatrick             "couldn't allocate a temporary region for the result: %s",
966061da546Spatrick             alloc_error.AsCString());
967061da546Spatrick         return;
968061da546Spatrick       }
969061da546Spatrick 
970061da546Spatrick       Status pointer_write_error;
971061da546Spatrick 
972061da546Spatrick       map.WritePointerToMemory(load_addr, m_temporary_allocation,
973061da546Spatrick                                pointer_write_error);
974061da546Spatrick 
975061da546Spatrick       if (!pointer_write_error.Success()) {
976061da546Spatrick         err.SetErrorStringWithFormat("couldn't write the address of the "
977061da546Spatrick                                      "temporary region for the result: %s",
978061da546Spatrick                                      pointer_write_error.AsCString());
979061da546Spatrick       }
980061da546Spatrick     }
981061da546Spatrick   }
982061da546Spatrick 
Dematerialize(lldb::StackFrameSP & frame_sp,IRMemoryMap & map,lldb::addr_t process_address,lldb::addr_t frame_top,lldb::addr_t frame_bottom,Status & err)983061da546Spatrick   void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
984061da546Spatrick                      lldb::addr_t process_address, lldb::addr_t frame_top,
985061da546Spatrick                      lldb::addr_t frame_bottom, Status &err) override {
986061da546Spatrick     err.Clear();
987061da546Spatrick 
988dda28197Spatrick     ExecutionContextScope *exe_scope = frame_sp.get();
989dda28197Spatrick     if (!exe_scope)
990dda28197Spatrick       exe_scope = map.GetBestExecutionContextScope();
991061da546Spatrick 
992061da546Spatrick     if (!exe_scope) {
993061da546Spatrick       err.SetErrorString("Couldn't dematerialize a result variable: invalid "
994061da546Spatrick                          "execution context scope");
995061da546Spatrick       return;
996061da546Spatrick     }
997061da546Spatrick 
998061da546Spatrick     lldb::addr_t address;
999061da546Spatrick     Status read_error;
1000061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
1001061da546Spatrick 
1002061da546Spatrick     map.ReadPointerFromMemory(&address, load_addr, read_error);
1003061da546Spatrick 
1004061da546Spatrick     if (!read_error.Success()) {
1005061da546Spatrick       err.SetErrorString("Couldn't dematerialize a result variable: couldn't "
1006061da546Spatrick                          "read its address");
1007061da546Spatrick       return;
1008061da546Spatrick     }
1009061da546Spatrick 
1010061da546Spatrick     lldb::TargetSP target_sp = exe_scope->CalculateTarget();
1011061da546Spatrick 
1012061da546Spatrick     if (!target_sp) {
1013061da546Spatrick       err.SetErrorString("Couldn't dematerialize a result variable: no target");
1014061da546Spatrick       return;
1015061da546Spatrick     }
1016061da546Spatrick 
1017061da546Spatrick     auto type_system_or_err =
1018061da546Spatrick         target_sp->GetScratchTypeSystemForLanguage(m_type.GetMinimumLanguage());
1019061da546Spatrick 
1020061da546Spatrick     if (auto error = type_system_or_err.takeError()) {
1021061da546Spatrick       err.SetErrorStringWithFormat("Couldn't dematerialize a result variable: "
1022061da546Spatrick                                    "couldn't get the corresponding type "
1023061da546Spatrick                                    "system: %s",
1024061da546Spatrick                                    llvm::toString(std::move(error)).c_str());
1025061da546Spatrick       return;
1026061da546Spatrick     }
1027*f6aab3d8Srobert     auto ts = *type_system_or_err;
1028*f6aab3d8Srobert     if (!ts) {
1029*f6aab3d8Srobert       err.SetErrorStringWithFormat("Couldn't dematerialize a result variable: "
1030*f6aab3d8Srobert                                    "couldn't corresponding type system is "
1031*f6aab3d8Srobert                                    "no longer live.");
1032*f6aab3d8Srobert       return;
1033*f6aab3d8Srobert     }
1034061da546Spatrick     PersistentExpressionState *persistent_state =
1035*f6aab3d8Srobert         ts->GetPersistentExpressionState();
1036061da546Spatrick 
1037061da546Spatrick     if (!persistent_state) {
1038061da546Spatrick       err.SetErrorString("Couldn't dematerialize a result variable: "
1039061da546Spatrick                          "corresponding type system doesn't handle persistent "
1040061da546Spatrick                          "variables");
1041061da546Spatrick       return;
1042061da546Spatrick     }
1043061da546Spatrick 
1044dda28197Spatrick     ConstString name = m_delegate
1045061da546Spatrick                            ? m_delegate->GetName()
1046dda28197Spatrick                            : persistent_state->GetNextPersistentVariableName();
1047061da546Spatrick 
1048061da546Spatrick     lldb::ExpressionVariableSP ret = persistent_state->CreatePersistentVariable(
1049061da546Spatrick         exe_scope, name, m_type, map.GetByteOrder(), map.GetAddressByteSize());
1050061da546Spatrick 
1051061da546Spatrick     if (!ret) {
1052061da546Spatrick       err.SetErrorStringWithFormat("couldn't dematerialize a result variable: "
1053061da546Spatrick                                    "failed to make persistent variable %s",
1054061da546Spatrick                                    name.AsCString());
1055061da546Spatrick       return;
1056061da546Spatrick     }
1057061da546Spatrick 
1058061da546Spatrick     lldb::ProcessSP process_sp =
1059061da546Spatrick         map.GetBestExecutionContextScope()->CalculateProcess();
1060061da546Spatrick 
1061061da546Spatrick     if (m_delegate) {
1062061da546Spatrick       m_delegate->DidDematerialize(ret);
1063061da546Spatrick     }
1064061da546Spatrick 
1065061da546Spatrick     bool can_persist =
1066061da546Spatrick         (m_is_program_reference && process_sp && process_sp->CanJIT() &&
1067061da546Spatrick          !(address >= frame_bottom && address < frame_top));
1068061da546Spatrick 
1069061da546Spatrick     if (can_persist && m_keep_in_memory) {
1070061da546Spatrick       ret->m_live_sp = ValueObjectConstResult::Create(exe_scope, m_type, name,
1071061da546Spatrick                                                       address, eAddressTypeLoad,
1072061da546Spatrick                                                       map.GetAddressByteSize());
1073061da546Spatrick     }
1074061da546Spatrick 
1075061da546Spatrick     ret->ValueUpdated();
1076061da546Spatrick 
1077*f6aab3d8Srobert     const size_t pvar_byte_size = ret->GetByteSize().value_or(0);
1078061da546Spatrick     uint8_t *pvar_data = ret->GetValueBytes();
1079061da546Spatrick 
1080061da546Spatrick     map.ReadMemory(pvar_data, address, pvar_byte_size, read_error);
1081061da546Spatrick 
1082061da546Spatrick     if (!read_error.Success()) {
1083061da546Spatrick       err.SetErrorString(
1084061da546Spatrick           "Couldn't dematerialize a result variable: couldn't read its memory");
1085061da546Spatrick       return;
1086061da546Spatrick     }
1087061da546Spatrick 
1088061da546Spatrick     if (!can_persist || !m_keep_in_memory) {
1089061da546Spatrick       ret->m_flags |= ExpressionVariable::EVNeedsAllocation;
1090061da546Spatrick 
1091061da546Spatrick       if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
1092061da546Spatrick         Status free_error;
1093061da546Spatrick         map.Free(m_temporary_allocation, free_error);
1094061da546Spatrick       }
1095061da546Spatrick     } else {
1096061da546Spatrick       ret->m_flags |= ExpressionVariable::EVIsLLDBAllocated;
1097061da546Spatrick     }
1098061da546Spatrick 
1099061da546Spatrick     m_temporary_allocation = LLDB_INVALID_ADDRESS;
1100061da546Spatrick     m_temporary_allocation_size = 0;
1101061da546Spatrick   }
1102061da546Spatrick 
DumpToLog(IRMemoryMap & map,lldb::addr_t process_address,Log * log)1103061da546Spatrick   void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address,
1104061da546Spatrick                  Log *log) override {
1105061da546Spatrick     StreamString dump_stream;
1106061da546Spatrick 
1107061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
1108061da546Spatrick 
1109061da546Spatrick     dump_stream.Printf("0x%" PRIx64 ": EntityResultVariable\n", load_addr);
1110061da546Spatrick 
1111061da546Spatrick     Status err;
1112061da546Spatrick 
1113061da546Spatrick     lldb::addr_t ptr = LLDB_INVALID_ADDRESS;
1114061da546Spatrick 
1115061da546Spatrick     {
1116061da546Spatrick       dump_stream.Printf("Pointer:\n");
1117061da546Spatrick 
1118061da546Spatrick       DataBufferHeap data(m_size, 0);
1119061da546Spatrick 
1120061da546Spatrick       map.ReadMemory(data.GetBytes(), load_addr, m_size, err);
1121061da546Spatrick 
1122061da546Spatrick       if (!err.Success()) {
1123061da546Spatrick         dump_stream.Printf("  <could not be read>\n");
1124061da546Spatrick       } else {
1125061da546Spatrick         DataExtractor extractor(data.GetBytes(), data.GetByteSize(),
1126061da546Spatrick                                 map.GetByteOrder(), map.GetAddressByteSize());
1127061da546Spatrick 
1128061da546Spatrick         DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
1129061da546Spatrick                      load_addr);
1130061da546Spatrick 
1131*f6aab3d8Srobert         lldb::offset_t offset = 0;
1132061da546Spatrick 
1133dda28197Spatrick         ptr = extractor.GetAddress(&offset);
1134061da546Spatrick 
1135061da546Spatrick         dump_stream.PutChar('\n');
1136061da546Spatrick       }
1137061da546Spatrick     }
1138061da546Spatrick 
1139061da546Spatrick     if (m_temporary_allocation == LLDB_INVALID_ADDRESS) {
1140061da546Spatrick       dump_stream.Printf("Points to process memory:\n");
1141061da546Spatrick     } else {
1142061da546Spatrick       dump_stream.Printf("Temporary allocation:\n");
1143061da546Spatrick     }
1144061da546Spatrick 
1145061da546Spatrick     if (ptr == LLDB_INVALID_ADDRESS) {
1146061da546Spatrick       dump_stream.Printf("  <could not be be found>\n");
1147061da546Spatrick     } else {
1148061da546Spatrick       DataBufferHeap data(m_temporary_allocation_size, 0);
1149061da546Spatrick 
1150061da546Spatrick       map.ReadMemory(data.GetBytes(), m_temporary_allocation,
1151061da546Spatrick                      m_temporary_allocation_size, err);
1152061da546Spatrick 
1153061da546Spatrick       if (!err.Success()) {
1154061da546Spatrick         dump_stream.Printf("  <could not be read>\n");
1155061da546Spatrick       } else {
1156061da546Spatrick         DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
1157061da546Spatrick                      load_addr);
1158061da546Spatrick 
1159061da546Spatrick         dump_stream.PutChar('\n');
1160061da546Spatrick       }
1161061da546Spatrick     }
1162061da546Spatrick 
1163061da546Spatrick     log->PutString(dump_stream.GetString());
1164061da546Spatrick   }
1165061da546Spatrick 
Wipe(IRMemoryMap & map,lldb::addr_t process_address)1166061da546Spatrick   void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {
1167061da546Spatrick     if (!m_keep_in_memory && m_temporary_allocation != LLDB_INVALID_ADDRESS) {
1168061da546Spatrick       Status free_error;
1169061da546Spatrick 
1170061da546Spatrick       map.Free(m_temporary_allocation, free_error);
1171061da546Spatrick     }
1172061da546Spatrick 
1173061da546Spatrick     m_temporary_allocation = LLDB_INVALID_ADDRESS;
1174061da546Spatrick     m_temporary_allocation_size = 0;
1175061da546Spatrick   }
1176061da546Spatrick 
1177061da546Spatrick private:
1178061da546Spatrick   CompilerType m_type;
1179061da546Spatrick   bool m_is_program_reference;
1180061da546Spatrick   bool m_keep_in_memory;
1181061da546Spatrick 
1182*f6aab3d8Srobert   lldb::addr_t m_temporary_allocation = LLDB_INVALID_ADDRESS;
1183*f6aab3d8Srobert   size_t m_temporary_allocation_size = 0;
1184061da546Spatrick   Materializer::PersistentVariableDelegate *m_delegate;
1185061da546Spatrick };
1186061da546Spatrick 
AddResultVariable(const CompilerType & type,bool is_program_reference,bool keep_in_memory,PersistentVariableDelegate * delegate,Status & err)1187061da546Spatrick uint32_t Materializer::AddResultVariable(const CompilerType &type,
1188061da546Spatrick                                          bool is_program_reference,
1189061da546Spatrick                                          bool keep_in_memory,
1190061da546Spatrick                                          PersistentVariableDelegate *delegate,
1191061da546Spatrick                                          Status &err) {
1192061da546Spatrick   EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
1193dda28197Spatrick   *iter = std::make_unique<EntityResultVariable>(type, is_program_reference,
1194dda28197Spatrick                                                  keep_in_memory, delegate);
1195061da546Spatrick   uint32_t ret = AddStructMember(**iter);
1196061da546Spatrick   (*iter)->SetOffset(ret);
1197061da546Spatrick   return ret;
1198061da546Spatrick }
1199061da546Spatrick 
1200061da546Spatrick class EntitySymbol : public Materializer::Entity {
1201061da546Spatrick public:
EntitySymbol(const Symbol & symbol)1202061da546Spatrick   EntitySymbol(const Symbol &symbol) : Entity(), m_symbol(symbol) {
1203061da546Spatrick     // Hard-coding to maximum size of a symbol
1204*f6aab3d8Srobert     m_size = g_default_var_byte_size;
1205*f6aab3d8Srobert     m_alignment = g_default_var_alignment;
1206061da546Spatrick   }
1207061da546Spatrick 
Materialize(lldb::StackFrameSP & frame_sp,IRMemoryMap & map,lldb::addr_t process_address,Status & err)1208061da546Spatrick   void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
1209061da546Spatrick                    lldb::addr_t process_address, Status &err) override {
1210*f6aab3d8Srobert     Log *log = GetLog(LLDBLog::Expressions);
1211061da546Spatrick 
1212061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
1213061da546Spatrick 
1214061da546Spatrick     if (log) {
1215061da546Spatrick       LLDB_LOGF(log,
1216061da546Spatrick                 "EntitySymbol::Materialize [address = 0x%" PRIx64
1217061da546Spatrick                 ", m_symbol = %s]",
1218061da546Spatrick                 (uint64_t)load_addr, m_symbol.GetName().AsCString());
1219061da546Spatrick     }
1220061da546Spatrick 
1221061da546Spatrick     const Address sym_address = m_symbol.GetAddress();
1222061da546Spatrick 
1223dda28197Spatrick     ExecutionContextScope *exe_scope = frame_sp.get();
1224dda28197Spatrick     if (!exe_scope)
1225dda28197Spatrick       exe_scope = map.GetBestExecutionContextScope();
1226061da546Spatrick 
1227061da546Spatrick     lldb::TargetSP target_sp;
1228061da546Spatrick 
1229061da546Spatrick     if (exe_scope)
1230061da546Spatrick       target_sp = map.GetBestExecutionContextScope()->CalculateTarget();
1231061da546Spatrick 
1232061da546Spatrick     if (!target_sp) {
1233061da546Spatrick       err.SetErrorStringWithFormat(
1234061da546Spatrick           "couldn't resolve symbol %s because there is no target",
1235061da546Spatrick           m_symbol.GetName().AsCString());
1236061da546Spatrick       return;
1237061da546Spatrick     }
1238061da546Spatrick 
1239061da546Spatrick     lldb::addr_t resolved_address = sym_address.GetLoadAddress(target_sp.get());
1240061da546Spatrick 
1241061da546Spatrick     if (resolved_address == LLDB_INVALID_ADDRESS)
1242061da546Spatrick       resolved_address = sym_address.GetFileAddress();
1243061da546Spatrick 
1244061da546Spatrick     Status pointer_write_error;
1245061da546Spatrick 
1246061da546Spatrick     map.WritePointerToMemory(load_addr, resolved_address, pointer_write_error);
1247061da546Spatrick 
1248061da546Spatrick     if (!pointer_write_error.Success()) {
1249061da546Spatrick       err.SetErrorStringWithFormat(
1250061da546Spatrick           "couldn't write the address of symbol %s: %s",
1251061da546Spatrick           m_symbol.GetName().AsCString(), pointer_write_error.AsCString());
1252061da546Spatrick       return;
1253061da546Spatrick     }
1254061da546Spatrick   }
1255061da546Spatrick 
Dematerialize(lldb::StackFrameSP & frame_sp,IRMemoryMap & map,lldb::addr_t process_address,lldb::addr_t frame_top,lldb::addr_t frame_bottom,Status & err)1256061da546Spatrick   void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
1257061da546Spatrick                      lldb::addr_t process_address, lldb::addr_t frame_top,
1258061da546Spatrick                      lldb::addr_t frame_bottom, Status &err) override {
1259*f6aab3d8Srobert     Log *log = GetLog(LLDBLog::Expressions);
1260061da546Spatrick 
1261061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
1262061da546Spatrick 
1263061da546Spatrick     if (log) {
1264061da546Spatrick       LLDB_LOGF(log,
1265061da546Spatrick                 "EntitySymbol::Dematerialize [address = 0x%" PRIx64
1266061da546Spatrick                 ", m_symbol = %s]",
1267061da546Spatrick                 (uint64_t)load_addr, m_symbol.GetName().AsCString());
1268061da546Spatrick     }
1269061da546Spatrick 
1270061da546Spatrick     // no work needs to be done
1271061da546Spatrick   }
1272061da546Spatrick 
DumpToLog(IRMemoryMap & map,lldb::addr_t process_address,Log * log)1273061da546Spatrick   void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address,
1274061da546Spatrick                  Log *log) override {
1275061da546Spatrick     StreamString dump_stream;
1276061da546Spatrick 
1277061da546Spatrick     Status err;
1278061da546Spatrick 
1279061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
1280061da546Spatrick 
1281061da546Spatrick     dump_stream.Printf("0x%" PRIx64 ": EntitySymbol (%s)\n", load_addr,
1282061da546Spatrick                        m_symbol.GetName().AsCString());
1283061da546Spatrick 
1284061da546Spatrick     {
1285061da546Spatrick       dump_stream.Printf("Pointer:\n");
1286061da546Spatrick 
1287061da546Spatrick       DataBufferHeap data(m_size, 0);
1288061da546Spatrick 
1289061da546Spatrick       map.ReadMemory(data.GetBytes(), load_addr, m_size, err);
1290061da546Spatrick 
1291061da546Spatrick       if (!err.Success()) {
1292061da546Spatrick         dump_stream.Printf("  <could not be read>\n");
1293061da546Spatrick       } else {
1294061da546Spatrick         DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
1295061da546Spatrick                      load_addr);
1296061da546Spatrick 
1297061da546Spatrick         dump_stream.PutChar('\n');
1298061da546Spatrick       }
1299061da546Spatrick     }
1300061da546Spatrick 
1301061da546Spatrick     log->PutString(dump_stream.GetString());
1302061da546Spatrick   }
1303061da546Spatrick 
Wipe(IRMemoryMap & map,lldb::addr_t process_address)1304061da546Spatrick   void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {}
1305061da546Spatrick 
1306061da546Spatrick private:
1307061da546Spatrick   Symbol m_symbol;
1308061da546Spatrick };
1309061da546Spatrick 
AddSymbol(const Symbol & symbol_sp,Status & err)1310061da546Spatrick uint32_t Materializer::AddSymbol(const Symbol &symbol_sp, Status &err) {
1311061da546Spatrick   EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
1312dda28197Spatrick   *iter = std::make_unique<EntitySymbol>(symbol_sp);
1313061da546Spatrick   uint32_t ret = AddStructMember(**iter);
1314061da546Spatrick   (*iter)->SetOffset(ret);
1315061da546Spatrick   return ret;
1316061da546Spatrick }
1317061da546Spatrick 
1318061da546Spatrick class EntityRegister : public Materializer::Entity {
1319061da546Spatrick public:
EntityRegister(const RegisterInfo & register_info)1320061da546Spatrick   EntityRegister(const RegisterInfo &register_info)
1321061da546Spatrick       : Entity(), m_register_info(register_info) {
1322061da546Spatrick     // Hard-coding alignment conservatively
1323061da546Spatrick     m_size = m_register_info.byte_size;
1324061da546Spatrick     m_alignment = m_register_info.byte_size;
1325061da546Spatrick   }
1326061da546Spatrick 
Materialize(lldb::StackFrameSP & frame_sp,IRMemoryMap & map,lldb::addr_t process_address,Status & err)1327061da546Spatrick   void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
1328061da546Spatrick                    lldb::addr_t process_address, Status &err) override {
1329*f6aab3d8Srobert     Log *log = GetLog(LLDBLog::Expressions);
1330061da546Spatrick 
1331061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
1332061da546Spatrick 
1333061da546Spatrick     if (log) {
1334061da546Spatrick       LLDB_LOGF(log,
1335061da546Spatrick                 "EntityRegister::Materialize [address = 0x%" PRIx64
1336061da546Spatrick                 ", m_register_info = %s]",
1337061da546Spatrick                 (uint64_t)load_addr, m_register_info.name);
1338061da546Spatrick     }
1339061da546Spatrick 
1340061da546Spatrick     RegisterValue reg_value;
1341061da546Spatrick 
1342061da546Spatrick     if (!frame_sp.get()) {
1343061da546Spatrick       err.SetErrorStringWithFormat(
1344061da546Spatrick           "couldn't materialize register %s without a stack frame",
1345061da546Spatrick           m_register_info.name);
1346061da546Spatrick       return;
1347061da546Spatrick     }
1348061da546Spatrick 
1349061da546Spatrick     lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext();
1350061da546Spatrick 
1351061da546Spatrick     if (!reg_context_sp->ReadRegister(&m_register_info, reg_value)) {
1352061da546Spatrick       err.SetErrorStringWithFormat("couldn't read the value of register %s",
1353061da546Spatrick                                    m_register_info.name);
1354061da546Spatrick       return;
1355061da546Spatrick     }
1356061da546Spatrick 
1357061da546Spatrick     DataExtractor register_data;
1358061da546Spatrick 
1359061da546Spatrick     if (!reg_value.GetData(register_data)) {
1360061da546Spatrick       err.SetErrorStringWithFormat("couldn't get the data for register %s",
1361061da546Spatrick                                    m_register_info.name);
1362061da546Spatrick       return;
1363061da546Spatrick     }
1364061da546Spatrick 
1365061da546Spatrick     if (register_data.GetByteSize() != m_register_info.byte_size) {
1366061da546Spatrick       err.SetErrorStringWithFormat(
1367061da546Spatrick           "data for register %s had size %llu but we expected %llu",
1368061da546Spatrick           m_register_info.name, (unsigned long long)register_data.GetByteSize(),
1369061da546Spatrick           (unsigned long long)m_register_info.byte_size);
1370061da546Spatrick       return;
1371061da546Spatrick     }
1372061da546Spatrick 
1373061da546Spatrick     m_register_contents = std::make_shared<DataBufferHeap>(
1374061da546Spatrick         register_data.GetDataStart(), register_data.GetByteSize());
1375061da546Spatrick 
1376061da546Spatrick     Status write_error;
1377061da546Spatrick 
1378061da546Spatrick     map.WriteMemory(load_addr, register_data.GetDataStart(),
1379061da546Spatrick                     register_data.GetByteSize(), write_error);
1380061da546Spatrick 
1381061da546Spatrick     if (!write_error.Success()) {
1382061da546Spatrick       err.SetErrorStringWithFormat(
1383061da546Spatrick           "couldn't write the contents of register %s: %s",
1384061da546Spatrick           m_register_info.name, write_error.AsCString());
1385061da546Spatrick       return;
1386061da546Spatrick     }
1387061da546Spatrick   }
1388061da546Spatrick 
Dematerialize(lldb::StackFrameSP & frame_sp,IRMemoryMap & map,lldb::addr_t process_address,lldb::addr_t frame_top,lldb::addr_t frame_bottom,Status & err)1389061da546Spatrick   void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
1390061da546Spatrick                      lldb::addr_t process_address, lldb::addr_t frame_top,
1391061da546Spatrick                      lldb::addr_t frame_bottom, Status &err) override {
1392*f6aab3d8Srobert     Log *log = GetLog(LLDBLog::Expressions);
1393061da546Spatrick 
1394061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
1395061da546Spatrick 
1396061da546Spatrick     if (log) {
1397061da546Spatrick       LLDB_LOGF(log,
1398061da546Spatrick                 "EntityRegister::Dematerialize [address = 0x%" PRIx64
1399061da546Spatrick                 ", m_register_info = %s]",
1400061da546Spatrick                 (uint64_t)load_addr, m_register_info.name);
1401061da546Spatrick     }
1402061da546Spatrick 
1403061da546Spatrick     Status extract_error;
1404061da546Spatrick 
1405061da546Spatrick     DataExtractor register_data;
1406061da546Spatrick 
1407061da546Spatrick     if (!frame_sp.get()) {
1408061da546Spatrick       err.SetErrorStringWithFormat(
1409061da546Spatrick           "couldn't dematerialize register %s without a stack frame",
1410061da546Spatrick           m_register_info.name);
1411061da546Spatrick       return;
1412061da546Spatrick     }
1413061da546Spatrick 
1414061da546Spatrick     lldb::RegisterContextSP reg_context_sp = frame_sp->GetRegisterContext();
1415061da546Spatrick 
1416061da546Spatrick     map.GetMemoryData(register_data, load_addr, m_register_info.byte_size,
1417061da546Spatrick                       extract_error);
1418061da546Spatrick 
1419061da546Spatrick     if (!extract_error.Success()) {
1420061da546Spatrick       err.SetErrorStringWithFormat("couldn't get the data for register %s: %s",
1421061da546Spatrick                                    m_register_info.name,
1422061da546Spatrick                                    extract_error.AsCString());
1423061da546Spatrick       return;
1424061da546Spatrick     }
1425061da546Spatrick 
1426061da546Spatrick     if (!memcmp(register_data.GetDataStart(), m_register_contents->GetBytes(),
1427061da546Spatrick                 register_data.GetByteSize())) {
1428061da546Spatrick       // No write required, and in particular we avoid errors if the register
1429061da546Spatrick       // wasn't writable
1430061da546Spatrick 
1431061da546Spatrick       m_register_contents.reset();
1432061da546Spatrick       return;
1433061da546Spatrick     }
1434061da546Spatrick 
1435061da546Spatrick     m_register_contents.reset();
1436061da546Spatrick 
1437be691f3bSpatrick     RegisterValue register_value(register_data.GetData(),
1438be691f3bSpatrick                                  register_data.GetByteOrder());
1439061da546Spatrick 
1440061da546Spatrick     if (!reg_context_sp->WriteRegister(&m_register_info, register_value)) {
1441061da546Spatrick       err.SetErrorStringWithFormat("couldn't write the value of register %s",
1442061da546Spatrick                                    m_register_info.name);
1443061da546Spatrick       return;
1444061da546Spatrick     }
1445061da546Spatrick   }
1446061da546Spatrick 
DumpToLog(IRMemoryMap & map,lldb::addr_t process_address,Log * log)1447061da546Spatrick   void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address,
1448061da546Spatrick                  Log *log) override {
1449061da546Spatrick     StreamString dump_stream;
1450061da546Spatrick 
1451061da546Spatrick     Status err;
1452061da546Spatrick 
1453061da546Spatrick     const lldb::addr_t load_addr = process_address + m_offset;
1454061da546Spatrick 
1455061da546Spatrick     dump_stream.Printf("0x%" PRIx64 ": EntityRegister (%s)\n", load_addr,
1456061da546Spatrick                        m_register_info.name);
1457061da546Spatrick 
1458061da546Spatrick     {
1459061da546Spatrick       dump_stream.Printf("Value:\n");
1460061da546Spatrick 
1461061da546Spatrick       DataBufferHeap data(m_size, 0);
1462061da546Spatrick 
1463061da546Spatrick       map.ReadMemory(data.GetBytes(), load_addr, m_size, err);
1464061da546Spatrick 
1465061da546Spatrick       if (!err.Success()) {
1466061da546Spatrick         dump_stream.Printf("  <could not be read>\n");
1467061da546Spatrick       } else {
1468061da546Spatrick         DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16,
1469061da546Spatrick                      load_addr);
1470061da546Spatrick 
1471061da546Spatrick         dump_stream.PutChar('\n');
1472061da546Spatrick       }
1473061da546Spatrick     }
1474061da546Spatrick 
1475061da546Spatrick     log->PutString(dump_stream.GetString());
1476061da546Spatrick   }
1477061da546Spatrick 
Wipe(IRMemoryMap & map,lldb::addr_t process_address)1478061da546Spatrick   void Wipe(IRMemoryMap &map, lldb::addr_t process_address) override {}
1479061da546Spatrick 
1480061da546Spatrick private:
1481061da546Spatrick   RegisterInfo m_register_info;
1482061da546Spatrick   lldb::DataBufferSP m_register_contents;
1483061da546Spatrick };
1484061da546Spatrick 
AddRegister(const RegisterInfo & register_info,Status & err)1485061da546Spatrick uint32_t Materializer::AddRegister(const RegisterInfo &register_info,
1486061da546Spatrick                                    Status &err) {
1487061da546Spatrick   EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
1488dda28197Spatrick   *iter = std::make_unique<EntityRegister>(register_info);
1489061da546Spatrick   uint32_t ret = AddStructMember(**iter);
1490061da546Spatrick   (*iter)->SetOffset(ret);
1491061da546Spatrick   return ret;
1492061da546Spatrick }
1493061da546Spatrick 
~Materializer()1494061da546Spatrick Materializer::~Materializer() {
1495061da546Spatrick   DematerializerSP dematerializer_sp = m_dematerializer_wp.lock();
1496061da546Spatrick 
1497061da546Spatrick   if (dematerializer_sp)
1498061da546Spatrick     dematerializer_sp->Wipe();
1499061da546Spatrick }
1500061da546Spatrick 
1501061da546Spatrick Materializer::DematerializerSP
Materialize(lldb::StackFrameSP & frame_sp,IRMemoryMap & map,lldb::addr_t process_address,Status & error)1502061da546Spatrick Materializer::Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
1503061da546Spatrick                           lldb::addr_t process_address, Status &error) {
1504061da546Spatrick   ExecutionContextScope *exe_scope = frame_sp.get();
1505061da546Spatrick   if (!exe_scope)
1506061da546Spatrick     exe_scope = map.GetBestExecutionContextScope();
1507061da546Spatrick 
1508061da546Spatrick   DematerializerSP dematerializer_sp = m_dematerializer_wp.lock();
1509061da546Spatrick 
1510061da546Spatrick   if (dematerializer_sp) {
1511061da546Spatrick     error.SetErrorToGenericError();
1512061da546Spatrick     error.SetErrorString("Couldn't materialize: already materialized");
1513061da546Spatrick   }
1514061da546Spatrick 
1515061da546Spatrick   DematerializerSP ret(
1516061da546Spatrick       new Dematerializer(*this, frame_sp, map, process_address));
1517061da546Spatrick 
1518061da546Spatrick   if (!exe_scope) {
1519061da546Spatrick     error.SetErrorToGenericError();
1520061da546Spatrick     error.SetErrorString("Couldn't materialize: target doesn't exist");
1521061da546Spatrick   }
1522061da546Spatrick 
1523061da546Spatrick   for (EntityUP &entity_up : m_entities) {
1524061da546Spatrick     entity_up->Materialize(frame_sp, map, process_address, error);
1525061da546Spatrick 
1526061da546Spatrick     if (!error.Success())
1527061da546Spatrick       return DematerializerSP();
1528061da546Spatrick   }
1529061da546Spatrick 
1530*f6aab3d8Srobert   if (Log *log = GetLog(LLDBLog::Expressions)) {
1531061da546Spatrick     LLDB_LOGF(
1532061da546Spatrick         log,
1533061da546Spatrick         "Materializer::Materialize (frame_sp = %p, process_address = 0x%" PRIx64
1534061da546Spatrick         ") materialized:",
1535061da546Spatrick         static_cast<void *>(frame_sp.get()), process_address);
1536061da546Spatrick     for (EntityUP &entity_up : m_entities)
1537061da546Spatrick       entity_up->DumpToLog(map, process_address, log);
1538061da546Spatrick   }
1539061da546Spatrick 
1540061da546Spatrick   m_dematerializer_wp = ret;
1541061da546Spatrick 
1542061da546Spatrick   return ret;
1543061da546Spatrick }
1544061da546Spatrick 
Dematerialize(Status & error,lldb::addr_t frame_bottom,lldb::addr_t frame_top)1545061da546Spatrick void Materializer::Dematerializer::Dematerialize(Status &error,
1546061da546Spatrick                                                  lldb::addr_t frame_bottom,
1547061da546Spatrick                                                  lldb::addr_t frame_top) {
1548061da546Spatrick   lldb::StackFrameSP frame_sp;
1549061da546Spatrick 
1550061da546Spatrick   lldb::ThreadSP thread_sp = m_thread_wp.lock();
1551061da546Spatrick   if (thread_sp)
1552061da546Spatrick     frame_sp = thread_sp->GetFrameWithStackID(m_stack_id);
1553061da546Spatrick 
1554dda28197Spatrick   ExecutionContextScope *exe_scope = frame_sp.get();
1555dda28197Spatrick   if (!exe_scope)
1556dda28197Spatrick     exe_scope = m_map->GetBestExecutionContextScope();
1557061da546Spatrick 
1558061da546Spatrick   if (!IsValid()) {
1559061da546Spatrick     error.SetErrorToGenericError();
1560061da546Spatrick     error.SetErrorString("Couldn't dematerialize: invalid dematerializer");
1561061da546Spatrick   }
1562061da546Spatrick 
1563061da546Spatrick   if (!exe_scope) {
1564061da546Spatrick     error.SetErrorToGenericError();
1565061da546Spatrick     error.SetErrorString("Couldn't dematerialize: target is gone");
1566061da546Spatrick   } else {
1567*f6aab3d8Srobert     if (Log *log = GetLog(LLDBLog::Expressions)) {
1568061da546Spatrick       LLDB_LOGF(log,
1569061da546Spatrick                 "Materializer::Dematerialize (frame_sp = %p, process_address "
1570061da546Spatrick                 "= 0x%" PRIx64 ") about to dematerialize:",
1571061da546Spatrick                 static_cast<void *>(frame_sp.get()), m_process_address);
1572061da546Spatrick       for (EntityUP &entity_up : m_materializer->m_entities)
1573061da546Spatrick         entity_up->DumpToLog(*m_map, m_process_address, log);
1574061da546Spatrick     }
1575061da546Spatrick 
1576061da546Spatrick     for (EntityUP &entity_up : m_materializer->m_entities) {
1577061da546Spatrick       entity_up->Dematerialize(frame_sp, *m_map, m_process_address, frame_top,
1578061da546Spatrick                                frame_bottom, error);
1579061da546Spatrick 
1580061da546Spatrick       if (!error.Success())
1581061da546Spatrick         break;
1582061da546Spatrick     }
1583061da546Spatrick   }
1584061da546Spatrick 
1585061da546Spatrick   Wipe();
1586061da546Spatrick }
1587061da546Spatrick 
Wipe()1588061da546Spatrick void Materializer::Dematerializer::Wipe() {
1589061da546Spatrick   if (!IsValid())
1590061da546Spatrick     return;
1591061da546Spatrick 
1592061da546Spatrick   for (EntityUP &entity_up : m_materializer->m_entities) {
1593061da546Spatrick     entity_up->Wipe(*m_map, m_process_address);
1594061da546Spatrick   }
1595061da546Spatrick 
1596061da546Spatrick   m_materializer = nullptr;
1597061da546Spatrick   m_map = nullptr;
1598061da546Spatrick   m_process_address = LLDB_INVALID_ADDRESS;
1599061da546Spatrick }
1600061da546Spatrick 
1601061da546Spatrick Materializer::PersistentVariableDelegate::~PersistentVariableDelegate() =
1602061da546Spatrick     default;
1603