LibCxxList.cpp revision 296417
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 26using namespace lldb; 27using namespace lldb_private; 28using namespace lldb_private::formatters; 29 30namespace { 31 32 class ListEntry 33 { 34 public: 35 ListEntry() = default; 36 ListEntry (ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} 37 ListEntry (const ListEntry& rhs) : m_entry_sp(rhs.m_entry_sp) {} 38 ListEntry (ValueObject* entry) : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} 39 40 ListEntry 41 next () 42 { 43 if (!m_entry_sp) 44 return ListEntry(); 45 return ListEntry(m_entry_sp->GetChildAtIndexPath({0,1})); 46 } 47 48 ListEntry 49 prev () 50 { 51 if (!m_entry_sp) 52 return ListEntry(); 53 return ListEntry(m_entry_sp->GetChildAtIndexPath({0,0})); 54 } 55 56 uint64_t 57 value () const 58 { 59 if (!m_entry_sp) 60 return 0; 61 return m_entry_sp->GetValueAsUnsigned(0); 62 } 63 64 bool 65 null() 66 { 67 return (value() == 0); 68 } 69 70 explicit operator bool () 71 { 72 return GetEntry().get() != nullptr && null() == false; 73 } 74 75 ValueObjectSP 76 GetEntry () 77 { 78 return m_entry_sp; 79 } 80 81 void 82 SetEntry (ValueObjectSP entry) 83 { 84 m_entry_sp = entry; 85 } 86 87 bool 88 operator == (const ListEntry& rhs) const 89 { 90 return value() == rhs.value(); 91 } 92 93 bool 94 operator != (const ListEntry& rhs) const 95 { 96 return !(*this == rhs); 97 } 98 99 private: 100 ValueObjectSP m_entry_sp; 101 }; 102 103 class ListIterator 104 { 105 public: 106 ListIterator() = default; 107 ListIterator (ListEntry entry) : m_entry(entry) {} 108 ListIterator (ValueObjectSP entry) : m_entry(entry) {} 109 ListIterator (const ListIterator& rhs) : m_entry(rhs.m_entry) {} 110 ListIterator (ValueObject* entry) : m_entry(entry) {} 111 112 ValueObjectSP 113 value () 114 { 115 return m_entry.GetEntry(); 116 } 117 118 ValueObjectSP 119 advance (size_t count) 120 { 121 if (count == 0) 122 return m_entry.GetEntry(); 123 if (count == 1) 124 { 125 next (); 126 return m_entry.GetEntry(); 127 } 128 while (count > 0) 129 { 130 next (); 131 count--; 132 if (m_entry.null()) 133 return lldb::ValueObjectSP(); 134 } 135 return m_entry.GetEntry(); 136 } 137 138 bool 139 operator == (const ListIterator& rhs) const 140 { 141 return (rhs.m_entry == m_entry); 142 } 143 144 protected: 145 void 146 next () 147 { 148 m_entry = m_entry.next(); 149 } 150 151 void 152 prev () 153 { 154 m_entry = m_entry.prev(); 155 } 156 157 private: 158 ListEntry m_entry; 159 }; 160 161} // end anonymous namespace 162 163namespace lldb_private { 164 namespace formatters { 165 class LibcxxStdListSyntheticFrontEnd : public SyntheticChildrenFrontEnd 166 { 167 public: 168 LibcxxStdListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); 169 170 ~LibcxxStdListSyntheticFrontEnd() override = default; 171 172 size_t 173 CalculateNumChildren() override; 174 175 lldb::ValueObjectSP 176 GetChildAtIndex(size_t idx) override; 177 178 bool 179 Update() override; 180 181 bool 182 MightHaveChildren() override; 183 184 size_t 185 GetIndexOfChildWithName(const ConstString &name) override; 186 187 private: 188 bool 189 HasLoop(size_t count); 190 191 size_t m_list_capping_size; 192 static const bool g_use_loop_detect = true; 193 194 size_t m_loop_detected; // The number of elements that have had loop detection run over them. 195 ListEntry m_slow_runner; // Used for loop detection 196 ListEntry m_fast_runner; // Used for loop detection 197 198 lldb::addr_t m_node_address; 199 ValueObject* m_head; 200 ValueObject* m_tail; 201 CompilerType m_element_type; 202 size_t m_count; 203 std::map<size_t,lldb::ValueObjectSP> m_children; 204 std::map<size_t, ListIterator> m_iterators; 205 }; 206 } // namespace formatters 207} // namespace lldb_private 208 209lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::LibcxxStdListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : 210SyntheticChildrenFrontEnd(*valobj_sp.get()), 211m_list_capping_size(0), 212m_loop_detected(0), 213m_node_address(), 214m_head(NULL), 215m_tail(NULL), 216m_element_type(), 217m_count(UINT32_MAX), 218m_children(), 219m_iterators() 220{ 221 if (valobj_sp) 222 Update(); 223} 224 225bool 226lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::HasLoop(size_t count) 227{ 228 if (g_use_loop_detect == false) 229 return false; 230 // don't bother checking for a loop if we won't actually need to jump nodes 231 if (m_count < 2) 232 return false; 233 234 if (m_loop_detected == 0) 235 { 236 // This is the first time we are being run (after the last update). Set up the loop 237 // invariant for the first element. 238 m_slow_runner = ListEntry(m_head).next(); 239 m_fast_runner = m_slow_runner.next(); 240 m_loop_detected = 1; 241 } 242 243 // Loop invariant: 244 // Loop detection has been run over the first m_loop_detected elements. If m_slow_runner == 245 // m_fast_runner then the loop has been detected after m_loop_detected elements. 246 const size_t steps_to_run = std::min(count,m_count); 247 while (m_loop_detected < steps_to_run 248 && m_slow_runner 249 && m_fast_runner 250 && m_slow_runner != m_fast_runner) { 251 252 m_slow_runner = m_slow_runner.next(); 253 m_fast_runner = m_fast_runner.next().next(); 254 m_loop_detected++; 255 } 256 if (count <= m_loop_detected) 257 return false; // No loop in the first m_loop_detected elements. 258 if (!m_slow_runner || !m_fast_runner) 259 return false; // Reached the end of the list. Definitely no loops. 260 return m_slow_runner == m_fast_runner; 261} 262 263size_t 264lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::CalculateNumChildren () 265{ 266 if (m_count != UINT32_MAX) 267 return m_count; 268 if (!m_head || !m_tail || m_node_address == 0) 269 return 0; 270 ValueObjectSP size_alloc(m_backend.GetChildMemberWithName(ConstString("__size_alloc_"), true)); 271 if (size_alloc) 272 { 273 ValueObjectSP first(size_alloc->GetChildMemberWithName(ConstString("__first_"), true)); 274 if (first) 275 { 276 m_count = first->GetValueAsUnsigned(UINT32_MAX); 277 } 278 } 279 if (m_count != UINT32_MAX) 280 { 281 return m_count; 282 } 283 else 284 { 285 uint64_t next_val = m_head->GetValueAsUnsigned(0); 286 uint64_t prev_val = m_tail->GetValueAsUnsigned(0); 287 if (next_val == 0 || prev_val == 0) 288 return 0; 289 if (next_val == m_node_address) 290 return 0; 291 if (next_val == prev_val) 292 return 1; 293 uint64_t size = 2; 294 ListEntry current(m_head); 295 while (current.next() && current.next().value() != m_node_address) 296 { 297 size++; 298 current = current.next(); 299 if (size > m_list_capping_size) 300 break; 301 } 302 return m_count = (size-1); 303 } 304} 305 306lldb::ValueObjectSP 307lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetChildAtIndex (size_t idx) 308{ 309 if (idx >= CalculateNumChildren()) 310 return lldb::ValueObjectSP(); 311 312 if (!m_head || !m_tail || m_node_address == 0) 313 return lldb::ValueObjectSP(); 314 315 auto cached = m_children.find(idx); 316 if (cached != m_children.end()) 317 return cached->second; 318 319 if (HasLoop(idx+1)) 320 return lldb::ValueObjectSP(); 321 322 size_t actual_advance = idx; 323 324 ListIterator current(m_head); 325 if (idx > 0) 326 { 327 auto cached_iterator = m_iterators.find(idx-1); 328 if (cached_iterator != m_iterators.end()) 329 { 330 current = cached_iterator->second; 331 actual_advance = 1; 332 } 333 } 334 335 ValueObjectSP current_sp(current.advance(actual_advance)); 336 if (!current_sp) 337 return lldb::ValueObjectSP(); 338 339 m_iterators[idx] = current; 340 341 current_sp = current_sp->GetChildAtIndex(1, true); // get the __value_ child 342 if (!current_sp) 343 return lldb::ValueObjectSP(); 344 // we need to copy current_sp into a new object otherwise we will end up with all items named __value_ 345 DataExtractor data; 346 Error error; 347 current_sp->GetData(data, error); 348 if (error.Fail()) 349 return lldb::ValueObjectSP(); 350 351 StreamString name; 352 name.Printf("[%" PRIu64 "]", (uint64_t)idx); 353 return (m_children[idx] = CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type)); 354} 355 356bool 357lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::Update() 358{ 359 m_children.clear(); 360 m_iterators.clear(); 361 m_head = m_tail = NULL; 362 m_node_address = 0; 363 m_count = UINT32_MAX; 364 m_loop_detected = 0; 365 m_slow_runner.SetEntry(nullptr); 366 m_fast_runner.SetEntry(nullptr); 367 368 Error err; 369 ValueObjectSP backend_addr(m_backend.AddressOf(err)); 370 m_list_capping_size = 0; 371 if (m_backend.GetTargetSP()) 372 m_list_capping_size = m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); 373 if (m_list_capping_size == 0) 374 m_list_capping_size = 255; 375 if (err.Fail() || backend_addr.get() == NULL) 376 return false; 377 m_node_address = backend_addr->GetValueAsUnsigned(0); 378 if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS) 379 return false; 380 ValueObjectSP impl_sp(m_backend.GetChildMemberWithName(ConstString("__end_"),true)); 381 if (!impl_sp) 382 return false; 383 CompilerType list_type = m_backend.GetCompilerType(); 384 if (list_type.IsReferenceType()) 385 list_type = list_type.GetNonReferenceType(); 386 387 if (list_type.GetNumTemplateArguments() == 0) 388 return false; 389 lldb::TemplateArgumentKind kind; 390 m_element_type = list_type.GetTemplateArgument(0, kind); 391 m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get(); 392 m_tail = impl_sp->GetChildMemberWithName(ConstString("__prev_"), true).get(); 393 return false; 394} 395 396bool 397lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::MightHaveChildren () 398{ 399 return true; 400} 401 402size_t 403lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) 404{ 405 return ExtractIndexFromString(name.GetCString()); 406} 407 408SyntheticChildrenFrontEnd* 409lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) 410{ 411 if (!valobj_sp) 412 return NULL; 413 return (new LibcxxStdListSyntheticFrontEnd(valobj_sp)); 414} 415