1 //===-- LibCxxList.cpp ------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 // C Includes 11 // C++ Includes 12 // Other libraries and framework includes 13 // Project includes 14 #include "LibCxx.h" 15 16 #include "lldb/Core/DataBufferHeap.h" 17 #include "lldb/Core/Error.h" 18 #include "lldb/Core/Stream.h" 19 #include "lldb/Core/ValueObject.h" 20 #include "lldb/Core/ValueObjectConstResult.h" 21 #include "lldb/DataFormatters/FormattersHelpers.h" 22 #include "lldb/Host/Endian.h" 23 #include "lldb/Symbol/ClangASTContext.h" 24 #include "lldb/Target/Target.h" 25 26 using namespace lldb; 27 using namespace lldb_private; 28 using namespace lldb_private::formatters; 29 30 class MapEntry { 31 public: 32 MapEntry() = default; 33 explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} 34 MapEntry(const MapEntry &rhs) = default; 35 explicit MapEntry(ValueObject *entry) 36 : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} 37 38 ValueObjectSP left() const { 39 static ConstString g_left("__left_"); 40 if (!m_entry_sp) 41 return m_entry_sp; 42 return m_entry_sp->GetChildMemberWithName(g_left, true); 43 } 44 45 ValueObjectSP right() const { 46 static ConstString g_right("__right_"); 47 if (!m_entry_sp) 48 return m_entry_sp; 49 return m_entry_sp->GetChildMemberWithName(g_right, true); 50 } 51 52 ValueObjectSP parent() const { 53 static ConstString g_parent("__parent_"); 54 if (!m_entry_sp) 55 return m_entry_sp; 56 return m_entry_sp->GetChildMemberWithName(g_parent, true); 57 } 58 59 uint64_t value() const { 60 if (!m_entry_sp) 61 return 0; 62 return m_entry_sp->GetValueAsUnsigned(0); 63 } 64 65 bool error() const { 66 if (!m_entry_sp) 67 return true; 68 return m_entry_sp->GetError().Fail(); 69 } 70 71 bool null() const { return (value() == 0); } 72 73 ValueObjectSP GetEntry() const { return m_entry_sp; } 74 75 void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; } 76 77 bool operator==(const MapEntry &rhs) const { 78 return (rhs.m_entry_sp.get() == m_entry_sp.get()); 79 } 80 81 private: 82 ValueObjectSP m_entry_sp; 83 }; 84 85 class MapIterator { 86 public: 87 MapIterator() = default; 88 MapIterator(MapEntry entry, size_t depth = 0) 89 : m_entry(entry), m_max_depth(depth), m_error(false) {} 90 MapIterator(ValueObjectSP entry, size_t depth = 0) 91 : m_entry(entry), m_max_depth(depth), m_error(false) {} 92 MapIterator(const MapIterator &rhs) 93 : m_entry(rhs.m_entry), m_max_depth(rhs.m_max_depth), m_error(false) {} 94 MapIterator(ValueObject *entry, size_t depth = 0) 95 : m_entry(entry), m_max_depth(depth), m_error(false) {} 96 97 ValueObjectSP value() { return m_entry.GetEntry(); } 98 99 ValueObjectSP advance(size_t count) { 100 ValueObjectSP fail; 101 if (m_error) 102 return fail; 103 size_t steps = 0; 104 while (count > 0) { 105 next(); 106 count--, steps++; 107 if (m_error || m_entry.null() || (steps > m_max_depth)) 108 return fail; 109 } 110 return m_entry.GetEntry(); 111 } 112 113 protected: 114 void next() { 115 if (m_entry.null()) 116 return; 117 MapEntry right(m_entry.right()); 118 if (!right.null()) { 119 m_entry = tree_min(std::move(right)); 120 return; 121 } 122 size_t steps = 0; 123 while (!is_left_child(m_entry)) { 124 if (m_entry.error()) { 125 m_error = true; 126 return; 127 } 128 m_entry.SetEntry(m_entry.parent()); 129 steps++; 130 if (steps > m_max_depth) { 131 m_entry = MapEntry(); 132 return; 133 } 134 } 135 m_entry = MapEntry(m_entry.parent()); 136 } 137 138 private: 139 MapEntry tree_min(MapEntry &&x) { 140 if (x.null()) 141 return MapEntry(); 142 MapEntry left(x.left()); 143 size_t steps = 0; 144 while (!left.null()) { 145 if (left.error()) { 146 m_error = true; 147 return MapEntry(); 148 } 149 x = left; 150 left.SetEntry(x.left()); 151 steps++; 152 if (steps > m_max_depth) 153 return MapEntry(); 154 } 155 return x; 156 } 157 158 bool is_left_child(const MapEntry &x) { 159 if (x.null()) 160 return false; 161 MapEntry rhs(x.parent()); 162 rhs.SetEntry(rhs.left()); 163 return x.value() == rhs.value(); 164 } 165 166 MapEntry m_entry; 167 size_t m_max_depth; 168 bool m_error; 169 }; 170 171 namespace lldb_private { 172 namespace formatters { 173 class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd { 174 public: 175 LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); 176 177 ~LibcxxStdMapSyntheticFrontEnd() override = default; 178 179 size_t CalculateNumChildren() override; 180 181 lldb::ValueObjectSP GetChildAtIndex(size_t idx) override; 182 183 bool Update() override; 184 185 bool MightHaveChildren() override; 186 187 size_t GetIndexOfChildWithName(const ConstString &name) override; 188 189 private: 190 bool GetDataType(); 191 192 void GetValueOffset(const lldb::ValueObjectSP &node); 193 194 ValueObject *m_tree; 195 ValueObject *m_root_node; 196 CompilerType m_element_type; 197 uint32_t m_skip_size; 198 size_t m_count; 199 std::map<size_t, MapIterator> m_iterators; 200 }; 201 } // namespace formatters 202 } // namespace lldb_private 203 204 lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: 205 LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) 206 : SyntheticChildrenFrontEnd(*valobj_sp), m_tree(nullptr), 207 m_root_node(nullptr), m_element_type(), m_skip_size(UINT32_MAX), 208 m_count(UINT32_MAX), m_iterators() { 209 if (valobj_sp) 210 Update(); 211 } 212 213 size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: 214 CalculateNumChildren() { 215 static ConstString g___pair3_("__pair3_"); 216 static ConstString g___first_("__first_"); 217 218 if (m_count != UINT32_MAX) 219 return m_count; 220 if (m_tree == nullptr) 221 return 0; 222 ValueObjectSP m_item(m_tree->GetChildMemberWithName(g___pair3_, true)); 223 if (!m_item) 224 return 0; 225 m_item = m_item->GetChildMemberWithName(g___first_, true); 226 if (!m_item) 227 return 0; 228 m_count = m_item->GetValueAsUnsigned(0); 229 return m_count; 230 } 231 232 bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() { 233 static ConstString g___value_("__value_"); 234 235 if (m_element_type.GetOpaqueQualType() && m_element_type.GetTypeSystem()) 236 return true; 237 m_element_type.Clear(); 238 ValueObjectSP deref; 239 Error error; 240 deref = m_root_node->Dereference(error); 241 if (!deref || error.Fail()) 242 return false; 243 deref = deref->GetChildMemberWithName(g___value_, true); 244 if (!deref) 245 return false; 246 m_element_type = deref->GetCompilerType(); 247 return true; 248 } 249 250 void lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetValueOffset( 251 const lldb::ValueObjectSP &node) { 252 if (m_skip_size != UINT32_MAX) 253 return; 254 if (!node) 255 return; 256 CompilerType node_type(node->GetCompilerType()); 257 uint64_t bit_offset; 258 if (node_type.GetIndexOfFieldWithName("__value_", nullptr, &bit_offset) == 259 UINT32_MAX) 260 return; 261 m_skip_size = bit_offset / 8u; 262 } 263 264 lldb::ValueObjectSP 265 lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex( 266 size_t idx) { 267 static ConstString g___cc("__cc"); 268 static ConstString g___nc("__nc"); 269 static ConstString g___value_("__value_"); 270 271 if (idx >= CalculateNumChildren()) 272 return lldb::ValueObjectSP(); 273 if (m_tree == nullptr || m_root_node == nullptr) 274 return lldb::ValueObjectSP(); 275 276 MapIterator iterator(m_root_node, CalculateNumChildren()); 277 278 const bool need_to_skip = (idx > 0); 279 size_t actual_advancde = idx; 280 if (need_to_skip) { 281 auto cached_iterator = m_iterators.find(idx - 1); 282 if (cached_iterator != m_iterators.end()) { 283 iterator = cached_iterator->second; 284 actual_advancde = 1; 285 } 286 } 287 288 ValueObjectSP iterated_sp(iterator.advance(actual_advancde)); 289 if (!iterated_sp) { 290 // this tree is garbage - stop 291 m_tree = 292 nullptr; // this will stop all future searches until an Update() happens 293 return iterated_sp; 294 } 295 if (GetDataType()) { 296 if (!need_to_skip) { 297 Error error; 298 iterated_sp = iterated_sp->Dereference(error); 299 if (!iterated_sp || error.Fail()) { 300 m_tree = nullptr; 301 return lldb::ValueObjectSP(); 302 } 303 GetValueOffset(iterated_sp); 304 iterated_sp = iterated_sp->GetChildMemberWithName(g___value_, true); 305 if (!iterated_sp) { 306 m_tree = nullptr; 307 return lldb::ValueObjectSP(); 308 } 309 } else { 310 // because of the way our debug info is made, we need to read item 0 first 311 // so that we can cache information used to generate other elements 312 if (m_skip_size == UINT32_MAX) 313 GetChildAtIndex(0); 314 if (m_skip_size == UINT32_MAX) { 315 m_tree = nullptr; 316 return lldb::ValueObjectSP(); 317 } 318 iterated_sp = iterated_sp->GetSyntheticChildAtOffset( 319 m_skip_size, m_element_type, true); 320 if (!iterated_sp) { 321 m_tree = nullptr; 322 return lldb::ValueObjectSP(); 323 } 324 } 325 } else { 326 m_tree = nullptr; 327 return lldb::ValueObjectSP(); 328 } 329 // at this point we have a valid 330 // we need to copy current_sp into a new object otherwise we will end up with 331 // all items named __value_ 332 DataExtractor data; 333 Error error; 334 iterated_sp->GetData(data, error); 335 if (error.Fail()) { 336 m_tree = nullptr; 337 return lldb::ValueObjectSP(); 338 } 339 StreamString name; 340 name.Printf("[%" PRIu64 "]", (uint64_t)idx); 341 auto potential_child_sp = CreateValueObjectFromData( 342 name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type); 343 if (potential_child_sp) { 344 switch (potential_child_sp->GetNumChildren()) { 345 case 1: { 346 auto child0_sp = potential_child_sp->GetChildAtIndex(0, true); 347 if (child0_sp && child0_sp->GetName() == g___cc) 348 potential_child_sp = child0_sp; 349 break; 350 } 351 case 2: { 352 auto child0_sp = potential_child_sp->GetChildAtIndex(0, true); 353 auto child1_sp = potential_child_sp->GetChildAtIndex(1, true); 354 if (child0_sp && child0_sp->GetName() == g___cc && child1_sp && 355 child1_sp->GetName() == g___nc) 356 potential_child_sp = child0_sp; 357 break; 358 } 359 } 360 potential_child_sp->SetName(ConstString(name.GetData())); 361 } 362 m_iterators[idx] = iterator; 363 return potential_child_sp; 364 } 365 366 bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() { 367 static ConstString g___tree_("__tree_"); 368 static ConstString g___begin_node_("__begin_node_"); 369 m_count = UINT32_MAX; 370 m_tree = m_root_node = nullptr; 371 m_iterators.clear(); 372 m_tree = m_backend.GetChildMemberWithName(g___tree_, true).get(); 373 if (!m_tree) 374 return false; 375 m_root_node = m_tree->GetChildMemberWithName(g___begin_node_, true).get(); 376 return false; 377 } 378 379 bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: 380 MightHaveChildren() { 381 return true; 382 } 383 384 size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: 385 GetIndexOfChildWithName(const ConstString &name) { 386 return ExtractIndexFromString(name.GetCString()); 387 } 388 389 SyntheticChildrenFrontEnd * 390 lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator( 391 CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { 392 return (valobj_sp ? new LibcxxStdMapSyntheticFrontEnd(valobj_sp) : nullptr); 393 } 394