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