xref: /freebsd/contrib/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
1 //===-- LibCxxVector.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 "LibCxx.h"
10 
11 #include "lldb/DataFormatters/FormattersHelpers.h"
12 #include "lldb/Utility/ConstString.h"
13 #include "lldb/ValueObject/ValueObject.h"
14 #include "lldb/lldb-enumerations.h"
15 #include "lldb/lldb-forward.h"
16 #include <optional>
17 
18 using namespace lldb;
19 using namespace lldb_private;
20 using namespace lldb_private::formatters;
21 
22 namespace lldb_private {
23 namespace formatters {
24 class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
25 public:
26   LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
27 
28   ~LibcxxStdVectorSyntheticFrontEnd() override;
29 
30   llvm::Expected<uint32_t> CalculateNumChildren() override;
31 
32   lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
33 
34   lldb::ChildCacheState Update() override;
35 
36   llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
37 
38 private:
39   ValueObject *m_start = nullptr;
40   ValueObject *m_finish = nullptr;
41   CompilerType m_element_type;
42   uint32_t m_element_size = 0;
43 };
44 
45 class LibcxxVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
46 public:
47   LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
48 
49   llvm::Expected<uint32_t> CalculateNumChildren() override;
50 
51   lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
52 
53   lldb::ChildCacheState Update() override;
54 
55   llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
56 
57 private:
58   CompilerType m_bool_type;
59   ExecutionContextRef m_exe_ctx_ref;
60   uint64_t m_count = 0;
61   lldb::addr_t m_base_data_address = 0;
62   std::map<size_t, lldb::ValueObjectSP> m_children;
63 };
64 
65 } // namespace formatters
66 } // namespace lldb_private
67 
68 lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)69     LibcxxStdVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
70     : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() {
71   if (valobj_sp)
72     Update();
73 }
74 
75 lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
~LibcxxStdVectorSyntheticFrontEnd()76     ~LibcxxStdVectorSyntheticFrontEnd() {
77   // these need to stay around because they are child objects who will follow
78   // their parent's life cycle
79   // delete m_start;
80   // delete m_finish;
81 }
82 
83 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()84     LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren() {
85   if (!m_start || !m_finish)
86     return llvm::createStringError(
87         "Failed to determine start/end of vector data.");
88 
89   uint64_t start_val = m_start->GetValueAsUnsigned(0);
90   uint64_t finish_val = m_finish->GetValueAsUnsigned(0);
91 
92   // A default-initialized empty vector.
93   if (start_val == 0 && finish_val == 0)
94     return 0;
95 
96   if (start_val == 0)
97     return llvm::createStringError("Invalid value for start of vector.");
98 
99   if (finish_val == 0)
100     return llvm::createStringError("Invalid value for end of vector.");
101 
102   if (start_val > finish_val)
103     return llvm::createStringError(
104         "Start of vector data begins after end pointer.");
105 
106   size_t num_children = (finish_val - start_val);
107   if (num_children % m_element_size)
108     return llvm::createStringError("Size not multiple of element size.");
109 
110   return num_children / m_element_size;
111 }
112 
113 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)114 lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex(
115     uint32_t idx) {
116   if (!m_start || !m_finish)
117     return lldb::ValueObjectSP();
118 
119   uint64_t offset = idx * m_element_size;
120   offset = offset + m_start->GetValueAsUnsigned(0);
121   StreamString name;
122   name.Printf("[%" PRIu64 "]", (uint64_t)idx);
123   return CreateValueObjectFromAddress(name.GetString(), offset,
124                                       m_backend.GetExecutionContextRef(),
125                                       m_element_type);
126 }
127 
GetDataPointer(ValueObject & root)128 static ValueObjectSP GetDataPointer(ValueObject &root) {
129   auto [cap_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
130       root, /*anon_struct_idx=*/2, "__cap_", "__end_cap_");
131   if (!cap_sp)
132     return nullptr;
133 
134   if (is_compressed_pair)
135     return GetFirstValueOfLibCXXCompressedPair(*cap_sp);
136 
137   return cap_sp;
138 }
139 
140 lldb::ChildCacheState
Update()141 lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() {
142   m_start = m_finish = nullptr;
143   ValueObjectSP data_sp(GetDataPointer(m_backend));
144 
145   if (!data_sp)
146     return lldb::ChildCacheState::eRefetch;
147 
148   m_element_type = data_sp->GetCompilerType().GetPointeeType();
149   llvm::Expected<uint64_t> size_or_err = m_element_type.GetByteSize(nullptr);
150   if (!size_or_err)
151     LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), size_or_err.takeError(),
152                     "{0}");
153   else {
154     m_element_size = *size_or_err;
155 
156     if (m_element_size > 0) {
157       // store raw pointers or end up with a circular dependency
158       m_start = m_backend.GetChildMemberWithName("__begin_").get();
159       m_finish = m_backend.GetChildMemberWithName("__end_").get();
160     }
161   }
162   return lldb::ChildCacheState::eRefetch;
163 }
164 
165 llvm::Expected<size_t>
166 lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)167     GetIndexOfChildWithName(ConstString name) {
168   if (!m_start || !m_finish)
169     return llvm::createStringError("Type has no child named '%s'",
170                                    name.AsCString());
171   auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
172   if (!optional_idx) {
173     return llvm::createStringError("Type has no child named '%s'",
174                                    name.AsCString());
175   }
176   return *optional_idx;
177 }
178 
179 lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::
LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)180     LibcxxVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
181     : SyntheticChildrenFrontEnd(*valobj_sp), m_bool_type(), m_exe_ctx_ref(),
182       m_children() {
183   if (valobj_sp) {
184     Update();
185     m_bool_type =
186         valobj_sp->GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeBool);
187   }
188 }
189 
190 llvm::Expected<uint32_t> lldb_private::formatters::
CalculateNumChildren()191     LibcxxVectorBoolSyntheticFrontEnd::CalculateNumChildren() {
192   return m_count;
193 }
194 
195 lldb::ValueObjectSP
GetChildAtIndex(uint32_t idx)196 lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex(
197     uint32_t idx) {
198   auto iter = m_children.find(idx), end = m_children.end();
199   if (iter != end)
200     return iter->second;
201   if (idx >= m_count)
202     return {};
203   if (m_base_data_address == 0 || m_count == 0)
204     return {};
205   if (!m_bool_type)
206     return {};
207   size_t byte_idx = (idx >> 3); // divide by 8 to get byte index
208   size_t bit_index = (idx & 7); // efficient idx % 8 for bit index
209   lldb::addr_t byte_location = m_base_data_address + byte_idx;
210   ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
211   if (!process_sp)
212     return {};
213   uint8_t byte = 0;
214   uint8_t mask = 0;
215   Status err;
216   size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err);
217   if (err.Fail() || bytes_read == 0)
218     return {};
219   mask = 1 << bit_index;
220   bool bit_set = ((byte & mask) != 0);
221   std::optional<uint64_t> size =
222       llvm::expectedToOptional(m_bool_type.GetByteSize(nullptr));
223   if (!size)
224     return {};
225   WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
226   if (bit_set && buffer_sp && buffer_sp->GetBytes()) {
227     // regardless of endianness, anything non-zero is true
228     *(buffer_sp->GetBytes()) = 1;
229   }
230   StreamString name;
231   name.Printf("[%" PRIu64 "]", (uint64_t)idx);
232   ValueObjectSP retval_sp(CreateValueObjectFromData(
233       name.GetString(),
234       DataExtractor(buffer_sp, process_sp->GetByteOrder(),
235                     process_sp->GetAddressByteSize()),
236       m_exe_ctx_ref, m_bool_type));
237   if (retval_sp)
238     m_children[idx] = retval_sp;
239   return retval_sp;
240 }
241 
242 lldb::ChildCacheState
Update()243 lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::Update() {
244   m_children.clear();
245   ValueObjectSP valobj_sp = m_backend.GetSP();
246   if (!valobj_sp)
247     return lldb::ChildCacheState::eRefetch;
248   m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
249   ValueObjectSP size_sp(valobj_sp->GetChildMemberWithName("__size_"));
250   if (!size_sp)
251     return lldb::ChildCacheState::eRefetch;
252   m_count = size_sp->GetValueAsUnsigned(0);
253   if (!m_count)
254     return lldb::ChildCacheState::eReuse;
255   ValueObjectSP begin_sp(valobj_sp->GetChildMemberWithName("__begin_"));
256   if (!begin_sp) {
257     m_count = 0;
258     return lldb::ChildCacheState::eRefetch;
259   }
260   m_base_data_address = begin_sp->GetValueAsUnsigned(0);
261   if (!m_base_data_address) {
262     m_count = 0;
263     return lldb::ChildCacheState::eRefetch;
264   }
265   return lldb::ChildCacheState::eRefetch;
266 }
267 
268 llvm::Expected<size_t>
269 lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name)270     GetIndexOfChildWithName(ConstString name) {
271   if (!m_count || !m_base_data_address)
272     return llvm::createStringError("Type has no child named '%s'",
273                                    name.AsCString());
274   auto optional_idx = ExtractIndexFromString(name.AsCString());
275   if (!optional_idx) {
276     return llvm::createStringError("Type has no child named '%s'",
277                                    name.AsCString());
278   }
279   uint32_t idx = *optional_idx;
280   if (idx >= CalculateNumChildrenIgnoringErrors())
281     return llvm::createStringError("Type has no child named '%s'",
282                                    name.AsCString());
283   return idx;
284 }
285 
286 lldb_private::SyntheticChildrenFrontEnd *
LibcxxStdVectorSyntheticFrontEndCreator(CXXSyntheticChildren *,lldb::ValueObjectSP valobj_sp)287 lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator(
288     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
289   if (!valobj_sp)
290     return nullptr;
291   CompilerType type = valobj_sp->GetCompilerType();
292   if (!type.IsValid() || type.GetNumTemplateArguments() == 0)
293     return nullptr;
294   CompilerType arg_type = type.GetTypeTemplateArgument(0);
295   if (arg_type.GetTypeName() == "bool")
296     return new LibcxxVectorBoolSyntheticFrontEnd(valobj_sp);
297   return new LibcxxStdVectorSyntheticFrontEnd(valobj_sp);
298 }
299