brintos

brintos / llvm-project-archived public Read only

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