//===-- heap_find.c ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file compiles into a dylib and can be used on darwin to find data that // is contained in active malloc blocks. To use this make the project, then // load the shared library in a debug session while you are stopped: // // (lldb) process load /path/to/libheap.dylib // // Now you can use the "find_pointer_in_heap" and "find_cstring_in_heap" // functions in the expression parser. // // This will grep everything in all active allocation blocks and print and // malloc blocks that contain the pointer 0x112233000000: // // (lldb) expression find_pointer_in_heap (0x112233000000) // // This will grep everything in all active allocation blocks and print and // malloc blocks that contain the C string "hello" (as a substring, no // NULL termination included): // // (lldb) expression find_cstring_in_heap ("hello") // // The results will be printed to the STDOUT of the inferior program. The // return value of the "find_pointer_in_heap" function is the number of // pointer references that were found. A quick example shows // // (lldb) expr find_pointer_in_heap(0x0000000104000410) // (uint32_t) $5 = 0x00000002 // 0x104000740: 0x0000000104000410 found in malloc block 0x104000730 + 16 // (malloc_size = 48) // 0x100820060: 0x0000000104000410 found in malloc block 0x100820000 + 96 // (malloc_size = 4096) // // From the above output we see that 0x104000410 was found in the malloc block // at 0x104000730 and 0x100820000. If we want to see what these blocks are, we // can display the memory for this block using the "address" ("A" for short) // format. The address format shows pointers, and if those pointers point to // objects that have symbols or know data contents, it will display information // about the pointers: // // (lldb) memory read --format address --count 1 0x104000730 // 0x104000730: 0x0000000100002460 (void *)0x0000000100002488: MyString // // We can see that the first block is a "MyString" object that contains our // pointer value at offset 16. // // Looking at the next pointers, are a bit more tricky: // (lldb) memory read -fA 0x100820000 -c1 // 0x100820000: 0x4f545541a1a1a1a1 // (lldb) memory read 0x100820000 // 0x100820000: a1 a1 a1 a1 41 55 54 4f 52 45 4c 45 41 53 45 21 ....AUTORELEASE! // 0x100820010: 78 00 82 00 01 00 00 00 60 f9 e8 75 ff 7f 00 00 x.......`..u.... // // This is an objective C auto release pool object that contains our pointer. // C++ classes will show up if they are virtual as something like: // (lldb) memory read --format address --count 1 0x104008000 // 0x104008000: 0x109008000 vtable for lldb_private::Process // // This is a clue that the 0x104008000 is a "lldb_private::Process *". //===----------------------------------------------------------------------===// // C includes #include #include #include #include #include #include #include #include #include #include // C++ includes #include //---------------------------------------------------------------------- // Redefine private types from "/usr/local/include/stack_logging.h" //---------------------------------------------------------------------- typedef struct { uint32_t type_flags; uint64_t stack_identifier; uint64_t argument; mach_vm_address_t address; } mach_stack_logging_record_t; //---------------------------------------------------------------------- // Redefine private defines from "/usr/local/include/stack_logging.h" //---------------------------------------------------------------------- #define stack_logging_type_free 0 #define stack_logging_type_generic 1 #define stack_logging_type_alloc 2 #define stack_logging_type_dealloc 4 // This bit is made up by this code #define stack_logging_type_vm_region 8 //---------------------------------------------------------------------- // Redefine private function prototypes from // "/usr/local/include/stack_logging.h" //---------------------------------------------------------------------- extern "C" kern_return_t __mach_stack_logging_set_file_path(task_t task, char *file_path); extern "C" kern_return_t __mach_stack_logging_get_frames(task_t task, mach_vm_address_t address, mach_vm_address_t *stack_frames_buffer, uint32_t max_stack_frames, uint32_t *count); extern "C" kern_return_t __mach_stack_logging_enumerate_records( task_t task, mach_vm_address_t address, void enumerator(mach_stack_logging_record_t, void *), void *context); extern "C" kern_return_t __mach_stack_logging_frames_for_uniqued_stack( task_t task, uint64_t stack_identifier, mach_vm_address_t *stack_frames_buffer, uint32_t max_stack_frames, uint32_t *count); extern "C" void *gdb_class_getClass(void *objc_class); static void range_info_callback(task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size); //---------------------------------------------------------------------- // Redefine private global variables prototypes from // "/usr/local/include/stack_logging.h" //---------------------------------------------------------------------- extern "C" int stack_logging_enable_logging; //---------------------------------------------------------------------- // Local defines //---------------------------------------------------------------------- #define MAX_FRAMES 1024 //---------------------------------------------------------------------- // Local Typedefs and Types //---------------------------------------------------------------------- typedef void range_callback_t(task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size); typedef void zone_callback_t(void *info, const malloc_zone_t *zone); typedef int (*comare_function_t)(const void *, const void *); struct range_callback_info_t { zone_callback_t *zone_callback; range_callback_t *range_callback; void *baton; int check_vm_regions; }; enum data_type_t { eDataTypeAddress, eDataTypeContainsData, eDataTypeObjC, eDataTypeHeapInfo }; struct aligned_data_t { const uint8_t *buffer; uint32_t size; uint32_t align; }; struct objc_data_t { void *match_isa; // Set to NULL for all objective C objects bool match_superclasses; }; struct range_contains_data_callback_info_t { data_type_t type; const void *lookup_addr; union { uintptr_t addr; aligned_data_t data; objc_data_t objc; }; uint32_t match_count; bool done; bool unique; }; struct malloc_match { void *addr; intptr_t size; intptr_t offset; uintptr_t type; }; struct malloc_stack_entry { const void *address; uint64_t argument; uint32_t type_flags; uint32_t num_frames; mach_vm_address_t frames[MAX_FRAMES]; }; struct malloc_block_contents { union { Class isa; void *pointers[2]; }; }; static int compare_void_ptr(const void *a, const void *b) { Class a_ptr = *(Class *)a; Class b_ptr = *(Class *)b; if (a_ptr < b_ptr) return -1; if (a_ptr > b_ptr) return +1; return 0; } class MatchResults { enum { k_max_entries = 8 * 1024 }; public: MatchResults() : m_size(0) {} void clear() { m_size = 0; bzero(&m_entries, sizeof(m_entries)); } bool empty() const { return m_size == 0; } void push_back(const malloc_match &m, bool unique = false) { if (unique) { // Don't add the entry if there is already a match for this address for (uint32_t i = 0; i < m_size; ++i) { if (((uint8_t *)m_entries[i].addr + m_entries[i].offset) == ((uint8_t *)m.addr + m.offset)) return; // Duplicate entry } } if (m_size < k_max_entries - 1) { m_entries[m_size] = m; m_size++; } } malloc_match *data() { // If empty, return NULL if (empty()) return NULL; // In not empty, terminate and return the result malloc_match terminator_entry = {NULL, 0, 0, 0}; // We always leave room for an empty entry at the end m_entries[m_size] = terminator_entry; return m_entries; } protected: malloc_match m_entries[k_max_entries]; uint32_t m_size; }; class MallocStackLoggingEntries { enum { k_max_entries = 128 }; public: MallocStackLoggingEntries() : m_size(0) {} void clear() { m_size = 0; } bool empty() const { return m_size == 0; } malloc_stack_entry *next() { if (m_size < k_max_entries - 1) { malloc_stack_entry *result = m_entries + m_size; ++m_size; return result; } return NULL; // Out of entries... } malloc_stack_entry *data() { // If empty, return NULL if (empty()) return NULL; // In not empty, terminate and return the result m_entries[m_size].address = NULL; m_entries[m_size].argument = 0; m_entries[m_size].type_flags = 0; m_entries[m_size].num_frames = 0; return m_entries; } protected: malloc_stack_entry m_entries[k_max_entries]; uint32_t m_size; }; //---------------------------------------------------------------------- // A safe way to allocate memory and keep it from interfering with the // malloc enumerators. //---------------------------------------------------------------------- void *safe_malloc(size_t n_bytes) { if (n_bytes > 0) { const int k_page_size = getpagesize(); const mach_vm_size_t vm_size = ((n_bytes + k_page_size - 1) / k_page_size) * k_page_size; vm_address_t address = 0; kern_return_t kerr = vm_allocate(mach_task_self(), &address, vm_size, true); if (kerr == KERN_SUCCESS) return (void *)address; } return NULL; } //---------------------------------------------------------------------- // ObjCClasses //---------------------------------------------------------------------- class ObjCClasses { public: ObjCClasses() : m_objc_class_ptrs(NULL), m_size(0) {} bool Update() { // TODO: find out if class list has changed and update if needed if (m_objc_class_ptrs == NULL) { m_size = objc_getClassList(NULL, 0); if (m_size > 0) { // Allocate the class pointers m_objc_class_ptrs = (Class *)safe_malloc(m_size * sizeof(Class)); m_size = objc_getClassList(m_objc_class_ptrs, m_size); // Sort Class pointers for quick lookup ::qsort(m_objc_class_ptrs, m_size, sizeof(Class), compare_void_ptr); } else return false; } return true; } uint32_t FindClassIndex(Class isa) { Class *matching_class = (Class *)bsearch(&isa, m_objc_class_ptrs, m_size, sizeof(Class), compare_void_ptr); if (matching_class) { uint32_t idx = matching_class - m_objc_class_ptrs; return idx; } return UINT32_MAX; } Class GetClassAtIndex(uint32_t idx) const { if (idx < m_size) return m_objc_class_ptrs[idx]; return NULL; } uint32_t GetSize() const { return m_size; } private: Class *m_objc_class_ptrs; uint32_t m_size; }; //---------------------------------------------------------------------- // Local global variables //---------------------------------------------------------------------- MatchResults g_matches; MallocStackLoggingEntries g_malloc_stack_history; ObjCClasses g_objc_classes; //---------------------------------------------------------------------- // ObjCClassInfo //---------------------------------------------------------------------- enum HeapInfoSortType { eSortTypeNone, eSortTypeBytes, eSortTypeCount }; class ObjCClassInfo { public: ObjCClassInfo() : m_entries(NULL), m_size(0), m_sort_type(eSortTypeNone) {} void Update(const ObjCClasses &objc_classes) { m_size = objc_classes.GetSize(); m_entries = (Entry *)safe_malloc(m_size * sizeof(Entry)); m_sort_type = eSortTypeNone; Reset(); } bool AddInstance(uint32_t idx, uint64_t ptr_size) { if (m_size == 0) Update(g_objc_classes); // Update the totals for the classes if (idx < m_size) { m_entries[idx].bytes += ptr_size; ++m_entries[idx].count; return true; } return false; } void Reset() { m_sort_type = eSortTypeNone; for (uint32_t i = 0; i < m_size; ++i) { // In case we sort the entries after gathering the data, we will // want to know the index into the m_objc_class_ptrs[] array. m_entries[i].idx = i; m_entries[i].bytes = 0; m_entries[i].count = 0; } } void SortByTotalBytes(const ObjCClasses &objc_classes, bool print) { if (m_sort_type != eSortTypeBytes && m_size > 0) { ::qsort(m_entries, m_size, sizeof(Entry), (comare_function_t)compare_bytes); m_sort_type = eSortTypeBytes; } if (print && m_size > 0) { puts("Objective C objects by total bytes:"); puts("Total Bytes Class Name"); puts("----------- " "-----------------------------------------------------------------"); for (uint32_t i = 0; i < m_size && m_entries[i].bytes > 0; ++i) { printf("%11llu %s\n", m_entries[i].bytes, class_getName(objc_classes.GetClassAtIndex(m_entries[i].idx))); } } } void SortByTotalCount(const ObjCClasses &objc_classes, bool print) { if (m_sort_type != eSortTypeCount && m_size > 0) { ::qsort(m_entries, m_size, sizeof(Entry), (comare_function_t)compare_count); m_sort_type = eSortTypeCount; } if (print && m_size > 0) { puts("Objective C objects by total count:"); puts("Count Class Name"); puts("-------- " "-----------------------------------------------------------------"); for (uint32_t i = 0; i < m_size && m_entries[i].count > 0; ++i) { printf("%8u %s\n", m_entries[i].count, class_getName(objc_classes.GetClassAtIndex(m_entries[i].idx))); } } } private: struct Entry { uint32_t idx; // Index into the m_objc_class_ptrs[] array uint32_t count; // Number of object instances that were found uint64_t bytes; // Total number of bytes for each objc class }; static int compare_bytes(const Entry *a, const Entry *b) { // Reverse the comparison to most bytes entries end up at top of list if (a->bytes > b->bytes) return -1; if (a->bytes < b->bytes) return +1; return 0; } static int compare_count(const Entry *a, const Entry *b) { // Reverse the comparison to most count entries end up at top of list if (a->count > b->count) return -1; if (a->count < b->count) return +1; return 0; } Entry *m_entries; uint32_t m_size; HeapInfoSortType m_sort_type; }; ObjCClassInfo g_objc_class_snapshot; //---------------------------------------------------------------------- // task_peek // // Reads memory from this tasks address space. This callback is needed // by the code that iterates through all of the malloc blocks to read // the memory in this process. //---------------------------------------------------------------------- static kern_return_t task_peek(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) { *local_memory = (void *)remote_address; return KERN_SUCCESS; } static const void foreach_zone_in_this_process(range_callback_info_t *info) { if (info == NULL || info->zone_callback == NULL) return; vm_address_t *zones = NULL; unsigned int num_zones = 0; kern_return_t err = malloc_get_all_zones(0, task_peek, &zones, &num_zones); if (KERN_SUCCESS == err) { for (unsigned int i = 0; i < num_zones; ++i) { info->zone_callback(info, (const malloc_zone_t *)zones[i]); } } if (info->check_vm_regions) { #if defined(VM_REGION_SUBMAP_SHORT_INFO_COUNT_64) typedef vm_region_submap_short_info_data_64_t RegionInfo; enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 }; #else typedef vm_region_submap_info_data_64_t RegionInfo; enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 }; #endif task_t task = mach_task_self(); mach_vm_address_t vm_region_base_addr; mach_vm_size_t vm_region_size; natural_t vm_region_depth; RegionInfo vm_region_info; ((range_contains_data_callback_info_t *)info->baton)->unique = true; for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size) { mach_msg_type_number_t vm_region_info_size = kRegionInfoSize; const kern_return_t err = mach_vm_region_recurse( task, &vm_region_base_addr, &vm_region_size, &vm_region_depth, (vm_region_recurse_info_t)&vm_region_info, &vm_region_info_size); if (err) break; // Check all read + write regions. This will cover the thread stacks // and any regions of memory that aren't covered by the heap if (vm_region_info.protection & VM_PROT_WRITE && vm_region_info.protection & VM_PROT_READ) { // 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); range_info_callback(task, info->baton, stack_logging_type_vm_region, vm_region_base_addr, vm_region_size); } } } } //---------------------------------------------------------------------- // dump_malloc_block_callback // // A simple callback that will dump each malloc block and all available // info from the enumeration callback perspective. //---------------------------------------------------------------------- static void dump_malloc_block_callback(task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size) { printf("task = 0x%4.4x: baton = %p, type = %u, ptr_addr = 0x%llx + 0x%llu\n", task, baton, type, ptr_addr, ptr_size); } static void ranges_callback(task_t task, void *baton, unsigned type, vm_range_t *ptrs, unsigned count) { range_callback_info_t *info = (range_callback_info_t *)baton; while (count--) { info->range_callback(task, info->baton, type, ptrs->address, ptrs->size); ptrs++; } } static void enumerate_range_in_zone(void *baton, const malloc_zone_t *zone) { range_callback_info_t *info = (range_callback_info_t *)baton; if (zone && zone->introspect) zone->introspect->enumerator( mach_task_self(), info, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, task_peek, ranges_callback); } static void range_info_callback(task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size) { const uint64_t end_addr = ptr_addr + ptr_size; range_contains_data_callback_info_t *info = (range_contains_data_callback_info_t *)baton; switch (info->type) { case eDataTypeAddress: // Check if the current malloc block contains an address specified by // "info->addr" if (ptr_addr <= info->addr && info->addr < end_addr) { ++info->match_count; malloc_match match = {(void *)ptr_addr, ptr_size, info->addr - ptr_addr, type}; g_matches.push_back(match, info->unique); } break; case eDataTypeContainsData: // Check if the current malloc block contains data specified in "info->data" { const uint32_t size = info->data.size; if (size < ptr_size) // Make sure this block can contain this data { uint8_t *ptr_data = NULL; if (task_peek(task, ptr_addr, ptr_size, (void **)&ptr_data) == KERN_SUCCESS) { const void *buffer = info->data.buffer; assert(ptr_data); const uint32_t align = info->data.align; for (uint64_t addr = ptr_addr; addr < end_addr && ((end_addr - addr) >= size); addr += align, ptr_data += align) { if (memcmp(buffer, ptr_data, size) == 0) { ++info->match_count; malloc_match match = {(void *)ptr_addr, ptr_size, addr - ptr_addr, type}; g_matches.push_back(match, info->unique); } } } else { printf("0x%llx: error: couldn't read %llu bytes\n", ptr_addr, ptr_size); } } } break; case eDataTypeObjC: // Check if the current malloc block contains an objective C object // of any sort where the first pointer in the object is an OBJC class // pointer (an isa) { malloc_block_contents *block_contents = NULL; if (task_peek(task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS) { // We assume that g_objc_classes is up to date // that the class list was verified to have some classes in it // before calling this function const uint32_t objc_class_idx = g_objc_classes.FindClassIndex(block_contents->isa); if (objc_class_idx != UINT32_MAX) { bool match = false; if (info->objc.match_isa == 0) { // Match any objective C object match = true; } else { // Only match exact isa values in the current class or // optionally in the super classes if (info->objc.match_isa == block_contents->isa) match = true; else if (info->objc.match_superclasses) { Class super = class_getSuperclass(block_contents->isa); while (super) { match = super == info->objc.match_isa; if (match) break; super = class_getSuperclass(super); } } } if (match) { // printf (" success\n"); ++info->match_count; malloc_match match = {(void *)ptr_addr, ptr_size, 0, type}; g_matches.push_back(match, info->unique); } else { // printf (" error: wrong class: %s\n", dl_info.dli_sname); } } else { // printf ("\terror: symbol not objc class: %s\n", dl_info.dli_sname); return; } } } break; case eDataTypeHeapInfo: // Check if the current malloc block contains an objective C object // of any sort where the first pointer in the object is an OBJC class // pointer (an isa) { malloc_block_contents *block_contents = NULL; if (task_peek(task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS) { // We assume that g_objc_classes is up to date // that the class list was verified to have some classes in it // before calling this function const uint32_t objc_class_idx = g_objc_classes.FindClassIndex(block_contents->isa); if (objc_class_idx != UINT32_MAX) { // This is an objective C object g_objc_class_snapshot.AddInstance(objc_class_idx, ptr_size); } else { // Classify other heap info } } } break; } } static void get_stack_for_address_enumerator(mach_stack_logging_record_t stack_record, void *task_ptr) { malloc_stack_entry *stack_entry = g_malloc_stack_history.next(); if (stack_entry) { stack_entry->address = (void *)stack_record.address; stack_entry->type_flags = stack_record.type_flags; stack_entry->argument = stack_record.argument; stack_entry->num_frames = 0; stack_entry->frames[0] = 0; kern_return_t err = __mach_stack_logging_frames_for_uniqued_stack( *(task_t *)task_ptr, stack_record.stack_identifier, stack_entry->frames, MAX_FRAMES, &stack_entry->num_frames); // Terminate the frames with zero if there is room if (stack_entry->num_frames < MAX_FRAMES) stack_entry->frames[stack_entry->num_frames] = 0; } } malloc_stack_entry *get_stack_history_for_address(const void *addr, int history) { if (!stack_logging_enable_logging) return NULL; g_malloc_stack_history.clear(); kern_return_t err; task_t task = mach_task_self(); if (history) { err = __mach_stack_logging_enumerate_records( task, (mach_vm_address_t)addr, get_stack_for_address_enumerator, &task); } else { malloc_stack_entry *stack_entry = g_malloc_stack_history.next(); if (stack_entry) { stack_entry->address = addr; stack_entry->type_flags = stack_logging_type_alloc; stack_entry->argument = 0; stack_entry->num_frames = 0; stack_entry->frames[0] = 0; err = __mach_stack_logging_get_frames(task, (mach_vm_address_t)addr, stack_entry->frames, MAX_FRAMES, &stack_entry->num_frames); if (err == 0 && stack_entry->num_frames > 0) { // Terminate the frames with zero if there is room if (stack_entry->num_frames < MAX_FRAMES) stack_entry->frames[stack_entry->num_frames] = 0; } else { g_malloc_stack_history.clear(); } } } // Return data if there is any return g_malloc_stack_history.data(); } //---------------------------------------------------------------------- // find_pointer_in_heap // // Finds a pointer value inside one or more currently valid malloc // blocks. //---------------------------------------------------------------------- malloc_match *find_pointer_in_heap(const void *addr, int check_vm_regions) { g_matches.clear(); // Setup "info" to look for a malloc block that contains data // that is the pointer if (addr) { range_contains_data_callback_info_t data_info; data_info.type = eDataTypeContainsData; // Check each block for data data_info.data.buffer = (uint8_t *)&addr; // What data? The pointer value passed in data_info.data.size = sizeof(addr); // How many bytes? The byte size of a pointer data_info.data.align = sizeof(addr); // Align to a pointer byte size data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop data_info.unique = false; // Set to true when iterating on the vm_regions range_callback_info_t info = {enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions}; foreach_zone_in_this_process(&info); } return g_matches.data(); } //---------------------------------------------------------------------- // find_pointer_in_memory // // Finds a pointer value inside one or more currently valid malloc // blocks. //---------------------------------------------------------------------- malloc_match *find_pointer_in_memory(uint64_t memory_addr, uint64_t memory_size, const void *addr) { g_matches.clear(); // Setup "info" to look for a malloc block that contains data // that is the pointer range_contains_data_callback_info_t data_info; data_info.type = eDataTypeContainsData; // Check each block for data data_info.data.buffer = (uint8_t *)&addr; // What data? The pointer value passed in data_info.data.size = sizeof(addr); // How many bytes? The byte size of a pointer data_info.data.align = sizeof(addr); // Align to a pointer byte size data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop data_info.unique = false; // Set to true when iterating on the vm_regions range_info_callback(mach_task_self(), &data_info, stack_logging_type_generic, memory_addr, memory_size); return g_matches.data(); } //---------------------------------------------------------------------- // find_objc_objects_in_memory // // Find all instances of ObjC classes 'c', or all ObjC classes if 'c' is // NULL. If 'c' is non NULL, then also check objects to see if they // inherit from 'c' //---------------------------------------------------------------------- malloc_match *find_objc_objects_in_memory(void *isa, int check_vm_regions) { g_matches.clear(); if (g_objc_classes.Update()) { // Setup "info" to look for a malloc block that contains data // that is the pointer range_contains_data_callback_info_t data_info; data_info.type = eDataTypeObjC; // Check each block for data data_info.objc.match_isa = isa; data_info.objc.match_superclasses = true; data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop data_info.unique = false; // Set to true when iterating on the vm_regions range_callback_info_t info = {enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions}; foreach_zone_in_this_process(&info); } return g_matches.data(); } //---------------------------------------------------------------------- // get_heap_info // // Gather information for all allocations on the heap and report // statistics. //---------------------------------------------------------------------- void get_heap_info(int sort_type) { if (g_objc_classes.Update()) { // Reset all stats g_objc_class_snapshot.Reset(); // Setup "info" to look for a malloc block that contains data // that is the pointer range_contains_data_callback_info_t data_info; data_info.type = eDataTypeHeapInfo; // Check each block for data data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop data_info.unique = false; // Set to true when iterating on the vm_regions const int check_vm_regions = false; range_callback_info_t info = {enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions}; foreach_zone_in_this_process(&info); // Sort and print byte total bytes switch (sort_type) { case eSortTypeNone: default: case eSortTypeBytes: g_objc_class_snapshot.SortByTotalBytes(g_objc_classes, true); break; case eSortTypeCount: g_objc_class_snapshot.SortByTotalCount(g_objc_classes, true); break; } } else { printf("error: no objective C classes\n"); } } //---------------------------------------------------------------------- // find_cstring_in_heap // // Finds a C string inside one or more currently valid malloc blocks. //---------------------------------------------------------------------- malloc_match *find_cstring_in_heap(const char *s, int check_vm_regions) { g_matches.clear(); if (s == NULL || s[0] == '\0') { printf("error: invalid argument (empty cstring)\n"); return NULL; } // Setup "info" to look for a malloc block that contains data // that is the C string passed in aligned on a 1 byte boundary range_contains_data_callback_info_t data_info; data_info.type = eDataTypeContainsData; // Check each block for data data_info.data.buffer = (uint8_t *)s; // What data? The C string passed in data_info.data.size = strlen(s); // How many bytes? The length of the C string data_info.data.align = 1; // Data doesn't need to be aligned, so set the alignment to 1 data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop data_info.unique = false; // Set to true when iterating on the vm_regions range_callback_info_t info = {enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions}; foreach_zone_in_this_process(&info); return g_matches.data(); } //---------------------------------------------------------------------- // find_block_for_address // // Find the malloc block that whose address range contains "addr". //---------------------------------------------------------------------- malloc_match *find_block_for_address(const void *addr, int check_vm_regions) { g_matches.clear(); // Setup "info" to look for a malloc block that contains data // that is the C string passed in aligned on a 1 byte boundary range_contains_data_callback_info_t data_info; data_info.type = eDataTypeAddress; // Check each block to see if the block // contains the address passed in data_info.addr = (uintptr_t)addr; // What data? The C string passed in data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop data_info.unique = false; // Set to true when iterating on the vm_regions range_callback_info_t info = {enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions}; foreach_zone_in_this_process(&info); return g_matches.data(); }