1 //===-- NSSet.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 "NSSet.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/Language.h" 25 #include "lldb/Target/ObjCLanguageRuntime.h" 26 #include "lldb/Target/Target.h" 27 28 using namespace lldb; 29 using namespace lldb_private; 30 using namespace lldb_private::formatters; 31 32 std::map<ConstString, CXXFunctionSummaryFormat::Callback>& 33 NSSet_Additionals::GetAdditionalSummaries () 34 { 35 static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map; 36 return g_map; 37 } 38 39 std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback>& 40 NSSet_Additionals::GetAdditionalSynthetics () 41 { 42 static std::map<ConstString, CXXSyntheticChildren::CreateFrontEndCallback> g_map; 43 return g_map; 44 } 45 46 namespace lldb_private { 47 namespace formatters { 48 class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd 49 { 50 public: 51 NSSetISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); 52 53 ~NSSetISyntheticFrontEnd() override; 54 55 size_t 56 CalculateNumChildren() override; 57 58 lldb::ValueObjectSP 59 GetChildAtIndex(size_t idx) override; 60 61 bool 62 Update() override; 63 64 bool 65 MightHaveChildren() override; 66 67 size_t 68 GetIndexOfChildWithName(const ConstString &name) override; 69 70 private: 71 struct DataDescriptor_32 72 { 73 uint32_t _used : 26; 74 uint32_t _szidx : 6; 75 }; 76 77 struct DataDescriptor_64 78 { 79 uint64_t _used : 58; 80 uint32_t _szidx : 6; 81 }; 82 83 struct SetItemDescriptor 84 { 85 lldb::addr_t item_ptr; 86 lldb::ValueObjectSP valobj_sp; 87 }; 88 89 ExecutionContextRef m_exe_ctx_ref; 90 uint8_t m_ptr_size; 91 DataDescriptor_32 *m_data_32; 92 DataDescriptor_64 *m_data_64; 93 lldb::addr_t m_data_ptr; 94 std::vector<SetItemDescriptor> m_children; 95 }; 96 97 class NSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd 98 { 99 public: 100 NSSetMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); 101 102 ~NSSetMSyntheticFrontEnd() override; 103 104 size_t 105 CalculateNumChildren() override; 106 107 lldb::ValueObjectSP 108 GetChildAtIndex(size_t idx) override; 109 110 bool 111 Update() override; 112 113 bool 114 MightHaveChildren() override; 115 116 size_t 117 GetIndexOfChildWithName(const ConstString &name) override; 118 119 private: 120 struct DataDescriptor_32 121 { 122 uint32_t _used : 26; 123 uint32_t _size; 124 uint32_t _mutations; 125 uint32_t _objs_addr; 126 }; 127 128 struct DataDescriptor_64 129 { 130 uint64_t _used : 58; 131 uint64_t _size; 132 uint64_t _mutations; 133 uint64_t _objs_addr; 134 }; 135 136 struct SetItemDescriptor 137 { 138 lldb::addr_t item_ptr; 139 lldb::ValueObjectSP valobj_sp; 140 }; 141 142 ExecutionContextRef m_exe_ctx_ref; 143 uint8_t m_ptr_size; 144 DataDescriptor_32 *m_data_32; 145 DataDescriptor_64 *m_data_64; 146 std::vector<SetItemDescriptor> m_children; 147 }; 148 149 class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd 150 { 151 public: 152 NSSetCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); 153 154 ~NSSetCodeRunningSyntheticFrontEnd() override; 155 156 size_t 157 CalculateNumChildren() override; 158 159 lldb::ValueObjectSP 160 GetChildAtIndex(size_t idx) override; 161 162 bool 163 Update() override; 164 165 bool 166 MightHaveChildren() override; 167 168 size_t 169 GetIndexOfChildWithName(const ConstString &name) override; 170 }; 171 } // namespace formatters 172 } // namespace lldb_private 173 174 template<bool cf_style> 175 bool 176 lldb_private::formatters::NSSetSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) 177 { 178 static ConstString g_TypeHint("NSSet"); 179 180 ProcessSP process_sp = valobj.GetProcessSP(); 181 if (!process_sp) 182 return false; 183 184 ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 185 186 if (!runtime) 187 return false; 188 189 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); 190 191 if (!descriptor || !descriptor->IsValid()) 192 return false; 193 194 uint32_t ptr_size = process_sp->GetAddressByteSize(); 195 bool is_64bit = (ptr_size == 8); 196 197 lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); 198 199 if (!valobj_addr) 200 return false; 201 202 uint64_t value = 0; 203 204 ConstString class_name_cs = descriptor->GetClassName(); 205 const char* class_name = class_name_cs.GetCString(); 206 207 if (!class_name || !*class_name) 208 return false; 209 210 if (!strcmp(class_name,"__NSSetI")) 211 { 212 Error error; 213 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); 214 if (error.Fail()) 215 return false; 216 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); 217 } 218 else if (!strcmp(class_name,"__NSSetM")) 219 { 220 Error error; 221 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); 222 if (error.Fail()) 223 return false; 224 value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U); 225 } 226 /*else if (!strcmp(class_name,"__NSCFSet")) 227 { 228 Error error; 229 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + (is_64bit ? 20 : 12), 4, 0, error); 230 if (error.Fail()) 231 return false; 232 if (is_64bit) 233 value &= ~0x1fff000000000000UL; 234 } 235 else if (!strcmp(class_name,"NSCountedSet")) 236 { 237 Error error; 238 value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); 239 if (error.Fail()) 240 return false; 241 value = process_sp->ReadUnsignedIntegerFromMemory(value + (is_64bit ? 20 : 12), 4, 0, error); 242 if (error.Fail()) 243 return false; 244 if (is_64bit) 245 value &= ~0x1fff000000000000UL; 246 }*/ 247 else 248 { 249 auto& map(NSSet_Additionals::GetAdditionalSummaries()); 250 auto iter = map.find(class_name_cs), end = map.end(); 251 if (iter != end) 252 return iter->second(valobj, stream, options); 253 else 254 return false; 255 } 256 257 std::string prefix,suffix; 258 if (Language* language = Language::FindPlugin(options.GetLanguage())) 259 { 260 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) 261 { 262 prefix.clear(); 263 suffix.clear(); 264 } 265 } 266 267 stream.Printf("%s%" PRIu64 " %s%s%s", 268 prefix.c_str(), 269 value, 270 "element", 271 value == 1 ? "" : "s", 272 suffix.c_str()); 273 return true; 274 } 275 276 SyntheticChildrenFrontEnd* lldb_private::formatters::NSSetSyntheticFrontEndCreator (CXXSyntheticChildren* synth, lldb::ValueObjectSP valobj_sp) 277 { 278 lldb::ProcessSP process_sp (valobj_sp->GetProcessSP()); 279 if (!process_sp) 280 return nullptr; 281 ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); 282 if (!runtime) 283 return nullptr; 284 285 CompilerType valobj_type(valobj_sp->GetCompilerType()); 286 Flags flags(valobj_type.GetTypeInfo()); 287 288 if (flags.IsClear(eTypeIsPointer)) 289 { 290 Error error; 291 valobj_sp = valobj_sp->AddressOf(error); 292 if (error.Fail() || !valobj_sp) 293 return nullptr; 294 } 295 296 ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp)); 297 298 if (!descriptor || !descriptor->IsValid()) 299 return nullptr; 300 301 ConstString class_name_cs = descriptor->GetClassName(); 302 const char* class_name = class_name_cs.GetCString(); 303 304 if (!class_name || !*class_name) 305 return nullptr; 306 307 if (!strcmp(class_name,"__NSSetI")) 308 { 309 return (new NSSetISyntheticFrontEnd(valobj_sp)); 310 } 311 else if (!strcmp(class_name,"__NSSetM")) 312 { 313 return (new NSSetMSyntheticFrontEnd(valobj_sp)); 314 } 315 else 316 { 317 auto& map(NSSet_Additionals::GetAdditionalSynthetics()); 318 auto iter = map.find(class_name_cs), end = map.end(); 319 if (iter != end) 320 return iter->second(synth, valobj_sp); 321 return nullptr; 322 } 323 } 324 325 lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : 326 SyntheticChildrenFrontEnd(*valobj_sp), 327 m_exe_ctx_ref(), 328 m_ptr_size(8), 329 m_data_32(nullptr), 330 m_data_64(nullptr) 331 { 332 if (valobj_sp) 333 Update(); 334 } 335 336 lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd () 337 { 338 delete m_data_32; 339 m_data_32 = nullptr; 340 delete m_data_64; 341 m_data_64 = nullptr; 342 } 343 344 size_t 345 lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) 346 { 347 const char* item_name = name.GetCString(); 348 uint32_t idx = ExtractIndexFromString(item_name); 349 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 350 return UINT32_MAX; 351 return idx; 352 } 353 354 size_t 355 lldb_private::formatters::NSSetISyntheticFrontEnd::CalculateNumChildren () 356 { 357 if (!m_data_32 && !m_data_64) 358 return 0; 359 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 360 } 361 362 bool 363 lldb_private::formatters::NSSetISyntheticFrontEnd::Update() 364 { 365 m_children.clear(); 366 delete m_data_32; 367 m_data_32 = nullptr; 368 delete m_data_64; 369 m_data_64 = nullptr; 370 m_ptr_size = 0; 371 ValueObjectSP valobj_sp = m_backend.GetSP(); 372 if (!valobj_sp) 373 return false; 374 if (!valobj_sp) 375 return false; 376 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 377 Error error; 378 if (valobj_sp->IsPointerType()) 379 { 380 valobj_sp = valobj_sp->Dereference(error); 381 if (error.Fail() || !valobj_sp) 382 return false; 383 } 384 error.Clear(); 385 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 386 if (!process_sp) 387 return false; 388 m_ptr_size = process_sp->GetAddressByteSize(); 389 uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size; 390 if (m_ptr_size == 4) 391 { 392 m_data_32 = new DataDescriptor_32(); 393 process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error); 394 } 395 else 396 { 397 m_data_64 = new DataDescriptor_64(); 398 process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error); 399 } 400 if (error.Fail()) 401 return false; 402 m_data_ptr = data_location + m_ptr_size; 403 return false; 404 } 405 406 bool 407 lldb_private::formatters::NSSetISyntheticFrontEnd::MightHaveChildren () 408 { 409 return true; 410 } 411 412 lldb::ValueObjectSP 413 lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex (size_t idx) 414 { 415 uint32_t num_children = CalculateNumChildren(); 416 417 if (idx >= num_children) 418 return lldb::ValueObjectSP(); 419 420 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 421 if (!process_sp) 422 return lldb::ValueObjectSP(); 423 424 if (m_children.empty()) 425 { 426 // do the scan phase 427 lldb::addr_t obj_at_idx = 0; 428 429 uint32_t tries = 0; 430 uint32_t test_idx = 0; 431 432 while(tries < num_children) 433 { 434 obj_at_idx = m_data_ptr + (test_idx * m_ptr_size); 435 if (!process_sp) 436 return lldb::ValueObjectSP(); 437 Error error; 438 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); 439 if (error.Fail()) 440 return lldb::ValueObjectSP(); 441 442 test_idx++; 443 444 if (!obj_at_idx) 445 continue; 446 tries++; 447 448 SetItemDescriptor descriptor = {obj_at_idx,lldb::ValueObjectSP()}; 449 450 m_children.push_back(descriptor); 451 } 452 } 453 454 if (idx >= m_children.size()) // should never happen 455 return lldb::ValueObjectSP(); 456 457 SetItemDescriptor &set_item = m_children[idx]; 458 if (!set_item.valobj_sp) 459 { 460 auto ptr_size = process_sp->GetAddressByteSize(); 461 DataBufferHeap buffer(ptr_size,0); 462 switch (ptr_size) 463 { 464 case 0: // architecture has no clue?? - fail 465 return lldb::ValueObjectSP(); 466 case 4: 467 *((uint32_t*)buffer.GetBytes()) = (uint32_t)set_item.item_ptr; 468 break; 469 case 8: 470 *((uint64_t*)buffer.GetBytes()) = (uint64_t)set_item.item_ptr; 471 break; 472 default: 473 assert(false && "pointer size is not 4 nor 8 - get out of here ASAP"); 474 } 475 StreamString idx_name; 476 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 477 478 DataExtractor data(buffer.GetBytes(), 479 buffer.GetByteSize(), 480 process_sp->GetByteOrder(), 481 process_sp->GetAddressByteSize()); 482 483 set_item.valobj_sp = 484 CreateValueObjectFromData(idx_name.GetData(), 485 data, 486 m_exe_ctx_ref, 487 m_backend.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID)); 488 } 489 return set_item.valobj_sp; 490 } 491 492 lldb_private::formatters::NSSetMSyntheticFrontEnd::NSSetMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : 493 SyntheticChildrenFrontEnd(*valobj_sp), 494 m_exe_ctx_ref(), 495 m_ptr_size(8), 496 m_data_32(nullptr), 497 m_data_64(nullptr) 498 { 499 if (valobj_sp) 500 Update (); 501 } 502 503 lldb_private::formatters::NSSetMSyntheticFrontEnd::~NSSetMSyntheticFrontEnd () 504 { 505 delete m_data_32; 506 m_data_32 = nullptr; 507 delete m_data_64; 508 m_data_64 = nullptr; 509 } 510 511 size_t 512 lldb_private::formatters::NSSetMSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) 513 { 514 const char* item_name = name.GetCString(); 515 uint32_t idx = ExtractIndexFromString(item_name); 516 if (idx < UINT32_MAX && idx >= CalculateNumChildren()) 517 return UINT32_MAX; 518 return idx; 519 } 520 521 size_t 522 lldb_private::formatters::NSSetMSyntheticFrontEnd::CalculateNumChildren () 523 { 524 if (!m_data_32 && !m_data_64) 525 return 0; 526 return (m_data_32 ? m_data_32->_used : m_data_64->_used); 527 } 528 529 bool 530 lldb_private::formatters::NSSetMSyntheticFrontEnd::Update() 531 { 532 m_children.clear(); 533 ValueObjectSP valobj_sp = m_backend.GetSP(); 534 m_ptr_size = 0; 535 delete m_data_32; 536 m_data_32 = nullptr; 537 delete m_data_64; 538 m_data_64 = nullptr; 539 if (!valobj_sp) 540 return false; 541 if (!valobj_sp) 542 return false; 543 m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); 544 Error error; 545 if (valobj_sp->IsPointerType()) 546 { 547 valobj_sp = valobj_sp->Dereference(error); 548 if (error.Fail() || !valobj_sp) 549 return false; 550 } 551 error.Clear(); 552 lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); 553 if (!process_sp) 554 return false; 555 m_ptr_size = process_sp->GetAddressByteSize(); 556 uint64_t data_location = valobj_sp->GetAddressOf() + m_ptr_size; 557 if (m_ptr_size == 4) 558 { 559 m_data_32 = new DataDescriptor_32(); 560 process_sp->ReadMemory (data_location, m_data_32, sizeof(DataDescriptor_32), error); 561 } 562 else 563 { 564 m_data_64 = new DataDescriptor_64(); 565 process_sp->ReadMemory (data_location, m_data_64, sizeof(DataDescriptor_64), error); 566 } 567 if (error.Fail()) 568 return false; 569 return false; 570 } 571 572 bool 573 lldb_private::formatters::NSSetMSyntheticFrontEnd::MightHaveChildren () 574 { 575 return true; 576 } 577 578 lldb::ValueObjectSP 579 lldb_private::formatters::NSSetMSyntheticFrontEnd::GetChildAtIndex (size_t idx) 580 { 581 lldb::addr_t m_objs_addr = (m_data_32 ? m_data_32->_objs_addr : m_data_64->_objs_addr); 582 583 uint32_t num_children = CalculateNumChildren(); 584 585 if (idx >= num_children) 586 return lldb::ValueObjectSP(); 587 588 ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); 589 if (!process_sp) 590 return lldb::ValueObjectSP(); 591 592 if (m_children.empty()) 593 { 594 // do the scan phase 595 lldb::addr_t obj_at_idx = 0; 596 597 uint32_t tries = 0; 598 uint32_t test_idx = 0; 599 600 while(tries < num_children) 601 { 602 obj_at_idx = m_objs_addr + (test_idx * m_ptr_size); 603 if (!process_sp) 604 return lldb::ValueObjectSP(); 605 Error error; 606 obj_at_idx = process_sp->ReadPointerFromMemory(obj_at_idx, error); 607 if (error.Fail()) 608 return lldb::ValueObjectSP(); 609 610 test_idx++; 611 612 if (!obj_at_idx) 613 continue; 614 tries++; 615 616 SetItemDescriptor descriptor = {obj_at_idx,lldb::ValueObjectSP()}; 617 618 m_children.push_back(descriptor); 619 } 620 } 621 622 if (idx >= m_children.size()) // should never happen 623 return lldb::ValueObjectSP(); 624 625 SetItemDescriptor &set_item = m_children[idx]; 626 if (!set_item.valobj_sp) 627 { 628 auto ptr_size = process_sp->GetAddressByteSize(); 629 DataBufferHeap buffer(ptr_size,0); 630 switch (ptr_size) 631 { 632 case 0: // architecture has no clue?? - fail 633 return lldb::ValueObjectSP(); 634 case 4: 635 *((uint32_t*)buffer.GetBytes()) = (uint32_t)set_item.item_ptr; 636 break; 637 case 8: 638 *((uint64_t*)buffer.GetBytes()) = (uint64_t)set_item.item_ptr; 639 break; 640 default: 641 assert(false && "pointer size is not 4 nor 8 - get out of here ASAP"); 642 } 643 StreamString idx_name; 644 idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); 645 646 DataExtractor data(buffer.GetBytes(), 647 buffer.GetByteSize(), 648 process_sp->GetByteOrder(), 649 process_sp->GetAddressByteSize()); 650 651 set_item.valobj_sp = 652 CreateValueObjectFromData(idx_name.GetData(), 653 data, 654 m_exe_ctx_ref, 655 m_backend.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID)); 656 } 657 return set_item.valobj_sp; 658 } 659 660 template bool 661 lldb_private::formatters::NSSetSummaryProvider<true> (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options); 662 663 template bool 664 lldb_private::formatters::NSSetSummaryProvider<false> (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options); 665