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