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 //----------------------------------------------------------------------
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 gloval 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
compare_void_ptr(const void * a,const void * b)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:
MatchResults()249 MatchResults () :
250 m_size(0)
251 {
252 }
253
254 void
clear()255 clear()
256 {
257 m_size = 0;
258 bzero (&m_entries, sizeof(m_entries));
259 }
260
261 bool
empty() const262 empty() const
263 {
264 return m_size == 0;
265 }
266
267 void
push_back(const malloc_match & m,bool unique=false)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 *
data()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:
MallocStackLoggingEntries()308 MallocStackLoggingEntries () :
309 m_size(0)
310 {
311 }
312
313 void
clear()314 clear()
315 {
316 m_size = 0;
317 }
318
319 bool
empty() const320 empty() const
321 {
322 return m_size == 0;
323 }
324
325
326 malloc_stack_entry *
next()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 *
data()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 *
safe_malloc(size_t n_bytes)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:
ObjCClasses()383 ObjCClasses() :
384 m_objc_class_ptrs (NULL),
385 m_size (0)
386 {
387 }
388
389 bool
Update()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
FindClassIndex(Class isa)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
GetClassAtIndex(uint32_t idx) const427 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
GetSize() const434 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:
ObjCClassInfo()466 ObjCClassInfo() :
467 m_entries (NULL),
468 m_size (0),
469 m_sort_type (eSortTypeNone)
470 {
471 }
472
473 void
Update(const ObjCClasses & objc_classes)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
AddInstance(uint32_t idx,uint64_t ptr_size)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
Reset()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
SortByTotalBytes(const ObjCClasses & objc_classes,bool print)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
SortByTotalCount(const ObjCClasses & objc_classes,bool print)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
compare_bytes(const Entry * a,const Entry * b)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
compare_count(const Entry * a,const Entry * b)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
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
task_peek(task_t task,vm_address_t remote_address,vm_size_t size,void ** local_memory)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
foreach_zone_in_this_process(range_callback_info_t * info)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 perpective.
663 //----------------------------------------------------------------------
664 static void
dump_malloc_block_callback(task_t task,void * baton,unsigned type,uint64_t ptr_addr,uint64_t ptr_size)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
ranges_callback(task_t task,void * baton,unsigned type,vm_range_t * ptrs,unsigned count)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
enumerate_range_in_zone(void * baton,const malloc_zone_t * zone)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
range_info_callback(task_t task,void * baton,unsigned type,uint64_t ptr_addr,uint64_t ptr_size)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
get_stack_for_address_enumerator(mach_stack_logging_record_t stack_record,void * task_ptr)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 *
get_stack_history_for_address(const void * addr,int history)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 *
find_pointer_in_heap(const void * addr,int check_vm_regions)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 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 //----------------------------------------------------------------------
931 malloc_match *
find_pointer_in_memory(uint64_t memory_addr,uint64_t memory_size,const void * addr)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 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 //----------------------------------------------------------------------
956 malloc_match *
find_objc_objects_in_memory(void * isa,int check_vm_regions)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 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
984 void
get_heap_info(int sort_type)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 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 //----------------------------------------------------------------------
1027 malloc_match *
find_cstring_in_heap(const char * s,int check_vm_regions)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 *
find_block_for_address(const void * addr,int check_vm_regions)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 }
1072