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