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