summaryrefslogtreecommitdiff
path: root/examples/darwin/heap_find/heap/heap_find.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'examples/darwin/heap_find/heap/heap_find.cpp')
-rw-r--r--examples/darwin/heap_find/heap/heap_find.cpp1071
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();
+}