1 //===-- LibCxxUnorderedMap.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 "Plugins/TypeSystem/Clang/TypeSystemClang.h" 12 #include "lldb/Core/ValueObject.h" 13 #include "lldb/Core/ValueObjectConstResult.h" 14 #include "lldb/DataFormatters/FormattersHelpers.h" 15 #include "lldb/Target/Target.h" 16 #include "lldb/Utility/ConstString.h" 17 #include "lldb/Utility/DataBufferHeap.h" 18 #include "lldb/Utility/Endian.h" 19 #include "lldb/Utility/Status.h" 20 #include "lldb/Utility/Stream.h" 21 #include "llvm/ADT/StringRef.h" 22 23 using namespace lldb; 24 using namespace lldb_private; 25 using namespace lldb_private::formatters; 26 27 namespace lldb_private { 28 namespace formatters { 29 class LibcxxStdUnorderedMapSyntheticFrontEnd 30 : public SyntheticChildrenFrontEnd { 31 public: 32 LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 33 34 ~LibcxxStdUnorderedMapSyntheticFrontEnd() override = default; 35 36 size_t CalculateNumChildren() override; 37 38 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 39 40 bool Update() override; 41 42 bool MightHaveChildren() override; 43 44 size_t GetIndexOfChildWithName(ConstString name) override; 45 46 private: 47 CompilerType m_element_type; 48 CompilerType m_node_type; 49 ValueObject *m_tree = nullptr; 50 size_t m_num_elements = 0; 51 ValueObject *m_next_element = nullptr; 52 std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache; 53 }; 54 } // namespace formatters 55 } // namespace lldb_private 56 57 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: 58 LibcxxStdUnorderedMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 59 : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), 60 m_elements_cache() { 61 if (valobj_sp) 62 Update(); 63 } 64 65 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: 66 CalculateNumChildren() { 67 return m_num_elements; 68 } 69 70 static void consumeInlineNamespace(llvm::StringRef &name) { 71 // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+:: 72 auto scratch = name; 73 if (scratch.consume_front("__") && std::isalnum(scratch[0])) { 74 scratch = scratch.drop_while([](char c) { return std::isalnum(c); }); 75 if (scratch.consume_front("::")) { 76 // Successfully consumed a namespace. 77 name = scratch; 78 } 79 } 80 } 81 82 static bool isStdTemplate(ConstString type_name, llvm::StringRef type) { 83 llvm::StringRef name = type_name.GetStringRef(); 84 // The type name may be prefixed with `std::__<inline-namespace>::`. 85 if (name.consume_front("std::")) 86 consumeInlineNamespace(name); 87 return name.consume_front(type) && name.startswith("<"); 88 } 89 90 static bool isUnorderedMap(ConstString type_name) { 91 return isStdTemplate(type_name, "unordered_map") || 92 isStdTemplate(type_name, "unordered_multimap"); 93 } 94 95 lldb::ValueObjectSP lldb_private::formatters:: 96 LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) { 97 if (idx >= CalculateNumChildren()) 98 return lldb::ValueObjectSP(); 99 if (m_tree == nullptr) 100 return lldb::ValueObjectSP(); 101 102 while (idx >= m_elements_cache.size()) { 103 if (m_next_element == nullptr) 104 return lldb::ValueObjectSP(); 105 106 Status error; 107 ValueObjectSP node_sp = m_next_element->Dereference(error); 108 if (!node_sp || error.Fail()) 109 return lldb::ValueObjectSP(); 110 111 ValueObjectSP value_sp = node_sp->GetChildMemberWithName("__value_", true); 112 ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_", true); 113 if (!hash_sp || !value_sp) { 114 if (!m_element_type) { 115 auto p1_sp = m_backend.GetChildAtNamePath({ConstString("__table_"), 116 ConstString("__p1_")}); 117 if (!p1_sp) 118 return nullptr; 119 120 ValueObjectSP first_sp = nullptr; 121 switch (p1_sp->GetCompilerType().GetNumDirectBaseClasses()) { 122 case 1: 123 // Assume a pre llvm r300140 __compressed_pair implementation: 124 first_sp = p1_sp->GetChildMemberWithName("__first_", true); 125 break; 126 case 2: { 127 // Assume a post llvm r300140 __compressed_pair implementation: 128 ValueObjectSP first_elem_parent_sp = 129 p1_sp->GetChildAtIndex(0, true); 130 first_sp = p1_sp->GetChildMemberWithName("__value_", true); 131 break; 132 } 133 default: 134 return nullptr; 135 } 136 137 if (!first_sp) 138 return nullptr; 139 m_element_type = first_sp->GetCompilerType(); 140 m_element_type = m_element_type.GetTypeTemplateArgument(0); 141 m_element_type = m_element_type.GetPointeeType(); 142 m_node_type = m_element_type; 143 m_element_type = m_element_type.GetTypeTemplateArgument(0); 144 // This synthetic provider is used for both unordered_(multi)map and 145 // unordered_(multi)set. For unordered_map, the element type has an 146 // additional type layer, an internal struct (`__hash_value_type`) 147 // that wraps a std::pair. Peel away the internal wrapper type - whose 148 // structure is of no value to users, to expose the std::pair. This 149 // matches the structure returned by the std::map synthetic provider. 150 if (isUnorderedMap(m_backend.GetTypeName())) { 151 std::string name; 152 CompilerType field_type = m_element_type.GetFieldAtIndex( 153 0, name, nullptr, nullptr, nullptr); 154 CompilerType actual_type = field_type.GetTypedefedType(); 155 if (isStdTemplate(actual_type.GetTypeName(), "pair")) 156 m_element_type = actual_type; 157 } 158 } 159 if (!m_node_type) 160 return nullptr; 161 node_sp = node_sp->Cast(m_node_type); 162 value_sp = node_sp->GetChildMemberWithName("__value_", true); 163 hash_sp = node_sp->GetChildMemberWithName("__hash_", true); 164 if (!value_sp || !hash_sp) 165 return nullptr; 166 } 167 m_elements_cache.push_back( 168 {value_sp.get(), hash_sp->GetValueAsUnsigned(0)}); 169 m_next_element = node_sp->GetChildMemberWithName("__next_", true).get(); 170 if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0) 171 m_next_element = nullptr; 172 } 173 174 std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx]; 175 if (!val_hash.first) 176 return lldb::ValueObjectSP(); 177 StreamString stream; 178 stream.Printf("[%" PRIu64 "]", (uint64_t)idx); 179 DataExtractor data; 180 Status error; 181 val_hash.first->GetData(data, error); 182 if (error.Fail()) 183 return lldb::ValueObjectSP(); 184 const bool thread_and_frame_only_if_stopped = true; 185 ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock( 186 thread_and_frame_only_if_stopped); 187 return CreateValueObjectFromData(stream.GetString(), data, exe_ctx, 188 m_element_type); 189 } 190 191 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: 192 Update() { 193 m_num_elements = 0; 194 m_next_element = nullptr; 195 m_elements_cache.clear(); 196 ValueObjectSP table_sp = m_backend.GetChildMemberWithName("__table_", true); 197 if (!table_sp) 198 return false; 199 200 ValueObjectSP p2_sp = table_sp->GetChildMemberWithName("__p2_", true); 201 ValueObjectSP num_elements_sp = nullptr; 202 llvm::SmallVector<ConstString, 3> next_path; 203 switch (p2_sp->GetCompilerType().GetNumDirectBaseClasses()) { 204 case 1: 205 // Assume a pre llvm r300140 __compressed_pair implementation: 206 num_elements_sp = p2_sp->GetChildMemberWithName("__first_", true); 207 next_path.append({ConstString("__p1_"), ConstString("__first_"), 208 ConstString("__next_")}); 209 break; 210 case 2: { 211 // Assume a post llvm r300140 __compressed_pair implementation: 212 ValueObjectSP first_elem_parent = p2_sp->GetChildAtIndex(0, true); 213 num_elements_sp = 214 first_elem_parent->GetChildMemberWithName("__value_", true); 215 next_path.append({ConstString("__p1_"), ConstString("__value_"), 216 ConstString("__next_")}); 217 break; 218 } 219 default: 220 return false; 221 } 222 223 if (!num_elements_sp) 224 return false; 225 226 m_tree = table_sp->GetChildAtNamePath(next_path).get(); 227 if (m_tree == nullptr) 228 return false; 229 230 m_num_elements = num_elements_sp->GetValueAsUnsigned(0); 231 232 if (m_num_elements > 0) 233 m_next_element = 234 table_sp->GetChildAtNamePath(next_path).get(); 235 return false; 236 } 237 238 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: 239 MightHaveChildren() { 240 return true; 241 } 242 243 size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: 244 GetIndexOfChildWithName(ConstString name) { 245 return ExtractIndexFromString(name.GetCString()); 246 } 247 248 SyntheticChildrenFrontEnd * 249 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator( 250 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 251 return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp) 252 : nullptr); 253 } 254