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