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