1 //===-- LibCxxMap.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/DataBufferHeap.h" 17 #include "lldb/Utility/Endian.h" 18 #include "lldb/Utility/Status.h" 19 #include "lldb/Utility/Stream.h" 20 21 using namespace lldb; 22 using namespace lldb_private; 23 using namespace lldb_private::formatters; 24 25 class MapEntry { 26 public: 27 MapEntry() = default; 28 explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} 29 explicit MapEntry(ValueObject *entry) 30 : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} 31 32 ValueObjectSP left() const { 33 static ConstString g_left("__left_"); 34 if (!m_entry_sp) 35 return m_entry_sp; 36 return m_entry_sp->GetSyntheticChildAtOffset( 37 0, m_entry_sp->GetCompilerType(), true); 38 } 39 40 ValueObjectSP right() const { 41 static ConstString g_right("__right_"); 42 if (!m_entry_sp) 43 return m_entry_sp; 44 return m_entry_sp->GetSyntheticChildAtOffset( 45 m_entry_sp->GetProcessSP()->GetAddressByteSize(), 46 m_entry_sp->GetCompilerType(), true); 47 } 48 49 ValueObjectSP parent() const { 50 static ConstString g_parent("__parent_"); 51 if (!m_entry_sp) 52 return m_entry_sp; 53 return m_entry_sp->GetSyntheticChildAtOffset( 54 2 * m_entry_sp->GetProcessSP()->GetAddressByteSize(), 55 m_entry_sp->GetCompilerType(), true); 56 } 57 58 uint64_t value() const { 59 if (!m_entry_sp) 60 return 0; 61 return m_entry_sp->GetValueAsUnsigned(0); 62 } 63 64 bool error() const { 65 if (!m_entry_sp) 66 return true; 67 return m_entry_sp->GetError().Fail(); 68 } 69 70 bool null() const { return (value() == 0); } 71 72 ValueObjectSP GetEntry() const { return m_entry_sp; } 73 74 void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; } 75 76 bool operator==(const MapEntry &rhs) const { 77 return (rhs.m_entry_sp.get() == m_entry_sp.get()); 78 } 79 80 private: 81 ValueObjectSP m_entry_sp; 82 }; 83 84 class MapIterator { 85 public: 86 MapIterator() = default; 87 MapIterator(MapEntry entry, size_t depth = 0) 88 : m_entry(std::move(entry)), m_max_depth(depth), m_error(false) {} 89 MapIterator(ValueObjectSP entry, size_t depth = 0) 90 : m_entry(std::move(entry)), m_max_depth(depth), m_error(false) {} 91 MapIterator(const MapIterator &rhs) 92 : m_entry(rhs.m_entry), m_max_depth(rhs.m_max_depth), m_error(false) {} 93 MapIterator(ValueObject *entry, size_t depth = 0) 94 : m_entry(entry), m_max_depth(depth), m_error(false) {} 95 96 MapIterator &operator=(const MapIterator &) = default; 97 98 ValueObjectSP value() { return m_entry.GetEntry(); } 99 100 ValueObjectSP advance(size_t count) { 101 ValueObjectSP fail; 102 if (m_error) 103 return fail; 104 size_t steps = 0; 105 while (count > 0) { 106 next(); 107 count--, steps++; 108 if (m_error || m_entry.null() || (steps > m_max_depth)) 109 return fail; 110 } 111 return m_entry.GetEntry(); 112 } 113 114 protected: 115 void next() { 116 if (m_entry.null()) 117 return; 118 MapEntry right(m_entry.right()); 119 if (!right.null()) { 120 m_entry = tree_min(std::move(right)); 121 return; 122 } 123 size_t steps = 0; 124 while (!is_left_child(m_entry)) { 125 if (m_entry.error()) { 126 m_error = true; 127 return; 128 } 129 m_entry.SetEntry(m_entry.parent()); 130 steps++; 131 if (steps > m_max_depth) { 132 m_entry = MapEntry(); 133 return; 134 } 135 } 136 m_entry = MapEntry(m_entry.parent()); 137 } 138 139 private: 140 MapEntry tree_min(MapEntry x) { 141 if (x.null()) 142 return MapEntry(); 143 MapEntry left(x.left()); 144 size_t steps = 0; 145 while (!left.null()) { 146 if (left.error()) { 147 m_error = true; 148 return MapEntry(); 149 } 150 x = left; 151 left.SetEntry(x.left()); 152 steps++; 153 if (steps > m_max_depth) 154 return MapEntry(); 155 } 156 return x; 157 } 158 159 bool is_left_child(const MapEntry &x) { 160 if (x.null()) 161 return false; 162 MapEntry rhs(x.parent()); 163 rhs.SetEntry(rhs.left()); 164 return x.value() == rhs.value(); 165 } 166 167 MapEntry m_entry; 168 size_t m_max_depth; 169 bool m_error; 170 }; 171 172 namespace lldb_private { 173 namespace formatters { 174 class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 175 public: 176 LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 177 178 ~LibcxxStdMapSyntheticFrontEnd() override = default; 179 180 size_t CalculateNumChildren() override; 181 182 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 183 184 bool Update() override; 185 186 bool MightHaveChildren() override; 187 188 size_t GetIndexOfChildWithName(ConstString name) override; 189 190 private: 191 bool GetDataType(); 192 193 void GetValueOffset(const lldb::ValueObjectSP &node); 194 195 ValueObject *m_tree; 196 ValueObject *m_root_node; 197 CompilerType m_element_type; 198 uint32_t m_skip_size; 199 size_t m_count; 200 std::map<size_t, MapIterator> m_iterators; 201 }; 202 } // namespace formatters 203 } // namespace lldb_private 204 205 lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: 206 LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 207 : SyntheticChildrenFrontEnd(*valobj_sp), m_tree(nullptr), 208 m_root_node(nullptr), m_element_type(), m_skip_size(UINT32_MAX), 209 m_count(UINT32_MAX), m_iterators() { 210 if (valobj_sp) 211 Update(); 212 } 213 214 size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: 215 CalculateNumChildren() { 216 static ConstString g___pair3_("__pair3_"); 217 static ConstString g___first_("__first_"); 218 static ConstString g___value_("__value_"); 219 220 if (m_count != UINT32_MAX) 221 return m_count; 222 if (m_tree == nullptr) 223 return 0; 224 ValueObjectSP m_item(m_tree->GetChildMemberWithName(g___pair3_, true)); 225 if (!m_item) 226 return 0; 227 228 switch (m_item->GetCompilerType().GetNumDirectBaseClasses()) { 229 case 1: 230 // Assume a pre llvm r300140 __compressed_pair implementation: 231 m_item = m_item->GetChildMemberWithName(g___first_, true); 232 break; 233 case 2: { 234 // Assume a post llvm r300140 __compressed_pair implementation: 235 ValueObjectSP first_elem_parent = m_item->GetChildAtIndex(0, true); 236 m_item = first_elem_parent->GetChildMemberWithName(g___value_, true); 237 break; 238 } 239 default: 240 return false; 241 } 242 243 if (!m_item) 244 return 0; 245 m_count = m_item->GetValueAsUnsigned(0); 246 return m_count; 247 } 248 249 bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() { 250 static ConstString g___value_("__value_"); 251 static ConstString g_tree_("__tree_"); 252 static ConstString g_pair3("__pair3_"); 253 254 if (m_element_type.GetOpaqueQualType() && m_element_type.GetTypeSystem()) 255 return true; 256 m_element_type.Clear(); 257 ValueObjectSP deref; 258 Status error; 259 deref = m_root_node->Dereference(error); 260 if (!deref || error.Fail()) 261 return false; 262 deref = deref->GetChildMemberWithName(g___value_, true); 263 if (deref) { 264 m_element_type = deref->GetCompilerType(); 265 return true; 266 } 267 deref = m_backend.GetChildAtNamePath({g_tree_, g_pair3}); 268 if (!deref) 269 return false; 270 m_element_type = deref->GetCompilerType() 271 .GetTypeTemplateArgument(1) 272 .GetTypeTemplateArgument(1); 273 if (m_element_type) { 274 std::string name; 275 uint64_t bit_offset_ptr; 276 uint32_t bitfield_bit_size_ptr; 277 bool is_bitfield_ptr; 278 m_element_type = m_element_type.GetFieldAtIndex( 279 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr); 280 m_element_type = m_element_type.GetTypedefedType(); 281 return m_element_type.IsValid(); 282 } else { 283 m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0); 284 return m_element_type.IsValid(); 285 } 286 } 287 288 void lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetValueOffset( 289 const lldb::ValueObjectSP &node) { 290 if (m_skip_size != UINT32_MAX) 291 return; 292 if (!node) 293 return; 294 CompilerType node_type(node->GetCompilerType()); 295 uint64_t bit_offset; 296 if (node_type.GetIndexOfFieldWithName("__value_", nullptr, &bit_offset) != 297 UINT32_MAX) { 298 m_skip_size = bit_offset / 8u; 299 } else { 300 TypeSystemClang *ast_ctx = 301 llvm::dyn_cast_or_null<TypeSystemClang>(node_type.GetTypeSystem()); 302 if (!ast_ctx) 303 return; 304 CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( 305 ConstString(), 306 {{"ptr0", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, 307 {"ptr1", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, 308 {"ptr2", ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, 309 {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)}, 310 {"payload", (m_element_type.GetCompleteType(), m_element_type)}}); 311 std::string child_name; 312 uint32_t child_byte_size; 313 int32_t child_byte_offset = 0; 314 uint32_t child_bitfield_bit_size; 315 uint32_t child_bitfield_bit_offset; 316 bool child_is_base_class; 317 bool child_is_deref_of_parent; 318 uint64_t language_flags; 319 if (tree_node_type 320 .GetChildCompilerTypeAtIndex( 321 nullptr, 4, true, true, true, child_name, child_byte_size, 322 child_byte_offset, child_bitfield_bit_size, 323 child_bitfield_bit_offset, child_is_base_class, 324 child_is_deref_of_parent, nullptr, language_flags) 325 .IsValid()) 326 m_skip_size = (uint32_t)child_byte_offset; 327 } 328 } 329 330 lldb::ValueObjectSP 331 lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex( 332 size_t idx) { 333 static ConstString g___cc("__cc"); 334 static ConstString g___nc("__nc"); 335 static ConstString g___value_("__value_"); 336 337 if (idx >= CalculateNumChildren()) 338 return lldb::ValueObjectSP(); 339 if (m_tree == nullptr || m_root_node == nullptr) 340 return lldb::ValueObjectSP(); 341 342 MapIterator iterator(m_root_node, CalculateNumChildren()); 343 344 const bool need_to_skip = (idx > 0); 345 size_t actual_advancde = idx; 346 if (need_to_skip) { 347 auto cached_iterator = m_iterators.find(idx - 1); 348 if (cached_iterator != m_iterators.end()) { 349 iterator = cached_iterator->second; 350 actual_advancde = 1; 351 } 352 } 353 354 ValueObjectSP iterated_sp(iterator.advance(actual_advancde)); 355 if (!iterated_sp) { 356 // this tree is garbage - stop 357 m_tree = 358 nullptr; // this will stop all future searches until an Update() happens 359 return iterated_sp; 360 } 361 if (GetDataType()) { 362 if (!need_to_skip) { 363 Status error; 364 iterated_sp = iterated_sp->Dereference(error); 365 if (!iterated_sp || error.Fail()) { 366 m_tree = nullptr; 367 return lldb::ValueObjectSP(); 368 } 369 GetValueOffset(iterated_sp); 370 auto child_sp = iterated_sp->GetChildMemberWithName(g___value_, true); 371 if (child_sp) 372 iterated_sp = child_sp; 373 else 374 iterated_sp = iterated_sp->GetSyntheticChildAtOffset( 375 m_skip_size, m_element_type, true); 376 if (!iterated_sp) { 377 m_tree = nullptr; 378 return lldb::ValueObjectSP(); 379 } 380 } else { 381 // because of the way our debug info is made, we need to read item 0 382 // first so that we can cache information used to generate other elements 383 if (m_skip_size == UINT32_MAX) 384 GetChildAtIndex(0); 385 if (m_skip_size == UINT32_MAX) { 386 m_tree = nullptr; 387 return lldb::ValueObjectSP(); 388 } 389 iterated_sp = iterated_sp->GetSyntheticChildAtOffset( 390 m_skip_size, m_element_type, true); 391 if (!iterated_sp) { 392 m_tree = nullptr; 393 return lldb::ValueObjectSP(); 394 } 395 } 396 } else { 397 m_tree = nullptr; 398 return lldb::ValueObjectSP(); 399 } 400 // at this point we have a valid 401 // we need to copy current_sp into a new object otherwise we will end up with 402 // all items named __value_ 403 DataExtractor data; 404 Status error; 405 iterated_sp->GetData(data, error); 406 if (error.Fail()) { 407 m_tree = nullptr; 408 return lldb::ValueObjectSP(); 409 } 410 StreamString name; 411 name.Printf("[%" PRIu64 "]", (uint64_t)idx); 412 auto potential_child_sp = CreateValueObjectFromData( 413 name.GetString(), data, m_backend.GetExecutionContextRef(), 414 m_element_type); 415 if (potential_child_sp) { 416 switch (potential_child_sp->GetNumChildren()) { 417 case 1: { 418 auto child0_sp = potential_child_sp->GetChildAtIndex(0, true); 419 if (child0_sp && child0_sp->GetName() == g___cc) 420 potential_child_sp = child0_sp->Clone(ConstString(name.GetString())); 421 break; 422 } 423 case 2: { 424 auto child0_sp = potential_child_sp->GetChildAtIndex(0, true); 425 auto child1_sp = potential_child_sp->GetChildAtIndex(1, true); 426 if (child0_sp && child0_sp->GetName() == g___cc && child1_sp && 427 child1_sp->GetName() == g___nc) 428 potential_child_sp = child0_sp->Clone(ConstString(name.GetString())); 429 break; 430 } 431 } 432 } 433 m_iterators[idx] = iterator; 434 return potential_child_sp; 435 } 436 437 bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() { 438 static ConstString g___tree_("__tree_"); 439 static ConstString g___begin_node_("__begin_node_"); 440 m_count = UINT32_MAX; 441 m_tree = m_root_node = nullptr; 442 m_iterators.clear(); 443 m_tree = m_backend.GetChildMemberWithName(g___tree_, true).get(); 444 if (!m_tree) 445 return false; 446 m_root_node = m_tree->GetChildMemberWithName(g___begin_node_, true).get(); 447 return false; 448 } 449 450 bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: 451 MightHaveChildren() { 452 return true; 453 } 454 455 size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: 456 GetIndexOfChildWithName(ConstString name) { 457 return ExtractIndexFromString(name.GetCString()); 458 } 459 460 SyntheticChildrenFrontEnd * 461 lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator( 462 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 463 return (valobj_sp ? new LibcxxStdMapSyntheticFrontEnd(valobj_sp) : nullptr); 464 } 465