diff options
Diffstat (limited to 'examples/darwin/heap_find/heap/heap_find.cpp')
-rw-r--r-- | examples/darwin/heap_find/heap/heap_find.cpp | 1071 |
1 files changed, 1071 insertions, 0 deletions
diff --git a/examples/darwin/heap_find/heap/heap_find.cpp b/examples/darwin/heap_find/heap/heap_find.cpp new file mode 100644 index 000000000000..de896775e402 --- /dev/null +++ b/examples/darwin/heap_find/heap/heap_find.cpp @@ -0,0 +1,1071 @@ +//===-- 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 <assert.h> +#include <ctype.h> +#include <dlfcn.h> +#include <mach/mach.h> +#include <mach/mach_vm.h> +#include <malloc/malloc.h> +#include <objc/objc-runtime.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +// C++ includes +#include <vector> + +//---------------------------------------------------------------------- +// 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(); +} |