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