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