1//===-- LibStdcpp.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 "LibStdcpp.h"
10#include "LibCxx.h"
11
12#include "Plugins/Language/CPlusPlus/Generic.h"
13#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
14#include "lldb/DataFormatters/FormattersHelpers.h"
15#include "lldb/DataFormatters/StringPrinter.h"
16#include "lldb/DataFormatters/VectorIterator.h"
17#include "lldb/Target/Target.h"
18#include "lldb/Utility/DataBufferHeap.h"
19#include "lldb/Utility/Endian.h"
20#include "lldb/Utility/Status.h"
21#include "lldb/Utility/Stream.h"
22#include "lldb/ValueObject/ValueObject.h"
23#include "lldb/ValueObject/ValueObjectConstResult.h"
24#include <optional>
25
26using namespace lldb;
27using namespace lldb_private;
28using namespace lldb_private::formatters;
29
30namespace {
31
32class LibstdcppMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
33 /*
34 (std::_Rb_tree_iterator<std::pair<const int, std::basic_string<char,
35 std::char_traits<char>, std::allocator<char> > > >) ibeg = {
36 (_Base_ptr) _M_node = 0x0000000100103910 {
37 (std::_Rb_tree_color) _M_color = _S_black
38 (std::_Rb_tree_node_base::_Base_ptr) _M_parent = 0x00000001001038c0
39 (std::_Rb_tree_node_base::_Base_ptr) _M_left = 0x0000000000000000
40 (std::_Rb_tree_node_base::_Base_ptr) _M_right = 0x0000000000000000
41 }
42 }
43 */
44
45public:
46 explicit LibstdcppMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
47
48 llvm::Expected<uint32_t> CalculateNumChildren() override;
49
50 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
51
52 lldb::ChildCacheState Update() override;
53
54 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
55
56private:
57 ExecutionContextRef m_exe_ctx_ref;
58 lldb::addr_t m_pair_address = 0;
59 CompilerType m_pair_type;
60 lldb::ValueObjectSP m_pair_sp;
61};
62
63class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
64public:
65 explicit LibStdcppSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
66
67 llvm::Expected<uint32_t> CalculateNumChildren() override;
68
69 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
70
71 lldb::ChildCacheState Update() override;
72
73 llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
74
75private:
76
77 // The lifetime of a ValueObject and all its derivative ValueObjects
78 // (children, clones, etc.) is managed by a ClusterManager. These
79 // objects are only destroyed when every shared pointer to any of them
80 // is destroyed, so we must not store a shared pointer to any ValueObject
81 // derived from our backend ValueObject (since we're in the same cluster).
82 ValueObject *m_ptr_obj = nullptr; // Underlying pointer (held, not owned)
83};
84
85} // end of anonymous namespace
86
87LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd(
88 lldb::ValueObjectSP valobj_sp)
89 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type(),
90 m_pair_sp() {
91 if (valobj_sp)
92 Update();
93}
94
95lldb::ChildCacheState LibstdcppMapIteratorSyntheticFrontEnd::Update() {
96 ValueObjectSP valobj_sp = m_backend.GetSP();
97 if (!valobj_sp)
98 return lldb::ChildCacheState::eRefetch;
99
100 TargetSP target_sp(valobj_sp->GetTargetSP());
101
102 if (!target_sp)
103 return lldb::ChildCacheState::eRefetch;
104
105 bool is_64bit = (target_sp->GetArchitecture().GetAddressByteSize() == 8);
106
107 if (!valobj_sp)
108 return lldb::ChildCacheState::eRefetch;
109 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
110
111 ValueObjectSP _M_node_sp(valobj_sp->GetChildMemberWithName(name: "_M_node"));
112 if (!_M_node_sp)
113 return lldb::ChildCacheState::eRefetch;
114
115 m_pair_address = _M_node_sp->GetValueAsUnsigned(fail_value: 0);
116 if (m_pair_address == 0)
117 return lldb::ChildCacheState::eRefetch;
118
119 m_pair_address += (is_64bit ? 32 : 16);
120
121 CompilerType my_type(valobj_sp->GetCompilerType());
122 if (my_type.GetNumTemplateArguments() >= 1) {
123 CompilerType pair_type = my_type.GetTypeTemplateArgument(idx: 0);
124 if (!pair_type)
125 return lldb::ChildCacheState::eRefetch;
126 m_pair_type = pair_type;
127 } else
128 return lldb::ChildCacheState::eRefetch;
129
130 return lldb::ChildCacheState::eReuse;
131}
132
133llvm::Expected<uint32_t>
134LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
135 return 2;
136}
137
138lldb::ValueObjectSP
139LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
140 if (m_pair_address != 0 && m_pair_type) {
141 if (!m_pair_sp)
142 m_pair_sp = CreateValueObjectFromAddress(name: "pair", address: m_pair_address,
143 exe_ctx: m_exe_ctx_ref, type: m_pair_type);
144 if (m_pair_sp)
145 return m_pair_sp->GetChildAtIndex(idx);
146 }
147 return lldb::ValueObjectSP();
148}
149
150llvm::Expected<size_t>
151LibstdcppMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName(
152 ConstString name) {
153 if (name == "first")
154 return 0;
155 if (name == "second")
156 return 1;
157 return llvm::createStringError(Fmt: "Type has no child named '%s'",
158 Vals: name.AsCString());
159}
160
161SyntheticChildrenFrontEnd *
162lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator(
163 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
164 return (valobj_sp ? new LibstdcppMapIteratorSyntheticFrontEnd(valobj_sp)
165 : nullptr);
166}
167
168/*
169 (lldb) fr var ibeg --ptr-depth 1
170 (__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >)
171 ibeg = {
172 _M_current = 0x00000001001037a0 {
173 *_M_current = 1
174 }
175 }
176 */
177
178SyntheticChildrenFrontEnd *
179lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator(
180 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
181 return (valobj_sp ? new VectorIteratorSyntheticFrontEnd(
182 valobj_sp, {ConstString("_M_current")})
183 : nullptr);
184}
185
186lldb_private::formatters::VectorIteratorSyntheticFrontEnd::
187 VectorIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp,
188 llvm::ArrayRef<ConstString> item_names)
189 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(),
190 m_item_names(item_names), m_item_sp() {
191 if (valobj_sp)
192 Update();
193}
194
195lldb::ChildCacheState VectorIteratorSyntheticFrontEnd::Update() {
196 m_item_sp.reset();
197
198 ValueObjectSP valobj_sp = m_backend.GetSP();
199 if (!valobj_sp)
200 return lldb::ChildCacheState::eRefetch;
201
202 if (!valobj_sp)
203 return lldb::ChildCacheState::eRefetch;
204
205 ValueObjectSP item_ptr =
206 formatters::GetChildMemberWithName(obj&: *valobj_sp, alternative_names: m_item_names);
207 if (!item_ptr)
208 return lldb::ChildCacheState::eRefetch;
209 if (item_ptr->GetValueAsUnsigned(fail_value: 0) == 0)
210 return lldb::ChildCacheState::eRefetch;
211 Status err;
212 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
213 m_item_sp = CreateValueObjectFromAddress(
214 name: "item", address: item_ptr->GetValueAsUnsigned(fail_value: 0), exe_ctx: m_exe_ctx_ref,
215 type: item_ptr->GetCompilerType().GetPointeeType());
216 if (err.Fail())
217 m_item_sp.reset();
218 return lldb::ChildCacheState::eRefetch;
219}
220
221llvm::Expected<uint32_t>
222VectorIteratorSyntheticFrontEnd::CalculateNumChildren() {
223 return 1;
224}
225
226lldb::ValueObjectSP
227VectorIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
228 if (idx == 0)
229 return m_item_sp;
230 return lldb::ValueObjectSP();
231}
232
233llvm::Expected<size_t>
234VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
235 if (name == "item")
236 return 0;
237 return llvm::createStringError(Fmt: "Type has no child named '%s'",
238 Vals: name.AsCString());
239}
240
241bool lldb_private::formatters::LibStdcppStringSummaryProvider(
242 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
243 ValueObjectSP ptr = valobj.GetChildAtNamePath(names: {"_M_dataplus", "_M_p"});
244 if (!ptr)
245 return false;
246
247 stream << ptr->GetSummaryAsCString();
248 return true;
249}
250
251LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd(
252 lldb::ValueObjectSP valobj_sp)
253 : SyntheticChildrenFrontEnd(*valobj_sp) {
254 if (valobj_sp)
255 Update();
256}
257
258llvm::Expected<uint32_t>
259LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() {
260 return 1;
261}
262
263lldb::ValueObjectSP
264LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
265 if (!m_ptr_obj)
266 return nullptr;
267
268 if (idx == 0)
269 return m_ptr_obj->GetSP();
270
271 if (idx == 1) {
272 ValueObjectSP valobj_sp = m_backend.GetSP();
273 if (!valobj_sp)
274 return nullptr;
275
276 Status status;
277 ValueObjectSP value_sp = m_ptr_obj->Dereference(error&: status);
278 if (status.Success())
279 return value_sp;
280 }
281 return lldb::ValueObjectSP();
282}
283
284lldb::ChildCacheState LibStdcppSharedPtrSyntheticFrontEnd::Update() {
285 auto backend = m_backend.GetSP();
286 if (!backend)
287 return lldb::ChildCacheState::eRefetch;
288
289 auto valobj_sp = backend->GetNonSyntheticValue();
290 if (!valobj_sp)
291 return lldb::ChildCacheState::eRefetch;
292
293 auto ptr_obj_sp = valobj_sp->GetChildMemberWithName(name: "_M_ptr");
294 if (!ptr_obj_sp)
295 return lldb::ChildCacheState::eRefetch;
296
297 auto cast_ptr_sp = GetDesugaredSmartPointerValue(ptr&: *ptr_obj_sp, container&: *valobj_sp);
298 if (!cast_ptr_sp)
299 return lldb::ChildCacheState::eRefetch;
300
301 m_ptr_obj = cast_ptr_sp->Clone(new_name: ConstString("pointer")).get();
302
303 return lldb::ChildCacheState::eRefetch;
304}
305
306llvm::Expected<size_t>
307LibStdcppSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
308 if (name == "pointer")
309 return 0;
310
311 if (name == "object" || name == "$$dereference$$")
312 return 1;
313
314 return llvm::createStringError(Fmt: "Type has no child named '%s'",
315 Vals: name.AsCString());
316}
317
318SyntheticChildrenFrontEnd *
319lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator(
320 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
321 return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp)
322 : nullptr);
323}
324
325bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider(
326 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
327 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
328 if (!valobj_sp)
329 return false;
330
331 ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName(name: "_M_ptr"));
332 if (!ptr_sp)
333 return false;
334
335 DumpCxxSmartPtrPointerSummary(stream, ptr&: *ptr_sp, options);
336
337 ValueObjectSP pi_sp = valobj_sp->GetChildAtNamePath(names: {"_M_refcount", "_M_pi"});
338 if (!pi_sp)
339 return false;
340
341 bool success;
342 uint64_t pi_addr = pi_sp->GetValueAsUnsigned(fail_value: 0, success: &success);
343 // Empty control field. We're done.
344 if (!success || pi_addr == 0)
345 return true;
346
347 int64_t shared_count = 0;
348 if (auto count_sp = pi_sp->GetChildMemberWithName(name: "_M_use_count")) {
349 bool success;
350 shared_count = count_sp->GetValueAsSigned(fail_value: 0, success: &success);
351 if (!success)
352 return false;
353
354 stream.Printf(format: " strong=%" PRId64, shared_count);
355 }
356
357 // _M_weak_count is the number of weak references + (_M_use_count != 0).
358 if (auto weak_count_sp = pi_sp->GetChildMemberWithName(name: "_M_weak_count")) {
359 bool success;
360 int64_t count = weak_count_sp->GetValueAsUnsigned(fail_value: 0, success: &success);
361 if (!success)
362 return false;
363
364 stream.Printf(format: " weak=%" PRId64, count - (shared_count != 0));
365 }
366
367 return true;
368}
369

source code of lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp