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