1 | //===-- LibCxx.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/Core/Debugger.h" |
12 | #include "lldb/Core/FormatEntity.h" |
13 | #include "lldb/Core/ValueObject.h" |
14 | #include "lldb/Core/ValueObjectConstResult.h" |
15 | #include "lldb/DataFormatters/FormattersHelpers.h" |
16 | #include "lldb/DataFormatters/StringPrinter.h" |
17 | #include "lldb/DataFormatters/TypeSummary.h" |
18 | #include "lldb/DataFormatters/VectorIterator.h" |
19 | #include "lldb/Target/SectionLoadList.h" |
20 | #include "lldb/Target/Target.h" |
21 | #include "lldb/Utility/ConstString.h" |
22 | #include "lldb/Utility/DataBufferHeap.h" |
23 | #include "lldb/Utility/Endian.h" |
24 | #include "lldb/Utility/Status.h" |
25 | #include "lldb/Utility/Stream.h" |
26 | |
27 | #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" |
28 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
29 | #include "lldb/lldb-enumerations.h" |
30 | #include <optional> |
31 | #include <tuple> |
32 | |
33 | using namespace lldb; |
34 | using namespace lldb_private; |
35 | using namespace lldb_private::formatters; |
36 | |
37 | lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName( |
38 | ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) { |
39 | for (ConstString name : alternative_names) { |
40 | lldb::ValueObjectSP child_sp = obj.GetChildMemberWithName(name); |
41 | |
42 | if (child_sp) |
43 | return child_sp; |
44 | } |
45 | return {}; |
46 | } |
47 | |
48 | lldb::ValueObjectSP |
49 | lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair( |
50 | ValueObject &pair) { |
51 | ValueObjectSP value; |
52 | ValueObjectSP first_child = pair.GetChildAtIndex(idx: 0); |
53 | if (first_child) |
54 | value = first_child->GetChildMemberWithName(name: "__value_" ); |
55 | if (!value) { |
56 | // pre-r300140 member name |
57 | value = pair.GetChildMemberWithName(name: "__first_" ); |
58 | } |
59 | return value; |
60 | } |
61 | |
62 | lldb::ValueObjectSP |
63 | lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair( |
64 | ValueObject &pair) { |
65 | ValueObjectSP value; |
66 | if (pair.GetNumChildrenIgnoringErrors() > 1) { |
67 | ValueObjectSP second_child = pair.GetChildAtIndex(idx: 1); |
68 | if (second_child) { |
69 | value = second_child->GetChildMemberWithName(name: "__value_" ); |
70 | } |
71 | } |
72 | if (!value) { |
73 | // pre-r300140 member name |
74 | value = pair.GetChildMemberWithName(name: "__second_" ); |
75 | } |
76 | return value; |
77 | } |
78 | |
79 | bool lldb_private::formatters::LibcxxFunctionSummaryProvider( |
80 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
81 | |
82 | ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); |
83 | |
84 | if (!valobj_sp) |
85 | return false; |
86 | |
87 | ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef()); |
88 | Process *process = exe_ctx.GetProcessPtr(); |
89 | |
90 | if (process == nullptr) |
91 | return false; |
92 | |
93 | CPPLanguageRuntime *cpp_runtime = CPPLanguageRuntime::Get(process&: *process); |
94 | |
95 | if (!cpp_runtime) |
96 | return false; |
97 | |
98 | CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info = |
99 | cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp); |
100 | |
101 | switch (callable_info.callable_case) { |
102 | case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid: |
103 | stream.Printf(format: " __f_ = %" PRIu64, callable_info.member_f_pointer_value); |
104 | return false; |
105 | break; |
106 | case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda: |
107 | stream.Printf( |
108 | format: " Lambda in File %s at Line %u" , |
109 | callable_info.callable_line_entry.GetFile().GetFilename().GetCString(), |
110 | callable_info.callable_line_entry.line); |
111 | break; |
112 | case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject: |
113 | stream.Printf( |
114 | format: " Function in File %s at Line %u" , |
115 | callable_info.callable_line_entry.GetFile().GetFilename().GetCString(), |
116 | callable_info.callable_line_entry.line); |
117 | break; |
118 | case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction: |
119 | stream.Printf(format: " Function = %s " , |
120 | callable_info.callable_symbol.GetName().GetCString()); |
121 | break; |
122 | } |
123 | |
124 | return true; |
125 | } |
126 | |
127 | bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider( |
128 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
129 | ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); |
130 | if (!valobj_sp) |
131 | return false; |
132 | ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName(name: "__ptr_" )); |
133 | ValueObjectSP count_sp( |
134 | valobj_sp->GetChildAtNamePath(names: {"__cntrl_" , "__shared_owners_" })); |
135 | ValueObjectSP weakcount_sp( |
136 | valobj_sp->GetChildAtNamePath(names: {"__cntrl_" , "__shared_weak_owners_" })); |
137 | |
138 | if (!ptr_sp) |
139 | return false; |
140 | |
141 | if (ptr_sp->GetValueAsUnsigned(fail_value: 0) == 0) { |
142 | stream.Printf(format: "nullptr" ); |
143 | return true; |
144 | } else { |
145 | bool print_pointee = false; |
146 | Status error; |
147 | ValueObjectSP pointee_sp = ptr_sp->Dereference(error); |
148 | if (pointee_sp && error.Success()) { |
149 | if (pointee_sp->DumpPrintableRepresentation( |
150 | s&: stream, val_obj_display: ValueObject::eValueObjectRepresentationStyleSummary, |
151 | custom_format: lldb::eFormatInvalid, |
152 | special: ValueObject::PrintableRepresentationSpecialCases::eDisable, |
153 | do_dump_error: false)) |
154 | print_pointee = true; |
155 | } |
156 | if (!print_pointee) |
157 | stream.Printf(format: "ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(fail_value: 0)); |
158 | } |
159 | |
160 | if (count_sp) |
161 | stream.Printf(format: " strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(fail_value: 0)); |
162 | |
163 | if (weakcount_sp) |
164 | stream.Printf(format: " weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(fail_value: 0)); |
165 | |
166 | return true; |
167 | } |
168 | |
169 | bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider( |
170 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
171 | ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); |
172 | if (!valobj_sp) |
173 | return false; |
174 | |
175 | ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName(name: "__ptr_" )); |
176 | if (!ptr_sp) |
177 | return false; |
178 | |
179 | ptr_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *ptr_sp); |
180 | if (!ptr_sp) |
181 | return false; |
182 | |
183 | if (ptr_sp->GetValueAsUnsigned(fail_value: 0) == 0) { |
184 | stream.Printf(format: "nullptr" ); |
185 | return true; |
186 | } else { |
187 | bool print_pointee = false; |
188 | Status error; |
189 | ValueObjectSP pointee_sp = ptr_sp->Dereference(error); |
190 | if (pointee_sp && error.Success()) { |
191 | if (pointee_sp->DumpPrintableRepresentation( |
192 | s&: stream, val_obj_display: ValueObject::eValueObjectRepresentationStyleSummary, |
193 | custom_format: lldb::eFormatInvalid, |
194 | special: ValueObject::PrintableRepresentationSpecialCases::eDisable, |
195 | do_dump_error: false)) |
196 | print_pointee = true; |
197 | } |
198 | if (!print_pointee) |
199 | stream.Printf(format: "ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(fail_value: 0)); |
200 | } |
201 | |
202 | return true; |
203 | } |
204 | |
205 | /* |
206 | (lldb) fr var ibeg --raw --ptr-depth 1 |
207 | (std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int, |
208 | std::__1::basic_string<char, std::__1::char_traits<char>, |
209 | std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::pair<int, |
210 | std::__1::basic_string<char, std::__1::char_traits<char>, |
211 | std::__1::allocator<char> > >, void *> *, long> >) ibeg = { |
212 | __i_ = { |
213 | __ptr_ = 0x0000000100103870 { |
214 | std::__1::__tree_node_base<void *> = { |
215 | std::__1::__tree_end_node<std::__1::__tree_node_base<void *> *> = { |
216 | __left_ = 0x0000000000000000 |
217 | } |
218 | __right_ = 0x0000000000000000 |
219 | __parent_ = 0x00000001001038b0 |
220 | __is_black_ = true |
221 | } |
222 | __value_ = { |
223 | first = 0 |
224 | second = { std::string } |
225 | */ |
226 | |
227 | lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: |
228 | LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
229 | : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() { |
230 | if (valobj_sp) |
231 | Update(); |
232 | } |
233 | |
234 | lldb::ChildCacheState |
235 | lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() { |
236 | m_pair_sp.reset(); |
237 | m_pair_ptr = nullptr; |
238 | |
239 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
240 | if (!valobj_sp) |
241 | return lldb::ChildCacheState::eRefetch; |
242 | |
243 | TargetSP target_sp(valobj_sp->GetTargetSP()); |
244 | |
245 | if (!target_sp) |
246 | return lldb::ChildCacheState::eRefetch; |
247 | |
248 | if (!valobj_sp) |
249 | return lldb::ChildCacheState::eRefetch; |
250 | |
251 | // this must be a ValueObject* because it is a child of the ValueObject we |
252 | // are producing children for it if were a ValueObjectSP, we would end up |
253 | // with a loop (iterator -> synthetic -> child -> parent == iterator) and |
254 | // that would in turn leak memory by never allowing the ValueObjects to die |
255 | // and free their memory |
256 | m_pair_ptr = valobj_sp |
257 | ->GetValueForExpressionPath( |
258 | expression: ".__i_.__ptr_->__value_" , reason_to_stop: nullptr, final_value_type: nullptr, |
259 | options: ValueObject::GetValueForExpressionPathOptions() |
260 | .DontCheckDotVsArrowSyntax() |
261 | .SetSyntheticChildrenTraversal( |
262 | ValueObject::GetValueForExpressionPathOptions:: |
263 | SyntheticChildrenTraversal::None), |
264 | final_task_on_target: nullptr) |
265 | .get(); |
266 | |
267 | if (!m_pair_ptr) { |
268 | m_pair_ptr = valobj_sp |
269 | ->GetValueForExpressionPath( |
270 | expression: ".__i_.__ptr_" , reason_to_stop: nullptr, final_value_type: nullptr, |
271 | options: ValueObject::GetValueForExpressionPathOptions() |
272 | .DontCheckDotVsArrowSyntax() |
273 | .SetSyntheticChildrenTraversal( |
274 | ValueObject::GetValueForExpressionPathOptions:: |
275 | SyntheticChildrenTraversal::None), |
276 | final_task_on_target: nullptr) |
277 | .get(); |
278 | if (m_pair_ptr) { |
279 | auto __i_(valobj_sp->GetChildMemberWithName(name: "__i_" )); |
280 | if (!__i_) { |
281 | m_pair_ptr = nullptr; |
282 | return lldb::ChildCacheState::eRefetch; |
283 | } |
284 | CompilerType pair_type( |
285 | __i_->GetCompilerType().GetTypeTemplateArgument(idx: 0)); |
286 | std::string name; |
287 | uint64_t bit_offset_ptr; |
288 | uint32_t bitfield_bit_size_ptr; |
289 | bool is_bitfield_ptr; |
290 | pair_type = pair_type.GetFieldAtIndex( |
291 | idx: 0, name, bit_offset_ptr: &bit_offset_ptr, bitfield_bit_size_ptr: &bitfield_bit_size_ptr, is_bitfield_ptr: &is_bitfield_ptr); |
292 | if (!pair_type) { |
293 | m_pair_ptr = nullptr; |
294 | return lldb::ChildCacheState::eRefetch; |
295 | } |
296 | |
297 | auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS)); |
298 | m_pair_ptr = nullptr; |
299 | if (addr && addr != LLDB_INVALID_ADDRESS) { |
300 | auto ts = pair_type.GetTypeSystem(); |
301 | auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>(); |
302 | if (!ast_ctx) |
303 | return lldb::ChildCacheState::eRefetch; |
304 | |
305 | // Mimick layout of std::__tree_iterator::__ptr_ and read it in |
306 | // from process memory. |
307 | // |
308 | // The following shows the contiguous block of memory: |
309 | // |
310 | // +-----------------------------+ class __tree_end_node |
311 | // __ptr_ | pointer __left_; | |
312 | // +-----------------------------+ class __tree_node_base |
313 | // | pointer __right_; | |
314 | // | __parent_pointer __parent_; | |
315 | // | bool __is_black_; | |
316 | // +-----------------------------+ class __tree_node |
317 | // | __node_value_type __value_; | <<< our key/value pair |
318 | // +-----------------------------+ |
319 | // |
320 | CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( |
321 | type_name: llvm::StringRef(), |
322 | type_fields: {{"ptr0" , |
323 | ast_ctx->GetBasicType(type: lldb::eBasicTypeVoid).GetPointerType()}, |
324 | {"ptr1" , |
325 | ast_ctx->GetBasicType(type: lldb::eBasicTypeVoid).GetPointerType()}, |
326 | {"ptr2" , |
327 | ast_ctx->GetBasicType(type: lldb::eBasicTypeVoid).GetPointerType()}, |
328 | {"cw" , ast_ctx->GetBasicType(type: lldb::eBasicTypeBool)}, |
329 | {"payload" , pair_type}}); |
330 | std::optional<uint64_t> size = tree_node_type.GetByteSize(exe_scope: nullptr); |
331 | if (!size) |
332 | return lldb::ChildCacheState::eRefetch; |
333 | WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0)); |
334 | ProcessSP process_sp(target_sp->GetProcessSP()); |
335 | Status error; |
336 | process_sp->ReadMemory(vm_addr: addr, buf: buffer_sp->GetBytes(), |
337 | size: buffer_sp->GetByteSize(), error); |
338 | if (error.Fail()) |
339 | return lldb::ChildCacheState::eRefetch; |
340 | DataExtractor (buffer_sp, process_sp->GetByteOrder(), |
341 | process_sp->GetAddressByteSize()); |
342 | auto pair_sp = CreateValueObjectFromData( |
343 | name: "pair" , data: extractor, exe_ctx: valobj_sp->GetExecutionContextRef(), |
344 | type: tree_node_type); |
345 | if (pair_sp) |
346 | m_pair_sp = pair_sp->GetChildAtIndex(idx: 4); |
347 | } |
348 | } |
349 | } |
350 | |
351 | return lldb::ChildCacheState::eRefetch; |
352 | } |
353 | |
354 | llvm::Expected<uint32_t> lldb_private::formatters:: |
355 | LibCxxMapIteratorSyntheticFrontEnd::CalculateNumChildren() { |
356 | return 2; |
357 | } |
358 | |
359 | lldb::ValueObjectSP |
360 | lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex( |
361 | uint32_t idx) { |
362 | if (m_pair_ptr) |
363 | return m_pair_ptr->GetChildAtIndex(idx); |
364 | if (m_pair_sp) |
365 | return m_pair_sp->GetChildAtIndex(idx); |
366 | return lldb::ValueObjectSP(); |
367 | } |
368 | |
369 | bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: |
370 | MightHaveChildren() { |
371 | return true; |
372 | } |
373 | |
374 | size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: |
375 | GetIndexOfChildWithName(ConstString name) { |
376 | if (name == "first" ) |
377 | return 0; |
378 | if (name == "second" ) |
379 | return 1; |
380 | return UINT32_MAX; |
381 | } |
382 | |
383 | lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd:: |
384 | ~LibCxxMapIteratorSyntheticFrontEnd() { |
385 | // this will be deleted when its parent dies (since it's a child object) |
386 | // delete m_pair_ptr; |
387 | } |
388 | |
389 | SyntheticChildrenFrontEnd * |
390 | lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator( |
391 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
392 | return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp) |
393 | : nullptr); |
394 | } |
395 | |
396 | lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: |
397 | LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
398 | : SyntheticChildrenFrontEnd(*valobj_sp) { |
399 | if (valobj_sp) |
400 | Update(); |
401 | } |
402 | |
403 | lldb::ChildCacheState lldb_private::formatters:: |
404 | LibCxxUnorderedMapIteratorSyntheticFrontEnd::Update() { |
405 | m_pair_sp.reset(); |
406 | m_iter_ptr = nullptr; |
407 | |
408 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
409 | if (!valobj_sp) |
410 | return lldb::ChildCacheState::eRefetch; |
411 | |
412 | TargetSP target_sp(valobj_sp->GetTargetSP()); |
413 | |
414 | if (!target_sp) |
415 | return lldb::ChildCacheState::eRefetch; |
416 | |
417 | if (!valobj_sp) |
418 | return lldb::ChildCacheState::eRefetch; |
419 | |
420 | auto exprPathOptions = ValueObject::GetValueForExpressionPathOptions() |
421 | .DontCheckDotVsArrowSyntax() |
422 | .SetSyntheticChildrenTraversal( |
423 | ValueObject::GetValueForExpressionPathOptions:: |
424 | SyntheticChildrenTraversal::None); |
425 | |
426 | // This must be a ValueObject* because it is a child of the ValueObject we |
427 | // are producing children for it if were a ValueObjectSP, we would end up |
428 | // with a loop (iterator -> synthetic -> child -> parent == iterator) and |
429 | // that would in turn leak memory by never allowing the ValueObjects to die |
430 | // and free their memory. |
431 | m_iter_ptr = |
432 | valobj_sp |
433 | ->GetValueForExpressionPath(expression: ".__i_.__node_" , reason_to_stop: nullptr, final_value_type: nullptr, |
434 | options: exprPathOptions, final_task_on_target: nullptr) |
435 | .get(); |
436 | |
437 | if (m_iter_ptr) { |
438 | auto iter_child(valobj_sp->GetChildMemberWithName(name: "__i_" )); |
439 | if (!iter_child) { |
440 | m_iter_ptr = nullptr; |
441 | return lldb::ChildCacheState::eRefetch; |
442 | } |
443 | |
444 | CompilerType node_type(iter_child->GetCompilerType() |
445 | .GetTypeTemplateArgument(idx: 0) |
446 | .GetPointeeType()); |
447 | |
448 | CompilerType pair_type(node_type.GetTypeTemplateArgument(idx: 0)); |
449 | |
450 | std::string name; |
451 | uint64_t bit_offset_ptr; |
452 | uint32_t bitfield_bit_size_ptr; |
453 | bool is_bitfield_ptr; |
454 | |
455 | pair_type = pair_type.GetFieldAtIndex( |
456 | idx: 0, name, bit_offset_ptr: &bit_offset_ptr, bitfield_bit_size_ptr: &bitfield_bit_size_ptr, is_bitfield_ptr: &is_bitfield_ptr); |
457 | if (!pair_type) { |
458 | m_iter_ptr = nullptr; |
459 | return lldb::ChildCacheState::eRefetch; |
460 | } |
461 | |
462 | uint64_t addr = m_iter_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); |
463 | m_iter_ptr = nullptr; |
464 | |
465 | if (addr == 0 || addr == LLDB_INVALID_ADDRESS) |
466 | return lldb::ChildCacheState::eRefetch; |
467 | |
468 | auto ts = pair_type.GetTypeSystem(); |
469 | auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>(); |
470 | if (!ast_ctx) |
471 | return lldb::ChildCacheState::eRefetch; |
472 | |
473 | // Mimick layout of std::__hash_iterator::__node_ and read it in |
474 | // from process memory. |
475 | // |
476 | // The following shows the contiguous block of memory: |
477 | // |
478 | // +-----------------------------+ class __hash_node_base |
479 | // __node_ | __next_pointer __next_; | |
480 | // +-----------------------------+ class __hash_node |
481 | // | size_t __hash_; | |
482 | // | __node_value_type __value_; | <<< our key/value pair |
483 | // +-----------------------------+ |
484 | // |
485 | CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( |
486 | type_name: llvm::StringRef(), |
487 | type_fields: {{"__next_" , |
488 | ast_ctx->GetBasicType(type: lldb::eBasicTypeVoid).GetPointerType()}, |
489 | {"__hash_" , ast_ctx->GetBasicType(type: lldb::eBasicTypeUnsignedLongLong)}, |
490 | {"__value_" , pair_type}}); |
491 | std::optional<uint64_t> size = tree_node_type.GetByteSize(exe_scope: nullptr); |
492 | if (!size) |
493 | return lldb::ChildCacheState::eRefetch; |
494 | WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0)); |
495 | ProcessSP process_sp(target_sp->GetProcessSP()); |
496 | Status error; |
497 | process_sp->ReadMemory(vm_addr: addr, buf: buffer_sp->GetBytes(), |
498 | size: buffer_sp->GetByteSize(), error); |
499 | if (error.Fail()) |
500 | return lldb::ChildCacheState::eRefetch; |
501 | DataExtractor (buffer_sp, process_sp->GetByteOrder(), |
502 | process_sp->GetAddressByteSize()); |
503 | auto pair_sp = CreateValueObjectFromData( |
504 | name: "pair" , data: extractor, exe_ctx: valobj_sp->GetExecutionContextRef(), type: tree_node_type); |
505 | if (pair_sp) |
506 | m_pair_sp = pair_sp->GetChildAtIndex(idx: 2); |
507 | } |
508 | |
509 | return lldb::ChildCacheState::eRefetch; |
510 | } |
511 | |
512 | llvm::Expected<uint32_t> lldb_private::formatters:: |
513 | LibCxxUnorderedMapIteratorSyntheticFrontEnd::CalculateNumChildren() { |
514 | return 2; |
515 | } |
516 | |
517 | lldb::ValueObjectSP lldb_private::formatters:: |
518 | LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { |
519 | if (m_pair_sp) |
520 | return m_pair_sp->GetChildAtIndex(idx); |
521 | return lldb::ValueObjectSP(); |
522 | } |
523 | |
524 | bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: |
525 | MightHaveChildren() { |
526 | return true; |
527 | } |
528 | |
529 | size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: |
530 | GetIndexOfChildWithName(ConstString name) { |
531 | if (name == "first" ) |
532 | return 0; |
533 | if (name == "second" ) |
534 | return 1; |
535 | return UINT32_MAX; |
536 | } |
537 | |
538 | SyntheticChildrenFrontEnd * |
539 | lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEndCreator( |
540 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
541 | return (valobj_sp ? new LibCxxUnorderedMapIteratorSyntheticFrontEnd(valobj_sp) |
542 | : nullptr); |
543 | } |
544 | |
545 | /* |
546 | (lldb) fr var ibeg --raw --ptr-depth 1 -T |
547 | (std::__1::__wrap_iter<int *>) ibeg = { |
548 | (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 { |
549 | (int) *__i = 1 |
550 | } |
551 | } |
552 | */ |
553 | |
554 | SyntheticChildrenFrontEnd * |
555 | lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator( |
556 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
557 | return (valobj_sp ? new VectorIteratorSyntheticFrontEnd( |
558 | valobj_sp, {ConstString("__i_" ), ConstString("__i" )}) |
559 | : nullptr); |
560 | } |
561 | |
562 | lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: |
563 | LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
564 | : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr) { |
565 | if (valobj_sp) |
566 | Update(); |
567 | } |
568 | |
569 | llvm::Expected<uint32_t> lldb_private::formatters:: |
570 | LibcxxSharedPtrSyntheticFrontEnd::CalculateNumChildren() { |
571 | return (m_cntrl ? 1 : 0); |
572 | } |
573 | |
574 | lldb::ValueObjectSP |
575 | lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex( |
576 | uint32_t idx) { |
577 | if (!m_cntrl) |
578 | return lldb::ValueObjectSP(); |
579 | |
580 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
581 | if (!valobj_sp) |
582 | return lldb::ValueObjectSP(); |
583 | |
584 | if (idx == 0) |
585 | return valobj_sp->GetChildMemberWithName(name: "__ptr_" ); |
586 | |
587 | if (idx == 1) { |
588 | if (auto ptr_sp = valobj_sp->GetChildMemberWithName(name: "__ptr_" )) { |
589 | Status status; |
590 | auto value_type_sp = |
591 | valobj_sp->GetCompilerType() |
592 | .GetTypeTemplateArgument(idx: 0).GetPointerType(); |
593 | ValueObjectSP cast_ptr_sp = ptr_sp->Cast(compiler_type: value_type_sp); |
594 | ValueObjectSP value_sp = cast_ptr_sp->Dereference(error&: status); |
595 | if (status.Success()) { |
596 | return value_sp; |
597 | } |
598 | } |
599 | } |
600 | |
601 | return lldb::ValueObjectSP(); |
602 | } |
603 | |
604 | lldb::ChildCacheState |
605 | lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() { |
606 | m_cntrl = nullptr; |
607 | |
608 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
609 | if (!valobj_sp) |
610 | return lldb::ChildCacheState::eRefetch; |
611 | |
612 | TargetSP target_sp(valobj_sp->GetTargetSP()); |
613 | if (!target_sp) |
614 | return lldb::ChildCacheState::eRefetch; |
615 | |
616 | lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName(name: "__cntrl_" )); |
617 | |
618 | m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular |
619 | // dependency |
620 | return lldb::ChildCacheState::eRefetch; |
621 | } |
622 | |
623 | bool lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: |
624 | MightHaveChildren() { |
625 | return true; |
626 | } |
627 | |
628 | size_t lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: |
629 | GetIndexOfChildWithName(ConstString name) { |
630 | if (name == "__ptr_" ) |
631 | return 0; |
632 | if (name == "$$dereference$$" ) |
633 | return 1; |
634 | return UINT32_MAX; |
635 | } |
636 | |
637 | lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: |
638 | ~LibcxxSharedPtrSyntheticFrontEnd() = default; |
639 | |
640 | SyntheticChildrenFrontEnd * |
641 | lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator( |
642 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
643 | return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp) |
644 | : nullptr); |
645 | } |
646 | |
647 | lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: |
648 | LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
649 | : SyntheticChildrenFrontEnd(*valobj_sp) { |
650 | if (valobj_sp) |
651 | Update(); |
652 | } |
653 | |
654 | lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: |
655 | ~LibcxxUniquePtrSyntheticFrontEnd() = default; |
656 | |
657 | SyntheticChildrenFrontEnd * |
658 | lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator( |
659 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
660 | return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp) |
661 | : nullptr); |
662 | } |
663 | |
664 | llvm::Expected<uint32_t> lldb_private::formatters:: |
665 | LibcxxUniquePtrSyntheticFrontEnd::CalculateNumChildren() { |
666 | if (m_value_ptr_sp) |
667 | return m_deleter_sp ? 2 : 1; |
668 | return 0; |
669 | } |
670 | |
671 | lldb::ValueObjectSP |
672 | lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex( |
673 | uint32_t idx) { |
674 | if (!m_value_ptr_sp) |
675 | return lldb::ValueObjectSP(); |
676 | |
677 | if (idx == 0) |
678 | return m_value_ptr_sp; |
679 | |
680 | if (idx == 1) |
681 | return m_deleter_sp; |
682 | |
683 | if (idx == 2) { |
684 | Status status; |
685 | auto value_sp = m_value_ptr_sp->Dereference(error&: status); |
686 | if (status.Success()) { |
687 | return value_sp; |
688 | } |
689 | } |
690 | |
691 | return lldb::ValueObjectSP(); |
692 | } |
693 | |
694 | lldb::ChildCacheState |
695 | lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() { |
696 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
697 | if (!valobj_sp) |
698 | return lldb::ChildCacheState::eRefetch; |
699 | |
700 | ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName(name: "__ptr_" )); |
701 | if (!ptr_sp) |
702 | return lldb::ChildCacheState::eRefetch; |
703 | |
704 | // Retrieve the actual pointer and the deleter, and clone them to give them |
705 | // user-friendly names. |
706 | ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *ptr_sp); |
707 | if (value_pointer_sp) |
708 | m_value_ptr_sp = value_pointer_sp->Clone(new_name: ConstString("pointer" )); |
709 | |
710 | ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(pair&: *ptr_sp); |
711 | if (deleter_sp) |
712 | m_deleter_sp = deleter_sp->Clone(new_name: ConstString("deleter" )); |
713 | |
714 | return lldb::ChildCacheState::eRefetch; |
715 | } |
716 | |
717 | bool lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: |
718 | MightHaveChildren() { |
719 | return true; |
720 | } |
721 | |
722 | size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: |
723 | GetIndexOfChildWithName(ConstString name) { |
724 | if (name == "pointer" ) |
725 | return 0; |
726 | if (name == "deleter" ) |
727 | return 1; |
728 | if (name == "$$dereference$$" ) |
729 | return 2; |
730 | return UINT32_MAX; |
731 | } |
732 | |
733 | bool lldb_private::formatters::LibcxxContainerSummaryProvider( |
734 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
735 | if (valobj.IsPointerType()) { |
736 | uint64_t value = valobj.GetValueAsUnsigned(fail_value: 0); |
737 | if (!value) |
738 | return false; |
739 | stream.Printf(format: "0x%016" PRIx64 " " , value); |
740 | } |
741 | return FormatEntity::FormatStringRef(format: "size=${svar%#}" , s&: stream, sc: nullptr, |
742 | exe_ctx: nullptr, addr: nullptr, valobj: &valobj, function_changed: false, initial_function: false); |
743 | } |
744 | |
745 | /// The field layout in a libc++ string (cap, side, data or data, size, cap). |
746 | namespace { |
747 | enum class StringLayout { CSD, DSC }; |
748 | } |
749 | |
750 | /// Determine the size in bytes of \p valobj (a libc++ std::string object) and |
751 | /// extract its data payload. Return the size + payload pair. |
752 | // TODO: Support big-endian architectures. |
753 | static std::optional<std::pair<uint64_t, ValueObjectSP>> |
754 | (ValueObject &valobj) { |
755 | ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName(name: "__r_" ); |
756 | if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) |
757 | return {}; |
758 | |
759 | // __r_ is a compressed_pair of the actual data and the allocator. The data we |
760 | // want is in the first base class. |
761 | ValueObjectSP valobj_r_base_sp = valobj_r_sp->GetChildAtIndex(idx: 0); |
762 | if (!valobj_r_base_sp) |
763 | return {}; |
764 | |
765 | ValueObjectSP valobj_rep_sp = |
766 | valobj_r_base_sp->GetChildMemberWithName(name: "__value_" ); |
767 | if (!valobj_rep_sp) |
768 | return {}; |
769 | |
770 | ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName(name: "__l" ); |
771 | if (!l) |
772 | return {}; |
773 | |
774 | StringLayout layout = l->GetIndexOfChildWithName(name: "__data_" ) == 0 |
775 | ? StringLayout::DSC |
776 | : StringLayout::CSD; |
777 | |
778 | bool short_mode = false; // this means the string is in short-mode and the |
779 | // data is stored inline |
780 | bool using_bitmasks = true; // Whether the class uses bitmasks for the mode |
781 | // flag (pre-D123580). |
782 | uint64_t size; |
783 | uint64_t size_mode_value = 0; |
784 | |
785 | ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName(name: "__s" ); |
786 | if (!short_sp) |
787 | return {}; |
788 | |
789 | ValueObjectSP is_long = short_sp->GetChildMemberWithName(name: "__is_long_" ); |
790 | ValueObjectSP size_sp = short_sp->GetChildMemberWithName(name: "__size_" ); |
791 | if (!size_sp) |
792 | return {}; |
793 | |
794 | if (is_long) { |
795 | using_bitmasks = false; |
796 | short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0); |
797 | size = size_sp->GetValueAsUnsigned(/*fail_value=*/0); |
798 | } else { |
799 | // The string mode is encoded in the size field. |
800 | size_mode_value = size_sp->GetValueAsUnsigned(fail_value: 0); |
801 | uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1; |
802 | short_mode = (size_mode_value & mode_mask) == 0; |
803 | } |
804 | |
805 | if (short_mode) { |
806 | ValueObjectSP location_sp = short_sp->GetChildMemberWithName(name: "__data_" ); |
807 | if (using_bitmasks) |
808 | size = (layout == StringLayout::DSC) ? size_mode_value |
809 | : ((size_mode_value >> 1) % 256); |
810 | |
811 | // When the small-string optimization takes place, the data must fit in the |
812 | // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's |
813 | // likely that the string isn't initialized and we're reading garbage. |
814 | ExecutionContext exe_ctx(location_sp->GetExecutionContextRef()); |
815 | const std::optional<uint64_t> max_bytes = |
816 | location_sp->GetCompilerType().GetByteSize( |
817 | exe_scope: exe_ctx.GetBestExecutionContextScope()); |
818 | if (!max_bytes || size > *max_bytes || !location_sp) |
819 | return {}; |
820 | |
821 | return std::make_pair(x&: size, y&: location_sp); |
822 | } |
823 | |
824 | // we can use the layout_decider object as the data pointer |
825 | ValueObjectSP location_sp = l->GetChildMemberWithName(name: "__data_" ); |
826 | ValueObjectSP size_vo = l->GetChildMemberWithName(name: "__size_" ); |
827 | ValueObjectSP capacity_vo = l->GetChildMemberWithName(name: "__cap_" ); |
828 | if (!size_vo || !location_sp || !capacity_vo) |
829 | return {}; |
830 | size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); |
831 | uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); |
832 | if (!using_bitmasks && layout == StringLayout::CSD) |
833 | capacity *= 2; |
834 | if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET || |
835 | capacity < size) |
836 | return {}; |
837 | return std::make_pair(x&: size, y&: location_sp); |
838 | } |
839 | |
840 | static bool |
841 | LibcxxWStringSummaryProvider(ValueObject &valobj, Stream &stream, |
842 | const TypeSummaryOptions &summary_options, |
843 | ValueObjectSP location_sp, size_t size) { |
844 | if (size == 0) { |
845 | stream.Printf(format: "L\"\"" ); |
846 | return true; |
847 | } |
848 | if (!location_sp) |
849 | return false; |
850 | |
851 | StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); |
852 | if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { |
853 | const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); |
854 | if (size > max_size) { |
855 | size = max_size; |
856 | options.SetIsTruncated(true); |
857 | } |
858 | } |
859 | |
860 | DataExtractor ; |
861 | const size_t bytes_read = location_sp->GetPointeeData(data&: extractor, item_idx: 0, item_count: size); |
862 | if (bytes_read < size) |
863 | return false; |
864 | |
865 | // std::wstring::size() is measured in 'characters', not bytes |
866 | TypeSystemClangSP scratch_ts_sp = |
867 | ScratchTypeSystemClang::GetForTarget(target&: *valobj.GetTargetSP()); |
868 | if (!scratch_ts_sp) |
869 | return false; |
870 | |
871 | auto wchar_t_size = |
872 | scratch_ts_sp->GetBasicType(type: lldb::eBasicTypeWChar).GetByteSize(exe_scope: nullptr); |
873 | if (!wchar_t_size) |
874 | return false; |
875 | |
876 | options.SetData(std::move(extractor)); |
877 | options.SetStream(&stream); |
878 | options.SetPrefixToken("L" ); |
879 | options.SetQuote('"'); |
880 | options.SetSourceSize(size); |
881 | options.SetBinaryZeroIsTerminator(false); |
882 | |
883 | switch (*wchar_t_size) { |
884 | case 1: |
885 | return StringPrinter::ReadBufferAndDumpToStream< |
886 | lldb_private::formatters::StringPrinter::StringElementType::UTF8>( |
887 | options); |
888 | break; |
889 | |
890 | case 2: |
891 | return StringPrinter::ReadBufferAndDumpToStream< |
892 | lldb_private::formatters::StringPrinter::StringElementType::UTF16>( |
893 | options); |
894 | break; |
895 | |
896 | case 4: |
897 | return StringPrinter::ReadBufferAndDumpToStream< |
898 | lldb_private::formatters::StringPrinter::StringElementType::UTF32>( |
899 | options); |
900 | } |
901 | return false; |
902 | } |
903 | |
904 | bool lldb_private::formatters::LibcxxWStringSummaryProvider( |
905 | ValueObject &valobj, Stream &stream, |
906 | const TypeSummaryOptions &summary_options) { |
907 | auto string_info = ExtractLibcxxStringInfo(valobj); |
908 | if (!string_info) |
909 | return false; |
910 | uint64_t size; |
911 | ValueObjectSP location_sp; |
912 | std::tie(args&: size, args&: location_sp) = *string_info; |
913 | |
914 | return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options, |
915 | location_sp, size); |
916 | } |
917 | |
918 | template <StringPrinter::StringElementType element_type> |
919 | static bool |
920 | LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, |
921 | const TypeSummaryOptions &summary_options, |
922 | std::string prefix_token, ValueObjectSP location_sp, |
923 | uint64_t size) { |
924 | |
925 | if (size == 0) { |
926 | stream.Printf(format: "\"\"" ); |
927 | return true; |
928 | } |
929 | |
930 | if (!location_sp) |
931 | return false; |
932 | |
933 | StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); |
934 | |
935 | if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { |
936 | const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); |
937 | if (size > max_size) { |
938 | size = max_size; |
939 | options.SetIsTruncated(true); |
940 | } |
941 | } |
942 | |
943 | { |
944 | DataExtractor ; |
945 | const size_t bytes_read = location_sp->GetPointeeData(data&: extractor, item_idx: 0, item_count: size); |
946 | if (bytes_read < size) |
947 | return false; |
948 | |
949 | options.SetData(std::move(extractor)); |
950 | } |
951 | options.SetStream(&stream); |
952 | if (prefix_token.empty()) |
953 | options.SetPrefixToken(nullptr); |
954 | else |
955 | options.SetPrefixToken(prefix_token); |
956 | options.SetQuote('"'); |
957 | options.SetSourceSize(size); |
958 | options.SetBinaryZeroIsTerminator(false); |
959 | return StringPrinter::ReadBufferAndDumpToStream<element_type>(options); |
960 | } |
961 | |
962 | template <StringPrinter::StringElementType element_type> |
963 | static bool |
964 | LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, |
965 | const TypeSummaryOptions &summary_options, |
966 | std::string prefix_token) { |
967 | auto string_info = ExtractLibcxxStringInfo(valobj); |
968 | if (!string_info) |
969 | return false; |
970 | uint64_t size; |
971 | ValueObjectSP location_sp; |
972 | std::tie(args&: size, args&: location_sp) = *string_info; |
973 | |
974 | return LibcxxStringSummaryProvider<element_type>( |
975 | valobj, stream, summary_options, prefix_token, location_sp, size); |
976 | } |
977 | template <StringPrinter::StringElementType element_type> |
978 | static bool formatStringImpl(ValueObject &valobj, Stream &stream, |
979 | const TypeSummaryOptions &summary_options, |
980 | std::string prefix_token) { |
981 | StreamString scratch_stream; |
982 | const bool success = LibcxxStringSummaryProvider<element_type>( |
983 | valobj, scratch_stream, summary_options, prefix_token); |
984 | if (success) |
985 | stream << scratch_stream.GetData(); |
986 | else |
987 | stream << "Summary Unavailable" ; |
988 | return true; |
989 | } |
990 | |
991 | bool lldb_private::formatters::LibcxxStringSummaryProviderASCII( |
992 | ValueObject &valobj, Stream &stream, |
993 | const TypeSummaryOptions &summary_options) { |
994 | return formatStringImpl<StringPrinter::StringElementType::ASCII>( |
995 | valobj, stream, summary_options, prefix_token: "" ); |
996 | } |
997 | |
998 | bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16( |
999 | ValueObject &valobj, Stream &stream, |
1000 | const TypeSummaryOptions &summary_options) { |
1001 | return formatStringImpl<StringPrinter::StringElementType::UTF16>( |
1002 | valobj, stream, summary_options, prefix_token: "u" ); |
1003 | } |
1004 | |
1005 | bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32( |
1006 | ValueObject &valobj, Stream &stream, |
1007 | const TypeSummaryOptions &summary_options) { |
1008 | return formatStringImpl<StringPrinter::StringElementType::UTF32>( |
1009 | valobj, stream, summary_options, prefix_token: "U" ); |
1010 | } |
1011 | |
1012 | static std::tuple<bool, ValueObjectSP, size_t> |
1013 | (ValueObject& valobj) { |
1014 | auto dataobj = GetChildMemberWithName( |
1015 | obj&: valobj, alternative_names: {ConstString("__data_" ), ConstString("__data" )}); |
1016 | auto sizeobj = GetChildMemberWithName( |
1017 | obj&: valobj, alternative_names: {ConstString("__size_" ), ConstString("__size" )}); |
1018 | if (!dataobj || !sizeobj) |
1019 | return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {}); |
1020 | |
1021 | if (!dataobj->GetError().Success() || !sizeobj->GetError().Success()) |
1022 | return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {}); |
1023 | |
1024 | bool success{false}; |
1025 | uint64_t size = sizeobj->GetValueAsUnsigned(fail_value: 0, success: &success); |
1026 | if (!success) |
1027 | return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {}); |
1028 | |
1029 | return std::make_tuple(args: true,args&: dataobj,args&: size); |
1030 | } |
1031 | |
1032 | template <StringPrinter::StringElementType element_type> |
1033 | static bool formatStringViewImpl(ValueObject &valobj, Stream &stream, |
1034 | const TypeSummaryOptions &summary_options, |
1035 | std::string prefix_token) { |
1036 | |
1037 | bool success; |
1038 | ValueObjectSP dataobj; |
1039 | size_t size; |
1040 | std::tie(args&: success, args&: dataobj, args&: size) = LibcxxExtractStringViewData(valobj); |
1041 | |
1042 | if (!success) { |
1043 | stream << "Summary Unavailable" ; |
1044 | return true; |
1045 | } |
1046 | |
1047 | return LibcxxStringSummaryProvider<element_type>( |
1048 | valobj, stream, summary_options, prefix_token, dataobj, size); |
1049 | } |
1050 | |
1051 | bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII( |
1052 | ValueObject &valobj, Stream &stream, |
1053 | const TypeSummaryOptions &summary_options) { |
1054 | return formatStringViewImpl<StringPrinter::StringElementType::ASCII>( |
1055 | valobj, stream, summary_options, prefix_token: "" ); |
1056 | } |
1057 | |
1058 | bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16( |
1059 | ValueObject &valobj, Stream &stream, |
1060 | const TypeSummaryOptions &summary_options) { |
1061 | return formatStringViewImpl<StringPrinter::StringElementType::UTF16>( |
1062 | valobj, stream, summary_options, prefix_token: "u" ); |
1063 | } |
1064 | |
1065 | bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32( |
1066 | ValueObject &valobj, Stream &stream, |
1067 | const TypeSummaryOptions &summary_options) { |
1068 | return formatStringViewImpl<StringPrinter::StringElementType::UTF32>( |
1069 | valobj, stream, summary_options, prefix_token: "U" ); |
1070 | } |
1071 | |
1072 | bool lldb_private::formatters::LibcxxWStringViewSummaryProvider( |
1073 | ValueObject &valobj, Stream &stream, |
1074 | const TypeSummaryOptions &summary_options) { |
1075 | |
1076 | bool success; |
1077 | ValueObjectSP dataobj; |
1078 | size_t size; |
1079 | std::tie(args&: success, args&: dataobj, args&: size) = LibcxxExtractStringViewData(valobj); |
1080 | |
1081 | if (!success) { |
1082 | stream << "Summary Unavailable" ; |
1083 | return true; |
1084 | } |
1085 | |
1086 | return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options, |
1087 | location_sp: dataobj, size); |
1088 | } |
1089 | |
1090 | static bool |
1091 | LibcxxChronoTimePointSecondsSummaryProvider(ValueObject &valobj, Stream &stream, |
1092 | const TypeSummaryOptions &options, |
1093 | const char *fmt) { |
1094 | ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__d_" ); |
1095 | if (!ptr_sp) |
1096 | return false; |
1097 | ptr_sp = ptr_sp->GetChildMemberWithName(name: "__rep_" ); |
1098 | if (!ptr_sp) |
1099 | return false; |
1100 | |
1101 | // The date time in the chrono library is valid in the range |
1102 | // [-32767-01-01T00:00:00Z, 32767-12-31T23:59:59Z]. A 64-bit time_t has a |
1103 | // larger range, the function strftime is not able to format the entire range |
1104 | // of time_t. The exact point has not been investigated; it's limited to |
1105 | // chrono's range. |
1106 | const std::time_t chrono_timestamp_min = |
1107 | -1'096'193'779'200; // -32767-01-01T00:00:00Z |
1108 | const std::time_t chrono_timestamp_max = |
1109 | 971'890'963'199; // 32767-12-31T23:59:59Z |
1110 | |
1111 | const std::time_t seconds = ptr_sp->GetValueAsSigned(fail_value: 0); |
1112 | if (seconds < chrono_timestamp_min || seconds > chrono_timestamp_max) |
1113 | stream.Printf(format: "timestamp=%" PRId64 " s" , static_cast<int64_t>(seconds)); |
1114 | else { |
1115 | std::array<char, 128> str; |
1116 | std::size_t size = |
1117 | std::strftime(s: str.data(), maxsize: str.size(), format: fmt, tp: gmtime(timer: &seconds)); |
1118 | if (size == 0) |
1119 | return false; |
1120 | |
1121 | stream.Printf(format: "date/time=%s timestamp=%" PRId64 " s" , str.data(), |
1122 | static_cast<int64_t>(seconds)); |
1123 | } |
1124 | |
1125 | return true; |
1126 | } |
1127 | |
1128 | bool lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider( |
1129 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
1130 | return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options, |
1131 | fmt: "%FT%H:%M:%SZ" ); |
1132 | } |
1133 | |
1134 | bool lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider( |
1135 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
1136 | return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options, |
1137 | fmt: "%FT%H:%M:%S" ); |
1138 | } |
1139 | |
1140 | static bool |
1141 | LibcxxChronoTimepointDaysSummaryProvider(ValueObject &valobj, Stream &stream, |
1142 | const TypeSummaryOptions &options, |
1143 | const char *fmt) { |
1144 | ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__d_" ); |
1145 | if (!ptr_sp) |
1146 | return false; |
1147 | ptr_sp = ptr_sp->GetChildMemberWithName(name: "__rep_" ); |
1148 | if (!ptr_sp) |
1149 | return false; |
1150 | |
1151 | // The date time in the chrono library is valid in the range |
1152 | // [-32767-01-01Z, 32767-12-31Z]. A 32-bit time_t has a larger range, the |
1153 | // function strftime is not able to format the entire range of time_t. The |
1154 | // exact point has not been investigated; it's limited to chrono's range. |
1155 | const int chrono_timestamp_min = -12'687'428; // -32767-01-01Z |
1156 | const int chrono_timestamp_max = 11'248'737; // 32767-12-31Z |
1157 | |
1158 | const int days = ptr_sp->GetValueAsSigned(fail_value: 0); |
1159 | if (days < chrono_timestamp_min || days > chrono_timestamp_max) |
1160 | stream.Printf(format: "timestamp=%d days" , days); |
1161 | |
1162 | else { |
1163 | const std::time_t seconds = std::time_t(86400) * days; |
1164 | |
1165 | std::array<char, 128> str; |
1166 | std::size_t size = |
1167 | std::strftime(s: str.data(), maxsize: str.size(), format: fmt, tp: gmtime(timer: &seconds)); |
1168 | if (size == 0) |
1169 | return false; |
1170 | |
1171 | stream.Printf(format: "date=%s timestamp=%d days" , str.data(), days); |
1172 | } |
1173 | |
1174 | return true; |
1175 | } |
1176 | |
1177 | bool lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider( |
1178 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
1179 | return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options, |
1180 | fmt: "%FZ" ); |
1181 | } |
1182 | |
1183 | bool lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider( |
1184 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
1185 | return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options, |
1186 | fmt: "%F" ); |
1187 | } |
1188 | |
1189 | bool lldb_private::formatters::LibcxxChronoMonthSummaryProvider( |
1190 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
1191 | // FIXME: These are the names used in the C++20 ostream operator. Since LLVM |
1192 | // uses C++17 it's not possible to use the ostream operator directly. |
1193 | static const std::array<std::string_view, 12> months = { |
1194 | "January" , "February" , "March" , "April" , "May" , "June" , |
1195 | "July" , "August" , "September" , "October" , "November" , "December" }; |
1196 | |
1197 | ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__m_" ); |
1198 | if (!ptr_sp) |
1199 | return false; |
1200 | |
1201 | const unsigned month = ptr_sp->GetValueAsUnsigned(fail_value: 0); |
1202 | if (month >= 1 && month <= 12) |
1203 | stream << "month=" << months[month - 1]; |
1204 | else |
1205 | stream.Printf(format: "month=%u" , month); |
1206 | |
1207 | return true; |
1208 | } |
1209 | |
1210 | bool lldb_private::formatters::LibcxxChronoWeekdaySummaryProvider( |
1211 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
1212 | // FIXME: These are the names used in the C++20 ostream operator. Since LLVM |
1213 | // uses C++17 it's not possible to use the ostream operator directly. |
1214 | static const std::array<std::string_view, 7> weekdays = { |
1215 | "Sunday" , "Monday" , "Tuesday" , "Wednesday" , |
1216 | "Thursday" , "Friday" , "Saturday" }; |
1217 | |
1218 | ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__wd_" ); |
1219 | if (!ptr_sp) |
1220 | return false; |
1221 | |
1222 | const unsigned weekday = ptr_sp->GetValueAsUnsigned(fail_value: 0); |
1223 | if (weekday < 7) |
1224 | stream << "weekday=" << weekdays[weekday]; |
1225 | else |
1226 | stream.Printf(format: "weekday=%u" , weekday); |
1227 | |
1228 | return true; |
1229 | } |
1230 | |
1231 | bool lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider( |
1232 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
1233 | ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__y_" ); |
1234 | if (!ptr_sp) |
1235 | return false; |
1236 | ptr_sp = ptr_sp->GetChildMemberWithName(name: "__y_" ); |
1237 | if (!ptr_sp) |
1238 | return false; |
1239 | int year = ptr_sp->GetValueAsSigned(fail_value: 0); |
1240 | |
1241 | ptr_sp = valobj.GetChildMemberWithName(name: "__m_" ); |
1242 | if (!ptr_sp) |
1243 | return false; |
1244 | ptr_sp = ptr_sp->GetChildMemberWithName(name: "__m_" ); |
1245 | if (!ptr_sp) |
1246 | return false; |
1247 | const unsigned month = ptr_sp->GetValueAsUnsigned(fail_value: 0); |
1248 | |
1249 | ptr_sp = valobj.GetChildMemberWithName(name: "__d_" ); |
1250 | if (!ptr_sp) |
1251 | return false; |
1252 | ptr_sp = ptr_sp->GetChildMemberWithName(name: "__d_" ); |
1253 | if (!ptr_sp) |
1254 | return false; |
1255 | const unsigned day = ptr_sp->GetValueAsUnsigned(fail_value: 0); |
1256 | |
1257 | stream << "date=" ; |
1258 | if (year < 0) { |
1259 | stream << '-'; |
1260 | year = -year; |
1261 | } |
1262 | stream.Printf(format: "%04d-%02u-%02u" , year, month, day); |
1263 | |
1264 | return true; |
1265 | } |
1266 | |