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//---------------------------------------------------------------------- 84typedef 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//---------------------------------------------------------------------- 105extern "C" kern_return_t 106__mach_stack_logging_set_file_path ( 107 task_t task, 108 char* file_path 109); 110 111extern "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 120extern "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 128extern "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 137extern "C" void *gdb_class_getClass (void *objc_class); 138 139static void 140range_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 151extern "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//---------------------------------------------------------------------- 161typedef void range_callback_t (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size); 162typedef void zone_callback_t (void *info, const malloc_zone_t *zone); 163typedef int (*comare_function_t)(const void *, const void *); 164struct 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 172enum data_type_t 173{ 174 eDataTypeAddress, 175 eDataTypeContainsData, 176 eDataTypeObjC, 177 eDataTypeHeapInfo 178}; 179 180struct aligned_data_t 181{ 182 const uint8_t *buffer; 183 uint32_t size; 184 uint32_t align; 185}; 186 187struct objc_data_t 188{ 189 void *match_isa; // Set to NULL for all objective C objects 190 bool match_superclasses; 191}; 192 193struct 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 208struct malloc_match 209{ 210 void *addr; 211 intptr_t size; 212 intptr_t offset; 213 uintptr_t type; 214}; 215 216struct 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 225struct malloc_block_contents 226{ 227 union { 228 Class isa; 229 void *pointers[2]; 230 }; 231}; 232 233static int 234compare_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 243class MatchResults 244{ 245 enum { 246 k_max_entries = 8 * 1024 247 }; 248public: 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 299protected: 300 malloc_match m_entries[k_max_entries]; 301 uint32_t m_size; 302}; 303 304class MallocStackLoggingEntries 305{ 306 enum { k_max_entries = 128 }; 307public: 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 352protected: 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//---------------------------------------------------------------------- 361void * 362safe_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//---------------------------------------------------------------------- 380class ObjCClasses 381{ 382public: 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 } 438private: 439 Class *m_objc_class_ptrs; 440 uint32_t m_size; 441}; 442 443 444 445//---------------------------------------------------------------------- 446// Local global variables 447//---------------------------------------------------------------------- 448MatchResults g_matches; 449MallocStackLoggingEntries g_malloc_stack_history; 450ObjCClasses g_objc_classes; 451 452//---------------------------------------------------------------------- 453// ObjCClassInfo 454//---------------------------------------------------------------------- 455 456enum HeapInfoSortType 457{ 458 eSortTypeNone, 459 eSortTypeBytes, 460 eSortTypeCount 461}; 462 463class ObjCClassInfo 464{ 465public: 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 } 548private: 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 579ObjCClassInfo 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//---------------------------------------------------------------------- 588static kern_return_t 589task_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 596static const void 597foreach_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//---------------------------------------------------------------------- 664static void 665dump_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 670static void 671ranges_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 680static void 681enumerate_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 694static void 695range_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 831static void 832get_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 853malloc_stack_entry * 854get_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//---------------------------------------------------------------------- 901malloc_match * 902find_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//---------------------------------------------------------------------- 931malloc_match * 932find_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//---------------------------------------------------------------------- 956malloc_match * 957find_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 984void 985get_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//---------------------------------------------------------------------- 1027malloc_match * 1028find_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//---------------------------------------------------------------------- 1056malloc_match * 1057find_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