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