1 //===-- head_find.c ---------------------------------------------*- 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 // This file compiles into a dylib and can be used on darwin to find data that 11 // is contained in active malloc blocks. To use this make the project, then 12 // load the shared library in a debug session while you are stopped: 13 // 14 // (lldb) process load /path/to/libheap.dylib 15 // 16 // Now you can use the "find_pointer_in_heap" and "find_cstring_in_heap" 17 // functions in the expression parser. 18 // 19 // This will grep everything in all active allocation blocks and print and 20 // malloc blocks that contain the pointer 0x112233000000: 21 // 22 // (lldb) expression find_pointer_in_heap (0x112233000000) 23 // 24 // This will grep everything in all active allocation blocks and print and 25 // malloc blocks that contain the C string "hello" (as a substring, no 26 // NULL termination included): 27 // 28 // (lldb) expression find_cstring_in_heap ("hello") 29 // 30 // The results will be printed to the STDOUT of the inferior program. The 31 // return value of the "find_pointer_in_heap" function is the number of 32 // pointer references that were found. A quick example shows 33 // 34 // (lldb) expr find_pointer_in_heap(0x0000000104000410) 35 // (uint32_t) $5 = 0x00000002 36 // 0x104000740: 0x0000000104000410 found in malloc block 0x104000730 + 16 (malloc_size = 48) 37 // 0x100820060: 0x0000000104000410 found in malloc block 0x100820000 + 96 (malloc_size = 4096) 38 // 39 // From the above output we see that 0x104000410 was found in the malloc block 40 // at 0x104000730 and 0x100820000. If we want to see what these blocks are, we 41 // can display the memory for this block using the "address" ("A" for short) 42 // format. The address format shows pointers, and if those pointers point to 43 // objects that have symbols or know data contents, it will display information 44 // about the pointers: 45 // 46 // (lldb) memory read --format address --count 1 0x104000730 47 // 0x104000730: 0x0000000100002460 (void *)0x0000000100002488: MyString 48 // 49 // We can see that the first block is a "MyString" object that contains our 50 // pointer value at offset 16. 51 // 52 // Looking at the next pointers, are a bit more tricky: 53 // (lldb) memory read -fA 0x100820000 -c1 54 // 0x100820000: 0x4f545541a1a1a1a1 55 // (lldb) memory read 0x100820000 56 // 0x100820000: a1 a1 a1 a1 41 55 54 4f 52 45 4c 45 41 53 45 21 ....AUTORELEASE! 57 // 0x100820010: 78 00 82 00 01 00 00 00 60 f9 e8 75 ff 7f 00 00 x.......`..u.... 58 // 59 // This is an objective C auto release pool object that contains our pointer. 60 // C++ classes will show up if they are virtual as something like: 61 // (lldb) memory read --format address --count 1 0x104008000 62 // 0x104008000: 0x109008000 vtable for lldb_private::Process 63 // 64 // This is a clue that the 0x104008000 is a "lldb_private::Process *". 65 //===----------------------------------------------------------------------===// 66 // C includes 67 #include <assert.h> 68 #include <ctype.h> 69 #include <dlfcn.h> 70 #include <mach/mach.h> 71 #include <mach/mach_vm.h> 72 #include <malloc/malloc.h> 73 #include <objc/objc-runtime.h> 74 #include <stdio.h> 75 #include <stdlib.h> 76 #include <unistd.h> 77 78 // C++ includes 79 #include <vector> 80 81 //---------------------------------------------------------------------- 82 // Redefine private types from "/usr/local/include/stack_logging.h" 83 //---------------------------------------------------------------------- 84 typedef struct { 85 uint32_t type_flags; 86 uint64_t stack_identifier; 87 uint64_t argument; 88 mach_vm_address_t address; 89 } mach_stack_logging_record_t; 90 91 //---------------------------------------------------------------------- 92 // Redefine private defines from "/usr/local/include/stack_logging.h" 93 //---------------------------------------------------------------------- 94 #define stack_logging_type_free 0 95 #define stack_logging_type_generic 1 96 #define stack_logging_type_alloc 2 97 #define stack_logging_type_dealloc 4 98 // This bit is made up by this code 99 #define stack_logging_type_vm_region 8 100 101 //---------------------------------------------------------------------- 102 // Redefine private function prototypes from 103 // "/usr/local/include/stack_logging.h" 104 //---------------------------------------------------------------------- 105 extern "C" kern_return_t 106 __mach_stack_logging_set_file_path ( 107 task_t task, 108 char* file_path 109 ); 110 111 extern "C" kern_return_t 112 __mach_stack_logging_get_frames ( 113 task_t task, 114 mach_vm_address_t address, 115 mach_vm_address_t *stack_frames_buffer, 116 uint32_t max_stack_frames, 117 uint32_t *count 118 ); 119 120 extern "C" kern_return_t 121 __mach_stack_logging_enumerate_records ( 122 task_t task, 123 mach_vm_address_t address, 124 void enumerator(mach_stack_logging_record_t, void *), 125 void *context 126 ); 127 128 extern "C" kern_return_t 129 __mach_stack_logging_frames_for_uniqued_stack ( 130 task_t task, 131 uint64_t stack_identifier, 132 mach_vm_address_t *stack_frames_buffer, 133 uint32_t max_stack_frames, 134 uint32_t *count 135 ); 136 137 extern "C" void *gdb_class_getClass (void *objc_class); 138 139 static void 140 range_info_callback (task_t task, 141 void *baton, 142 unsigned type, 143 uint64_t ptr_addr, 144 uint64_t ptr_size); 145 146 //---------------------------------------------------------------------- 147 // Redefine private gloval variables prototypes from 148 // "/usr/local/include/stack_logging.h" 149 //---------------------------------------------------------------------- 150 151 extern "C" int stack_logging_enable_logging; 152 153 //---------------------------------------------------------------------- 154 // Local defines 155 //---------------------------------------------------------------------- 156 #define MAX_FRAMES 1024 157 158 //---------------------------------------------------------------------- 159 // Local Typedefs and Types 160 //---------------------------------------------------------------------- 161 typedef void range_callback_t (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size); 162 typedef void zone_callback_t (void *info, const malloc_zone_t *zone); 163 typedef int (*comare_function_t)(const void *, const void *); 164 struct range_callback_info_t 165 { 166 zone_callback_t *zone_callback; 167 range_callback_t *range_callback; 168 void *baton; 169 int check_vm_regions; 170 }; 171 172 enum data_type_t 173 { 174 eDataTypeAddress, 175 eDataTypeContainsData, 176 eDataTypeObjC, 177 eDataTypeHeapInfo 178 }; 179 180 struct aligned_data_t 181 { 182 const uint8_t *buffer; 183 uint32_t size; 184 uint32_t align; 185 }; 186 187 struct objc_data_t 188 { 189 void *match_isa; // Set to NULL for all objective C objects 190 bool match_superclasses; 191 }; 192 193 struct range_contains_data_callback_info_t 194 { 195 data_type_t type; 196 const void *lookup_addr; 197 union 198 { 199 uintptr_t addr; 200 aligned_data_t data; 201 objc_data_t objc; 202 }; 203 uint32_t match_count; 204 bool done; 205 bool unique; 206 }; 207 208 struct malloc_match 209 { 210 void *addr; 211 intptr_t size; 212 intptr_t offset; 213 uintptr_t type; 214 }; 215 216 struct malloc_stack_entry 217 { 218 const void *address; 219 uint64_t argument; 220 uint32_t type_flags; 221 uint32_t num_frames; 222 mach_vm_address_t frames[MAX_FRAMES]; 223 }; 224 225 struct malloc_block_contents 226 { 227 union { 228 Class isa; 229 void *pointers[2]; 230 }; 231 }; 232 233 static int 234 compare_void_ptr (const void *a, const void *b) 235 { 236 Class a_ptr = *(Class *)a; 237 Class b_ptr = *(Class *)b; 238 if (a_ptr < b_ptr) return -1; 239 if (a_ptr > b_ptr) return +1; 240 return 0; 241 } 242 243 class MatchResults 244 { 245 enum { 246 k_max_entries = 8 * 1024 247 }; 248 public: 249 MatchResults () : 250 m_size(0) 251 { 252 } 253 254 void 255 clear() 256 { 257 m_size = 0; 258 bzero (&m_entries, sizeof(m_entries)); 259 } 260 261 bool 262 empty() const 263 { 264 return m_size == 0; 265 } 266 267 void 268 push_back (const malloc_match& m, bool unique = false) 269 { 270 if (unique) 271 { 272 // Don't add the entry if there is already a match for this address 273 for (uint32_t i=0; i<m_size; ++i) 274 { 275 if (((uint8_t *)m_entries[i].addr + m_entries[i].offset) == ((uint8_t *)m.addr + m.offset)) 276 return; // Duplicate entry 277 } 278 } 279 if (m_size < k_max_entries - 1) 280 { 281 m_entries[m_size] = m; 282 m_size++; 283 } 284 } 285 286 malloc_match * 287 data () 288 { 289 // If empty, return NULL 290 if (empty()) 291 return NULL; 292 // In not empty, terminate and return the result 293 malloc_match terminator_entry = { NULL, 0, 0, 0 }; 294 // We always leave room for an empty entry at the end 295 m_entries[m_size] = terminator_entry; 296 return m_entries; 297 } 298 299 protected: 300 malloc_match m_entries[k_max_entries]; 301 uint32_t m_size; 302 }; 303 304 class MallocStackLoggingEntries 305 { 306 enum { k_max_entries = 128 }; 307 public: 308 MallocStackLoggingEntries () : 309 m_size(0) 310 { 311 } 312 313 void 314 clear() 315 { 316 m_size = 0; 317 } 318 319 bool 320 empty() const 321 { 322 return m_size == 0; 323 } 324 325 326 malloc_stack_entry * 327 next () 328 { 329 if (m_size < k_max_entries - 1) 330 { 331 malloc_stack_entry * result = m_entries + m_size; 332 ++m_size; 333 return result; 334 } 335 return NULL; // Out of entries... 336 } 337 338 malloc_stack_entry * 339 data () 340 { 341 // If empty, return NULL 342 if (empty()) 343 return NULL; 344 // In not empty, terminate and return the result 345 m_entries[m_size].address = NULL; 346 m_entries[m_size].argument = 0; 347 m_entries[m_size].type_flags = 0; 348 m_entries[m_size].num_frames = 0; 349 return m_entries; 350 } 351 352 protected: 353 malloc_stack_entry m_entries[k_max_entries]; 354 uint32_t m_size; 355 }; 356 357 //---------------------------------------------------------------------- 358 // A safe way to allocate memory and keep it from interfering with the 359 // malloc enumerators. 360 //---------------------------------------------------------------------- 361 void * 362 safe_malloc(size_t n_bytes) 363 { 364 if (n_bytes > 0) 365 { 366 const int k_page_size = getpagesize(); 367 const mach_vm_size_t vm_size = ((n_bytes + k_page_size - 1)/k_page_size) * k_page_size; 368 vm_address_t address = 0; 369 kern_return_t kerr = vm_allocate (mach_task_self(), &address, vm_size, true); 370 if (kerr == KERN_SUCCESS) 371 return (void *)address; 372 } 373 return NULL; 374 } 375 376 377 //---------------------------------------------------------------------- 378 // ObjCClasses 379 //---------------------------------------------------------------------- 380 class ObjCClasses 381 { 382 public: 383 ObjCClasses() : 384 m_objc_class_ptrs (NULL), 385 m_size (0) 386 { 387 } 388 389 bool 390 Update() 391 { 392 // TODO: find out if class list has changed and update if needed 393 if (m_objc_class_ptrs == NULL) 394 { 395 m_size = objc_getClassList(NULL, 0); 396 if (m_size > 0) 397 { 398 // Allocate the class pointers 399 m_objc_class_ptrs = (Class *)safe_malloc (m_size * sizeof(Class)); 400 m_size = objc_getClassList(m_objc_class_ptrs, m_size); 401 // Sort Class pointers for quick lookup 402 ::qsort (m_objc_class_ptrs, m_size, sizeof(Class), compare_void_ptr); 403 } 404 else 405 return false; 406 } 407 return true; 408 } 409 410 uint32_t 411 FindClassIndex (Class isa) 412 { 413 Class *matching_class = (Class *)bsearch (&isa, 414 m_objc_class_ptrs, 415 m_size, 416 sizeof(Class), 417 compare_void_ptr); 418 if (matching_class) 419 { 420 uint32_t idx = matching_class - m_objc_class_ptrs; 421 return idx; 422 } 423 return UINT32_MAX; 424 } 425 426 Class 427 GetClassAtIndex (uint32_t idx) const 428 { 429 if (idx < m_size) 430 return m_objc_class_ptrs[idx]; 431 return NULL; 432 } 433 uint32_t 434 GetSize() const 435 { 436 return m_size; 437 } 438 private: 439 Class *m_objc_class_ptrs; 440 uint32_t m_size; 441 }; 442 443 444 445 //---------------------------------------------------------------------- 446 // Local global variables 447 //---------------------------------------------------------------------- 448 MatchResults g_matches; 449 MallocStackLoggingEntries g_malloc_stack_history; 450 ObjCClasses g_objc_classes; 451 452 //---------------------------------------------------------------------- 453 // ObjCClassInfo 454 //---------------------------------------------------------------------- 455 456 enum HeapInfoSortType 457 { 458 eSortTypeNone, 459 eSortTypeBytes, 460 eSortTypeCount 461 }; 462 463 class ObjCClassInfo 464 { 465 public: 466 ObjCClassInfo() : 467 m_entries (NULL), 468 m_size (0), 469 m_sort_type (eSortTypeNone) 470 { 471 } 472 473 void 474 Update (const ObjCClasses &objc_classes) 475 { 476 m_size = objc_classes.GetSize(); 477 m_entries = (Entry *)safe_malloc (m_size * sizeof(Entry)); 478 m_sort_type = eSortTypeNone; 479 Reset (); 480 } 481 482 bool 483 AddInstance (uint32_t idx, uint64_t ptr_size) 484 { 485 if (m_size == 0) 486 Update (g_objc_classes); 487 // Update the totals for the classes 488 if (idx < m_size) 489 { 490 m_entries[idx].bytes += ptr_size; 491 ++m_entries[idx].count; 492 return true; 493 } 494 return false; 495 } 496 497 void 498 Reset () 499 { 500 m_sort_type = eSortTypeNone; 501 for (uint32_t i=0; i<m_size; ++i) 502 { 503 // In case we sort the entries after gathering the data, we will 504 // want to know the index into the m_objc_class_ptrs[] array. 505 m_entries[i].idx = i; 506 m_entries[i].bytes = 0; 507 m_entries[i].count = 0; 508 } 509 } 510 void 511 SortByTotalBytes (const ObjCClasses &objc_classes, bool print) 512 { 513 if (m_sort_type != eSortTypeBytes && m_size > 0) 514 { 515 ::qsort (m_entries, m_size, sizeof(Entry), (comare_function_t)compare_bytes); 516 m_sort_type = eSortTypeBytes; 517 } 518 if (print && m_size > 0) 519 { 520 puts("Objective C objects by total bytes:"); 521 puts("Total Bytes Class Name"); 522 puts("----------- -----------------------------------------------------------------"); 523 for (uint32_t i=0; i<m_size && m_entries[i].bytes > 0; ++i) 524 { 525 printf ("%11llu %s\n", m_entries[i].bytes, class_getName (objc_classes.GetClassAtIndex(m_entries[i].idx))); 526 } 527 } 528 } 529 void 530 SortByTotalCount (const ObjCClasses &objc_classes, bool print) 531 { 532 if (m_sort_type != eSortTypeCount && m_size > 0) 533 { 534 ::qsort (m_entries, m_size, sizeof(Entry), (comare_function_t)compare_count); 535 m_sort_type = eSortTypeCount; 536 } 537 if (print && m_size > 0) 538 { 539 puts("Objective C objects by total count:"); 540 puts("Count Class Name"); 541 puts("-------- -----------------------------------------------------------------"); 542 for (uint32_t i=0; i<m_size && m_entries[i].count > 0; ++i) 543 { 544 printf ("%8u %s\n", m_entries[i].count, class_getName (objc_classes.GetClassAtIndex(m_entries[i].idx))); 545 } 546 } 547 } 548 private: 549 struct Entry 550 { 551 uint32_t idx; // Index into the m_objc_class_ptrs[] array 552 uint32_t count; // Number of object instances that were found 553 uint64_t bytes; // Total number of bytes for each objc class 554 }; 555 556 static int 557 compare_bytes (const Entry *a, const Entry *b) 558 { 559 // Reverse the comparisong to most bytes entries end up at top of list 560 if (a->bytes > b->bytes) return -1; 561 if (a->bytes < b->bytes) return +1; 562 return 0; 563 } 564 565 static int 566 compare_count (const Entry *a, const Entry *b) 567 { 568 // Reverse the comparisong to most count entries end up at top of list 569 if (a->count > b->count) return -1; 570 if (a->count < b->count) return +1; 571 return 0; 572 } 573 574 Entry *m_entries; 575 uint32_t m_size; 576 HeapInfoSortType m_sort_type; 577 }; 578 579 ObjCClassInfo g_objc_class_snapshot; 580 581 //---------------------------------------------------------------------- 582 // task_peek 583 // 584 // Reads memory from this tasks address space. This callback is needed 585 // by the code that iterates through all of the malloc blocks to read 586 // the memory in this process. 587 //---------------------------------------------------------------------- 588 static kern_return_t 589 task_peek (task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) 590 { 591 *local_memory = (void*) remote_address; 592 return KERN_SUCCESS; 593 } 594 595 596 static const void 597 foreach_zone_in_this_process (range_callback_info_t *info) 598 { 599 if (info == NULL || info->zone_callback == NULL) 600 return; 601 602 vm_address_t *zones = NULL; 603 unsigned int num_zones = 0; 604 605 kern_return_t err = malloc_get_all_zones (0, task_peek, &zones, &num_zones); 606 if (KERN_SUCCESS == err) 607 { 608 for (unsigned int i=0; i<num_zones; ++i) 609 { 610 info->zone_callback (info, (const malloc_zone_t *)zones[i]); 611 } 612 } 613 614 if (info->check_vm_regions) 615 { 616 #if defined (VM_REGION_SUBMAP_SHORT_INFO_COUNT_64) 617 typedef vm_region_submap_short_info_data_64_t RegionInfo; 618 enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 }; 619 #else 620 typedef vm_region_submap_info_data_64_t RegionInfo; 621 enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 }; 622 #endif 623 task_t task = mach_task_self(); 624 mach_vm_address_t vm_region_base_addr; 625 mach_vm_size_t vm_region_size; 626 natural_t vm_region_depth; 627 RegionInfo vm_region_info; 628 629 ((range_contains_data_callback_info_t *)info->baton)->unique = true; 630 631 for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size) 632 { 633 mach_msg_type_number_t vm_region_info_size = kRegionInfoSize; 634 const kern_return_t err = mach_vm_region_recurse (task, 635 &vm_region_base_addr, 636 &vm_region_size, 637 &vm_region_depth, 638 (vm_region_recurse_info_t)&vm_region_info, 639 &vm_region_info_size); 640 if (err) 641 break; 642 // Check all read + write regions. This will cover the thread stacks 643 // and any regions of memory that aren't covered by the heap 644 if (vm_region_info.protection & VM_PROT_WRITE && 645 vm_region_info.protection & VM_PROT_READ) 646 { 647 //printf ("checking vm_region: [0x%16.16llx - 0x%16.16llx)\n", (uint64_t)vm_region_base_addr, (uint64_t)vm_region_base_addr + vm_region_size); 648 range_info_callback (task, 649 info->baton, 650 stack_logging_type_vm_region, 651 vm_region_base_addr, 652 vm_region_size); 653 } 654 } 655 } 656 } 657 658 //---------------------------------------------------------------------- 659 // dump_malloc_block_callback 660 // 661 // A simple callback that will dump each malloc block and all available 662 // info from the enumeration callback perpective. 663 //---------------------------------------------------------------------- 664 static void 665 dump_malloc_block_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size) 666 { 667 printf ("task = 0x%4.4x: baton = %p, type = %u, ptr_addr = 0x%llx + 0x%llu\n", task, baton, type, ptr_addr, ptr_size); 668 } 669 670 static void 671 ranges_callback (task_t task, void *baton, unsigned type, vm_range_t *ptrs, unsigned count) 672 { 673 range_callback_info_t *info = (range_callback_info_t *)baton; 674 while(count--) { 675 info->range_callback (task, info->baton, type, ptrs->address, ptrs->size); 676 ptrs++; 677 } 678 } 679 680 static void 681 enumerate_range_in_zone (void *baton, const malloc_zone_t *zone) 682 { 683 range_callback_info_t *info = (range_callback_info_t *)baton; 684 685 if (zone && zone->introspect) 686 zone->introspect->enumerator (mach_task_self(), 687 info, 688 MALLOC_PTR_IN_USE_RANGE_TYPE, 689 (vm_address_t)zone, 690 task_peek, 691 ranges_callback); 692 } 693 694 static void 695 range_info_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size) 696 { 697 const uint64_t end_addr = ptr_addr + ptr_size; 698 699 range_contains_data_callback_info_t *info = (range_contains_data_callback_info_t *)baton; 700 switch (info->type) 701 { 702 case eDataTypeAddress: 703 // Check if the current malloc block contains an address specified by "info->addr" 704 if (ptr_addr <= info->addr && info->addr < end_addr) 705 { 706 ++info->match_count; 707 malloc_match match = { (void *)ptr_addr, ptr_size, info->addr - ptr_addr, type }; 708 g_matches.push_back(match, info->unique); 709 } 710 break; 711 712 case eDataTypeContainsData: 713 // Check if the current malloc block contains data specified in "info->data" 714 { 715 const uint32_t size = info->data.size; 716 if (size < ptr_size) // Make sure this block can contain this data 717 { 718 uint8_t *ptr_data = NULL; 719 if (task_peek (task, ptr_addr, ptr_size, (void **)&ptr_data) == KERN_SUCCESS) 720 { 721 const void *buffer = info->data.buffer; 722 assert (ptr_data); 723 const uint32_t align = info->data.align; 724 for (uint64_t addr = ptr_addr; 725 addr < end_addr && ((end_addr - addr) >= size); 726 addr += align, ptr_data += align) 727 { 728 if (memcmp (buffer, ptr_data, size) == 0) 729 { 730 ++info->match_count; 731 malloc_match match = { (void *)ptr_addr, ptr_size, addr - ptr_addr, type }; 732 g_matches.push_back(match, info->unique); 733 } 734 } 735 } 736 else 737 { 738 printf ("0x%llx: error: couldn't read %llu bytes\n", ptr_addr, ptr_size); 739 } 740 } 741 } 742 break; 743 744 case eDataTypeObjC: 745 // Check if the current malloc block contains an objective C object 746 // of any sort where the first pointer in the object is an OBJC class 747 // pointer (an isa) 748 { 749 malloc_block_contents *block_contents = NULL; 750 if (task_peek (task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS) 751 { 752 // We assume that g_objc_classes is up to date 753 // that the class list was verified to have some classes in it 754 // before calling this function 755 const uint32_t objc_class_idx = g_objc_classes.FindClassIndex (block_contents->isa); 756 if (objc_class_idx != UINT32_MAX) 757 { 758 bool match = false; 759 if (info->objc.match_isa == 0) 760 { 761 // Match any objective C object 762 match = true; 763 } 764 else 765 { 766 // Only match exact isa values in the current class or 767 // optionally in the super classes 768 if (info->objc.match_isa == block_contents->isa) 769 match = true; 770 else if (info->objc.match_superclasses) 771 { 772 Class super = class_getSuperclass(block_contents->isa); 773 while (super) 774 { 775 match = super == info->objc.match_isa; 776 if (match) 777 break; 778 super = class_getSuperclass(super); 779 } 780 } 781 } 782 if (match) 783 { 784 //printf (" success\n"); 785 ++info->match_count; 786 malloc_match match = { (void *)ptr_addr, ptr_size, 0, type }; 787 g_matches.push_back(match, info->unique); 788 } 789 else 790 { 791 //printf (" error: wrong class: %s\n", dl_info.dli_sname); 792 } 793 } 794 else 795 { 796 //printf ("\terror: symbol not objc class: %s\n", dl_info.dli_sname); 797 return; 798 } 799 } 800 } 801 break; 802 803 case eDataTypeHeapInfo: 804 // Check if the current malloc block contains an objective C object 805 // of any sort where the first pointer in the object is an OBJC class 806 // pointer (an isa) 807 { 808 malloc_block_contents *block_contents = NULL; 809 if (task_peek (task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS) 810 { 811 // We assume that g_objc_classes is up to date 812 // that the class list was verified to have some classes in it 813 // before calling this function 814 const uint32_t objc_class_idx = g_objc_classes.FindClassIndex (block_contents->isa); 815 if (objc_class_idx != UINT32_MAX) 816 { 817 // This is an objective C object 818 g_objc_class_snapshot.AddInstance (objc_class_idx, ptr_size); 819 } 820 else 821 { 822 // Classify other heap info 823 } 824 } 825 } 826 break; 827 828 } 829 } 830 831 static void 832 get_stack_for_address_enumerator(mach_stack_logging_record_t stack_record, void *task_ptr) 833 { 834 malloc_stack_entry *stack_entry = g_malloc_stack_history.next(); 835 if (stack_entry) 836 { 837 stack_entry->address = (void *)stack_record.address; 838 stack_entry->type_flags = stack_record.type_flags; 839 stack_entry->argument = stack_record.argument; 840 stack_entry->num_frames = 0; 841 stack_entry->frames[0] = 0; 842 kern_return_t err = __mach_stack_logging_frames_for_uniqued_stack (*(task_t *)task_ptr, 843 stack_record.stack_identifier, 844 stack_entry->frames, 845 MAX_FRAMES, 846 &stack_entry->num_frames); 847 // Terminate the frames with zero if there is room 848 if (stack_entry->num_frames < MAX_FRAMES) 849 stack_entry->frames[stack_entry->num_frames] = 0; 850 } 851 } 852 853 malloc_stack_entry * 854 get_stack_history_for_address (const void * addr, int history) 855 { 856 if (!stack_logging_enable_logging) 857 return NULL; 858 g_malloc_stack_history.clear(); 859 kern_return_t err; 860 task_t task = mach_task_self(); 861 if (history) 862 { 863 err = __mach_stack_logging_enumerate_records (task, 864 (mach_vm_address_t)addr, 865 get_stack_for_address_enumerator, 866 &task); 867 } 868 else 869 { 870 malloc_stack_entry *stack_entry = g_malloc_stack_history.next(); 871 if (stack_entry) 872 { 873 stack_entry->address = addr; 874 stack_entry->type_flags = stack_logging_type_alloc; 875 stack_entry->argument = 0; 876 stack_entry->num_frames = 0; 877 stack_entry->frames[0] = 0; 878 err = __mach_stack_logging_get_frames(task, (mach_vm_address_t)addr, stack_entry->frames, MAX_FRAMES, &stack_entry->num_frames); 879 if (err == 0 && stack_entry->num_frames > 0) 880 { 881 // Terminate the frames with zero if there is room 882 if (stack_entry->num_frames < MAX_FRAMES) 883 stack_entry->frames[stack_entry->num_frames] = 0; 884 } 885 else 886 { 887 g_malloc_stack_history.clear(); 888 } 889 } 890 } 891 // Return data if there is any 892 return g_malloc_stack_history.data(); 893 } 894 895 //---------------------------------------------------------------------- 896 // find_pointer_in_heap 897 // 898 // Finds a pointer value inside one or more currently valid malloc 899 // blocks. 900 //---------------------------------------------------------------------- 901 malloc_match * 902 find_pointer_in_heap (const void * addr, int check_vm_regions) 903 { 904 g_matches.clear(); 905 // Setup "info" to look for a malloc block that contains data 906 // that is the a pointer 907 if (addr) 908 { 909 range_contains_data_callback_info_t data_info; 910 data_info.type = eDataTypeContainsData; // Check each block for data 911 data_info.data.buffer = (uint8_t *)&addr; // What data? The pointer value passed in 912 data_info.data.size = sizeof(addr); // How many bytes? The byte size of a pointer 913 data_info.data.align = sizeof(addr); // Align to a pointer byte size 914 data_info.match_count = 0; // Initialize the match count to zero 915 data_info.done = false; // Set done to false so searching doesn't stop 916 data_info.unique = false; // Set to true when iterating on the vm_regions 917 range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; 918 foreach_zone_in_this_process (&info); 919 920 921 } 922 return g_matches.data(); 923 } 924 925 //---------------------------------------------------------------------- 926 // find_pointer_in_memory 927 // 928 // Finds a pointer value inside one or more currently valid malloc 929 // blocks. 930 //---------------------------------------------------------------------- 931 malloc_match * 932 find_pointer_in_memory (uint64_t memory_addr, uint64_t memory_size, const void * addr) 933 { 934 g_matches.clear(); 935 // Setup "info" to look for a malloc block that contains data 936 // that is the a pointer 937 range_contains_data_callback_info_t data_info; 938 data_info.type = eDataTypeContainsData; // Check each block for data 939 data_info.data.buffer = (uint8_t *)&addr; // What data? The pointer value passed in 940 data_info.data.size = sizeof(addr); // How many bytes? The byte size of a pointer 941 data_info.data.align = sizeof(addr); // Align to a pointer byte size 942 data_info.match_count = 0; // Initialize the match count to zero 943 data_info.done = false; // Set done to false so searching doesn't stop 944 data_info.unique = false; // Set to true when iterating on the vm_regions 945 range_info_callback (mach_task_self(), &data_info, stack_logging_type_generic, memory_addr, memory_size); 946 return g_matches.data(); 947 } 948 949 //---------------------------------------------------------------------- 950 // find_objc_objects_in_memory 951 // 952 // Find all instances of ObjC classes 'c', or all ObjC classes if 'c' is 953 // NULL. If 'c' is non NULL, then also check objects to see if they 954 // inherit from 'c' 955 //---------------------------------------------------------------------- 956 malloc_match * 957 find_objc_objects_in_memory (void *isa, int check_vm_regions) 958 { 959 g_matches.clear(); 960 if (g_objc_classes.Update()) 961 { 962 // Setup "info" to look for a malloc block that contains data 963 // that is the a pointer 964 range_contains_data_callback_info_t data_info; 965 data_info.type = eDataTypeObjC; // Check each block for data 966 data_info.objc.match_isa = isa; 967 data_info.objc.match_superclasses = true; 968 data_info.match_count = 0; // Initialize the match count to zero 969 data_info.done = false; // Set done to false so searching doesn't stop 970 data_info.unique = false; // Set to true when iterating on the vm_regions 971 range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; 972 foreach_zone_in_this_process (&info); 973 } 974 return g_matches.data(); 975 } 976 977 //---------------------------------------------------------------------- 978 // get_heap_info 979 // 980 // Gather information for all allocations on the heap and report 981 // statistics. 982 //---------------------------------------------------------------------- 983 984 void 985 get_heap_info (int sort_type) 986 { 987 if (g_objc_classes.Update()) 988 { 989 // Reset all stats 990 g_objc_class_snapshot.Reset (); 991 // Setup "info" to look for a malloc block that contains data 992 // that is the a pointer 993 range_contains_data_callback_info_t data_info; 994 data_info.type = eDataTypeHeapInfo; // Check each block for data 995 data_info.match_count = 0; // Initialize the match count to zero 996 data_info.done = false; // Set done to false so searching doesn't stop 997 data_info.unique = false; // Set to true when iterating on the vm_regions 998 const int check_vm_regions = false; 999 range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; 1000 foreach_zone_in_this_process (&info); 1001 1002 // Sort and print byte total bytes 1003 switch (sort_type) 1004 { 1005 case eSortTypeNone: 1006 default: 1007 case eSortTypeBytes: 1008 g_objc_class_snapshot.SortByTotalBytes(g_objc_classes, true); 1009 break; 1010 1011 case eSortTypeCount: 1012 g_objc_class_snapshot.SortByTotalCount(g_objc_classes, true); 1013 break; 1014 } 1015 } 1016 else 1017 { 1018 printf ("error: no objective C classes\n"); 1019 } 1020 } 1021 1022 //---------------------------------------------------------------------- 1023 // find_cstring_in_heap 1024 // 1025 // Finds a C string inside one or more currently valid malloc blocks. 1026 //---------------------------------------------------------------------- 1027 malloc_match * 1028 find_cstring_in_heap (const char *s, int check_vm_regions) 1029 { 1030 g_matches.clear(); 1031 if (s == NULL || s[0] == '\0') 1032 { 1033 printf ("error: invalid argument (empty cstring)\n"); 1034 return NULL; 1035 } 1036 // Setup "info" to look for a malloc block that contains data 1037 // that is the C string passed in aligned on a 1 byte boundary 1038 range_contains_data_callback_info_t data_info; 1039 data_info.type = eDataTypeContainsData; // Check each block for data 1040 data_info.data.buffer = (uint8_t *)s; // What data? The C string passed in 1041 data_info.data.size = strlen(s); // How many bytes? The length of the C string 1042 data_info.data.align = 1; // Data doesn't need to be aligned, so set the alignment to 1 1043 data_info.match_count = 0; // Initialize the match count to zero 1044 data_info.done = false; // Set done to false so searching doesn't stop 1045 data_info.unique = false; // Set to true when iterating on the vm_regions 1046 range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; 1047 foreach_zone_in_this_process (&info); 1048 return g_matches.data(); 1049 } 1050 1051 //---------------------------------------------------------------------- 1052 // find_block_for_address 1053 // 1054 // Find the malloc block that whose address range contains "addr". 1055 //---------------------------------------------------------------------- 1056 malloc_match * 1057 find_block_for_address (const void *addr, int check_vm_regions) 1058 { 1059 g_matches.clear(); 1060 // Setup "info" to look for a malloc block that contains data 1061 // that is the C string passed in aligned on a 1 byte boundary 1062 range_contains_data_callback_info_t data_info; 1063 data_info.type = eDataTypeAddress; // Check each block to see if the block contains the address passed in 1064 data_info.addr = (uintptr_t)addr; // What data? The C string passed in 1065 data_info.match_count = 0; // Initialize the match count to zero 1066 data_info.done = false; // Set done to false so searching doesn't stop 1067 data_info.unique = false; // Set to true when iterating on the vm_regions 1068 range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; 1069 foreach_zone_in_this_process (&info); 1070 return g_matches.data(); 1071 } 1072