8fb2a8c95927e31d7b20ff2b1833ab4b7cae5c7a
[lldb.git] / lldb / examples / darwin / heap_find / heap.py
1 #!/usr/bin/env python
2
3 #----------------------------------------------------------------------
4 # This module is designed to live inside the "lldb" python package
5 # in the "lldb.macosx" package. To use this in the embedded python
6 # interpreter using "lldb" just import it:
7 #
8 #   (lldb) script import lldb.macosx.heap
9 #----------------------------------------------------------------------
10
11 from __future__ import print_function
12 import lldb
13 import optparse
14 import os
15 import os.path
16 import re
17 import shlex
18 import string
19 import sys
20 import tempfile
21 import lldb.utils.symbolication
22
23 g_libheap_dylib_dir = None
24 g_libheap_dylib_dict = dict()
25
26
27 def get_iterate_memory_expr(
28         options,
29         process,
30         user_init_code,
31         user_return_code):
32     expr = '''
33 typedef unsigned natural_t;
34 typedef uintptr_t vm_size_t;
35 typedef uintptr_t vm_address_t;
36 typedef natural_t task_t;
37 typedef int kern_return_t;
38 #define KERN_SUCCESS 0
39 typedef void (*range_callback_t)(task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size);
40 '''
41     if options.search_vm_regions:
42         expr += '''
43 typedef int vm_prot_t;
44 typedef unsigned int vm_inherit_t;
45 typedef unsigned long long      memory_object_offset_t;
46 typedef unsigned int boolean_t;
47 typedef int vm_behavior_t;
48 typedef uint32_t vm32_object_id_t;
49 typedef natural_t mach_msg_type_number_t;
50 typedef uint64_t mach_vm_address_t;
51 typedef uint64_t mach_vm_offset_t;
52 typedef uint64_t mach_vm_size_t;
53 typedef uint64_t vm_map_offset_t;
54 typedef uint64_t vm_map_address_t;
55 typedef uint64_t vm_map_size_t;
56 #define VM_PROT_NONE ((vm_prot_t) 0x00)
57 #define VM_PROT_READ ((vm_prot_t) 0x01)
58 #define VM_PROT_WRITE ((vm_prot_t) 0x02)
59 #define VM_PROT_EXECUTE ((vm_prot_t) 0x04)
60 typedef struct vm_region_submap_short_info_data_64_t {
61     vm_prot_t protection;
62     vm_prot_t max_protection;
63     vm_inherit_t inheritance;
64     memory_object_offset_t offset;              // offset into object/map
65     unsigned int user_tag;      // user tag on map entry
66     unsigned int ref_count;      // obj/map mappers, etc
67     unsigned short shadow_depth;        // only for obj
68     unsigned char external_pager;  // only for obj
69     unsigned char share_mode;   // see enumeration
70     boolean_t is_submap;        // submap vs obj
71     vm_behavior_t behavior;     // access behavior hint
72     vm32_object_id_t object_id; // obj/map name, not a handle
73     unsigned short user_wired_count;
74 } vm_region_submap_short_info_data_64_t;
75 #define VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ((mach_msg_type_number_t)(sizeof(vm_region_submap_short_info_data_64_t)/sizeof(int)))'''
76         if user_init_code:
77             expr += user_init_code
78         expr += '''
79 task_t task = (task_t)mach_task_self();
80 mach_vm_address_t vm_region_base_addr;
81 mach_vm_size_t vm_region_size;
82 natural_t vm_region_depth;
83 vm_region_submap_short_info_data_64_t vm_region_info;
84 kern_return_t err;
85 for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size)
86 {
87     mach_msg_type_number_t vm_region_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
88     err = (kern_return_t)mach_vm_region_recurse (task,
89                                                  &vm_region_base_addr,
90                                                  &vm_region_size,
91                                                  &vm_region_depth,
92                                                  &vm_region_info,
93                                                  &vm_region_info_size);
94     if (err)
95         break;
96     // Check all read + write regions. This will cover the thread stacks
97     // and any regions of memory like __DATA segments, that might contain
98     // data we are looking for
99     if (vm_region_info.protection & VM_PROT_WRITE &&
100         vm_region_info.protection & VM_PROT_READ)
101     {
102         baton.callback (task,
103                         &baton,
104                         64,
105                         vm_region_base_addr,
106                         vm_region_size);
107     }
108 }'''
109     else:
110         if options.search_stack:
111             expr += get_thread_stack_ranges_struct(process)
112         if options.search_segments:
113             expr += get_sections_ranges_struct(process)
114         if user_init_code:
115             expr += user_init_code
116         if options.search_heap:
117             expr += '''
118 #define MALLOC_PTR_IN_USE_RANGE_TYPE 1
119 typedef struct vm_range_t {
120     vm_address_t        address;
121     vm_size_t           size;
122 } vm_range_t;
123 typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory);
124 typedef void (*vm_range_recorder_t)(task_t task, void *baton, unsigned type, vm_range_t *range, unsigned size);
125 typedef struct malloc_introspection_t {
126     kern_return_t (*enumerator)(task_t task, void *, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, vm_range_recorder_t recorder); /* enumerates all the malloc pointers in use */
127 } malloc_introspection_t;
128 typedef struct malloc_zone_t {
129     void *reserved1[12];
130     struct malloc_introspection_t       *introspect;
131 } malloc_zone_t;
132 memory_reader_t task_peek = [](task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) -> kern_return_t {
133     *local_memory = (void*) remote_address;
134     return KERN_SUCCESS;
135 };
136 vm_address_t *zones = 0;
137 unsigned int num_zones = 0;task_t task = 0;
138 kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones);
139 if (KERN_SUCCESS == err)
140 {
141     for (unsigned int i=0; i<num_zones; ++i)
142     {
143         const malloc_zone_t *zone = (const malloc_zone_t *)zones[i];
144         if (zone && zone->introspect)
145             zone->introspect->enumerator (task,
146                                           &baton,
147                                           MALLOC_PTR_IN_USE_RANGE_TYPE,
148                                           (vm_address_t)zone,
149                                           task_peek,
150                                           [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void
151                                           {
152                                               range_callback_t callback = ((callback_baton_t *)baton)->callback;
153                                               for (unsigned i=0; i<size; ++i)
154                                               {
155                                                   callback (task, baton, type, ranges[i].address, ranges[i].size);
156                                               }
157                                           });
158     }
159 }'''
160
161         if options.search_stack:
162             expr += '''
163 #ifdef NUM_STACKS
164 // Call the callback for the thread stack ranges
165 for (uint32_t i=0; i<NUM_STACKS; ++i) {
166     range_callback(task, &baton, 8, stacks[i].base, stacks[i].size);
167     if (STACK_RED_ZONE_SIZE > 0) {
168         range_callback(task, &baton, 16, stacks[i].base - STACK_RED_ZONE_SIZE, STACK_RED_ZONE_SIZE);
169     }
170 }
171 #endif'''
172
173         if options.search_segments:
174             expr += '''
175 #ifdef NUM_SEGMENTS
176 // Call the callback for all segments
177 for (uint32_t i=0; i<NUM_SEGMENTS; ++i)
178     range_callback(task, &baton, 32, segments[i].base, segments[i].size);
179 #endif'''
180
181     if user_return_code:
182         expr += "\n%s" % (user_return_code,)
183
184     return expr
185
186
187 def get_member_types_for_offset(value_type, offset, member_list):
188     member = value_type.GetFieldAtIndex(0)
189     search_bases = False
190     if member:
191         if member.GetOffsetInBytes() <= offset:
192             for field_idx in range(value_type.GetNumberOfFields()):
193                 member = value_type.GetFieldAtIndex(field_idx)
194                 member_byte_offset = member.GetOffsetInBytes()
195                 member_end_byte_offset = member_byte_offset + member.type.size
196                 if member_byte_offset <= offset and offset < member_end_byte_offset:
197                     member_list.append(member)
198                     get_member_types_for_offset(
199                         member.type, offset - member_byte_offset, member_list)
200                     return
201         else:
202             search_bases = True
203     else:
204         search_bases = True
205     if search_bases:
206         for field_idx in range(value_type.GetNumberOfDirectBaseClasses()):
207             member = value_type.GetDirectBaseClassAtIndex(field_idx)
208             member_byte_offset = member.GetOffsetInBytes()
209             member_end_byte_offset = member_byte_offset + member.type.size
210             if member_byte_offset <= offset and offset < member_end_byte_offset:
211                 member_list.append(member)
212                 get_member_types_for_offset(
213                     member.type, offset - member_byte_offset, member_list)
214                 return
215         for field_idx in range(value_type.GetNumberOfVirtualBaseClasses()):
216             member = value_type.GetVirtualBaseClassAtIndex(field_idx)
217             member_byte_offset = member.GetOffsetInBytes()
218             member_end_byte_offset = member_byte_offset + member.type.size
219             if member_byte_offset <= offset and offset < member_end_byte_offset:
220                 member_list.append(member)
221                 get_member_types_for_offset(
222                     member.type, offset - member_byte_offset, member_list)
223                 return
224
225
226 def append_regex_callback(option, opt, value, parser):
227     try:
228         ivar_regex = re.compile(value)
229         parser.values.ivar_regex_exclusions.append(ivar_regex)
230     except:
231         print('error: an exception was thrown when compiling the ivar regular expression for "%s"' % value)
232
233
234 def add_common_options(parser):
235     parser.add_option(
236         '-v',
237         '--verbose',
238         action='store_true',
239         dest='verbose',
240         help='display verbose debug info',
241         default=False)
242     parser.add_option(
243         '-t',
244         '--type',
245         action='store_true',
246         dest='print_type',
247         help='print the full value of the type for each matching malloc block',
248         default=False)
249     parser.add_option(
250         '-o',
251         '--po',
252         action='store_true',
253         dest='print_object_description',
254         help='print the object descriptions for any matches',
255         default=False)
256     parser.add_option(
257         '-z',
258         '--size',
259         action='store_true',
260         dest='show_size',
261         help='print the allocation size in bytes',
262         default=False)
263     parser.add_option(
264         '-r',
265         '--range',
266         action='store_true',
267         dest='show_range',
268         help='print the allocation address range instead of just the allocation base address',
269         default=False)
270     parser.add_option(
271         '-m',
272         '--memory',
273         action='store_true',
274         dest='memory',
275         help='dump the memory for each matching block',
276         default=False)
277     parser.add_option(
278         '-f',
279         '--format',
280         type='string',
281         dest='format',
282         help='the format to use when dumping memory if --memory is specified',
283         default=None)
284     parser.add_option(
285         '-I',
286         '--omit-ivar-regex',
287         type='string',
288         action='callback',
289         callback=append_regex_callback,
290         dest='ivar_regex_exclusions',
291         default=[],
292         help='specify one or more regular expressions used to backlist any matches that are in ivars')
293     parser.add_option(
294         '-s',
295         '--stack',
296         action='store_true',
297         dest='stack',
298         help='gets the stack that allocated each malloc block if MallocStackLogging is enabled',
299         default=False)
300     parser.add_option(
301         '-S',
302         '--stack-history',
303         action='store_true',
304         dest='stack_history',
305         help='gets the stack history for all allocations whose start address matches each malloc block if MallocStackLogging is enabled',
306         default=False)
307     parser.add_option(
308         '-F',
309         '--max-frames',
310         type='int',
311         dest='max_frames',
312         help='the maximum number of stack frames to print when using the --stack or --stack-history options (default=128)',
313         default=128)
314     parser.add_option(
315         '-H',
316         '--max-history',
317         type='int',
318         dest='max_history',
319         help='the maximum number of stack history backtraces to print for each allocation when using the --stack-history option (default=16)',
320         default=16)
321     parser.add_option(
322         '-M',
323         '--max-matches',
324         type='int',
325         dest='max_matches',
326         help='the maximum number of matches to print',
327         default=32)
328     parser.add_option(
329         '-O',
330         '--offset',
331         type='int',
332         dest='offset',
333         help='the matching data must be at this offset',
334         default=-1)
335     parser.add_option(
336         '--ignore-stack',
337         action='store_false',
338         dest='search_stack',
339         help="Don't search the stack when enumerating memory",
340         default=True)
341     parser.add_option(
342         '--ignore-heap',
343         action='store_false',
344         dest='search_heap',
345         help="Don't search the heap allocations when enumerating memory",
346         default=True)
347     parser.add_option(
348         '--ignore-segments',
349         action='store_false',
350         dest='search_segments',
351         help="Don't search readable executable segments enumerating memory",
352         default=True)
353     parser.add_option(
354         '-V',
355         '--vm-regions',
356         action='store_true',
357         dest='search_vm_regions',
358         help='Check all VM regions instead of searching the heap, stack and segments',
359         default=False)
360
361
362 def type_flags_to_string(type_flags):
363     if type_flags == 0:
364         type_str = 'free'
365     elif type_flags & 2:
366         type_str = 'malloc'
367     elif type_flags & 4:
368         type_str = 'free'
369     elif type_flags & 1:
370         type_str = 'generic'
371     elif type_flags & 8:
372         type_str = 'stack'
373     elif type_flags & 16:
374         type_str = 'stack (red zone)'
375     elif type_flags & 32:
376         type_str = 'segment'
377     elif type_flags & 64:
378         type_str = 'vm_region'
379     else:
380         type_str = hex(type_flags)
381     return type_str
382
383
384 def find_variable_containing_address(verbose, frame, match_addr):
385     variables = frame.GetVariables(True, True, True, True)
386     matching_var = None
387     for var in variables:
388         var_addr = var.GetLoadAddress()
389         if var_addr != lldb.LLDB_INVALID_ADDRESS:
390             byte_size = var.GetType().GetByteSize()
391             if verbose:
392                 print('frame #%u: [%#x - %#x) %s' % (frame.GetFrameID(), var.load_addr, var.load_addr + byte_size, var.name))
393             if var_addr == match_addr:
394                 if verbose:
395                     print('match')
396                 return var
397             else:
398                 if byte_size > 0 and var_addr <= match_addr and match_addr < (
399                         var_addr + byte_size):
400                     if verbose:
401                         print('match')
402                     return var
403     return None
404
405
406 def find_frame_for_stack_address(process, addr):
407     closest_delta = sys.maxsize
408     closest_frame = None
409     # print 'find_frame_for_stack_address(%#x)' % (addr)
410     for thread in process:
411         prev_sp = lldb.LLDB_INVALID_ADDRESS
412         for frame in thread:
413             cfa = frame.GetCFA()
414             # print 'frame #%u: cfa = %#x' % (frame.GetFrameID(), cfa)
415             if addr < cfa:
416                 delta = cfa - addr
417                 # print '%#x < %#x, delta = %i' % (addr, cfa, delta)
418                 if delta < closest_delta:
419                     # print 'closest'
420                     closest_delta = delta
421                     closest_frame = frame
422                 # else:
423                 #     print 'delta >= closest_delta'
424     return closest_frame
425
426
427 def type_flags_to_description(
428         process,
429         type_flags,
430         ptr_addr,
431         ptr_size,
432         offset,
433         match_addr):
434     show_offset = False
435     if type_flags == 0 or type_flags & 4:
436         type_str = 'free(%#x)' % (ptr_addr,)
437     elif type_flags & 2 or type_flags & 1:
438         type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr)
439         show_offset = True
440     elif type_flags & 8:
441         type_str = 'stack'
442         frame = find_frame_for_stack_address(process, match_addr)
443         if frame:
444             type_str += ' in frame #%u of thread #%u: tid %#x' % (frame.GetFrameID(
445             ), frame.GetThread().GetIndexID(), frame.GetThread().GetThreadID())
446         variables = frame.GetVariables(True, True, True, True)
447         matching_var = None
448         for var in variables:
449             var_addr = var.GetLoadAddress()
450             if var_addr != lldb.LLDB_INVALID_ADDRESS:
451                 # print 'variable "%s" @ %#x (%#x)' % (var.name, var.load_addr,
452                 # match_addr)
453                 if var_addr == match_addr:
454                     matching_var = var
455                     break
456                 else:
457                     byte_size = var.GetType().GetByteSize()
458                     if byte_size > 0 and var_addr <= match_addr and match_addr < (
459                             var_addr + byte_size):
460                         matching_var = var
461                         break
462         if matching_var:
463             type_str += ' in variable at %#x:\n    %s' % (
464                 matching_var.GetLoadAddress(), matching_var)
465     elif type_flags & 16:
466         type_str = 'stack (red zone)'
467     elif type_flags & 32:
468         sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
469         type_str = 'segment [%#x - %#x), %s + %u, %s' % (
470             ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
471     elif type_flags & 64:
472         sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
473         type_str = 'vm_region [%#x - %#x), %s + %u, %s' % (
474             ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
475     else:
476         type_str = '%#x' % (ptr_addr,)
477         show_offset = True
478     if show_offset and offset != 0:
479         type_str += ' + %-6u' % (offset,)
480     return type_str
481
482
483 def dump_stack_history_entry(options, result, stack_history_entry, idx):
484     address = int(stack_history_entry.address)
485     if address:
486         type_flags = int(stack_history_entry.type_flags)
487         symbolicator = lldb.utils.symbolication.Symbolicator()
488         symbolicator.target = lldb.debugger.GetSelectedTarget()
489         type_str = type_flags_to_string(type_flags)
490         result.AppendMessage(
491             'stack[%u]: addr = 0x%x, type=%s, frames:' %
492             (idx, address, type_str))
493         frame_idx = 0
494         idx = 0
495         pc = int(stack_history_entry.frames[idx])
496         while pc != 0:
497             if pc >= 0x1000:
498                 frames = symbolicator.symbolicate(pc)
499                 if frames:
500                     for frame in frames:
501                         result.AppendMessage(
502                             '     [%u] %s' %
503                             (frame_idx, frame))
504                         frame_idx += 1
505                 else:
506                     result.AppendMessage('     [%u] 0x%x' % (frame_idx, pc))
507                     frame_idx += 1
508                 idx = idx + 1
509                 pc = int(stack_history_entry.frames[idx])
510             else:
511                 pc = 0
512         if idx >= options.max_frames:
513             result.AppendMessage(
514                 'warning: the max number of stack frames (%u) was reached, use the "--max-frames=<COUNT>" option to see more frames' %
515                 (options.max_frames))
516
517         result.AppendMessage('')
518
519
520 def dump_stack_history_entries(options, result, addr, history):
521     # malloc_stack_entry *get_stack_history_for_address (const void * addr)
522     expr_prefix = '''
523 typedef int kern_return_t;
524 typedef struct $malloc_stack_entry {
525     uint64_t address;
526     uint64_t argument;
527     uint32_t type_flags;
528     uint32_t num_frames;
529     uint64_t frames[512];
530     kern_return_t err;
531 } $malloc_stack_entry;
532 '''
533     single_expr = '''
534 #define MAX_FRAMES %u
535 typedef unsigned task_t;
536 $malloc_stack_entry stack;
537 stack.address = 0x%x;
538 stack.type_flags = 2;
539 stack.num_frames = 0;
540 stack.frames[0] = 0;
541 uint32_t max_stack_frames = MAX_FRAMES;
542 stack.err = (kern_return_t)__mach_stack_logging_get_frames (
543     (task_t)mach_task_self(),
544     stack.address,
545     &stack.frames[0],
546     max_stack_frames,
547     &stack.num_frames);
548 if (stack.num_frames < MAX_FRAMES)
549     stack.frames[stack.num_frames] = 0;
550 else
551     stack.frames[MAX_FRAMES-1] = 0;
552 stack''' % (options.max_frames, addr)
553
554     history_expr = '''
555 typedef int kern_return_t;
556 typedef unsigned task_t;
557 #define MAX_FRAMES %u
558 #define MAX_HISTORY %u
559 typedef struct mach_stack_logging_record_t {
560         uint32_t type_flags;
561         uint64_t stack_identifier;
562         uint64_t argument;
563         uint64_t address;
564 } mach_stack_logging_record_t;
565 typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
566 typedef struct malloc_stack_entry {
567     uint64_t address;
568     uint64_t argument;
569     uint32_t type_flags;
570     uint32_t num_frames;
571     uint64_t frames[MAX_FRAMES];
572     kern_return_t frames_err;
573 } malloc_stack_entry;
574 typedef struct $malloc_stack_history {
575     task_t task;
576     unsigned idx;
577     malloc_stack_entry entries[MAX_HISTORY];
578 } $malloc_stack_history;
579 $malloc_stack_history lldb_info = { (task_t)mach_task_self(), 0 };
580 uint32_t max_stack_frames = MAX_FRAMES;
581 enumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void {
582     $malloc_stack_history *lldb_info = ($malloc_stack_history *)baton;
583     if (lldb_info->idx < MAX_HISTORY) {
584         malloc_stack_entry *stack_entry = &(lldb_info->entries[lldb_info->idx]);
585         stack_entry->address = stack_record.address;
586         stack_entry->type_flags = stack_record.type_flags;
587         stack_entry->argument = stack_record.argument;
588         stack_entry->num_frames = 0;
589         stack_entry->frames[0] = 0;
590         stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack (
591             lldb_info->task,
592             stack_record.stack_identifier,
593             stack_entry->frames,
594             (uint32_t)MAX_FRAMES,
595             &stack_entry->num_frames);
596         // Terminate the frames with zero if there is room
597         if (stack_entry->num_frames < MAX_FRAMES)
598             stack_entry->frames[stack_entry->num_frames] = 0;
599     }
600     ++lldb_info->idx;
601 };
602 (kern_return_t)__mach_stack_logging_enumerate_records (lldb_info.task, (uint64_t)0x%x, callback, &lldb_info);
603 lldb_info''' % (options.max_frames, options.max_history, addr)
604
605     frame = lldb.debugger.GetSelectedTarget().GetProcess(
606     ).GetSelectedThread().GetSelectedFrame()
607     if history:
608         expr = history_expr
609     else:
610         expr = single_expr
611     expr_options = lldb.SBExpressionOptions()
612     expr_options.SetIgnoreBreakpoints(True)
613     expr_options.SetTimeoutInMicroSeconds(5 * 1000 * 1000)  # 5 second timeout
614     expr_options.SetTryAllThreads(True)
615     expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
616     expr_options.SetPrefix(expr_prefix)
617     expr_sbvalue = frame.EvaluateExpression(expr, expr_options)
618     if options.verbose:
619         print("expression:")
620         print(expr)
621         print("expression result:")
622         print(expr_sbvalue)
623     if expr_sbvalue.error.Success():
624         if history:
625             malloc_stack_history = lldb.value(expr_sbvalue)
626             num_stacks = int(malloc_stack_history.idx)
627             if num_stacks <= options.max_history:
628                 i_max = num_stacks
629             else:
630                 i_max = options.max_history
631             for i in range(i_max):
632                 stack_history_entry = malloc_stack_history.entries[i]
633                 dump_stack_history_entry(
634                     options, result, stack_history_entry, i)
635             if num_stacks > options.max_history:
636                 result.AppendMessage(
637                     'warning: the max number of stacks (%u) was reached, use the "--max-history=%u" option to see all of the stacks' %
638                     (options.max_history, num_stacks))
639         else:
640             stack_history_entry = lldb.value(expr_sbvalue)
641             dump_stack_history_entry(options, result, stack_history_entry, 0)
642
643     else:
644         result.AppendMessage(
645             'error: expression failed "%s" => %s' %
646             (expr, expr_sbvalue.error))
647
648
649 def display_match_results(
650         process,
651         result,
652         options,
653         arg_str_description,
654         expr,
655         print_no_matches,
656         expr_prefix=None):
657     frame = lldb.debugger.GetSelectedTarget().GetProcess(
658     ).GetSelectedThread().GetSelectedFrame()
659     if not frame:
660         result.AppendMessage('error: invalid frame')
661         return 0
662     expr_options = lldb.SBExpressionOptions()
663     expr_options.SetIgnoreBreakpoints(True)
664     expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues)
665     expr_options.SetTimeoutInMicroSeconds(
666         30 * 1000 * 1000)  # 30 second timeout
667     expr_options.SetTryAllThreads(False)
668     expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
669     if expr_prefix:
670         expr_options.SetPrefix(expr_prefix)
671     expr_sbvalue = frame.EvaluateExpression(expr, expr_options)
672     if options.verbose:
673         print("expression:")
674         print(expr)
675         print("expression result:")
676         print(expr_sbvalue)
677     if expr_sbvalue.error.Success():
678         match_value = lldb.value(expr_sbvalue)
679         i = 0
680         match_idx = 0
681         while True:
682             print_entry = True
683             match_entry = match_value[i]
684             i += 1
685             if i > options.max_matches:
686                 result.AppendMessage(
687                     'warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' %
688                     (options.max_matches))
689                 break
690             malloc_addr = match_entry.addr.sbvalue.unsigned
691             if malloc_addr == 0:
692                 break
693             malloc_size = int(match_entry.size)
694             offset = int(match_entry.offset)
695
696             if options.offset >= 0 and options.offset != offset:
697                 print_entry = False
698             else:
699                 match_addr = malloc_addr + offset
700                 type_flags = int(match_entry.type)
701                 #result.AppendMessage (hex(malloc_addr + offset))
702                 if type_flags == 64:
703                     search_stack_old = options.search_stack
704                     search_segments_old = options.search_segments
705                     search_heap_old = options.search_heap
706                     search_vm_regions = options.search_vm_regions
707                     options.search_stack = True
708                     options.search_segments = True
709                     options.search_heap = True
710                     options.search_vm_regions = False
711                     if malloc_info_impl(lldb.debugger, result, options, [
712                                         hex(malloc_addr + offset)]):
713                         print_entry = False
714                     options.search_stack = search_stack_old
715                     options.search_segments = search_segments_old
716                     options.search_heap = search_heap_old
717                     options.search_vm_regions = search_vm_regions
718                 if print_entry:
719                     description = '%#16.16x: %s' % (match_addr, type_flags_to_description(
720                         process, type_flags, malloc_addr, malloc_size, offset, match_addr))
721                     if options.show_size:
722                         description += ' <%5u>' % (malloc_size)
723                     if options.show_range:
724                         description += ' [%#x - %#x)' % (
725                             malloc_addr, malloc_addr + malloc_size)
726                     derefed_dynamic_value = None
727                     dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(
728                         lldb.eDynamicCanRunTarget)
729                     if dynamic_value.type.name == 'void *':
730                         if options.type == 'pointer' and malloc_size == 4096:
731                             error = lldb.SBError()
732                             process = expr_sbvalue.GetProcess()
733                             target = expr_sbvalue.GetTarget()
734                             data = bytearray(
735                                 process.ReadMemory(
736                                     malloc_addr, 16, error))
737                             if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
738                                 ptr_size = target.addr_size
739                                 thread = process.ReadUnsignedFromMemory(
740                                     malloc_addr + 16 + ptr_size, ptr_size, error)
741                                 #   4 bytes  0xa1a1a1a1
742                                 #  12 bytes  'AUTORELEASE!'
743                                 # ptr bytes  autorelease insertion point
744                                 # ptr bytes  pthread_t
745                                 # ptr bytes  next colder page
746                                 # ptr bytes  next hotter page
747                                 #   4 bytes  this page's depth in the list
748                                 #   4 bytes  high-water mark
749                                 description += ' AUTORELEASE! for pthread_t %#x' % (
750                                     thread)
751                         #     else:
752                         #         description += 'malloc(%u)' % (malloc_size)
753                         # else:
754                         #     description += 'malloc(%u)' % (malloc_size)
755                     else:
756                         derefed_dynamic_value = dynamic_value.deref
757                         if derefed_dynamic_value:
758                             derefed_dynamic_type = derefed_dynamic_value.type
759                             derefed_dynamic_type_size = derefed_dynamic_type.size
760                             derefed_dynamic_type_name = derefed_dynamic_type.name
761                             description += ' '
762                             description += derefed_dynamic_type_name
763                             if offset < derefed_dynamic_type_size:
764                                 member_list = list()
765                                 get_member_types_for_offset(
766                                     derefed_dynamic_type, offset, member_list)
767                                 if member_list:
768                                     member_path = ''
769                                     for member in member_list:
770                                         member_name = member.name
771                                         if member_name:
772                                             if member_path:
773                                                 member_path += '.'
774                                             member_path += member_name
775                                     if member_path:
776                                         if options.ivar_regex_exclusions:
777                                             for ivar_regex in options.ivar_regex_exclusions:
778                                                 if ivar_regex.match(
779                                                         member_path):
780                                                     print_entry = False
781                                         description += '.%s' % (member_path)
782                             else:
783                                 description += '%u bytes after %s' % (
784                                     offset - derefed_dynamic_type_size, derefed_dynamic_type_name)
785                         else:
786                             # strip the "*" from the end of the name since we
787                             # were unable to dereference this
788                             description += dynamic_value.type.name[0:-1]
789             if print_entry:
790                 match_idx += 1
791                 result_output = ''
792                 if description:
793                     result_output += description
794                     if options.print_type and derefed_dynamic_value:
795                         result_output += ' %s' % (derefed_dynamic_value)
796                     if options.print_object_description and dynamic_value:
797                         desc = dynamic_value.GetObjectDescription()
798                         if desc:
799                             result_output += '\n%s' % (desc)
800                 if result_output:
801                     result.AppendMessage(result_output)
802                 if options.memory:
803                     cmd_result = lldb.SBCommandReturnObject()
804                     if options.format is None:
805                         memory_command = "memory read --force 0x%x 0x%x" % (
806                             malloc_addr, malloc_addr + malloc_size)
807                     else:
808                         memory_command = "memory read --force -f %s 0x%x 0x%x" % (
809                             options.format, malloc_addr, malloc_addr + malloc_size)
810                     if options.verbose:
811                         result.AppendMessage(memory_command)
812                     lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
813                     result.AppendMessage(cmd_result.GetOutput())
814                 if options.stack_history:
815                     dump_stack_history_entries(options, result, malloc_addr, 1)
816                 elif options.stack:
817                     dump_stack_history_entries(options, result, malloc_addr, 0)
818         return i
819     else:
820         result.AppendMessage(str(expr_sbvalue.error))
821     return 0
822
823
824 def get_ptr_refs_options():
825     usage = "usage: %prog [options] <EXPR> [EXPR ...]"
826     description = '''Searches all allocations on the heap for pointer values on
827 darwin user space programs. Any matches that were found will dump the malloc
828 blocks that contain the pointers and might be able to print what kind of
829 objects the pointers are contained in using dynamic type information in the
830 program.'''
831     parser = optparse.OptionParser(
832         description=description,
833         prog='ptr_refs',
834         usage=usage)
835     add_common_options(parser)
836     return parser
837
838
839 def find_variable(debugger, command, result, dict):
840     usage = "usage: %prog [options] <ADDR> [ADDR ...]"
841     description = '''Searches for a local variable in all frames that contains a hex ADDR.'''
842     command_args = shlex.split(command)
843     parser = optparse.OptionParser(
844         description=description,
845         prog='find_variable',
846         usage=usage)
847     parser.add_option(
848         '-v',
849         '--verbose',
850         action='store_true',
851         dest='verbose',
852         help='display verbose debug info',
853         default=False)
854     try:
855         (options, args) = parser.parse_args(command_args)
856     except:
857         return
858
859     process = debugger.GetSelectedTarget().GetProcess()
860     if not process:
861         result.AppendMessage('error: invalid process')
862         return
863
864     for arg in args:
865         var_addr = int(arg, 16)
866         print("Finding a variable with address %#x..." % (var_addr), file=result)
867         done = False
868         for thread in process:
869             for frame in thread:
870                 var = find_variable_containing_address(
871                     options.verbose, frame, var_addr)
872                 if var:
873                     print(var)
874                     done = True
875                     break
876             if done:
877                 break
878
879
880 def ptr_refs(debugger, command, result, dict):
881     command_args = shlex.split(command)
882     parser = get_ptr_refs_options()
883     try:
884         (options, args) = parser.parse_args(command_args)
885     except:
886         return
887
888     process = debugger.GetSelectedTarget().GetProcess()
889     if not process:
890         result.AppendMessage('error: invalid process')
891         return
892     frame = process.GetSelectedThread().GetSelectedFrame()
893     if not frame:
894         result.AppendMessage('error: invalid frame')
895         return
896
897     options.type = 'pointer'
898     if options.format is None:
899         options.format = "A"  # 'A' is "address" format
900
901     if args:
902         # When we initialize the expression, we must define any types that
903         # we will need when looking at every allocation. We must also define
904         # a type named callback_baton_t and make an instance named "baton"
905         # and initialize it how ever we want to. The address of "baton" will
906         # be passed into our range callback. callback_baton_t must contain
907         # a member named "callback" whose type is "range_callback_t". This
908         # will be used by our zone callbacks to call the range callback for
909         # each malloc range.
910         expr_prefix = '''
911 struct $malloc_match {
912     void *addr;
913     uintptr_t size;
914     uintptr_t offset;
915     uintptr_t type;
916 };
917 '''
918         user_init_code_format = '''
919 #define MAX_MATCHES %u
920 typedef struct callback_baton_t {
921     range_callback_t callback;
922     unsigned num_matches;
923     $malloc_match matches[MAX_MATCHES];
924     void *ptr;
925 } callback_baton_t;
926 range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
927     callback_baton_t *lldb_info = (callback_baton_t *)baton;
928     typedef void* T;
929     const unsigned size = sizeof(T);
930     T *array = (T*)ptr_addr;
931     for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) {
932         if (array[idx] == lldb_info->ptr) {
933             if (lldb_info->num_matches < MAX_MATCHES) {
934                 lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
935                 lldb_info->matches[lldb_info->num_matches].size = ptr_size;
936                 lldb_info->matches[lldb_info->num_matches].offset = idx*sizeof(T);
937                 lldb_info->matches[lldb_info->num_matches].type = type;
938                 ++lldb_info->num_matches;
939             }
940         }
941     }
942 };
943 callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
944 '''
945         # We must also define a snippet of code to be run that returns
946         # the result of the expression we run.
947         # Here we return NULL if our pointer was not found in any malloc blocks,
948         # and we return the address of the matches array so we can then access
949         # the matching results
950         user_return_code = '''if (baton.num_matches < MAX_MATCHES)
951     baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
952 baton.matches'''
953         # Iterate through all of our pointer expressions and display the
954         # results
955         for ptr_expr in args:
956             user_init_code = user_init_code_format % (
957                 options.max_matches, ptr_expr)
958             expr = get_iterate_memory_expr(
959                 options, process, user_init_code, user_return_code)
960             arg_str_description = 'malloc block containing pointer %s' % ptr_expr
961             display_match_results(
962                 process,
963                 result,
964                 options,
965                 arg_str_description,
966                 expr,
967                 True,
968                 expr_prefix)
969     else:
970         result.AppendMessage('error: no pointer arguments were given')
971
972
973 def get_cstr_refs_options():
974     usage = "usage: %prog [options] <CSTR> [CSTR ...]"
975     description = '''Searches all allocations on the heap for C string values on
976 darwin user space programs. Any matches that were found will dump the malloc
977 blocks that contain the C strings and might be able to print what kind of
978 objects the pointers are contained in using dynamic type information in the
979 program.'''
980     parser = optparse.OptionParser(
981         description=description,
982         prog='cstr_refs',
983         usage=usage)
984     add_common_options(parser)
985     return parser
986
987
988 def cstr_refs(debugger, command, result, dict):
989     command_args = shlex.split(command)
990     parser = get_cstr_refs_options()
991     try:
992         (options, args) = parser.parse_args(command_args)
993     except:
994         return
995
996     process = debugger.GetSelectedTarget().GetProcess()
997     if not process:
998         result.AppendMessage('error: invalid process')
999         return
1000     frame = process.GetSelectedThread().GetSelectedFrame()
1001     if not frame:
1002         result.AppendMessage('error: invalid frame')
1003         return
1004
1005     options.type = 'cstr'
1006     if options.format is None:
1007         options.format = "Y"  # 'Y' is "bytes with ASCII" format
1008
1009     if args:
1010         # When we initialize the expression, we must define any types that
1011         # we will need when looking at every allocation. We must also define
1012         # a type named callback_baton_t and make an instance named "baton"
1013         # and initialize it how ever we want to. The address of "baton" will
1014         # be passed into our range callback. callback_baton_t must contain
1015         # a member named "callback" whose type is "range_callback_t". This
1016         # will be used by our zone callbacks to call the range callback for
1017         # each malloc range.
1018         expr_prefix = '''
1019 struct $malloc_match {
1020     void *addr;
1021     uintptr_t size;
1022     uintptr_t offset;
1023     uintptr_t type;
1024 };
1025 '''
1026         user_init_code_format = '''
1027 #define MAX_MATCHES %u
1028 typedef struct callback_baton_t {
1029     range_callback_t callback;
1030     unsigned num_matches;
1031     $malloc_match matches[MAX_MATCHES];
1032     const char *cstr;
1033     unsigned cstr_len;
1034 } callback_baton_t;
1035 range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
1036     callback_baton_t *lldb_info = (callback_baton_t *)baton;
1037     if (lldb_info->cstr_len < ptr_size) {
1038         const char *begin = (const char *)ptr_addr;
1039         const char *end = begin + ptr_size - lldb_info->cstr_len;
1040         for (const char *s = begin; s < end; ++s) {
1041             if ((int)memcmp(s, lldb_info->cstr, lldb_info->cstr_len) == 0) {
1042                 if (lldb_info->num_matches < MAX_MATCHES) {
1043                     lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
1044                     lldb_info->matches[lldb_info->num_matches].size = ptr_size;
1045                     lldb_info->matches[lldb_info->num_matches].offset = s - begin;
1046                     lldb_info->matches[lldb_info->num_matches].type = type;
1047                     ++lldb_info->num_matches;
1048                 }
1049             }
1050         }
1051     }
1052 };
1053 const char *cstr = "%s";
1054 callback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };'''
1055         # We must also define a snippet of code to be run that returns
1056         # the result of the expression we run.
1057         # Here we return NULL if our pointer was not found in any malloc blocks,
1058         # and we return the address of the matches array so we can then access
1059         # the matching results
1060         user_return_code = '''if (baton.num_matches < MAX_MATCHES)
1061     baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
1062 baton.matches'''
1063         # Iterate through all of our pointer expressions and display the
1064         # results
1065         for cstr in args:
1066             user_init_code = user_init_code_format % (
1067                 options.max_matches, cstr)
1068             expr = get_iterate_memory_expr(
1069                 options, process, user_init_code, user_return_code)
1070             arg_str_description = 'malloc block containing "%s"' % cstr
1071             display_match_results(
1072                 process,
1073                 result,
1074                 options,
1075                 arg_str_description,
1076                 expr,
1077                 True,
1078                 expr_prefix)
1079     else:
1080         result.AppendMessage(
1081             'error: command takes one or more C string arguments')
1082
1083
1084 def get_malloc_info_options():
1085     usage = "usage: %prog [options] <EXPR> [EXPR ...]"
1086     description = '''Searches the heap a malloc block that contains the addresses
1087 specified as one or more address expressions. Any matches that were found will
1088 dump the malloc blocks that match or contain the specified address. The matching
1089 blocks might be able to show what kind of objects they are using dynamic type
1090 information in the program.'''
1091     parser = optparse.OptionParser(
1092         description=description,
1093         prog='malloc_info',
1094         usage=usage)
1095     add_common_options(parser)
1096     return parser
1097
1098
1099 def malloc_info(debugger, command, result, dict):
1100     command_args = shlex.split(command)
1101     parser = get_malloc_info_options()
1102     try:
1103         (options, args) = parser.parse_args(command_args)
1104     except:
1105         return
1106     malloc_info_impl(debugger, result, options, args)
1107
1108
1109 def malloc_info_impl(debugger, result, options, args):
1110     # We are specifically looking for something on the heap only
1111     options.type = 'malloc_info'
1112
1113     process = debugger.GetSelectedTarget().GetProcess()
1114     if not process:
1115         result.AppendMessage('error: invalid process')
1116         return
1117     frame = process.GetSelectedThread().GetSelectedFrame()
1118     if not frame:
1119         result.AppendMessage('error: invalid frame')
1120         return
1121     expr_prefix = '''
1122 struct $malloc_match {
1123     void *addr;
1124     uintptr_t size;
1125     uintptr_t offset;
1126     uintptr_t type;
1127 };
1128 '''
1129
1130     user_init_code_format = '''
1131 typedef struct callback_baton_t {
1132     range_callback_t callback;
1133     unsigned num_matches;
1134     $malloc_match matches[2]; // Two items so they can be NULL terminated
1135     void *ptr;
1136 } callback_baton_t;
1137 range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
1138     callback_baton_t *lldb_info = (callback_baton_t *)baton;
1139     if (lldb_info->num_matches == 0) {
1140         uint8_t *p = (uint8_t *)lldb_info->ptr;
1141         uint8_t *lo = (uint8_t *)ptr_addr;
1142         uint8_t *hi = lo + ptr_size;
1143         if (lo <= p && p < hi) {
1144             lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
1145             lldb_info->matches[lldb_info->num_matches].size = ptr_size;
1146             lldb_info->matches[lldb_info->num_matches].offset = p - lo;
1147             lldb_info->matches[lldb_info->num_matches].type = type;
1148             lldb_info->num_matches = 1;
1149         }
1150     }
1151 };
1152 callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
1153 baton.matches[0].addr = 0;
1154 baton.matches[1].addr = 0;'''
1155     if args:
1156         total_matches = 0
1157         for ptr_expr in args:
1158             user_init_code = user_init_code_format % (ptr_expr)
1159             expr = get_iterate_memory_expr(
1160                 options, process, user_init_code, 'baton.matches')
1161             arg_str_description = 'malloc block that contains %s' % ptr_expr
1162             total_matches += display_match_results(
1163                 process, result, options, arg_str_description, expr, True, expr_prefix)
1164         return total_matches
1165     else:
1166         result.AppendMessage(
1167             'error: command takes one or more pointer expressions')
1168         return 0
1169
1170
1171 def get_thread_stack_ranges_struct(process):
1172     '''Create code that defines a structure that represents threads stack bounds
1173     for all  threads. It returns a static sized array initialized with all of
1174     the tid, base, size structs for all the threads.'''
1175     stack_dicts = list()
1176     if process:
1177         i = 0
1178         for thread in process:
1179             min_sp = thread.frame[0].sp
1180             max_sp = min_sp
1181             for frame in thread.frames:
1182                 sp = frame.sp
1183                 if sp < min_sp:
1184                     min_sp = sp
1185                 if sp > max_sp:
1186                     max_sp = sp
1187             if min_sp < max_sp:
1188                 stack_dicts.append({'tid': thread.GetThreadID(
1189                 ), 'base': min_sp, 'size': max_sp - min_sp, 'index': i})
1190                 i += 1
1191     stack_dicts_len = len(stack_dicts)
1192     if stack_dicts_len > 0:
1193         result = '''
1194 #define NUM_STACKS %u
1195 #define STACK_RED_ZONE_SIZE %u
1196 typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
1197 thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize())
1198         for stack_dict in stack_dicts:
1199             result += '''
1200 stacks[%(index)u].tid  = 0x%(tid)x;
1201 stacks[%(index)u].base = 0x%(base)x;
1202 stacks[%(index)u].size = 0x%(size)x;''' % stack_dict
1203         return result
1204     else:
1205         return ''
1206
1207
1208 def get_sections_ranges_struct(process):
1209     '''Create code that defines a structure that represents all segments that
1210     can contain data for all images in "target". It returns a static sized
1211     array initialized with all of base, size structs for all the threads.'''
1212     target = process.target
1213     segment_dicts = list()
1214     for (module_idx, module) in enumerate(target.modules):
1215         for sect_idx in range(module.GetNumSections()):
1216             section = module.GetSectionAtIndex(sect_idx)
1217             if not section:
1218                 break
1219             name = section.name
1220             if name != '__TEXT' and name != '__LINKEDIT' and name != '__PAGEZERO':
1221                 base = section.GetLoadAddress(target)
1222                 size = section.GetByteSize()
1223                 if base != lldb.LLDB_INVALID_ADDRESS and size > 0:
1224                     segment_dicts.append({'base': base, 'size': size})
1225     segment_dicts_len = len(segment_dicts)
1226     if segment_dicts_len > 0:
1227         result = '''
1228 #define NUM_SEGMENTS %u
1229 typedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t;
1230 segment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,)
1231         for (idx, segment_dict) in enumerate(segment_dicts):
1232             segment_dict['index'] = idx
1233             result += '''
1234 segments[%(index)u].base = 0x%(base)x;
1235 segments[%(index)u].size = 0x%(size)x;''' % segment_dict
1236         return result
1237     else:
1238         return ''
1239
1240
1241 def section_ptr_refs(debugger, command, result, dict):
1242     command_args = shlex.split(command)
1243     usage = "usage: %prog [options] <EXPR> [EXPR ...]"
1244     description = '''Searches section contents for pointer values in darwin user space programs.'''
1245     parser = optparse.OptionParser(
1246         description=description,
1247         prog='section_ptr_refs',
1248         usage=usage)
1249     add_common_options(parser)
1250     parser.add_option(
1251         '--section',
1252         action='append',
1253         type='string',
1254         dest='section_names',
1255         help='section name to search',
1256         default=list())
1257     try:
1258         (options, args) = parser.parse_args(command_args)
1259     except:
1260         return
1261
1262     options.type = 'pointer'
1263
1264     sections = list()
1265     section_modules = list()
1266     if not options.section_names:
1267         result.AppendMessage(
1268             'error: at least one section must be specified with the --section option')
1269         return
1270
1271     target = debugger.GetSelectedTarget()
1272     for module in target.modules:
1273         for section_name in options.section_names:
1274             section = module.section[section_name]
1275             if section:
1276                 sections.append(section)
1277                 section_modules.append(module)
1278     if sections:
1279         dylid_load_err = load_dylib()
1280         if dylid_load_err:
1281             result.AppendMessage(dylid_load_err)
1282             return
1283         frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
1284         for expr_str in args:
1285             for (idx, section) in enumerate(sections):
1286                 expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (
1287                     section.addr.load_addr, section.size, expr_str)
1288                 arg_str_description = 'section %s.%s containing "%s"' % (
1289                     section_modules[idx].file.fullpath, section.name, expr_str)
1290                 num_matches = display_match_results(
1291                     target.GetProcess(), result, options, arg_str_description, expr, False)
1292                 if num_matches:
1293                     if num_matches < options.max_matches:
1294                         options.max_matches = options.max_matches - num_matches
1295                     else:
1296                         options.max_matches = 0
1297                 if options.max_matches == 0:
1298                     return
1299     else:
1300         result.AppendMessage(
1301             'error: no sections were found that match any of %s' %
1302             (', '.join(
1303                 options.section_names)))
1304
1305
1306 def get_objc_refs_options():
1307     usage = "usage: %prog [options] <CLASS> [CLASS ...]"
1308     description = '''Searches all allocations on the heap for instances of
1309 objective C classes, or any classes that inherit from the specified classes
1310 in darwin user space programs. Any matches that were found will dump the malloc
1311 blocks that contain the C strings and might be able to print what kind of
1312 objects the pointers are contained in using dynamic type information in the
1313 program.'''
1314     parser = optparse.OptionParser(
1315         description=description,
1316         prog='objc_refs',
1317         usage=usage)
1318     add_common_options(parser)
1319     return parser
1320
1321
1322 def objc_refs(debugger, command, result, dict):
1323     command_args = shlex.split(command)
1324     parser = get_objc_refs_options()
1325     try:
1326         (options, args) = parser.parse_args(command_args)
1327     except:
1328         return
1329
1330     process = debugger.GetSelectedTarget().GetProcess()
1331     if not process:
1332         result.AppendMessage('error: invalid process')
1333         return
1334     frame = process.GetSelectedThread().GetSelectedFrame()
1335     if not frame:
1336         result.AppendMessage('error: invalid frame')
1337         return
1338
1339     options.type = 'isa'
1340     if options.format is None:
1341         options.format = "A"  # 'A' is "address" format
1342
1343     expr_options = lldb.SBExpressionOptions()
1344     expr_options.SetIgnoreBreakpoints(True)
1345     expr_options.SetTimeoutInMicroSeconds(
1346         3 * 1000 * 1000)  # 3 second infinite timeout
1347     expr_options.SetTryAllThreads(True)
1348     expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
1349     num_objc_classes_value = frame.EvaluateExpression(
1350         "(int)objc_getClassList((void *)0, (int)0)", expr_options)
1351     if not num_objc_classes_value.error.Success():
1352         result.AppendMessage('error: %s' %
1353                              num_objc_classes_value.error.GetCString())
1354         return
1355
1356     num_objc_classes = num_objc_classes_value.GetValueAsUnsigned()
1357     if num_objc_classes == 0:
1358         result.AppendMessage('error: no objective C classes in program')
1359         return
1360
1361     if args:
1362         # When we initialize the expression, we must define any types that
1363         # we will need when looking at every allocation. We must also define
1364         # a type named callback_baton_t and make an instance named "baton"
1365         # and initialize it how ever we want to. The address of "baton" will
1366         # be passed into our range callback. callback_baton_t must contain
1367         # a member named "callback" whose type is "range_callback_t". This
1368         # will be used by our zone callbacks to call the range callback for
1369         # each malloc range.
1370         expr_prefix = '''
1371 struct $malloc_match {
1372     void *addr;
1373     uintptr_t size;
1374     uintptr_t offset;
1375     uintptr_t type;
1376 };
1377 '''
1378
1379         user_init_code_format = '''
1380 #define MAX_MATCHES %u
1381 typedef int (*compare_callback_t)(const void *a, const void *b);
1382 typedef struct callback_baton_t {
1383     range_callback_t callback;
1384     compare_callback_t compare_callback;
1385     unsigned num_matches;
1386     $malloc_match matches[MAX_MATCHES];
1387     void *isa;
1388     Class classes[%u];
1389 } callback_baton_t;
1390 compare_callback_t compare_callback = [](const void *a, const void *b) -> int {
1391      Class a_ptr = *(Class *)a;
1392      Class b_ptr = *(Class *)b;
1393      if (a_ptr < b_ptr) return -1;
1394      if (a_ptr > b_ptr) return +1;
1395      return 0;
1396 };
1397 typedef Class (*class_getSuperclass_type)(void *isa);
1398 range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
1399     class_getSuperclass_type class_getSuperclass_impl = (class_getSuperclass_type)class_getSuperclass;
1400     callback_baton_t *lldb_info = (callback_baton_t *)baton;
1401     if (sizeof(Class) <= ptr_size) {
1402         Class *curr_class_ptr = (Class *)ptr_addr;
1403         Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr,
1404                                                       (const void *)lldb_info->classes,
1405                                                       sizeof(lldb_info->classes)/sizeof(Class),
1406                                                       sizeof(Class),
1407                                                       lldb_info->compare_callback);
1408         if (matching_class_ptr) {
1409             bool match = false;
1410             if (lldb_info->isa) {
1411                 Class isa = *curr_class_ptr;
1412                 if (lldb_info->isa == isa)
1413                     match = true;
1414                 else { // if (lldb_info->objc.match_superclasses) {
1415                     Class super = class_getSuperclass_impl(isa);
1416                     while (super) {
1417                         if (super == lldb_info->isa) {
1418                             match = true;
1419                             break;
1420                         }
1421                         super = class_getSuperclass_impl(super);
1422                     }
1423                 }
1424             }
1425             else
1426                 match = true;
1427             if (match) {
1428                 if (lldb_info->num_matches < MAX_MATCHES) {
1429                     lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
1430                     lldb_info->matches[lldb_info->num_matches].size = ptr_size;
1431                     lldb_info->matches[lldb_info->num_matches].offset = 0;
1432                     lldb_info->matches[lldb_info->num_matches].type = type;
1433                     ++lldb_info->num_matches;
1434                 }
1435             }
1436         }
1437     }
1438 };
1439 callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
1440 int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class));
1441 (void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);'''
1442         # We must also define a snippet of code to be run that returns
1443         # the result of the expression we run.
1444         # Here we return NULL if our pointer was not found in any malloc blocks,
1445         # and we return the address of the matches array so we can then access
1446         # the matching results
1447         user_return_code = '''if (baton.num_matches < MAX_MATCHES)
1448     baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
1449         baton.matches'''
1450         # Iterate through all of our ObjC class name arguments
1451         for class_name in args:
1452             addr_expr_str = "(void *)[%s class]" % class_name
1453             expr_options = lldb.SBExpressionOptions()
1454             expr_options.SetIgnoreBreakpoints(True)
1455             expr_options.SetTimeoutInMicroSeconds(
1456                 1 * 1000 * 1000)  # 1 second timeout
1457             expr_options.SetTryAllThreads(True)
1458             expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
1459             expr_sbvalue = frame.EvaluateExpression(
1460                 addr_expr_str, expr_options)
1461             if expr_sbvalue.error.Success():
1462                 isa = expr_sbvalue.unsigned
1463                 if isa:
1464                     options.type = 'isa'
1465                     result.AppendMessage(
1466                         'Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' %
1467                         (class_name, isa))
1468                     user_init_code = user_init_code_format % (
1469                         options.max_matches, num_objc_classes, isa)
1470                     expr = get_iterate_memory_expr(
1471                         options, process, user_init_code, user_return_code)
1472                     arg_str_description = 'objective C classes with isa 0x%x' % isa
1473                     display_match_results(
1474                         process,
1475                         result,
1476                         options,
1477                         arg_str_description,
1478                         expr,
1479                         True,
1480                         expr_prefix)
1481                 else:
1482                     result.AppendMessage(
1483                         'error: Can\'t find isa for an ObjC class named "%s"' %
1484                         (class_name))
1485             else:
1486                 result.AppendMessage(
1487                     'error: expression error for "%s": %s' %
1488                     (addr_expr_str, expr_sbvalue.error))
1489     else:
1490         result.AppendMessage(
1491             'error: command takes one or more C string arguments')
1492
1493 if __name__ == '__main__':
1494     lldb.debugger = lldb.SBDebugger.Create()
1495
1496 # Make the options so we can generate the help text for the new LLDB
1497 # command line command prior to registering it with LLDB below. This way
1498 # if clients in LLDB type "help malloc_info", they will see the exact same
1499 # output as typing "malloc_info --help".
1500 ptr_refs.__doc__ = get_ptr_refs_options().format_help()
1501 cstr_refs.__doc__ = get_cstr_refs_options().format_help()
1502 malloc_info.__doc__ = get_malloc_info_options().format_help()
1503 objc_refs.__doc__ = get_objc_refs_options().format_help()
1504 lldb.debugger.HandleCommand(
1505     'command script add -f %s.ptr_refs ptr_refs' %
1506     __name__)
1507 lldb.debugger.HandleCommand(
1508     'command script add -f %s.cstr_refs cstr_refs' %
1509     __name__)
1510 lldb.debugger.HandleCommand(
1511     'command script add -f %s.malloc_info malloc_info' %
1512     __name__)
1513 lldb.debugger.HandleCommand(
1514     'command script add -f %s.find_variable find_variable' %
1515     __name__)
1516 # lldb.debugger.HandleCommand('command script add -f %s.heap heap' % package_name)
1517 # lldb.debugger.HandleCommand('command script add -f %s.section_ptr_refs section_ptr_refs' % package_name)
1518 # lldb.debugger.HandleCommand('command script add -f %s.stack_ptr_refs stack_ptr_refs' % package_name)
1519 lldb.debugger.HandleCommand(
1520     'command script add -f %s.objc_refs objc_refs' %
1521     __name__)
1522 print('"malloc_info", "ptr_refs", "cstr_refs", "find_variable", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help.')