LLDB mainline
LibStdcpp.cpp
Go to the documentation of this file.
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
15
18#include "lldb/Target/Target.h"
20#include "lldb/Utility/Endian.h"
22#include "lldb/Utility/Status.h"
23#include "lldb/Utility/Stream.h"
26#include "llvm/Support/ErrorExtras.h"
27#include <optional>
28
29using namespace lldb;
30using namespace lldb_private;
31using namespace lldb_private::formatters;
32
33namespace {
34
36
37class LibstdcppMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
38 /*
39 (std::_Rb_tree_iterator<std::pair<const int, std::basic_string<char,
40 std::char_traits<char>, std::allocator<char> > > >) ibeg = {
41 (_Base_ptr) _M_node = 0x0000000100103910 {
42 (std::_Rb_tree_color) _M_color = _S_black
43 (std::_Rb_tree_node_base::_Base_ptr) _M_parent = 0x00000001001038c0
44 (std::_Rb_tree_node_base::_Base_ptr) _M_left = 0x0000000000000000
45 (std::_Rb_tree_node_base::_Base_ptr) _M_right = 0x0000000000000000
46 }
47 }
48 */
49
50public:
51 explicit LibstdcppMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
52
53 llvm::Expected<uint32_t> CalculateNumChildren() override;
54
55 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
56
57 lldb::ChildCacheState Update() override;
58
59 llvm::Expected<size_t> GetIndexOfChildWithName(llvm::StringRef name) override;
60
61private:
62 ExecutionContextRef m_exe_ctx_ref;
63 lldb::addr_t m_pair_address = 0;
64 CompilerType m_pair_type;
65 lldb::ValueObjectSP m_pair_sp;
66};
67
68class LibStdcppSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
69public:
70 explicit LibStdcppSharedPtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
71
72 llvm::Expected<uint32_t> CalculateNumChildren() override;
73
74 lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
75
76 lldb::ChildCacheState Update() override;
77
78 llvm::Expected<size_t> GetIndexOfChildWithName(llvm::StringRef name) override;
79
80private:
81 // The lifetime of a ValueObject and all its derivative ValueObjects
82 // (children, clones, etc.) is managed by a ClusterManager. These
83 // objects are only destroyed when every shared pointer to any of them
84 // is destroyed, so we must not store a shared pointer to any ValueObject
85 // derived from our backend ValueObject (since we're in the same cluster).
86 ValueObject *m_ptr_obj = nullptr; // Underlying pointer (held, not owned)
87};
88
89} // end of anonymous namespace
90
91LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd(
92 lldb::ValueObjectSP valobj_sp)
93 : SyntheticChildrenFrontEnd(*valobj_sp), m_exe_ctx_ref(), m_pair_type(),
94 m_pair_sp() {
95 if (valobj_sp)
96 Update();
97}
98
99lldb::ChildCacheState LibstdcppMapIteratorSyntheticFrontEnd::Update() {
100 ValueObjectSP valobj_sp = m_backend.GetSP();
101 if (!valobj_sp)
103
104 TargetSP target_sp(valobj_sp->GetTargetSP());
105
106 if (!target_sp)
108
109 bool is_64bit = (target_sp->GetArchitecture().GetAddressByteSize() == 8);
110
111 if (!valobj_sp)
113 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
114
115 ValueObjectSP _M_node_sp(valobj_sp->GetChildMemberWithName("_M_node"));
116 if (!_M_node_sp)
118
119 m_pair_address = _M_node_sp->GetValueAsUnsigned(0);
120 if (m_pair_address == 0)
122
123 m_pair_address += (is_64bit ? 32 : 16);
124
125 CompilerType my_type(valobj_sp->GetCompilerType());
126 if (my_type.GetNumTemplateArguments() >= 1) {
127 CompilerType pair_type = my_type.GetTypeTemplateArgument(0);
128 if (!pair_type)
130 m_pair_type = pair_type;
131 } else
133
135}
136
137llvm::Expected<uint32_t>
138LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
139 return 2;
140}
141
143LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
144 if (m_pair_address != 0 && m_pair_type) {
145 if (!m_pair_sp)
146 m_pair_sp = CreateChildValueObjectFromAddress("pair", m_pair_address,
147 m_exe_ctx_ref, m_pair_type);
148 if (m_pair_sp)
149 return m_pair_sp->GetChildAtIndex(idx);
150 }
151 return lldb::ValueObjectSP();
152}
153
154llvm::Expected<size_t>
155LibstdcppMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName(
156 llvm::StringRef name) {
157 if (name == "first")
158 return 0;
159 if (name == "second")
160 return 1;
161 return llvm::createStringErrorV("type has no child named '{0}'", name);
162}
163
164SyntheticChildrenFrontEnd *
166 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
167 return (valobj_sp ? new LibstdcppMapIteratorSyntheticFrontEnd(valobj_sp)
168 : nullptr);
169}
170
171/*
172 (lldb) fr var ibeg --ptr-depth 1
173 (__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >)
174 ibeg = {
175 _M_current = 0x00000001001037a0 {
176 *_M_current = 1
177 }
178 }
179 */
180
181SyntheticChildrenFrontEnd *
183 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
184 return (valobj_sp ? new VectorIteratorSyntheticFrontEnd(
185 valobj_sp, {ConstString("_M_current")})
186 : nullptr);
187}
188
190 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
191 ValueObjectSP ptr = valobj.GetChildAtNamePath({"_M_dataplus", "_M_p"});
192 if (!ptr || !ptr->GetError().Success())
193 stream << "Summary Unavailable";
194 else
195 stream << ptr->GetSummaryAsCString();
196
197 return true;
198}
199
200template <StringPrinter::StringElementType element_type>
201static bool formatStringViewImpl(ValueObject &valobj, Stream &stream,
202 const TypeSummaryOptions &summary_options,
203 std::string prefix_token) {
204 auto data_sp = valobj.GetChildMemberWithName("_M_str");
205 auto size_sp = valobj.GetChildMemberWithName("_M_len");
206 if (!data_sp || !size_sp)
207 return false;
208
209 bool success = false;
210 uint64_t size = size_sp->GetValueAsUnsigned(0, &success);
211 if (!success) {
212 stream << "Summary Unavailable";
213 return true;
214 }
215
216 StreamString scratch_stream;
218 scratch_stream, summary_options, data_sp, size, prefix_token);
219
220 if (success)
221 stream << scratch_stream.GetData();
222 else
223 stream << "Summary Unavailable";
224 return true;
225}
226
228 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
229 auto wchar_t_size = GetWCharByteSize(valobj);
230 if (!wchar_t_size)
231 return false;
232
233 switch (*wchar_t_size) {
234 case 1:
236 options, "L");
237 case 2:
239 options, "L");
240 case 4:
242 options, "L");
243 }
244 return false;
245}
246
247template <StringElementType element_type>
248static constexpr const char *getPrefixToken() {
249 switch (element_type) {
250 case StringElementType::ASCII:
251 return "";
252 case StringElementType::UTF8:
253 return "u8";
254 case StringElementType::UTF16:
255 return "u";
256 case StringElementType::UTF32:
257 return "U";
258 }
259 llvm_unreachable("invalid element type");
260}
261
262template <StringPrinter::StringElementType element_type>
268
270 StringElementType::ASCII>(ValueObject &, Stream &,
271 const TypeSummaryOptions &);
273 StringElementType::UTF8>(ValueObject &, Stream &,
274 const TypeSummaryOptions &);
276 StringElementType::UTF16>(ValueObject &, Stream &,
277 const TypeSummaryOptions &);
279 StringElementType::UTF32>(ValueObject &, Stream &,
280 const TypeSummaryOptions &);
281
282LibStdcppSharedPtrSyntheticFrontEnd::LibStdcppSharedPtrSyntheticFrontEnd(
283 lldb::ValueObjectSP valobj_sp)
284 : SyntheticChildrenFrontEnd(*valobj_sp) {
285 if (valobj_sp)
286 Update();
287}
288
289llvm::Expected<uint32_t>
290LibStdcppSharedPtrSyntheticFrontEnd::CalculateNumChildren() {
291 return 1;
292}
293
295LibStdcppSharedPtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
296 if (!m_ptr_obj)
297 return nullptr;
298
299 if (idx == 0)
300 return m_ptr_obj->GetSP();
301
302 if (idx == 1) {
303 ValueObjectSP valobj_sp = m_backend.GetSP();
304 if (!valobj_sp)
305 return nullptr;
306
307 Status status;
308 ValueObjectSP value_sp = m_ptr_obj->Dereference(status);
309 if (status.Success())
310 return value_sp;
311 }
312 return lldb::ValueObjectSP();
313}
314
315lldb::ChildCacheState LibStdcppSharedPtrSyntheticFrontEnd::Update() {
316 auto backend = m_backend.GetSP();
317 if (!backend)
319
320 auto valobj_sp = backend->GetNonSyntheticValue();
321 if (!valobj_sp)
323
324 auto ptr_obj_sp = valobj_sp->GetChildMemberWithName("_M_ptr");
325 if (!ptr_obj_sp)
327
328 auto cast_ptr_sp = GetDesugaredSmartPointerValue(*ptr_obj_sp, *valobj_sp);
329 if (!cast_ptr_sp)
331
332 m_ptr_obj = cast_ptr_sp->Clone("pointer").get();
333
335}
336
337llvm::Expected<size_t>
338LibStdcppSharedPtrSyntheticFrontEnd::GetIndexOfChildWithName(
339 llvm::StringRef name) {
340 if (name == "pointer")
341 return 0;
342
343 if (name == "object" || name == "$$dereference$$")
344 return 1;
345
346 return llvm::createStringErrorV("type has no child named '{0}'", name);
347}
348
349SyntheticChildrenFrontEnd *
351 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
352 return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp)
353 : nullptr);
354}
355
357 bool is_atomic_child) {
358 ValueObjectSP refcount_sp = parent.GetChildMemberWithName("_M_refcount");
359 if (!refcount_sp)
360 return nullptr;
361
362 if (!is_atomic_child)
363 return refcount_sp->GetChildMemberWithName("_M_pi");
364
365 ValueObjectSP val_sp = refcount_sp->GetChildMemberWithName("_M_val");
366 if (!val_sp || !val_sp->GetCompilerType())
367 return nullptr;
368
369 // May be a scalar value depending on libstdc++ version
370 if (!val_sp->GetCompilerType().IsScalarType()) {
371 val_sp = val_sp->GetChildMemberWithName("_M_i");
372 if (!val_sp)
373 return nullptr;
374 }
375
376 // Extract and align the raw value
377 bool is_success = false;
378 uint64_t raw_val = val_sp->GetValueAsUnsigned(0, &is_success);
379 if (!is_success || raw_val == 0)
380 return nullptr;
381 // Clear the least significant bit. See
382 // https://github.com/gcc-mirror/gcc/blob/releases/gcc-15.2.0/libstdc%2B%2B-v3/include/bits/shared_ptr_atomic.h#L465
383 raw_val &= ~1ULL;
384
385 // Get the pointer type
386 CompilerType ref_count_ptr_type =
387 refcount_sp->GetCompilerType().GetDirectNestedTypeWithName("pointer");
388 if (!ref_count_ptr_type)
389 return nullptr;
390
392 "", raw_val, parent.GetExecutionContextRef(), ref_count_ptr_type,
393 /*do_deref=*/false);
394}
395
397 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options,
398 bool is_atomic_child) {
399 ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
400 if (!valobj_sp)
401 return false;
402
403 ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("_M_ptr"));
404 if (!ptr_sp)
405 return false;
406
407 DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);
408
409 ValueObjectSP pi_sp = GetReferenceCountPointer(*valobj_sp, is_atomic_child);
410 if (!pi_sp)
411 return false;
412
413 bool success;
414 uint64_t pi_addr = pi_sp->GetValueAsUnsigned(0, &success);
415 // Empty control field. We're done.
416 if (!success || pi_addr == 0)
417 return true;
418
419 int64_t shared_count = 0;
420 if (auto count_sp = pi_sp->GetChildMemberWithName("_M_use_count")) {
421 bool success;
422 shared_count = count_sp->GetValueAsSigned(0, &success);
423 if (!success)
424 return false;
425
426 stream.Printf(" strong=%" PRId64, shared_count);
427 }
428
429 // _M_weak_count is the number of weak references + (_M_use_count != 0).
430 if (auto weak_count_sp = pi_sp->GetChildMemberWithName("_M_weak_count")) {
431 bool success;
432 int64_t count = weak_count_sp->GetValueAsUnsigned(0, &success);
433 if (!success)
434 return false;
435
436 stream.Printf(" weak=%" PRId64, count - (shared_count != 0));
437 }
438
439 return true;
440}
441
442static uint64_t LibStdcppVariantNposValue(size_t index_byte_size) {
443 switch (index_byte_size) {
444 case 1:
445 return 0xff;
446 case 2:
447 return 0xffff;
448 default:
449 return 0xffff'ffff;
450 }
451}
452
454 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
455 ValueObjectSP valobj_sp = valobj.GetNonSyntheticValue();
456 if (!valobj_sp)
457 return false;
458
459 ValueObjectSP index_obj = valobj_sp->GetChildMemberWithName("_M_index");
460 ValueObjectSP data_obj = valobj_sp->GetChildMemberWithName("_M_u");
461 if (!index_obj || !data_obj)
462 return false;
463
464 auto index_bytes_or_err = index_obj->GetByteSize();
465 if (!index_bytes_or_err) {
467 index_bytes_or_err.takeError(),
468 "failed to get variant index byte size: {0}");
469 return false;
470 }
471 auto npos_value = LibStdcppVariantNposValue(*index_bytes_or_err);
472 auto index = index_obj->GetValueAsUnsigned(0);
473 if (index == npos_value) {
474 stream.Printf(" No Value");
475 return true;
476 }
477
478 auto variant_type =
479 valobj_sp->GetCompilerType().GetCanonicalType().GetNonReferenceType();
480 if (!variant_type)
481 return false;
482 if (index >= variant_type.GetNumTemplateArguments(true)) {
483 stream.Printf(" <Invalid>");
484 return true;
485 }
486
487 auto active_type = variant_type.GetTypeTemplateArgument(index, true);
488 stream << " Active Type = " << active_type.GetDisplayTypeName() << " ";
489 return true;
490}
491
492static std::optional<int64_t>
494 lldb::ValueObjectSP value_sp = valobj.GetChildMemberWithName("_M_value");
495 if (!value_sp)
496 return std::nullopt;
497 bool success;
498 int64_t value = value_sp->GetValueAsSigned(0, &success);
499 if (!success)
500 return std::nullopt;
501 return value;
502}
503
505 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
506 std::optional<int64_t> value = LibStdcppExtractOrderingValue(valobj);
507 if (!value)
508 return false;
509 switch (*value) {
510 case -1:
511 stream << "less";
512 break;
513 case 0:
514 stream << "equivalent";
515 break;
516 case 1:
517 stream << "greater";
518 break;
519 case -128:
520 case 2:
521 stream << "unordered";
522 break;
523 default:
524 return false;
525 }
526 return true;
527}
528
530 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
531 std::optional<int64_t> value = LibStdcppExtractOrderingValue(valobj);
532 if (!value)
533 return false;
534 switch (*value) {
535 case -1:
536 stream << "less";
537 break;
538 case 0:
539 stream << "equivalent";
540 break;
541 case 1:
542 stream << "greater";
543 break;
544 default:
545 return false;
546 }
547 return true;
548}
549
551 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
552 std::optional<int64_t> value = LibStdcppExtractOrderingValue(valobj);
553 if (!value)
554 return false;
555 switch (*value) {
556 case -1:
557 stream << "less";
558 break;
559 case 0:
560 stream << "equal";
561 break;
562 case 1:
563 stream << "greater";
564 break;
565 default:
566 return false;
567 }
568 return true;
569}
570
572 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
573 ValueObjectSP impl_sp = valobj.GetChildMemberWithName("_M_impl");
574 if (!impl_sp)
575 return false;
576
577 ValueObjectSP file_sp = impl_sp->GetChildMemberWithName("_M_file_name");
578 ValueObjectSP function_sp =
579 impl_sp->GetChildMemberWithName("_M_function_name");
580 ValueObjectSP line_sp = impl_sp->GetChildMemberWithName("_M_line");
581 ValueObjectSP column_sp = impl_sp->GetChildMemberWithName("_M_column");
582
583 if (!file_sp || !function_sp || !line_sp || !column_sp)
584 return false;
585
586 bool success = false;
587 uint64_t line = line_sp->GetValueAsUnsigned(0, &success);
588 if (!success)
589 return false;
590
591 uint64_t column = column_sp->GetValueAsUnsigned(0, &success);
592 if (!success)
593 return false;
594
595 const char *file = file_sp->GetSummaryAsCString();
596 stream.Format("{0}:{1}:{2}", file ? file : "<unknown>", line, column);
597
598 if (const char *function = function_sp->GetSummaryAsCString())
599 stream.Printf(" (%s)", function);
600
601 return true;
602}
static bool formatStringViewImpl(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &summary_options, std::string prefix_token)
Definition LibCxx.cpp:725
static constexpr const char * getPrefixToken()
static std::optional< int64_t > LibStdcppExtractOrderingValue(ValueObject &valobj)
static ValueObjectSP GetReferenceCountPointer(ValueObject &parent, bool is_atomic_child)
static uint64_t LibStdcppVariantNposValue(size_t index_byte_size)
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:405
StringPrinter::StringElementType StringElementType
static std::optional< size_t > CalculateNumChildren(CompilerType container_elem_type, uint64_t num_elements, CompilerType element_type)
Calculates the number of elements stored in a container (with element type 'container_elem_type') as ...
Generic representation of a type in a programming language.
bool Success() const
Test for success condition.
Definition Status.cpp:303
const char * GetData() const
A stream class that can stream formatted output to a file.
Definition Stream.h:28
void Format(const char *format, Args &&... args)
Forwards the arguments to llvm::formatv and writes to the stream.
Definition Stream.h:370
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition Stream.cpp:134
lldb::ValueObjectSP GetSP()
virtual lldb::ValueObjectSP GetChildMemberWithName(llvm::StringRef name, bool can_create=true)
lldb::ValueObjectSP GetChildAtNamePath(llvm::ArrayRef< llvm::StringRef > names)
virtual lldb::ValueObjectSP Clone(llvm::StringRef new_name)
Creates a copy of the ValueObject with a new name and setting the current ValueObject as its parent.
virtual lldb::ValueObjectSP Dereference(Status &error)
virtual lldb::ValueObjectSP GetNonSyntheticValue()
const ExecutionContextRef & GetExecutionContextRef() const
static lldb::ValueObjectSP CreateValueObjectFromAddress(llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, CompilerType type, bool do_deref=true, ValueObject *parent=nullptr)
Given an address either create a value object containing the value at that address,...
lldb::ValueObjectSP GetDesugaredSmartPointerValue(ValueObject &ptr, ValueObject &container)
Return the ValueObjectSP of the underlying pointer member whose type is a desugared 'std::shared_ptr:...
Definition Generic.cpp:13
bool LibStdcppStringViewSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
bool LibStdcppStrongOrderingSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
std::optional< uint64_t > GetWCharByteSize(ValueObject &valobj)
bool StringBufferSummaryProvider(Stream &stream, const TypeSummaryOptions &summary_options, lldb::ValueObjectSP location_sp, uint64_t size, std::string prefix_token)
Print a summary for a string buffer to stream.
bool LibStdcppStringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
SyntheticChildrenFrontEnd * LibStdcppVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
SyntheticChildrenFrontEnd * LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
bool LibStdcppPartialOrderingSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
SyntheticChildrenFrontEnd * LibStdcppSharedPtrSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP)
bool LibStdcppSmartPointerSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options, bool is_atomic_child=false)
bool LibStdcppSourceLocationSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
bool LibStdcppVariantSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
void DumpCxxSmartPtrPointerSummary(Stream &stream, ValueObject &ptr, const TypeSummaryOptions &options)
Prints the summary for the pointer value of a C++ std::unique_ptr/stdshared_ptr/stdweak_ptr.
bool LibStdcppWStringViewSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
bool LibStdcppWeakOrderingSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:338
ChildCacheState
Specifies if children need to be re-computed after a call to SyntheticChildrenFrontEnd::Update.
@ eRefetch
Children need to be recomputed dynamically.
@ eReuse
Children did not change and don't need to be recomputed; re-use what we computed the last time we cal...
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP
uint64_t addr_t
Definition lldb-types.h:80
std::shared_ptr< lldb_private::Target > TargetSP