1 | //===-- GenericBitset.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 | #include "LibStdcpp.h" |
11 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
12 | #include "lldb/DataFormatters/FormattersHelpers.h" |
13 | #include "lldb/Target/Target.h" |
14 | #include <optional> |
15 | |
16 | using namespace lldb; |
17 | using namespace lldb_private; |
18 | |
19 | namespace { |
20 | |
21 | /// This class can be used for handling bitsets from both libcxx and libstdcpp. |
22 | class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd { |
23 | public: |
24 | enum class StdLib { |
25 | LibCxx, |
26 | LibStdcpp, |
27 | }; |
28 | |
29 | GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib); |
30 | |
31 | size_t GetIndexOfChildWithName(ConstString name) override { |
32 | return formatters::ExtractIndexFromString(item_name: name.GetCString()); |
33 | } |
34 | |
35 | bool MightHaveChildren() override { return true; } |
36 | lldb::ChildCacheState Update() override; |
37 | llvm::Expected<uint32_t> CalculateNumChildren() override { |
38 | return m_elements.size(); |
39 | } |
40 | ValueObjectSP GetChildAtIndex(uint32_t idx) override; |
41 | |
42 | private: |
43 | llvm::StringRef GetDataContainerMemberName(); |
44 | |
45 | // The lifetime of a ValueObject and all its derivative ValueObjects |
46 | // (children, clones, etc.) is managed by a ClusterManager. These |
47 | // objects are only destroyed when every shared pointer to any of them |
48 | // is destroyed, so we must not store a shared pointer to any ValueObject |
49 | // derived from our backend ValueObject (since we're in the same cluster). |
50 | // Value objects created from raw data (i.e. in a different cluster) must |
51 | // be referenced via shared pointer to keep them alive, however. |
52 | std::vector<ValueObjectSP> m_elements; |
53 | ValueObject *m_first = nullptr; |
54 | CompilerType m_bool_type; |
55 | ByteOrder m_byte_order = eByteOrderInvalid; |
56 | uint8_t m_byte_size = 0; |
57 | StdLib m_stdlib; |
58 | }; |
59 | } // namespace |
60 | |
61 | GenericBitsetFrontEnd::GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib) |
62 | : SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) { |
63 | m_bool_type = valobj.GetCompilerType().GetBasicTypeFromAST(basic_type: eBasicTypeBool); |
64 | if (auto target_sp = m_backend.GetTargetSP()) { |
65 | m_byte_order = target_sp->GetArchitecture().GetByteOrder(); |
66 | m_byte_size = target_sp->GetArchitecture().GetAddressByteSize(); |
67 | Update(); |
68 | } |
69 | } |
70 | |
71 | llvm::StringRef GenericBitsetFrontEnd::GetDataContainerMemberName() { |
72 | static constexpr llvm::StringLiteral s_libcxx_case("__first_" ); |
73 | static constexpr llvm::StringLiteral s_libstdcpp_case("_M_w" ); |
74 | switch (m_stdlib) { |
75 | case StdLib::LibCxx: |
76 | return s_libcxx_case; |
77 | case StdLib::LibStdcpp: |
78 | return s_libstdcpp_case; |
79 | } |
80 | llvm_unreachable("Unknown StdLib enum" ); |
81 | } |
82 | |
83 | lldb::ChildCacheState GenericBitsetFrontEnd::Update() { |
84 | m_elements.clear(); |
85 | m_first = nullptr; |
86 | |
87 | TargetSP target_sp = m_backend.GetTargetSP(); |
88 | if (!target_sp) |
89 | return lldb::ChildCacheState::eRefetch; |
90 | |
91 | size_t size = 0; |
92 | |
93 | if (auto arg = m_backend.GetCompilerType().GetIntegralTemplateArgument(idx: 0)) |
94 | size = arg->value.getLimitedValue(); |
95 | |
96 | m_elements.assign(n: size, val: ValueObjectSP()); |
97 | m_first = |
98 | m_backend.GetChildMemberWithName(name: GetDataContainerMemberName()).get(); |
99 | return lldb::ChildCacheState::eRefetch; |
100 | } |
101 | |
102 | ValueObjectSP GenericBitsetFrontEnd::GetChildAtIndex(uint32_t idx) { |
103 | if (idx >= m_elements.size() || !m_first) |
104 | return ValueObjectSP(); |
105 | |
106 | if (m_elements[idx]) |
107 | return m_elements[idx]; |
108 | |
109 | ExecutionContext ctx = m_backend.GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped: false); |
110 | CompilerType type; |
111 | ValueObjectSP chunk; |
112 | // For small bitsets __first_ is not an array, but a plain size_t. |
113 | if (m_first->GetCompilerType().IsArrayType(element_type: &type)) { |
114 | std::optional<uint64_t> bit_size = |
115 | type.GetBitSize(exe_scope: ctx.GetBestExecutionContextScope()); |
116 | if (!bit_size || *bit_size == 0) |
117 | return {}; |
118 | chunk = m_first->GetChildAtIndex(idx: idx / *bit_size); |
119 | } else { |
120 | type = m_first->GetCompilerType(); |
121 | chunk = m_first->GetSP(); |
122 | } |
123 | if (!type || !chunk) |
124 | return {}; |
125 | |
126 | std::optional<uint64_t> bit_size = |
127 | type.GetBitSize(exe_scope: ctx.GetBestExecutionContextScope()); |
128 | if (!bit_size || *bit_size == 0) |
129 | return {}; |
130 | size_t chunk_idx = idx % *bit_size; |
131 | uint8_t value = !!(chunk->GetValueAsUnsigned(fail_value: 0) & (uint64_t(1) << chunk_idx)); |
132 | DataExtractor data(&value, sizeof(value), m_byte_order, m_byte_size); |
133 | |
134 | m_elements[idx] = CreateValueObjectFromData(name: llvm::formatv(Fmt: "[{0}]" , Vals&: idx).str(), |
135 | data, exe_ctx: ctx, type: m_bool_type); |
136 | |
137 | return m_elements[idx]; |
138 | } |
139 | |
140 | SyntheticChildrenFrontEnd *formatters::LibStdcppBitsetSyntheticFrontEndCreator( |
141 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
142 | if (valobj_sp) |
143 | return new GenericBitsetFrontEnd(*valobj_sp, |
144 | GenericBitsetFrontEnd::StdLib::LibStdcpp); |
145 | return nullptr; |
146 | } |
147 | |
148 | SyntheticChildrenFrontEnd *formatters::LibcxxBitsetSyntheticFrontEndCreator( |
149 | CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { |
150 | if (valobj_sp) |
151 | return new GenericBitsetFrontEnd(*valobj_sp, |
152 | GenericBitsetFrontEnd::StdLib::LibCxx); |
153 | return nullptr; |
154 | } |
155 | |