1//===-- head_find.c ---------------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file compiles into a dylib and can be used on darwin to find data that
11// is contained in active malloc blocks. To use this make the project, then
12// load the shared library in a debug session while you are stopped:
13//
14// (lldb) process load /path/to/libheap.dylib
15//
16// Now you can use the "find_pointer_in_heap" and "find_cstring_in_heap"
17// functions in the expression parser.
18//
19// This will grep everything in all active allocation blocks and print and
20// malloc blocks that contain the pointer 0x112233000000:
21//
22// (lldb) expression find_pointer_in_heap (0x112233000000)
23//
24// This will grep everything in all active allocation blocks and print and
25// malloc blocks that contain the C string "hello" (as a substring, no
26// NULL termination included):
27//
28// (lldb) expression find_cstring_in_heap ("hello")
29//
30// The results will be printed to the STDOUT of the inferior program. The
31// return value of the "find_pointer_in_heap" function is the number of
32// pointer references that were found. A quick example shows
33//
34// (lldb) expr find_pointer_in_heap(0x0000000104000410)
35// (uint32_t) $5 = 0x00000002
36// 0x104000740: 0x0000000104000410 found in malloc block 0x104000730 + 16 (malloc_size = 48)
37// 0x100820060: 0x0000000104000410 found in malloc block 0x100820000 + 96 (malloc_size = 4096)
38//
39// From the above output we see that 0x104000410 was found in the malloc block
40// at 0x104000730 and 0x100820000. If we want to see what these blocks are, we
41// can display the memory for this block using the "address" ("A" for short)
42// format. The address format shows pointers, and if those pointers point to
43// objects that have symbols or know data contents, it will display information
44// about the pointers:
45//
46// (lldb) memory read --format address --count 1 0x104000730
47// 0x104000730: 0x0000000100002460 (void *)0x0000000100002488: MyString
48//
49// We can see that the first block is a "MyString" object that contains our
50// pointer value at offset 16.
51//
52// Looking at the next pointers, are a bit more tricky:
53// (lldb) memory read -fA 0x100820000 -c1
54// 0x100820000: 0x4f545541a1a1a1a1
55// (lldb) memory read 0x100820000
56// 0x100820000: a1 a1 a1 a1 41 55 54 4f 52 45 4c 45 41 53 45 21  ....AUTORELEASE!
57// 0x100820010: 78 00 82 00 01 00 00 00 60 f9 e8 75 ff 7f 00 00  x.......`..u....
58//
59// This is an objective C auto release pool object that contains our pointer.
60// C++ classes will show up if they are virtual as something like:
61// (lldb) memory read --format address --count 1 0x104008000
62// 0x104008000: 0x109008000 vtable for lldb_private::Process
63//
64// This is a clue that the 0x104008000 is a "lldb_private::Process *".
65//===----------------------------------------------------------------------===//
66// C includes
67#include <assert.h>
68#include <ctype.h>
69#include <dlfcn.h>
70#include <mach/mach.h>
71#include <mach/mach_vm.h>
72#include <malloc/malloc.h>
73#include <objc/objc-runtime.h>
74#include <stdio.h>
75#include <stdlib.h>
76#include <unistd.h>
77
78// C++ includes
79#include <vector>
80
81//----------------------------------------------------------------------
82// Redefine private types from "/usr/local/include/stack_logging.h"
83//----------------------------------------------------------------------
84typedef 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//----------------------------------------------------------------------
105extern "C" kern_return_t
106__mach_stack_logging_set_file_path (
107    task_t task,
108    char* file_path
109);
110
111extern "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
120extern "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
128extern "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
137extern "C" void *gdb_class_getClass (void *objc_class);
138
139static void
140range_info_callback (task_t task,
141                     void *baton,
142                     unsigned type,
143                     uint64_t ptr_addr,
144                     uint64_t ptr_size);
145
146//----------------------------------------------------------------------
147// Redefine private gloval variables prototypes from
148// "/usr/local/include/stack_logging.h"
149//----------------------------------------------------------------------
150
151extern "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//----------------------------------------------------------------------
161typedef void range_callback_t (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size);
162typedef void zone_callback_t (void *info, const malloc_zone_t *zone);
163typedef int (*comare_function_t)(const void *, const void *);
164struct 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
172enum data_type_t
173{
174    eDataTypeAddress,
175    eDataTypeContainsData,
176    eDataTypeObjC,
177    eDataTypeHeapInfo
178};
179
180struct aligned_data_t
181{
182    const uint8_t *buffer;
183    uint32_t size;
184    uint32_t align;
185};
186
187struct objc_data_t
188{
189    void *match_isa; // Set to NULL for all objective C objects
190    bool match_superclasses;
191};
192
193struct 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
208struct malloc_match
209{
210    void *addr;
211    intptr_t size;
212    intptr_t offset;
213    uintptr_t type;
214};
215
216struct 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
225struct malloc_block_contents
226{
227    union {
228        Class isa;
229        void *pointers[2];
230    };
231};
232
233static int
234compare_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
243class MatchResults
244{
245    enum {
246        k_max_entries = 8 * 1024
247    };
248public:
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
299protected:
300    malloc_match m_entries[k_max_entries];
301    uint32_t m_size;
302};
303
304class MallocStackLoggingEntries
305{
306    enum {  k_max_entries = 128 };
307public:
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
352protected:
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//----------------------------------------------------------------------
361void *
362safe_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//----------------------------------------------------------------------
380class ObjCClasses
381{
382public:
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    }
438private:
439    Class *m_objc_class_ptrs;
440    uint32_t m_size;
441};
442
443
444
445//----------------------------------------------------------------------
446// Local global variables
447//----------------------------------------------------------------------
448MatchResults g_matches;
449MallocStackLoggingEntries g_malloc_stack_history;
450ObjCClasses g_objc_classes;
451
452//----------------------------------------------------------------------
453// ObjCClassInfo
454//----------------------------------------------------------------------
455
456enum HeapInfoSortType
457{
458    eSortTypeNone,
459    eSortTypeBytes,
460    eSortTypeCount
461};
462
463class ObjCClassInfo
464{
465public:
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    }
548private:
549    struct Entry
550    {
551        uint32_t idx;   // Index into the m_objc_class_ptrs[] array
552        uint32_t count; // Number of object instances that were found
553        uint64_t bytes; // Total number of bytes for each objc class
554    };
555
556    static int
557    compare_bytes (const Entry *a, const Entry *b)
558    {
559        // Reverse the comparisong to most bytes entries end up at top of list
560        if (a->bytes > b->bytes) return -1;
561        if (a->bytes < b->bytes) return +1;
562        return 0;
563    }
564
565    static int
566    compare_count (const Entry *a, const Entry *b)
567    {
568        // Reverse the comparisong to most count entries end up at top of list
569        if (a->count > b->count) return -1;
570        if (a->count < b->count) return +1;
571        return 0;
572    }
573
574    Entry *m_entries;
575    uint32_t m_size;
576    HeapInfoSortType m_sort_type;
577};
578
579ObjCClassInfo 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//----------------------------------------------------------------------
588static kern_return_t
589task_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
596static const void
597foreach_zone_in_this_process (range_callback_info_t *info)
598{
599    if (info == NULL || info->zone_callback == NULL)
600        return;
601
602    vm_address_t *zones = NULL;
603    unsigned int num_zones = 0;
604
605    kern_return_t err = malloc_get_all_zones (0, task_peek, &zones, &num_zones);
606    if (KERN_SUCCESS == err)
607    {
608        for (unsigned int i=0; i<num_zones; ++i)
609        {
610            info->zone_callback (info, (const malloc_zone_t *)zones[i]);
611        }
612    }
613
614    if (info->check_vm_regions)
615    {
616#if defined (VM_REGION_SUBMAP_SHORT_INFO_COUNT_64)
617        typedef vm_region_submap_short_info_data_64_t RegionInfo;
618        enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 };
619#else
620        typedef vm_region_submap_info_data_64_t RegionInfo;
621        enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 };
622#endif
623        task_t task = mach_task_self();
624    	mach_vm_address_t vm_region_base_addr;
625    	mach_vm_size_t vm_region_size;
626    	natural_t vm_region_depth;
627    	RegionInfo vm_region_info;
628
629        ((range_contains_data_callback_info_t *)info->baton)->unique = true;
630
631        for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size)
632        {
633            mach_msg_type_number_t vm_region_info_size = kRegionInfoSize;
634            const kern_return_t err = mach_vm_region_recurse (task,
635                                                              &vm_region_base_addr,
636                                                              &vm_region_size,
637                                                              &vm_region_depth,
638                                                              (vm_region_recurse_info_t)&vm_region_info,
639                                                              &vm_region_info_size);
640            if (err)
641                break;
642            // Check all read + write regions. This will cover the thread stacks
643            // and any regions of memory that aren't covered by the heap
644            if (vm_region_info.protection & VM_PROT_WRITE &&
645                vm_region_info.protection & VM_PROT_READ)
646            {
647                //printf ("checking vm_region: [0x%16.16llx - 0x%16.16llx)\n", (uint64_t)vm_region_base_addr, (uint64_t)vm_region_base_addr + vm_region_size);
648                range_info_callback (task,
649                                     info->baton,
650                                     stack_logging_type_vm_region,
651                                     vm_region_base_addr,
652                                     vm_region_size);
653            }
654        }
655    }
656}
657
658//----------------------------------------------------------------------
659// dump_malloc_block_callback
660//
661// A simple callback that will dump each malloc block and all available
662// info from the enumeration callback perpective.
663//----------------------------------------------------------------------
664static void
665dump_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
670static void
671ranges_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
680static void
681enumerate_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
694static void
695range_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
831static void
832get_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
853malloc_stack_entry *
854get_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//----------------------------------------------------------------------
901malloc_match *
902find_pointer_in_heap (const void * addr, int check_vm_regions)
903{
904    g_matches.clear();
905    // Setup "info" to look for a malloc block that contains data
906    // that is the a pointer
907    if (addr)
908    {
909        range_contains_data_callback_info_t data_info;
910        data_info.type = eDataTypeContainsData;      // Check each block for data
911        data_info.data.buffer = (uint8_t *)&addr;    // What data? The pointer value passed in
912        data_info.data.size = sizeof(addr);          // How many bytes? The byte size of a pointer
913        data_info.data.align = sizeof(addr);         // Align to a pointer byte size
914        data_info.match_count = 0;                   // Initialize the match count to zero
915        data_info.done = false;                      // Set done to false so searching doesn't stop
916        data_info.unique = false;                    // Set to true when iterating on the vm_regions
917        range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions };
918        foreach_zone_in_this_process (&info);
919
920
921    }
922    return g_matches.data();
923}
924
925//----------------------------------------------------------------------
926// find_pointer_in_memory
927//
928// Finds a pointer value inside one or more currently valid malloc
929// blocks.
930//----------------------------------------------------------------------
931malloc_match *
932find_pointer_in_memory (uint64_t memory_addr, uint64_t memory_size, const void * addr)
933{
934    g_matches.clear();
935    // Setup "info" to look for a malloc block that contains data
936    // that is the a pointer
937    range_contains_data_callback_info_t data_info;
938    data_info.type = eDataTypeContainsData;      // Check each block for data
939    data_info.data.buffer = (uint8_t *)&addr;    // What data? The pointer value passed in
940    data_info.data.size = sizeof(addr);          // How many bytes? The byte size of a pointer
941    data_info.data.align = sizeof(addr);         // Align to a pointer byte size
942    data_info.match_count = 0;                   // Initialize the match count to zero
943    data_info.done = false;                      // Set done to false so searching doesn't stop
944    data_info.unique = false;                    // Set to true when iterating on the vm_regions
945    range_info_callback (mach_task_self(), &data_info, stack_logging_type_generic, memory_addr, memory_size);
946    return g_matches.data();
947}
948
949//----------------------------------------------------------------------
950// find_objc_objects_in_memory
951//
952// Find all instances of ObjC classes 'c', or all ObjC classes if 'c' is
953// NULL. If 'c' is non NULL, then also check objects to see if they
954// inherit from 'c'
955//----------------------------------------------------------------------
956malloc_match *
957find_objc_objects_in_memory (void *isa, int check_vm_regions)
958{
959    g_matches.clear();
960    if (g_objc_classes.Update())
961    {
962        // Setup "info" to look for a malloc block that contains data
963        // that is the a pointer
964        range_contains_data_callback_info_t data_info;
965        data_info.type = eDataTypeObjC;      // Check each block for data
966        data_info.objc.match_isa = isa;
967        data_info.objc.match_superclasses = true;
968        data_info.match_count = 0;                   // Initialize the match count to zero
969        data_info.done = false;                      // Set done to false so searching doesn't stop
970        data_info.unique = false;                    // Set to true when iterating on the vm_regions
971        range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions };
972        foreach_zone_in_this_process (&info);
973    }
974    return g_matches.data();
975}
976
977//----------------------------------------------------------------------
978// get_heap_info
979//
980// Gather information for all allocations on the heap and report
981// statistics.
982//----------------------------------------------------------------------
983
984void
985get_heap_info (int sort_type)
986{
987    if (g_objc_classes.Update())
988    {
989        // Reset all stats
990        g_objc_class_snapshot.Reset ();
991        // Setup "info" to look for a malloc block that contains data
992        // that is the a pointer
993        range_contains_data_callback_info_t data_info;
994        data_info.type = eDataTypeHeapInfo; // Check each block for data
995        data_info.match_count = 0;          // Initialize the match count to zero
996        data_info.done = false;             // Set done to false so searching doesn't stop
997        data_info.unique = false;           // Set to true when iterating on the vm_regions
998        const int check_vm_regions = false;
999        range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions };
1000        foreach_zone_in_this_process (&info);
1001
1002        // Sort and print byte total bytes
1003        switch (sort_type)
1004        {
1005        case eSortTypeNone:
1006        default:
1007        case eSortTypeBytes:
1008            g_objc_class_snapshot.SortByTotalBytes(g_objc_classes, true);
1009            break;
1010
1011        case eSortTypeCount:
1012            g_objc_class_snapshot.SortByTotalCount(g_objc_classes, true);
1013            break;
1014        }
1015    }
1016    else
1017    {
1018        printf ("error: no objective C classes\n");
1019    }
1020}
1021
1022//----------------------------------------------------------------------
1023// find_cstring_in_heap
1024//
1025// Finds a C string inside one or more currently valid malloc blocks.
1026//----------------------------------------------------------------------
1027malloc_match *
1028find_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//----------------------------------------------------------------------
1056malloc_match *
1057find_block_for_address (const void *addr, int check_vm_regions)
1058{
1059    g_matches.clear();
1060    // Setup "info" to look for a malloc block that contains data
1061    // that is the C string passed in aligned on a 1 byte boundary
1062    range_contains_data_callback_info_t data_info;
1063    data_info.type = eDataTypeAddress;  // Check each block to see if the block contains the address passed in
1064    data_info.addr = (uintptr_t)addr;   // What data? The C string passed in
1065    data_info.match_count = 0;          // Initialize the match count to zero
1066    data_info.done = false;             // Set done to false so searching doesn't stop
1067    data_info.unique = false;           // Set to true when iterating on the vm_regions
1068    range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions };
1069    foreach_zone_in_this_process (&info);
1070    return g_matches.data();
1071}
1072