• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
11import lldb
12import commands
13import optparse
14import os
15import os.path
16import re
17import shlex
18import string
19import tempfile
20import lldb.utils.symbolication
21
22g_libheap_dylib_dir = None
23g_libheap_dylib_dict = dict()
24
25def get_iterate_memory_expr(options, process, user_init_code, user_return_code):
26    expr = '''
27typedef unsigned natural_t;
28typedef uintptr_t vm_size_t;
29typedef uintptr_t vm_address_t;
30typedef natural_t task_t;
31typedef int kern_return_t;
32#define KERN_SUCCESS 0
33typedef 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 += '''
37typedef int vm_prot_t;
38typedef unsigned int vm_inherit_t;
39typedef unsigned long long	memory_object_offset_t;
40typedef unsigned int boolean_t;
41typedef int vm_behavior_t;
42typedef uint32_t vm32_object_id_t;
43typedef natural_t mach_msg_type_number_t;
44typedef uint64_t mach_vm_address_t;
45typedef uint64_t mach_vm_offset_t;
46typedef uint64_t mach_vm_size_t;
47typedef uint64_t vm_map_offset_t;
48typedef uint64_t vm_map_address_t;
49typedef 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)
54typedef 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 += '''
73task_t task = (task_t)mach_task_self();
74mach_vm_address_t vm_region_base_addr;
75mach_vm_size_t vm_region_size;
76natural_t vm_region_depth;
77vm_region_submap_short_info_data_64_t vm_region_info;
78kern_return_t err;
79for (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
113typedef struct vm_range_t {
114    vm_address_t	address;
115    vm_size_t		size;
116} vm_range_t;
117typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory);
118typedef void (*vm_range_recorder_t)(task_t task, void *baton, unsigned type, vm_range_t *range, unsigned size);
119typedef 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;
122typedef struct malloc_zone_t {
123    void *reserved1[12];
124    struct malloc_introspection_t	*introspect;
125} malloc_zone_t;
126memory_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};
130vm_address_t *zones = 0;
131unsigned int num_zones = 0;task_t task = 0;
132kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones);
133if (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
158for (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
169for (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
177def 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
212def 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
219def 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
239def 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
260def 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
284def 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
314def dump_stack_history_entries(options, result, addr, history):
315    # malloc_stack_entry *get_stack_history_for_address (const void * addr)
316    single_expr = '''
317typedef int kern_return_t;
318#define MAX_FRAMES %u
319typedef 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;
327typedef unsigned task_t;
328$malloc_stack_entry stack;
329stack.address = 0x%x;
330stack.type_flags = 2;
331stack.num_frames = 0;
332stack.frames[0] = 0;
333uint32_t max_stack_frames = MAX_FRAMES;
334stack.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);
340if (stack.num_frames < MAX_FRAMES)
341    stack.frames[stack.num_frames] = 0;
342else
343    stack.frames[MAX_FRAMES-1] = 0;
344stack''' % (options.max_frames, addr);
345
346    history_expr = '''
347typedef int kern_return_t;
348typedef unsigned task_t;
349#define MAX_FRAMES %u
350#define MAX_HISTORY %u
351typedef 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;
357typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
358typedef 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;
366typedef 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 };
372uint32_t max_stack_frames = MAX_FRAMES;
373enumerate_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);
395info''' % (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
433def 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
581def get_ptr_refs_options ():
582    usage = "usage: %prog [options] <EXPR> [EXPR ...]"
583    description='''Searches all allocations on the heap for pointer values on
584darwin user space programs. Any matches that were found will dump the malloc
585blocks that contain the pointers and might be able to print what kind of
586objects the pointers are contained in using dynamic type information in the
587program.'''
588    parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
589    add_common_options(parser)
590    return parser
591
592def 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
624struct $malloc_match {
625    void *addr;
626    uintptr_t size;
627    uintptr_t offset;
628    uintptr_t type;
629};
630typedef 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;
636range_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};
653callback_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
662baton.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
672def get_cstr_refs_options():
673    usage = "usage: %prog [options] <CSTR> [CSTR ...]"
674    description='''Searches all allocations on the heap for C string values on
675darwin user space programs. Any matches that were found will dump the malloc
676blocks that contain the C strings and might be able to print what kind of
677objects the pointers are contained in using dynamic type information in the
678program.'''
679    parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
680    add_common_options(parser)
681    return parser
682
683def 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
716struct $malloc_match {
717    void *addr;
718    uintptr_t size;
719    uintptr_t offset;
720    uintptr_t type;
721};
722typedef 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;
729range_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};
747const char *cstr = "%s";
748callback_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
756baton.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
767def get_malloc_info_options():
768    usage = "usage: %prog [options] <EXPR> [EXPR ...]"
769    description='''Searches the heap a malloc block that contains the addresses
770specified as one or more address expressions. Any matches that were found will
771dump the malloc blocks that match or contain the specified address. The matching
772blocks might be able to show what kind of objects they are using dynamic type
773information in the program.'''
774    parser = optparse.OptionParser(description=description, prog='malloc_info',usage=usage)
775    add_common_options(parser)
776    return parser
777
778def 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
787def 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 = '''
801struct $malloc_match {
802    void *addr;
803    uintptr_t size;
804    uintptr_t offset;
805    uintptr_t type;
806};
807typedef 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;
813range_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};
828callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
829baton.matches[0].addr = 0;
830baton.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
843def 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
865typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
866thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize())
867        for stack_dict in stack_dicts:
868            result += '''
869stacks[%(index)u].tid  = 0x%(tid)x;
870stacks[%(index)u].base = 0x%(base)x;
871stacks[%(index)u].size = 0x%(size)x;''' % stack_dict
872        return result
873    else:
874        return None
875
876def 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
897typedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t;
898segment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,)
899        for (idx, segment_dict) in enumerate(segment_dicts):
900            segment_dict['index'] = idx
901            result += '''
902segments[%(index)u].base = 0x%(base)x;
903segments[%(index)u].size = 0x%(size)x;''' % segment_dict
904        return result
905    else:
906        return None
907
908def 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
956def get_objc_refs_options():
957    usage = "usage: %prog [options] <CLASS> [CLASS ...]"
958    description='''Searches all allocations on the heap for instances of
959objective C classes, or any classes that inherit from the specified classes
960in darwin user space programs. Any matches that were found will dump the malloc
961blocks that contain the C strings and might be able to print what kind of
962objects the pointers are contained in using dynamic type information in the
963program.'''
964    parser = optparse.OptionParser(description=description, prog='objc_refs',usage=usage)
965    add_common_options(parser)
966    return parser
967
968def 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
1014struct $malloc_match {
1015    void *addr;
1016    uintptr_t size;
1017    uintptr_t offset;
1018    uintptr_t type;
1019};
1020typedef int (*compare_callback_t)(const void *a, const void *b);
1021typedef 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;
1029compare_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};
1036range_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};
1076callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
1077int 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
1111if __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".
1118ptr_refs.__doc__ = get_ptr_refs_options().format_help()
1119cstr_refs.__doc__ = get_cstr_refs_options().format_help()
1120malloc_info.__doc__ = get_malloc_info_options().format_help()
1121objc_refs.__doc__ = get_objc_refs_options().format_help()
1122lldb.debugger.HandleCommand('command script add -f %s.ptr_refs ptr_refs' % __name__)
1123lldb.debugger.HandleCommand('command script add -f %s.cstr_refs cstr_refs' % __name__)
1124lldb.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)
1128lldb.debugger.HandleCommand('command script add -f %s.objc_refs objc_refs' % __name__)
1129print '"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