1 //===-- heap_find.c ---------------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file compiles into a dylib and can be used on darwin to find data that
10 // is contained in active malloc blocks. To use this make the project, then
11 // load the shared library in a debug session while you are stopped:
13 // (lldb) process load /path/to/libheap.dylib
15 // Now you can use the "find_pointer_in_heap" and "find_cstring_in_heap"
16 // functions in the expression parser.
18 // This will grep everything in all active allocation blocks and print and
19 // malloc blocks that contain the pointer 0x112233000000:
21 // (lldb) expression find_pointer_in_heap (0x112233000000)
23 // This will grep everything in all active allocation blocks and print and
24 // malloc blocks that contain the C string "hello" (as a substring, no
25 // NULL termination included):
27 // (lldb) expression find_cstring_in_heap ("hello")
29 // The results will be printed to the STDOUT of the inferior program. The
30 // return value of the "find_pointer_in_heap" function is the number of
31 // pointer references that were found. A quick example shows
33 // (lldb) expr find_pointer_in_heap(0x0000000104000410)
34 // (uint32_t) $5 = 0x00000002
35 // 0x104000740: 0x0000000104000410 found in malloc block 0x104000730 + 16
37 // 0x100820060: 0x0000000104000410 found in malloc block 0x100820000 + 96
38 // (malloc_size = 4096)
40 // From the above output we see that 0x104000410 was found in the malloc block
41 // at 0x104000730 and 0x100820000. If we want to see what these blocks are, we
42 // can display the memory for this block using the "address" ("A" for short)
43 // format. The address format shows pointers, and if those pointers point to
44 // objects that have symbols or know data contents, it will display information
45 // about the pointers:
47 // (lldb) memory read --format address --count 1 0x104000730
48 // 0x104000730: 0x0000000100002460 (void *)0x0000000100002488: MyString
50 // We can see that the first block is a "MyString" object that contains our
51 // pointer value at offset 16.
53 // Looking at the next pointers, are a bit more tricky:
54 // (lldb) memory read -fA 0x100820000 -c1
55 // 0x100820000: 0x4f545541a1a1a1a1
56 // (lldb) memory read 0x100820000
57 // 0x100820000: a1 a1 a1 a1 41 55 54 4f 52 45 4c 45 41 53 45 21 ....AUTORELEASE!
58 // 0x100820010: 78 00 82 00 01 00 00 00 60 f9 e8 75 ff 7f 00 00 x.......`..u....
60 // This is an objective C auto release pool object that contains our pointer.
61 // C++ classes will show up if they are virtual as something like:
62 // (lldb) memory read --format address --count 1 0x104008000
63 // 0x104008000: 0x109008000 vtable for lldb_private::Process
65 // This is a clue that the 0x104008000 is a "lldb_private::Process *".
66 //===----------------------------------------------------------------------===//
71 #include <mach/mach.h>
72 #include <mach/mach_vm.h>
73 #include <malloc/malloc.h>
74 #include <objc/objc-runtime.h>
82 // Redefine private types from "/usr/local/include/stack_logging.h"
85 uint64_t stack_identifier
;
87 mach_vm_address_t address
;
88 } mach_stack_logging_record_t
;
90 // Redefine private defines from "/usr/local/include/stack_logging.h"
91 #define stack_logging_type_free 0
92 #define stack_logging_type_generic 1
93 #define stack_logging_type_alloc 2
94 #define stack_logging_type_dealloc 4
95 // This bit is made up by this code
96 #define stack_logging_type_vm_region 8
98 // Redefine private function prototypes from
99 // "/usr/local/include/stack_logging.h"
100 extern "C" kern_return_t
__mach_stack_logging_set_file_path(task_t task
,
103 extern "C" kern_return_t
104 __mach_stack_logging_get_frames(task_t task
, mach_vm_address_t address
,
105 mach_vm_address_t
*stack_frames_buffer
,
106 uint32_t max_stack_frames
, uint32_t *count
);
108 extern "C" kern_return_t
__mach_stack_logging_enumerate_records(
109 task_t task
, mach_vm_address_t address
,
110 void enumerator(mach_stack_logging_record_t
, void *), void *context
);
112 extern "C" kern_return_t
__mach_stack_logging_frames_for_uniqued_stack(
113 task_t task
, uint64_t stack_identifier
,
114 mach_vm_address_t
*stack_frames_buffer
, uint32_t max_stack_frames
,
117 extern "C" void *gdb_class_getClass(void *objc_class
);
119 static void range_info_callback(task_t task
, void *baton
, unsigned type
,
120 uint64_t ptr_addr
, uint64_t ptr_size
);
122 // Redefine private global variables prototypes from
123 // "/usr/local/include/stack_logging.h"
125 extern "C" int stack_logging_enable_logging
;
128 #define MAX_FRAMES 1024
130 // Local Typedefs and Types
131 typedef void range_callback_t(task_t task
, void *baton
, unsigned type
,
132 uint64_t ptr_addr
, uint64_t ptr_size
);
133 typedef void zone_callback_t(void *info
, const malloc_zone_t
*zone
);
134 typedef int (*comare_function_t
)(const void *, const void *);
135 struct range_callback_info_t
{
136 zone_callback_t
*zone_callback
;
137 range_callback_t
*range_callback
;
139 int check_vm_regions
;
144 eDataTypeContainsData
,
149 struct aligned_data_t
{
150 const uint8_t *buffer
;
156 void *match_isa
; // Set to NULL for all objective C objects
157 bool match_superclasses
;
160 struct range_contains_data_callback_info_t
{
162 const void *lookup_addr
;
168 uint32_t match_count
;
173 struct malloc_match
{
180 struct malloc_stack_entry
{
185 mach_vm_address_t frames
[MAX_FRAMES
];
188 struct malloc_block_contents
{
195 static int compare_void_ptr(const void *a
, const void *b
) {
196 Class a_ptr
= *(Class
*)a
;
197 Class b_ptr
= *(Class
*)b
;
206 enum { k_max_entries
= 8 * 1024 };
209 MatchResults() : m_size(0) {}
213 bzero(&m_entries
, sizeof(m_entries
));
216 bool empty() const { return m_size
== 0; }
218 void push_back(const malloc_match
&m
, bool unique
= false) {
220 // Don't add the entry if there is already a match for this address
221 for (uint32_t i
= 0; i
< m_size
; ++i
) {
222 if (((uint8_t *)m_entries
[i
].addr
+ m_entries
[i
].offset
) ==
223 ((uint8_t *)m
.addr
+ m
.offset
))
224 return; // Duplicate entry
227 if (m_size
< k_max_entries
- 1) {
228 m_entries
[m_size
] = m
;
233 malloc_match
*data() {
234 // If empty, return NULL
237 // In not empty, terminate and return the result
238 malloc_match terminator_entry
= {NULL
, 0, 0, 0};
239 // We always leave room for an empty entry at the end
240 m_entries
[m_size
] = terminator_entry
;
245 malloc_match m_entries
[k_max_entries
];
249 class MallocStackLoggingEntries
{
250 enum { k_max_entries
= 128 };
253 MallocStackLoggingEntries() : m_size(0) {}
255 void clear() { m_size
= 0; }
257 bool empty() const { return m_size
== 0; }
259 malloc_stack_entry
*next() {
260 if (m_size
< k_max_entries
- 1) {
261 malloc_stack_entry
*result
= m_entries
+ m_size
;
265 return NULL
; // Out of entries...
268 malloc_stack_entry
*data() {
269 // If empty, return NULL
272 // In not empty, terminate and return the result
273 m_entries
[m_size
].address
= NULL
;
274 m_entries
[m_size
].argument
= 0;
275 m_entries
[m_size
].type_flags
= 0;
276 m_entries
[m_size
].num_frames
= 0;
281 malloc_stack_entry m_entries
[k_max_entries
];
285 // A safe way to allocate memory and keep it from interfering with the
286 // malloc enumerators.
287 void *safe_malloc(size_t n_bytes
) {
289 const int k_page_size
= getpagesize();
290 const mach_vm_size_t vm_size
=
291 ((n_bytes
+ k_page_size
- 1) / k_page_size
) * k_page_size
;
292 vm_address_t address
= 0;
293 kern_return_t kerr
= vm_allocate(mach_task_self(), &address
, vm_size
, true);
294 if (kerr
== KERN_SUCCESS
)
295 return (void *)address
;
303 ObjCClasses() : m_objc_class_ptrs(NULL
), m_size(0) {}
306 // TODO: find out if class list has changed and update if needed
307 if (m_objc_class_ptrs
== NULL
) {
308 m_size
= objc_getClassList(NULL
, 0);
310 // Allocate the class pointers
311 m_objc_class_ptrs
= (Class
*)safe_malloc(m_size
* sizeof(Class
));
312 m_size
= objc_getClassList(m_objc_class_ptrs
, m_size
);
313 // Sort Class pointers for quick lookup
314 ::qsort(m_objc_class_ptrs
, m_size
, sizeof(Class
), compare_void_ptr
);
321 uint32_t FindClassIndex(Class isa
) {
322 Class
*matching_class
= (Class
*)bsearch(&isa
, m_objc_class_ptrs
, m_size
,
323 sizeof(Class
), compare_void_ptr
);
324 if (matching_class
) {
325 uint32_t idx
= matching_class
- m_objc_class_ptrs
;
331 Class
GetClassAtIndex(uint32_t idx
) const {
333 return m_objc_class_ptrs
[idx
];
336 uint32_t GetSize() const { return m_size
; }
339 Class
*m_objc_class_ptrs
;
343 // Local global variables
344 MatchResults g_matches
;
345 MallocStackLoggingEntries g_malloc_stack_history
;
346 ObjCClasses g_objc_classes
;
350 enum HeapInfoSortType
{ eSortTypeNone
, eSortTypeBytes
, eSortTypeCount
};
352 class ObjCClassInfo
{
354 ObjCClassInfo() : m_entries(NULL
), m_size(0), m_sort_type(eSortTypeNone
) {}
356 void Update(const ObjCClasses
&objc_classes
) {
357 m_size
= objc_classes
.GetSize();
358 m_entries
= (Entry
*)safe_malloc(m_size
* sizeof(Entry
));
359 m_sort_type
= eSortTypeNone
;
363 bool AddInstance(uint32_t idx
, uint64_t ptr_size
) {
365 Update(g_objc_classes
);
366 // Update the totals for the classes
368 m_entries
[idx
].bytes
+= ptr_size
;
369 ++m_entries
[idx
].count
;
376 m_sort_type
= eSortTypeNone
;
377 for (uint32_t i
= 0; i
< m_size
; ++i
) {
378 // In case we sort the entries after gathering the data, we will
379 // want to know the index into the m_objc_class_ptrs[] array.
380 m_entries
[i
].idx
= i
;
381 m_entries
[i
].bytes
= 0;
382 m_entries
[i
].count
= 0;
385 void SortByTotalBytes(const ObjCClasses
&objc_classes
, bool print
) {
386 if (m_sort_type
!= eSortTypeBytes
&& m_size
> 0) {
387 ::qsort(m_entries
, m_size
, sizeof(Entry
),
388 (comare_function_t
)compare_bytes
);
389 m_sort_type
= eSortTypeBytes
;
391 if (print
&& m_size
> 0) {
392 puts("Objective-C objects by total bytes:");
393 puts("Total Bytes Class Name");
395 "-----------------------------------------------------------------");
396 for (uint32_t i
= 0; i
< m_size
&& m_entries
[i
].bytes
> 0; ++i
) {
397 printf("%11llu %s\n", m_entries
[i
].bytes
,
398 class_getName(objc_classes
.GetClassAtIndex(m_entries
[i
].idx
)));
402 void SortByTotalCount(const ObjCClasses
&objc_classes
, bool print
) {
403 if (m_sort_type
!= eSortTypeCount
&& m_size
> 0) {
404 ::qsort(m_entries
, m_size
, sizeof(Entry
),
405 (comare_function_t
)compare_count
);
406 m_sort_type
= eSortTypeCount
;
408 if (print
&& m_size
> 0) {
409 puts("Objective-C objects by total count:");
410 puts("Count Class Name");
412 "-----------------------------------------------------------------");
413 for (uint32_t i
= 0; i
< m_size
&& m_entries
[i
].count
> 0; ++i
) {
414 printf("%8u %s\n", m_entries
[i
].count
,
415 class_getName(objc_classes
.GetClassAtIndex(m_entries
[i
].idx
)));
422 uint32_t idx
; // Index into the m_objc_class_ptrs[] array
423 uint32_t count
; // Number of object instances that were found
424 uint64_t bytes
; // Total number of bytes for each objc class
427 static int compare_bytes(const Entry
*a
, const Entry
*b
) {
428 // Reverse the comparison to most bytes entries end up at top of list
429 if (a
->bytes
> b
->bytes
)
431 if (a
->bytes
< b
->bytes
)
436 static int compare_count(const Entry
*a
, const Entry
*b
) {
437 // Reverse the comparison to most count entries end up at top of list
438 if (a
->count
> b
->count
)
440 if (a
->count
< b
->count
)
447 HeapInfoSortType m_sort_type
;
450 ObjCClassInfo g_objc_class_snapshot
;
454 // Reads memory from this tasks address space. This callback is needed
455 // by the code that iterates through all of the malloc blocks to read
456 // the memory in this process.
457 static kern_return_t
task_peek(task_t task
, vm_address_t remote_address
,
458 vm_size_t size
, void **local_memory
) {
459 *local_memory
= (void *)remote_address
;
463 static const void foreach_zone_in_this_process(range_callback_info_t
*info
) {
464 if (info
== NULL
|| info
->zone_callback
== NULL
)
467 vm_address_t
*zones
= NULL
;
468 unsigned int num_zones
= 0;
470 kern_return_t err
= malloc_get_all_zones(0, task_peek
, &zones
, &num_zones
);
471 if (KERN_SUCCESS
== err
) {
472 for (unsigned int i
= 0; i
< num_zones
; ++i
) {
473 info
->zone_callback(info
, (const malloc_zone_t
*)zones
[i
]);
477 if (info
->check_vm_regions
) {
478 #if defined(VM_REGION_SUBMAP_SHORT_INFO_COUNT_64)
479 typedef vm_region_submap_short_info_data_64_t RegionInfo
;
480 enum { kRegionInfoSize
= VM_REGION_SUBMAP_SHORT_INFO_COUNT_64
};
482 typedef vm_region_submap_info_data_64_t RegionInfo
;
483 enum { kRegionInfoSize
= VM_REGION_SUBMAP_INFO_COUNT_64
};
485 task_t task
= mach_task_self();
486 mach_vm_address_t vm_region_base_addr
;
487 mach_vm_size_t vm_region_size
;
488 natural_t vm_region_depth
;
489 RegionInfo vm_region_info
;
491 ((range_contains_data_callback_info_t
*)info
->baton
)->unique
= true;
493 for (vm_region_base_addr
= 0, vm_region_size
= 1; vm_region_size
!= 0;
494 vm_region_base_addr
+= vm_region_size
) {
495 mach_msg_type_number_t vm_region_info_size
= kRegionInfoSize
;
496 const kern_return_t err
= mach_vm_region_recurse(
497 task
, &vm_region_base_addr
, &vm_region_size
, &vm_region_depth
,
498 (vm_region_recurse_info_t
)&vm_region_info
, &vm_region_info_size
);
501 // Check all read + write regions. This will cover the thread stacks
502 // and any regions of memory that aren't covered by the heap
503 if (vm_region_info
.protection
& VM_PROT_WRITE
&&
504 vm_region_info
.protection
& VM_PROT_READ
) {
505 // printf ("checking vm_region: [0x%16.16llx - 0x%16.16llx)\n",
506 // (uint64_t)vm_region_base_addr, (uint64_t)vm_region_base_addr +
508 range_info_callback(task
, info
->baton
, stack_logging_type_vm_region
,
509 vm_region_base_addr
, vm_region_size
);
515 // dump_malloc_block_callback
517 // A simple callback that will dump each malloc block and all available
518 // info from the enumeration callback perspective.
519 static void dump_malloc_block_callback(task_t task
, void *baton
, unsigned type
,
520 uint64_t ptr_addr
, uint64_t ptr_size
) {
521 printf("task = 0x%4.4x: baton = %p, type = %u, ptr_addr = 0x%llx + 0x%llu\n",
522 task
, baton
, type
, ptr_addr
, ptr_size
);
525 static void ranges_callback(task_t task
, void *baton
, unsigned type
,
526 vm_range_t
*ptrs
, unsigned count
) {
527 range_callback_info_t
*info
= (range_callback_info_t
*)baton
;
529 info
->range_callback(task
, info
->baton
, type
, ptrs
->address
, ptrs
->size
);
534 static void enumerate_range_in_zone(void *baton
, const malloc_zone_t
*zone
) {
535 range_callback_info_t
*info
= (range_callback_info_t
*)baton
;
537 if (zone
&& zone
->introspect
)
538 zone
->introspect
->enumerator(
539 mach_task_self(), info
, MALLOC_PTR_IN_USE_RANGE_TYPE
,
540 (vm_address_t
)zone
, task_peek
, ranges_callback
);
543 static void range_info_callback(task_t task
, void *baton
, unsigned type
,
544 uint64_t ptr_addr
, uint64_t ptr_size
) {
545 const uint64_t end_addr
= ptr_addr
+ ptr_size
;
547 range_contains_data_callback_info_t
*info
=
548 (range_contains_data_callback_info_t
*)baton
;
549 switch (info
->type
) {
550 case eDataTypeAddress
:
551 // Check if the current malloc block contains an address specified by
553 if (ptr_addr
<= info
->addr
&& info
->addr
< end_addr
) {
555 malloc_match match
= {(void *)ptr_addr
, ptr_size
, info
->addr
- ptr_addr
,
557 g_matches
.push_back(match
, info
->unique
);
561 case eDataTypeContainsData
:
562 // Check if the current malloc block contains data specified in "info->data"
564 const uint32_t size
= info
->data
.size
;
565 if (size
< ptr_size
) // Make sure this block can contain this data
567 uint8_t *ptr_data
= NULL
;
568 if (task_peek(task
, ptr_addr
, ptr_size
, (void **)&ptr_data
) ==
570 const void *buffer
= info
->data
.buffer
;
572 const uint32_t align
= info
->data
.align
;
573 for (uint64_t addr
= ptr_addr
;
574 addr
< end_addr
&& ((end_addr
- addr
) >= size
);
575 addr
+= align
, ptr_data
+= align
) {
576 if (memcmp(buffer
, ptr_data
, size
) == 0) {
578 malloc_match match
= {(void *)ptr_addr
, ptr_size
, addr
- ptr_addr
,
580 g_matches
.push_back(match
, info
->unique
);
584 printf("0x%llx: error: couldn't read %llu bytes\n", ptr_addr
,
592 // Check if the current malloc block contains an objective C object
593 // of any sort where the first pointer in the object is an OBJC class
596 malloc_block_contents
*block_contents
= NULL
;
597 if (task_peek(task
, ptr_addr
, sizeof(void *), (void **)&block_contents
) ==
599 // We assume that g_objc_classes is up to date
600 // that the class list was verified to have some classes in it
601 // before calling this function
602 const uint32_t objc_class_idx
=
603 g_objc_classes
.FindClassIndex(block_contents
->isa
);
604 if (objc_class_idx
!= UINT32_MAX
) {
606 if (info
->objc
.match_isa
== 0) {
607 // Match any objective C object
610 // Only match exact isa values in the current class or
611 // optionally in the super classes
612 if (info
->objc
.match_isa
== block_contents
->isa
)
614 else if (info
->objc
.match_superclasses
) {
615 Class super
= class_getSuperclass(block_contents
->isa
);
617 match
= super
== info
->objc
.match_isa
;
620 super
= class_getSuperclass(super
);
625 // printf (" success\n");
627 malloc_match match
= {(void *)ptr_addr
, ptr_size
, 0, type
};
628 g_matches
.push_back(match
, info
->unique
);
630 // printf (" error: wrong class: %s\n", dl_info.dli_sname);
633 // printf ("\terror: symbol not objc class: %s\n", dl_info.dli_sname);
640 case eDataTypeHeapInfo
:
641 // Check if the current malloc block contains an objective C object
642 // of any sort where the first pointer in the object is an OBJC class
645 malloc_block_contents
*block_contents
= NULL
;
646 if (task_peek(task
, ptr_addr
, sizeof(void *), (void **)&block_contents
) ==
648 // We assume that g_objc_classes is up to date
649 // that the class list was verified to have some classes in it
650 // before calling this function
651 const uint32_t objc_class_idx
=
652 g_objc_classes
.FindClassIndex(block_contents
->isa
);
653 if (objc_class_idx
!= UINT32_MAX
) {
654 // This is an objective C object
655 g_objc_class_snapshot
.AddInstance(objc_class_idx
, ptr_size
);
657 // Classify other heap info
666 get_stack_for_address_enumerator(mach_stack_logging_record_t stack_record
,
668 malloc_stack_entry
*stack_entry
= g_malloc_stack_history
.next();
670 stack_entry
->address
= (void *)stack_record
.address
;
671 stack_entry
->type_flags
= stack_record
.type_flags
;
672 stack_entry
->argument
= stack_record
.argument
;
673 stack_entry
->num_frames
= 0;
674 stack_entry
->frames
[0] = 0;
675 kern_return_t err
= __mach_stack_logging_frames_for_uniqued_stack(
676 *(task_t
*)task_ptr
, stack_record
.stack_identifier
, stack_entry
->frames
,
677 MAX_FRAMES
, &stack_entry
->num_frames
);
678 // Terminate the frames with zero if there is room
679 if (stack_entry
->num_frames
< MAX_FRAMES
)
680 stack_entry
->frames
[stack_entry
->num_frames
] = 0;
684 malloc_stack_entry
*get_stack_history_for_address(const void *addr
,
686 if (!stack_logging_enable_logging
)
688 g_malloc_stack_history
.clear();
690 task_t task
= mach_task_self();
692 err
= __mach_stack_logging_enumerate_records(
693 task
, (mach_vm_address_t
)addr
, get_stack_for_address_enumerator
, &task
);
695 malloc_stack_entry
*stack_entry
= g_malloc_stack_history
.next();
697 stack_entry
->address
= addr
;
698 stack_entry
->type_flags
= stack_logging_type_alloc
;
699 stack_entry
->argument
= 0;
700 stack_entry
->num_frames
= 0;
701 stack_entry
->frames
[0] = 0;
702 err
= __mach_stack_logging_get_frames(task
, (mach_vm_address_t
)addr
,
703 stack_entry
->frames
, MAX_FRAMES
,
704 &stack_entry
->num_frames
);
705 if (err
== 0 && stack_entry
->num_frames
> 0) {
706 // Terminate the frames with zero if there is room
707 if (stack_entry
->num_frames
< MAX_FRAMES
)
708 stack_entry
->frames
[stack_entry
->num_frames
] = 0;
710 g_malloc_stack_history
.clear();
714 // Return data if there is any
715 return g_malloc_stack_history
.data();
718 // find_pointer_in_heap
720 // Finds a pointer value inside one or more currently valid malloc
722 malloc_match
*find_pointer_in_heap(const void *addr
, int check_vm_regions
) {
724 // Setup "info" to look for a malloc block that contains data
725 // that is the pointer
727 range_contains_data_callback_info_t data_info
;
728 data_info
.type
= eDataTypeContainsData
; // Check each block for data
729 data_info
.data
.buffer
=
730 (uint8_t *)&addr
; // What data? The pointer value passed in
731 data_info
.data
.size
=
732 sizeof(addr
); // How many bytes? The byte size of a pointer
733 data_info
.data
.align
= sizeof(addr
); // Align to a pointer byte size
734 data_info
.match_count
= 0; // Initialize the match count to zero
735 data_info
.done
= false; // Set done to false so searching doesn't stop
736 data_info
.unique
= false; // Set to true when iterating on the vm_regions
737 range_callback_info_t info
= {enumerate_range_in_zone
, range_info_callback
,
738 &data_info
, check_vm_regions
};
739 foreach_zone_in_this_process(&info
);
741 return g_matches
.data();
744 // find_pointer_in_memory
746 // Finds a pointer value inside one or more currently valid malloc
748 malloc_match
*find_pointer_in_memory(uint64_t memory_addr
, uint64_t memory_size
,
751 // Setup "info" to look for a malloc block that contains data
752 // that is the pointer
753 range_contains_data_callback_info_t data_info
;
754 data_info
.type
= eDataTypeContainsData
; // Check each block for data
755 data_info
.data
.buffer
=
756 (uint8_t *)&addr
; // What data? The pointer value passed in
757 data_info
.data
.size
=
758 sizeof(addr
); // How many bytes? The byte size of a pointer
759 data_info
.data
.align
= sizeof(addr
); // Align to a pointer byte size
760 data_info
.match_count
= 0; // Initialize the match count to zero
761 data_info
.done
= false; // Set done to false so searching doesn't stop
762 data_info
.unique
= false; // Set to true when iterating on the vm_regions
763 range_info_callback(mach_task_self(), &data_info
, stack_logging_type_generic
,
764 memory_addr
, memory_size
);
765 return g_matches
.data();
768 // find_objc_objects_in_memory
770 // Find all instances of ObjC classes 'c', or all ObjC classes if 'c' is
771 // NULL. If 'c' is non NULL, then also check objects to see if they
773 malloc_match
*find_objc_objects_in_memory(void *isa
, int check_vm_regions
) {
775 if (g_objc_classes
.Update()) {
776 // Setup "info" to look for a malloc block that contains data
777 // that is the pointer
778 range_contains_data_callback_info_t data_info
;
779 data_info
.type
= eDataTypeObjC
; // Check each block for data
780 data_info
.objc
.match_isa
= isa
;
781 data_info
.objc
.match_superclasses
= true;
782 data_info
.match_count
= 0; // Initialize the match count to zero
783 data_info
.done
= false; // Set done to false so searching doesn't stop
784 data_info
.unique
= false; // Set to true when iterating on the vm_regions
785 range_callback_info_t info
= {enumerate_range_in_zone
, range_info_callback
,
786 &data_info
, check_vm_regions
};
787 foreach_zone_in_this_process(&info
);
789 return g_matches
.data();
794 // Gather information for all allocations on the heap and report
797 void get_heap_info(int sort_type
) {
798 if (g_objc_classes
.Update()) {
800 g_objc_class_snapshot
.Reset();
801 // Setup "info" to look for a malloc block that contains data
802 // that is the pointer
803 range_contains_data_callback_info_t data_info
;
804 data_info
.type
= eDataTypeHeapInfo
; // Check each block for data
805 data_info
.match_count
= 0; // Initialize the match count to zero
806 data_info
.done
= false; // Set done to false so searching doesn't stop
807 data_info
.unique
= false; // Set to true when iterating on the vm_regions
808 const int check_vm_regions
= false;
809 range_callback_info_t info
= {enumerate_range_in_zone
, range_info_callback
,
810 &data_info
, check_vm_regions
};
811 foreach_zone_in_this_process(&info
);
813 // Sort and print byte total bytes
818 g_objc_class_snapshot
.SortByTotalBytes(g_objc_classes
, true);
822 g_objc_class_snapshot
.SortByTotalCount(g_objc_classes
, true);
826 printf("error: no objective C classes\n");
830 // find_cstring_in_heap
832 // Finds a C string inside one or more currently valid malloc blocks.
833 malloc_match
*find_cstring_in_heap(const char *s
, int check_vm_regions
) {
835 if (s
== NULL
|| s
[0] == '\0') {
836 printf("error: invalid argument (empty cstring)\n");
839 // Setup "info" to look for a malloc block that contains data
840 // that is the C string passed in aligned on a 1 byte boundary
841 range_contains_data_callback_info_t data_info
;
842 data_info
.type
= eDataTypeContainsData
; // Check each block for data
843 data_info
.data
.buffer
= (uint8_t *)s
; // What data? The C string passed in
844 data_info
.data
.size
= strlen(s
); // How many bytes? The length of the C string
845 data_info
.data
.align
=
846 1; // Data doesn't need to be aligned, so set the alignment to 1
847 data_info
.match_count
= 0; // Initialize the match count to zero
848 data_info
.done
= false; // Set done to false so searching doesn't stop
849 data_info
.unique
= false; // Set to true when iterating on the vm_regions
850 range_callback_info_t info
= {enumerate_range_in_zone
, range_info_callback
,
851 &data_info
, check_vm_regions
};
852 foreach_zone_in_this_process(&info
);
853 return g_matches
.data();
856 // find_block_for_address
858 // Find the malloc block that whose address range contains "addr".
859 malloc_match
*find_block_for_address(const void *addr
, int check_vm_regions
) {
861 // Setup "info" to look for a malloc block that contains data
862 // that is the C string passed in aligned on a 1 byte boundary
863 range_contains_data_callback_info_t data_info
;
864 data_info
.type
= eDataTypeAddress
; // Check each block to see if the block
865 // contains the address passed in
866 data_info
.addr
= (uintptr_t)addr
; // What data? The C string passed in
867 data_info
.match_count
= 0; // Initialize the match count to zero
868 data_info
.done
= false; // Set done to false so searching doesn't stop
869 data_info
.unique
= false; // Set to true when iterating on the vm_regions
870 range_callback_info_t info
= {enumerate_range_in_zone
, range_info_callback
,
871 &data_info
, check_vm_regions
};
872 foreach_zone_in_this_process(&info
);
873 return g_matches
.data();