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