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/DataFormatters/FormattersHelpers.h" |
14 | #include "lldb/DataFormatters/StringPrinter.h" |
15 | #include "lldb/DataFormatters/TypeSummary.h" |
16 | #include "lldb/DataFormatters/VectorIterator.h" |
17 | #include "lldb/Target/SectionLoadList.h" |
18 | #include "lldb/Target/Target.h" |
19 | #include "lldb/Utility/ConstString.h" |
20 | #include "lldb/Utility/DataBufferHeap.h" |
21 | #include "lldb/Utility/Endian.h" |
22 | #include "lldb/Utility/Status.h" |
23 | #include "lldb/Utility/Stream.h" |
24 | #include "lldb/ValueObject/ValueObject.h" |
25 | #include "lldb/ValueObject/ValueObjectConstResult.h" |
26 | |
27 | #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" |
28 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
29 | #include "lldb/lldb-enumerations.h" |
30 | #include "lldb/lldb-forward.h" |
31 | #include <optional> |
32 | #include <tuple> |
33 | |
34 | using namespace lldb; |
35 | using namespace lldb_private; |
36 | using namespace lldb_private::formatters; |
37 | |
38 | static void consumeInlineNamespace(llvm::StringRef &name) { |
39 | // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+:: |
40 | auto scratch = name; |
41 | if (scratch.consume_front(Prefix: "__" ) && std::isalnum(scratch[0])) { |
42 | scratch = scratch.drop_while(F: [](char c) { return std::isalnum(c); }); |
43 | if (scratch.consume_front(Prefix: "::" )) { |
44 | // Successfully consumed a namespace. |
45 | name = scratch; |
46 | } |
47 | } |
48 | } |
49 | |
50 | bool lldb_private::formatters::isOldCompressedPairLayout( |
51 | ValueObject &pair_obj) { |
52 | return isStdTemplate(type_name: pair_obj.GetTypeName(), type: "__compressed_pair" ); |
53 | } |
54 | |
55 | bool lldb_private::formatters::isStdTemplate(ConstString type_name, |
56 | llvm::StringRef type) { |
57 | llvm::StringRef name = type_name.GetStringRef(); |
58 | // The type name may be prefixed with `std::__<inline-namespace>::`. |
59 | if (name.consume_front(Prefix: "std::" )) |
60 | consumeInlineNamespace(name); |
61 | return name.consume_front(Prefix: type) && name.starts_with(Prefix: "<" ); |
62 | } |
63 | |
64 | lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName( |
65 | ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) { |
66 | for (ConstString name : alternative_names) { |
67 | lldb::ValueObjectSP child_sp = obj.GetChildMemberWithName(name); |
68 | |
69 | if (child_sp) |
70 | return child_sp; |
71 | } |
72 | return {}; |
73 | } |
74 | |
75 | lldb::ValueObjectSP |
76 | lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair( |
77 | ValueObject &pair) { |
78 | ValueObjectSP value; |
79 | ValueObjectSP first_child = pair.GetChildAtIndex(idx: 0); |
80 | if (first_child) |
81 | value = first_child->GetChildMemberWithName(name: "__value_" ); |
82 | if (!value) { |
83 | // pre-c88580c member name |
84 | value = pair.GetChildMemberWithName(name: "__first_" ); |
85 | } |
86 | return value; |
87 | } |
88 | |
89 | lldb::ValueObjectSP |
90 | lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair( |
91 | ValueObject &pair) { |
92 | ValueObjectSP value; |
93 | if (pair.GetNumChildrenIgnoringErrors() > 1) { |
94 | ValueObjectSP second_child = pair.GetChildAtIndex(idx: 1); |
95 | if (second_child) { |
96 | value = second_child->GetChildMemberWithName(name: "__value_" ); |
97 | } |
98 | } |
99 | if (!value) { |
100 | // pre-c88580c member name |
101 | value = pair.GetChildMemberWithName(name: "__second_" ); |
102 | } |
103 | return value; |
104 | } |
105 | |
106 | bool lldb_private::formatters::LibcxxFunctionSummaryProvider( |
107 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
108 | |
109 | ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); |
110 | |
111 | if (!valobj_sp) |
112 | return false; |
113 | |
114 | ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef()); |
115 | Process *process = exe_ctx.GetProcessPtr(); |
116 | |
117 | if (process == nullptr) |
118 | return false; |
119 | |
120 | CPPLanguageRuntime *cpp_runtime = CPPLanguageRuntime::Get(process&: *process); |
121 | |
122 | if (!cpp_runtime) |
123 | return false; |
124 | |
125 | CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info = |
126 | cpp_runtime->FindLibCppStdFunctionCallableInfo(valobj_sp); |
127 | |
128 | switch (callable_info.callable_case) { |
129 | case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Invalid: |
130 | stream.Printf(format: " __f_ = %" PRIu64, callable_info.member_f_pointer_value); |
131 | return false; |
132 | break; |
133 | case CPPLanguageRuntime::LibCppStdFunctionCallableCase::Lambda: |
134 | stream.Printf( |
135 | format: " Lambda in File %s at Line %u" , |
136 | callable_info.callable_line_entry.GetFile().GetFilename().GetCString(), |
137 | callable_info.callable_line_entry.line); |
138 | break; |
139 | case CPPLanguageRuntime::LibCppStdFunctionCallableCase::CallableObject: |
140 | stream.Printf( |
141 | format: " Function in File %s at Line %u" , |
142 | callable_info.callable_line_entry.GetFile().GetFilename().GetCString(), |
143 | callable_info.callable_line_entry.line); |
144 | break; |
145 | case CPPLanguageRuntime::LibCppStdFunctionCallableCase::FreeOrMemberFunction: |
146 | stream.Printf(format: " Function = %s " , |
147 | callable_info.callable_symbol.GetName().GetCString()); |
148 | break; |
149 | } |
150 | |
151 | return true; |
152 | } |
153 | |
154 | bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider( |
155 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
156 | ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); |
157 | if (!valobj_sp) |
158 | return false; |
159 | ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName(name: "__ptr_" )); |
160 | ValueObjectSP count_sp( |
161 | valobj_sp->GetChildAtNamePath(names: {"__cntrl_" , "__shared_owners_" })); |
162 | ValueObjectSP weakcount_sp( |
163 | valobj_sp->GetChildAtNamePath(names: {"__cntrl_" , "__shared_weak_owners_" })); |
164 | |
165 | if (!ptr_sp) |
166 | return false; |
167 | |
168 | if (ptr_sp->GetValueAsUnsigned(fail_value: 0) == 0) { |
169 | stream.Printf(format: "nullptr" ); |
170 | return true; |
171 | } else { |
172 | bool print_pointee = false; |
173 | Status error; |
174 | ValueObjectSP pointee_sp = ptr_sp->Dereference(error); |
175 | if (pointee_sp && error.Success()) { |
176 | if (pointee_sp->DumpPrintableRepresentation( |
177 | s&: stream, val_obj_display: ValueObject::eValueObjectRepresentationStyleSummary, |
178 | custom_format: lldb::eFormatInvalid, |
179 | special: ValueObject::PrintableRepresentationSpecialCases::eDisable, |
180 | do_dump_error: false)) |
181 | print_pointee = true; |
182 | } |
183 | if (!print_pointee) |
184 | stream.Printf(format: "ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(fail_value: 0)); |
185 | } |
186 | |
187 | if (count_sp) |
188 | stream.Printf(format: " strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(fail_value: 0)); |
189 | |
190 | if (weakcount_sp) |
191 | stream.Printf(format: " weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(fail_value: 0)); |
192 | |
193 | return true; |
194 | } |
195 | |
196 | bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider( |
197 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
198 | ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); |
199 | if (!valobj_sp) |
200 | return false; |
201 | |
202 | ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName(name: "__ptr_" )); |
203 | if (!ptr_sp) |
204 | return false; |
205 | |
206 | if (isOldCompressedPairLayout(pair_obj&: *ptr_sp)) |
207 | ptr_sp = GetFirstValueOfLibCXXCompressedPair(pair&: *ptr_sp); |
208 | |
209 | if (!ptr_sp) |
210 | return false; |
211 | |
212 | if (ptr_sp->GetValueAsUnsigned(fail_value: 0) == 0) { |
213 | stream.Printf(format: "nullptr" ); |
214 | return true; |
215 | } else { |
216 | bool print_pointee = false; |
217 | Status error; |
218 | ValueObjectSP pointee_sp = ptr_sp->Dereference(error); |
219 | if (pointee_sp && error.Success()) { |
220 | if (pointee_sp->DumpPrintableRepresentation( |
221 | s&: stream, val_obj_display: ValueObject::eValueObjectRepresentationStyleSummary, |
222 | custom_format: lldb::eFormatInvalid, |
223 | special: ValueObject::PrintableRepresentationSpecialCases::eDisable, |
224 | do_dump_error: false)) |
225 | print_pointee = true; |
226 | } |
227 | if (!print_pointee) |
228 | stream.Printf(format: "ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(fail_value: 0)); |
229 | } |
230 | |
231 | return true; |
232 | } |
233 | |
234 | /* |
235 | (lldb) fr var ibeg --raw --ptr-depth 1 -T |
236 | (std::__1::__wrap_iter<int *>) ibeg = { |
237 | (std::__1::__wrap_iter<int *>::iterator_type) __i = 0x00000001001037a0 { |
238 | (int) *__i = 1 |
239 | } |
240 | } |
241 | */ |
242 | |
243 | SyntheticChildrenFrontEnd * |
244 | lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator( |
245 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
246 | return (valobj_sp ? new VectorIteratorSyntheticFrontEnd( |
247 | valobj_sp, {ConstString("__i_" ), ConstString("__i" )}) |
248 | : nullptr); |
249 | } |
250 | |
251 | lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: |
252 | LibcxxSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
253 | : SyntheticChildrenFrontEnd(*valobj_sp), m_cntrl(nullptr) { |
254 | if (valobj_sp) |
255 | Update(); |
256 | } |
257 | |
258 | llvm::Expected<uint32_t> lldb_private::formatters:: |
259 | LibcxxSharedPtrSyntheticFrontEnd::CalculateNumChildren() { |
260 | return (m_cntrl ? 1 : 0); |
261 | } |
262 | |
263 | lldb::ValueObjectSP |
264 | lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex( |
265 | uint32_t idx) { |
266 | if (!m_cntrl) |
267 | return lldb::ValueObjectSP(); |
268 | |
269 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
270 | if (!valobj_sp) |
271 | return lldb::ValueObjectSP(); |
272 | |
273 | if (idx == 0) |
274 | return valobj_sp->GetChildMemberWithName(name: "__ptr_" ); |
275 | |
276 | if (idx == 1) { |
277 | if (auto ptr_sp = valobj_sp->GetChildMemberWithName(name: "__ptr_" )) { |
278 | Status status; |
279 | auto value_type_sp = |
280 | valobj_sp->GetCompilerType() |
281 | .GetTypeTemplateArgument(idx: 0).GetPointerType(); |
282 | ValueObjectSP cast_ptr_sp = ptr_sp->Cast(compiler_type: value_type_sp); |
283 | ValueObjectSP value_sp = cast_ptr_sp->Dereference(error&: status); |
284 | if (status.Success()) { |
285 | return value_sp; |
286 | } |
287 | } |
288 | } |
289 | |
290 | return lldb::ValueObjectSP(); |
291 | } |
292 | |
293 | lldb::ChildCacheState |
294 | lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() { |
295 | m_cntrl = nullptr; |
296 | |
297 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
298 | if (!valobj_sp) |
299 | return lldb::ChildCacheState::eRefetch; |
300 | |
301 | TargetSP target_sp(valobj_sp->GetTargetSP()); |
302 | if (!target_sp) |
303 | return lldb::ChildCacheState::eRefetch; |
304 | |
305 | lldb::ValueObjectSP cntrl_sp(valobj_sp->GetChildMemberWithName(name: "__cntrl_" )); |
306 | |
307 | m_cntrl = cntrl_sp.get(); // need to store the raw pointer to avoid a circular |
308 | // dependency |
309 | return lldb::ChildCacheState::eRefetch; |
310 | } |
311 | |
312 | llvm::Expected<size_t> |
313 | lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: |
314 | GetIndexOfChildWithName(ConstString name) { |
315 | if (name == "__ptr_" ) |
316 | return 0; |
317 | if (name == "$$dereference$$" ) |
318 | return 1; |
319 | return llvm::createStringError(Fmt: "Type has no child named '%s'" , |
320 | Vals: name.AsCString()); |
321 | } |
322 | |
323 | lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd:: |
324 | ~LibcxxSharedPtrSyntheticFrontEnd() = default; |
325 | |
326 | SyntheticChildrenFrontEnd * |
327 | lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator( |
328 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
329 | return (valobj_sp ? new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp) |
330 | : nullptr); |
331 | } |
332 | |
333 | lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: |
334 | LibcxxUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) |
335 | : SyntheticChildrenFrontEnd(*valobj_sp) { |
336 | if (valobj_sp) |
337 | Update(); |
338 | } |
339 | |
340 | lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: |
341 | ~LibcxxUniquePtrSyntheticFrontEnd() = default; |
342 | |
343 | SyntheticChildrenFrontEnd * |
344 | lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEndCreator( |
345 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
346 | return (valobj_sp ? new LibcxxUniquePtrSyntheticFrontEnd(valobj_sp) |
347 | : nullptr); |
348 | } |
349 | |
350 | llvm::Expected<uint32_t> lldb_private::formatters:: |
351 | LibcxxUniquePtrSyntheticFrontEnd::CalculateNumChildren() { |
352 | if (m_value_ptr_sp) |
353 | return m_deleter_sp ? 2 : 1; |
354 | return 0; |
355 | } |
356 | |
357 | lldb::ValueObjectSP |
358 | lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::GetChildAtIndex( |
359 | uint32_t idx) { |
360 | if (!m_value_ptr_sp) |
361 | return lldb::ValueObjectSP(); |
362 | |
363 | if (idx == 0) |
364 | return m_value_ptr_sp; |
365 | |
366 | if (idx == 1) |
367 | return m_deleter_sp; |
368 | |
369 | if (idx == 2) { |
370 | Status status; |
371 | auto value_sp = m_value_ptr_sp->Dereference(error&: status); |
372 | if (status.Success()) { |
373 | return value_sp; |
374 | } |
375 | } |
376 | |
377 | return lldb::ValueObjectSP(); |
378 | } |
379 | |
380 | lldb::ChildCacheState |
381 | lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() { |
382 | ValueObjectSP valobj_sp = m_backend.GetSP(); |
383 | if (!valobj_sp) |
384 | return lldb::ChildCacheState::eRefetch; |
385 | |
386 | ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName(name: "__ptr_" )); |
387 | if (!ptr_sp) |
388 | return lldb::ChildCacheState::eRefetch; |
389 | |
390 | // Retrieve the actual pointer and the deleter, and clone them to give them |
391 | // user-friendly names. |
392 | if (isOldCompressedPairLayout(pair_obj&: *ptr_sp)) { |
393 | if (ValueObjectSP value_pointer_sp = |
394 | GetFirstValueOfLibCXXCompressedPair(pair&: *ptr_sp)) |
395 | m_value_ptr_sp = value_pointer_sp->Clone(new_name: ConstString("pointer" )); |
396 | |
397 | if (ValueObjectSP deleter_sp = |
398 | GetSecondValueOfLibCXXCompressedPair(pair&: *ptr_sp)) |
399 | m_deleter_sp = deleter_sp->Clone(new_name: ConstString("deleter" )); |
400 | } else { |
401 | m_value_ptr_sp = ptr_sp->Clone(new_name: ConstString("pointer" )); |
402 | |
403 | if (ValueObjectSP deleter_sp = |
404 | valobj_sp->GetChildMemberWithName(name: "__deleter_" )) |
405 | if (deleter_sp->GetNumChildrenIgnoringErrors() > 0) |
406 | m_deleter_sp = deleter_sp->Clone(new_name: ConstString("deleter" )); |
407 | } |
408 | |
409 | return lldb::ChildCacheState::eRefetch; |
410 | } |
411 | |
412 | llvm::Expected<size_t> |
413 | lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: |
414 | GetIndexOfChildWithName(ConstString name) { |
415 | if (name == "pointer" ) |
416 | return 0; |
417 | if (name == "deleter" ) |
418 | return 1; |
419 | if (name == "$$dereference$$" ) |
420 | return 2; |
421 | return llvm::createStringError(Fmt: "Type has no child named '%s'" , |
422 | Vals: name.AsCString()); |
423 | } |
424 | |
425 | bool lldb_private::formatters::LibcxxContainerSummaryProvider( |
426 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
427 | return FormatEntity::FormatStringRef(format: "size=${svar%#}" , s&: stream, sc: nullptr, |
428 | exe_ctx: nullptr, addr: nullptr, valobj: &valobj, function_changed: false, initial_function: false); |
429 | } |
430 | |
431 | /// The field layout in a libc++ string (cap, side, data or data, size, cap). |
432 | namespace { |
433 | enum class StringLayout { CSD, DSC }; |
434 | } |
435 | |
436 | static ValueObjectSP (ValueObject &valobj) { |
437 | if (auto rep_sp = valobj.GetChildMemberWithName(name: "__rep_" )) |
438 | return rep_sp; |
439 | |
440 | ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName(name: "__r_" ); |
441 | if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) |
442 | return nullptr; |
443 | |
444 | if (!isOldCompressedPairLayout(pair_obj&: *valobj_r_sp)) |
445 | return nullptr; |
446 | |
447 | return GetFirstValueOfLibCXXCompressedPair(pair&: *valobj_r_sp); |
448 | } |
449 | |
450 | /// Determine the size in bytes of \p valobj (a libc++ std::string object) and |
451 | /// extract its data payload. Return the size + payload pair. |
452 | // TODO: Support big-endian architectures. |
453 | static std::optional<std::pair<uint64_t, ValueObjectSP>> |
454 | (ValueObject &valobj) { |
455 | ValueObjectSP valobj_rep_sp = ExtractLibCxxStringData(valobj); |
456 | if (!valobj_rep_sp || !valobj_rep_sp->GetError().Success()) |
457 | return {}; |
458 | |
459 | ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName(name: "__l" ); |
460 | if (!l) |
461 | return {}; |
462 | |
463 | auto index_or_err = l->GetIndexOfChildWithName(name: "__data_" ); |
464 | if (!index_or_err) { |
465 | LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters), index_or_err.takeError(), |
466 | "{0}" ); |
467 | return {}; |
468 | } |
469 | |
470 | StringLayout layout = |
471 | *index_or_err == 0 ? StringLayout::DSC : StringLayout::CSD; |
472 | |
473 | bool short_mode = false; // this means the string is in short-mode and the |
474 | // data is stored inline |
475 | bool using_bitmasks = true; // Whether the class uses bitmasks for the mode |
476 | // flag (pre-D123580). |
477 | uint64_t size; |
478 | uint64_t size_mode_value = 0; |
479 | |
480 | ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName(name: "__s" ); |
481 | if (!short_sp) |
482 | return {}; |
483 | |
484 | ValueObjectSP is_long = short_sp->GetChildMemberWithName(name: "__is_long_" ); |
485 | ValueObjectSP size_sp = short_sp->GetChildMemberWithName(name: "__size_" ); |
486 | if (!size_sp) |
487 | return {}; |
488 | |
489 | if (is_long) { |
490 | using_bitmasks = false; |
491 | short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0); |
492 | size = size_sp->GetValueAsUnsigned(/*fail_value=*/0); |
493 | } else { |
494 | // The string mode is encoded in the size field. |
495 | size_mode_value = size_sp->GetValueAsUnsigned(fail_value: 0); |
496 | uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1; |
497 | short_mode = (size_mode_value & mode_mask) == 0; |
498 | } |
499 | |
500 | if (short_mode) { |
501 | ValueObjectSP location_sp = short_sp->GetChildMemberWithName(name: "__data_" ); |
502 | if (using_bitmasks) |
503 | size = (layout == StringLayout::DSC) ? size_mode_value |
504 | : ((size_mode_value >> 1) % 256); |
505 | |
506 | if (!location_sp) |
507 | return {}; |
508 | |
509 | // When the small-string optimization takes place, the data must fit in the |
510 | // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's |
511 | // likely that the string isn't initialized and we're reading garbage. |
512 | ExecutionContext exe_ctx(location_sp->GetExecutionContextRef()); |
513 | const std::optional<uint64_t> max_bytes = |
514 | llvm::expectedToOptional(E: location_sp->GetCompilerType().GetByteSize( |
515 | exe_scope: exe_ctx.GetBestExecutionContextScope())); |
516 | if (!max_bytes || size > *max_bytes) |
517 | return {}; |
518 | |
519 | return std::make_pair(x&: size, y&: location_sp); |
520 | } |
521 | |
522 | // we can use the layout_decider object as the data pointer |
523 | ValueObjectSP location_sp = l->GetChildMemberWithName(name: "__data_" ); |
524 | ValueObjectSP size_vo = l->GetChildMemberWithName(name: "__size_" ); |
525 | ValueObjectSP capacity_vo = l->GetChildMemberWithName(name: "__cap_" ); |
526 | if (!size_vo || !location_sp || !capacity_vo) |
527 | return {}; |
528 | size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); |
529 | uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); |
530 | if (!using_bitmasks && layout == StringLayout::CSD) |
531 | capacity *= 2; |
532 | if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET || |
533 | capacity < size) |
534 | return {}; |
535 | return std::make_pair(x&: size, y&: location_sp); |
536 | } |
537 | |
538 | static bool |
539 | LibcxxWStringSummaryProvider(ValueObject &valobj, Stream &stream, |
540 | const TypeSummaryOptions &summary_options, |
541 | ValueObjectSP location_sp, size_t size) { |
542 | if (size == 0) { |
543 | stream.Printf(format: "L\"\"" ); |
544 | return true; |
545 | } |
546 | if (!location_sp) |
547 | return false; |
548 | |
549 | StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); |
550 | if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { |
551 | const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); |
552 | if (size > max_size) { |
553 | size = max_size; |
554 | options.SetIsTruncated(true); |
555 | } |
556 | } |
557 | |
558 | DataExtractor ; |
559 | const size_t bytes_read = location_sp->GetPointeeData(data&: extractor, item_idx: 0, item_count: size); |
560 | if (bytes_read < size) |
561 | return false; |
562 | |
563 | // std::wstring::size() is measured in 'characters', not bytes |
564 | TypeSystemClangSP scratch_ts_sp = |
565 | ScratchTypeSystemClang::GetForTarget(target&: *valobj.GetTargetSP()); |
566 | if (!scratch_ts_sp) |
567 | return false; |
568 | |
569 | auto wchar_t_size = |
570 | scratch_ts_sp->GetBasicType(type: lldb::eBasicTypeWChar).GetByteSize(exe_scope: nullptr); |
571 | if (!wchar_t_size) |
572 | return false; |
573 | |
574 | options.SetData(std::move(extractor)); |
575 | options.SetStream(&stream); |
576 | options.SetPrefixToken("L" ); |
577 | options.SetQuote('"'); |
578 | options.SetSourceSize(size); |
579 | options.SetBinaryZeroIsTerminator(false); |
580 | |
581 | switch (*wchar_t_size) { |
582 | case 1: |
583 | return StringPrinter::ReadBufferAndDumpToStream< |
584 | lldb_private::formatters::StringPrinter::StringElementType::UTF8>( |
585 | options); |
586 | break; |
587 | |
588 | case 2: |
589 | return StringPrinter::ReadBufferAndDumpToStream< |
590 | lldb_private::formatters::StringPrinter::StringElementType::UTF16>( |
591 | options); |
592 | break; |
593 | |
594 | case 4: |
595 | return StringPrinter::ReadBufferAndDumpToStream< |
596 | lldb_private::formatters::StringPrinter::StringElementType::UTF32>( |
597 | options); |
598 | } |
599 | return false; |
600 | } |
601 | |
602 | bool lldb_private::formatters::LibcxxWStringSummaryProvider( |
603 | ValueObject &valobj, Stream &stream, |
604 | const TypeSummaryOptions &summary_options) { |
605 | auto string_info = ExtractLibcxxStringInfo(valobj); |
606 | if (!string_info) |
607 | return false; |
608 | uint64_t size; |
609 | ValueObjectSP location_sp; |
610 | std::tie(args&: size, args&: location_sp) = *string_info; |
611 | |
612 | return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options, |
613 | location_sp, size); |
614 | } |
615 | |
616 | template <StringPrinter::StringElementType element_type> |
617 | static bool |
618 | LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, |
619 | const TypeSummaryOptions &summary_options, |
620 | std::string prefix_token, ValueObjectSP location_sp, |
621 | uint64_t size) { |
622 | |
623 | if (size == 0) { |
624 | stream.Printf(format: "\"\"" ); |
625 | return true; |
626 | } |
627 | |
628 | if (!location_sp) |
629 | return false; |
630 | |
631 | StringPrinter::ReadBufferAndDumpToStreamOptions options(valobj); |
632 | |
633 | if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) { |
634 | const auto max_size = valobj.GetTargetSP()->GetMaximumSizeOfStringSummary(); |
635 | if (size > max_size) { |
636 | size = max_size; |
637 | options.SetIsTruncated(true); |
638 | } |
639 | } |
640 | |
641 | { |
642 | DataExtractor ; |
643 | const size_t bytes_read = location_sp->GetPointeeData(data&: extractor, item_idx: 0, item_count: size); |
644 | if (bytes_read < size) |
645 | return false; |
646 | |
647 | options.SetData(std::move(extractor)); |
648 | } |
649 | options.SetStream(&stream); |
650 | if (prefix_token.empty()) |
651 | options.SetPrefixToken(nullptr); |
652 | else |
653 | options.SetPrefixToken(prefix_token); |
654 | options.SetQuote('"'); |
655 | options.SetSourceSize(size); |
656 | options.SetBinaryZeroIsTerminator(false); |
657 | return StringPrinter::ReadBufferAndDumpToStream<element_type>(options); |
658 | } |
659 | |
660 | template <StringPrinter::StringElementType element_type> |
661 | static bool |
662 | LibcxxStringSummaryProvider(ValueObject &valobj, Stream &stream, |
663 | const TypeSummaryOptions &summary_options, |
664 | std::string prefix_token) { |
665 | auto string_info = ExtractLibcxxStringInfo(valobj); |
666 | if (!string_info) |
667 | return false; |
668 | uint64_t size; |
669 | ValueObjectSP location_sp; |
670 | std::tie(args&: size, args&: location_sp) = *string_info; |
671 | |
672 | return LibcxxStringSummaryProvider<element_type>( |
673 | valobj, stream, summary_options, prefix_token, location_sp, size); |
674 | } |
675 | template <StringPrinter::StringElementType element_type> |
676 | static bool formatStringImpl(ValueObject &valobj, Stream &stream, |
677 | const TypeSummaryOptions &summary_options, |
678 | std::string prefix_token) { |
679 | StreamString scratch_stream; |
680 | const bool success = LibcxxStringSummaryProvider<element_type>( |
681 | valobj, scratch_stream, summary_options, prefix_token); |
682 | if (success) |
683 | stream << scratch_stream.GetData(); |
684 | else |
685 | stream << "Summary Unavailable" ; |
686 | return true; |
687 | } |
688 | |
689 | bool lldb_private::formatters::LibcxxStringSummaryProviderASCII( |
690 | ValueObject &valobj, Stream &stream, |
691 | const TypeSummaryOptions &summary_options) { |
692 | return formatStringImpl<StringPrinter::StringElementType::ASCII>( |
693 | valobj, stream, summary_options, prefix_token: "" ); |
694 | } |
695 | |
696 | bool lldb_private::formatters::LibcxxStringSummaryProviderUTF16( |
697 | ValueObject &valobj, Stream &stream, |
698 | const TypeSummaryOptions &summary_options) { |
699 | return formatStringImpl<StringPrinter::StringElementType::UTF16>( |
700 | valobj, stream, summary_options, prefix_token: "u" ); |
701 | } |
702 | |
703 | bool lldb_private::formatters::LibcxxStringSummaryProviderUTF32( |
704 | ValueObject &valobj, Stream &stream, |
705 | const TypeSummaryOptions &summary_options) { |
706 | return formatStringImpl<StringPrinter::StringElementType::UTF32>( |
707 | valobj, stream, summary_options, prefix_token: "U" ); |
708 | } |
709 | |
710 | static std::tuple<bool, ValueObjectSP, size_t> |
711 | (ValueObject& valobj) { |
712 | auto dataobj = GetChildMemberWithName( |
713 | obj&: valobj, alternative_names: {ConstString("__data_" ), ConstString("__data" )}); |
714 | auto sizeobj = GetChildMemberWithName( |
715 | obj&: valobj, alternative_names: {ConstString("__size_" ), ConstString("__size" )}); |
716 | if (!dataobj || !sizeobj) |
717 | return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {}); |
718 | |
719 | if (!dataobj->GetError().Success() || !sizeobj->GetError().Success()) |
720 | return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {}); |
721 | |
722 | bool success{false}; |
723 | uint64_t size = sizeobj->GetValueAsUnsigned(fail_value: 0, success: &success); |
724 | if (!success) |
725 | return std::make_tuple<bool,ValueObjectSP,size_t>(args: false, args: {}, args: {}); |
726 | |
727 | return std::make_tuple(args: true,args&: dataobj,args&: size); |
728 | } |
729 | |
730 | template <StringPrinter::StringElementType element_type> |
731 | static bool formatStringViewImpl(ValueObject &valobj, Stream &stream, |
732 | const TypeSummaryOptions &summary_options, |
733 | std::string prefix_token) { |
734 | |
735 | bool success; |
736 | ValueObjectSP dataobj; |
737 | size_t size; |
738 | std::tie(args&: success, args&: dataobj, args&: size) = LibcxxExtractStringViewData(valobj); |
739 | |
740 | if (!success) { |
741 | stream << "Summary Unavailable" ; |
742 | return true; |
743 | } |
744 | |
745 | return LibcxxStringSummaryProvider<element_type>( |
746 | valobj, stream, summary_options, prefix_token, dataobj, size); |
747 | } |
748 | |
749 | bool lldb_private::formatters::LibcxxStringViewSummaryProviderASCII( |
750 | ValueObject &valobj, Stream &stream, |
751 | const TypeSummaryOptions &summary_options) { |
752 | return formatStringViewImpl<StringPrinter::StringElementType::ASCII>( |
753 | valobj, stream, summary_options, prefix_token: "" ); |
754 | } |
755 | |
756 | bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF16( |
757 | ValueObject &valobj, Stream &stream, |
758 | const TypeSummaryOptions &summary_options) { |
759 | return formatStringViewImpl<StringPrinter::StringElementType::UTF16>( |
760 | valobj, stream, summary_options, prefix_token: "u" ); |
761 | } |
762 | |
763 | bool lldb_private::formatters::LibcxxStringViewSummaryProviderUTF32( |
764 | ValueObject &valobj, Stream &stream, |
765 | const TypeSummaryOptions &summary_options) { |
766 | return formatStringViewImpl<StringPrinter::StringElementType::UTF32>( |
767 | valobj, stream, summary_options, prefix_token: "U" ); |
768 | } |
769 | |
770 | bool lldb_private::formatters::LibcxxWStringViewSummaryProvider( |
771 | ValueObject &valobj, Stream &stream, |
772 | const TypeSummaryOptions &summary_options) { |
773 | |
774 | bool success; |
775 | ValueObjectSP dataobj; |
776 | size_t size; |
777 | std::tie(args&: success, args&: dataobj, args&: size) = LibcxxExtractStringViewData(valobj); |
778 | |
779 | if (!success) { |
780 | stream << "Summary Unavailable" ; |
781 | return true; |
782 | } |
783 | |
784 | return ::LibcxxWStringSummaryProvider(valobj, stream, summary_options, |
785 | location_sp: dataobj, size); |
786 | } |
787 | |
788 | static bool |
789 | LibcxxChronoTimePointSecondsSummaryProvider(ValueObject &valobj, Stream &stream, |
790 | const TypeSummaryOptions &options, |
791 | const char *fmt) { |
792 | ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__d_" ); |
793 | if (!ptr_sp) |
794 | return false; |
795 | ptr_sp = ptr_sp->GetChildMemberWithName(name: "__rep_" ); |
796 | if (!ptr_sp) |
797 | return false; |
798 | |
799 | #ifndef _WIN32 |
800 | // The date time in the chrono library is valid in the range |
801 | // [-32767-01-01T00:00:00Z, 32767-12-31T23:59:59Z]. A 64-bit time_t has a |
802 | // larger range, the function strftime is not able to format the entire range |
803 | // of time_t. The exact point has not been investigated; it's limited to |
804 | // chrono's range. |
805 | const std::time_t chrono_timestamp_min = |
806 | -1'096'193'779'200; // -32767-01-01T00:00:00Z |
807 | const std::time_t chrono_timestamp_max = |
808 | 971'890'963'199; // 32767-12-31T23:59:59Z |
809 | #else |
810 | const std::time_t chrono_timestamp_min = -43'200; // 1969-12-31T12:00:00Z |
811 | const std::time_t chrono_timestamp_max = |
812 | 32'536'850'399; // 3001-01-19T21:59:59 |
813 | #endif |
814 | |
815 | const std::time_t seconds = ptr_sp->GetValueAsSigned(fail_value: 0); |
816 | if (seconds < chrono_timestamp_min || seconds > chrono_timestamp_max) |
817 | stream.Printf(format: "timestamp=%" PRId64 " s" , static_cast<int64_t>(seconds)); |
818 | else { |
819 | std::array<char, 128> str; |
820 | std::size_t size = |
821 | std::strftime(s: str.data(), maxsize: str.size(), format: fmt, tp: gmtime(timer: &seconds)); |
822 | if (size == 0) |
823 | return false; |
824 | |
825 | stream.Printf(format: "date/time=%s timestamp=%" PRId64 " s" , str.data(), |
826 | static_cast<int64_t>(seconds)); |
827 | } |
828 | |
829 | return true; |
830 | } |
831 | |
832 | bool lldb_private::formatters::LibcxxChronoSysSecondsSummaryProvider( |
833 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
834 | return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options, |
835 | fmt: "%FT%H:%M:%SZ" ); |
836 | } |
837 | |
838 | bool lldb_private::formatters::LibcxxChronoLocalSecondsSummaryProvider( |
839 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
840 | return LibcxxChronoTimePointSecondsSummaryProvider(valobj, stream, options, |
841 | fmt: "%FT%H:%M:%S" ); |
842 | } |
843 | |
844 | static bool |
845 | LibcxxChronoTimepointDaysSummaryProvider(ValueObject &valobj, Stream &stream, |
846 | const TypeSummaryOptions &options, |
847 | const char *fmt) { |
848 | ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__d_" ); |
849 | if (!ptr_sp) |
850 | return false; |
851 | ptr_sp = ptr_sp->GetChildMemberWithName(name: "__rep_" ); |
852 | if (!ptr_sp) |
853 | return false; |
854 | |
855 | #ifndef _WIN32 |
856 | // The date time in the chrono library is valid in the range |
857 | // [-32767-01-01Z, 32767-12-31Z]. A 32-bit time_t has a larger range, the |
858 | // function strftime is not able to format the entire range of time_t. The |
859 | // exact point has not been investigated; it's limited to chrono's range. |
860 | const int chrono_timestamp_min = -12'687'428; // -32767-01-01Z |
861 | const int chrono_timestamp_max = 11'248'737; // 32767-12-31Z |
862 | #else |
863 | const int chrono_timestamp_min = 0; // 1970-01-01Z |
864 | const int chrono_timestamp_max = 376'583; // 3001-01-19Z |
865 | #endif |
866 | |
867 | const int days = ptr_sp->GetValueAsSigned(fail_value: 0); |
868 | if (days < chrono_timestamp_min || days > chrono_timestamp_max) |
869 | stream.Printf(format: "timestamp=%d days" , days); |
870 | |
871 | else { |
872 | const std::time_t seconds = std::time_t(86400) * days; |
873 | |
874 | std::array<char, 128> str; |
875 | std::size_t size = |
876 | std::strftime(s: str.data(), maxsize: str.size(), format: fmt, tp: gmtime(timer: &seconds)); |
877 | if (size == 0) |
878 | return false; |
879 | |
880 | stream.Printf(format: "date=%s timestamp=%d days" , str.data(), days); |
881 | } |
882 | |
883 | return true; |
884 | } |
885 | |
886 | bool lldb_private::formatters::LibcxxChronoSysDaysSummaryProvider( |
887 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
888 | return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options, |
889 | fmt: "%FZ" ); |
890 | } |
891 | |
892 | bool lldb_private::formatters::LibcxxChronoLocalDaysSummaryProvider( |
893 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
894 | return LibcxxChronoTimepointDaysSummaryProvider(valobj, stream, options, |
895 | fmt: "%F" ); |
896 | } |
897 | |
898 | bool lldb_private::formatters::LibcxxChronoMonthSummaryProvider( |
899 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
900 | // FIXME: These are the names used in the C++20 ostream operator. Since LLVM |
901 | // uses C++17 it's not possible to use the ostream operator directly. |
902 | static const std::array<std::string_view, 12> months = { |
903 | "January" , "February" , "March" , "April" , "May" , "June" , |
904 | "July" , "August" , "September" , "October" , "November" , "December" }; |
905 | |
906 | ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__m_" ); |
907 | if (!ptr_sp) |
908 | return false; |
909 | |
910 | const unsigned month = ptr_sp->GetValueAsUnsigned(fail_value: 0); |
911 | if (month >= 1 && month <= 12) |
912 | stream << "month=" << months[month - 1]; |
913 | else |
914 | stream.Printf(format: "month=%u" , month); |
915 | |
916 | return true; |
917 | } |
918 | |
919 | bool lldb_private::formatters::LibcxxChronoWeekdaySummaryProvider( |
920 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
921 | // FIXME: These are the names used in the C++20 ostream operator. Since LLVM |
922 | // uses C++17 it's not possible to use the ostream operator directly. |
923 | static const std::array<std::string_view, 7> weekdays = { |
924 | "Sunday" , "Monday" , "Tuesday" , "Wednesday" , |
925 | "Thursday" , "Friday" , "Saturday" }; |
926 | |
927 | ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__wd_" ); |
928 | if (!ptr_sp) |
929 | return false; |
930 | |
931 | const unsigned weekday = ptr_sp->GetValueAsUnsigned(fail_value: 0); |
932 | if (weekday < 7) |
933 | stream << "weekday=" << weekdays[weekday]; |
934 | else |
935 | stream.Printf(format: "weekday=%u" , weekday); |
936 | |
937 | return true; |
938 | } |
939 | |
940 | bool lldb_private::formatters::LibcxxChronoYearMonthDaySummaryProvider( |
941 | ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { |
942 | ValueObjectSP ptr_sp = valobj.GetChildMemberWithName(name: "__y_" ); |
943 | if (!ptr_sp) |
944 | return false; |
945 | ptr_sp = ptr_sp->GetChildMemberWithName(name: "__y_" ); |
946 | if (!ptr_sp) |
947 | return false; |
948 | int year = ptr_sp->GetValueAsSigned(fail_value: 0); |
949 | |
950 | ptr_sp = valobj.GetChildMemberWithName(name: "__m_" ); |
951 | if (!ptr_sp) |
952 | return false; |
953 | ptr_sp = ptr_sp->GetChildMemberWithName(name: "__m_" ); |
954 | if (!ptr_sp) |
955 | return false; |
956 | const unsigned month = ptr_sp->GetValueAsUnsigned(fail_value: 0); |
957 | |
958 | ptr_sp = valobj.GetChildMemberWithName(name: "__d_" ); |
959 | if (!ptr_sp) |
960 | return false; |
961 | ptr_sp = ptr_sp->GetChildMemberWithName(name: "__d_" ); |
962 | if (!ptr_sp) |
963 | return false; |
964 | const unsigned day = ptr_sp->GetValueAsUnsigned(fail_value: 0); |
965 | |
966 | stream << "date=" ; |
967 | if (year < 0) { |
968 | stream << '-'; |
969 | year = -year; |
970 | } |
971 | stream.Printf(format: "%04d-%02u-%02u" , year, month, day); |
972 | |
973 | return true; |
974 | } |
975 | |