1be691f3bSpatrick#!/usr/bin/env python3
2061da546Spatrick
3061da546Spatrick#----------------------------------------------------------------------
4061da546Spatrick# This module is designed to live inside the "lldb" python package
5061da546Spatrick# in the "lldb.macosx" package. To use this in the embedded python
6061da546Spatrick# interpreter using "lldb" just import it:
7061da546Spatrick#
8061da546Spatrick#   (lldb) script import lldb.macosx.heap
9061da546Spatrick#----------------------------------------------------------------------
10061da546Spatrick
11061da546Spatrickimport lldb
12061da546Spatrickimport optparse
13061da546Spatrickimport os
14061da546Spatrickimport os.path
15061da546Spatrickimport re
16061da546Spatrickimport shlex
17061da546Spatrickimport string
18061da546Spatrickimport sys
19061da546Spatrickimport tempfile
20061da546Spatrickimport lldb.utils.symbolication
21061da546Spatrick
22061da546Spatrickg_libheap_dylib_dir = None
23061da546Spatrickg_libheap_dylib_dict = dict()
24061da546Spatrick
25061da546Spatrick
26061da546Spatrickdef get_iterate_memory_expr(
27061da546Spatrick        options,
28061da546Spatrick        process,
29061da546Spatrick        user_init_code,
30061da546Spatrick        user_return_code):
31061da546Spatrick    expr = '''
32061da546Spatricktypedef unsigned natural_t;
33061da546Spatricktypedef uintptr_t vm_size_t;
34061da546Spatricktypedef uintptr_t vm_address_t;
35061da546Spatricktypedef natural_t task_t;
36061da546Spatricktypedef int kern_return_t;
37061da546Spatrick#define KERN_SUCCESS 0
38be691f3bSpatricktypedef void (*range_callback_t)(task_t, void *, unsigned, uintptr_t, uintptr_t);
39061da546Spatrick'''
40061da546Spatrick    if options.search_vm_regions:
41061da546Spatrick        expr += '''
42061da546Spatricktypedef int vm_prot_t;
43061da546Spatricktypedef unsigned int vm_inherit_t;
44061da546Spatricktypedef unsigned long long	memory_object_offset_t;
45061da546Spatricktypedef unsigned int boolean_t;
46061da546Spatricktypedef int vm_behavior_t;
47061da546Spatricktypedef uint32_t vm32_object_id_t;
48061da546Spatricktypedef natural_t mach_msg_type_number_t;
49061da546Spatricktypedef uint64_t mach_vm_address_t;
50061da546Spatricktypedef uint64_t mach_vm_offset_t;
51061da546Spatricktypedef uint64_t mach_vm_size_t;
52061da546Spatricktypedef uint64_t vm_map_offset_t;
53061da546Spatricktypedef uint64_t vm_map_address_t;
54061da546Spatricktypedef uint64_t vm_map_size_t;
55061da546Spatrick#define	VM_PROT_NONE ((vm_prot_t) 0x00)
56061da546Spatrick#define VM_PROT_READ ((vm_prot_t) 0x01)
57061da546Spatrick#define VM_PROT_WRITE ((vm_prot_t) 0x02)
58061da546Spatrick#define VM_PROT_EXECUTE ((vm_prot_t) 0x04)
59061da546Spatricktypedef struct vm_region_submap_short_info_data_64_t {
60061da546Spatrick    vm_prot_t protection;
61061da546Spatrick    vm_prot_t max_protection;
62061da546Spatrick    vm_inherit_t inheritance;
63061da546Spatrick    memory_object_offset_t offset;		// offset into object/map
64061da546Spatrick    unsigned int user_tag;	// user tag on map entry
65061da546Spatrick    unsigned int ref_count;	 // obj/map mappers, etc
66061da546Spatrick    unsigned short shadow_depth; 	// only for obj
67061da546Spatrick    unsigned char external_pager;  // only for obj
68061da546Spatrick    unsigned char share_mode;	// see enumeration
69061da546Spatrick    boolean_t is_submap;	// submap vs obj
70061da546Spatrick    vm_behavior_t behavior;	// access behavior hint
71061da546Spatrick    vm32_object_id_t object_id;	// obj/map name, not a handle
72061da546Spatrick    unsigned short user_wired_count;
73061da546Spatrick} vm_region_submap_short_info_data_64_t;
74061da546Spatrick#define VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ((mach_msg_type_number_t)(sizeof(vm_region_submap_short_info_data_64_t)/sizeof(int)))'''
75061da546Spatrick        if user_init_code:
76061da546Spatrick            expr += user_init_code
77061da546Spatrick        expr += '''
78061da546Spatricktask_t task = (task_t)mach_task_self();
79061da546Spatrickmach_vm_address_t vm_region_base_addr;
80061da546Spatrickmach_vm_size_t vm_region_size;
81061da546Spatricknatural_t vm_region_depth;
82061da546Spatrickvm_region_submap_short_info_data_64_t vm_region_info;
83061da546Spatrickkern_return_t err;
84061da546Spatrickfor (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size)
85061da546Spatrick{
86061da546Spatrick    mach_msg_type_number_t vm_region_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
87061da546Spatrick    err = (kern_return_t)mach_vm_region_recurse (task,
88061da546Spatrick                                                 &vm_region_base_addr,
89061da546Spatrick                                                 &vm_region_size,
90061da546Spatrick                                                 &vm_region_depth,
91061da546Spatrick                                                 &vm_region_info,
92061da546Spatrick                                                 &vm_region_info_size);
93061da546Spatrick    if (err)
94061da546Spatrick        break;
95061da546Spatrick    // Check all read + write regions. This will cover the thread stacks
96061da546Spatrick    // and any regions of memory like __DATA segments, that might contain
97061da546Spatrick    // data we are looking for
98061da546Spatrick    if (vm_region_info.protection & VM_PROT_WRITE &&
99061da546Spatrick        vm_region_info.protection & VM_PROT_READ)
100061da546Spatrick    {
101061da546Spatrick        baton.callback (task,
102061da546Spatrick                        &baton,
103061da546Spatrick                        64,
104061da546Spatrick                        vm_region_base_addr,
105061da546Spatrick                        vm_region_size);
106061da546Spatrick    }
107061da546Spatrick}'''
108061da546Spatrick    else:
109061da546Spatrick        if options.search_stack:
110061da546Spatrick            expr += get_thread_stack_ranges_struct(process)
111061da546Spatrick        if options.search_segments:
112061da546Spatrick            expr += get_sections_ranges_struct(process)
113061da546Spatrick        if user_init_code:
114061da546Spatrick            expr += user_init_code
115061da546Spatrick        if options.search_heap:
116061da546Spatrick            expr += '''
117061da546Spatrick#define MALLOC_PTR_IN_USE_RANGE_TYPE 1
118061da546Spatricktypedef struct vm_range_t {
119061da546Spatrick    vm_address_t	address;
120061da546Spatrick    vm_size_t		size;
121061da546Spatrick} vm_range_t;
122be691f3bSpatricktypedef kern_return_t (*memory_reader_t)(task_t, vm_address_t, vm_size_t, void **);
123be691f3bSpatricktypedef void (*vm_range_recorder_t)(task_t, void *, unsigned, vm_range_t *, unsigned);
124061da546Spatricktypedef struct malloc_introspection_t {
125061da546Spatrick    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 */
126061da546Spatrick} malloc_introspection_t;
127061da546Spatricktypedef struct malloc_zone_t {
128061da546Spatrick    void *reserved1[12];
129061da546Spatrick    struct malloc_introspection_t	*introspect;
130061da546Spatrick} malloc_zone_t;
131*f6aab3d8Srobertkern_return_t malloc_get_all_zones(task_t, memory_reader_t, vm_address_t **, unsigned *);
132be691f3bSpatrickmemory_reader_t task_peek = [](task_t, vm_address_t remote_address, vm_size_t, void **local_memory) -> kern_return_t {
133061da546Spatrick    *local_memory = (void*) remote_address;
134061da546Spatrick    return KERN_SUCCESS;
135061da546Spatrick};
136061da546Spatrickvm_address_t *zones = 0;
137061da546Spatrickunsigned int num_zones = 0;task_t task = 0;
138061da546Spatrickkern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones);
139061da546Spatrickif (KERN_SUCCESS == err)
140061da546Spatrick{
141061da546Spatrick    for (unsigned int i=0; i<num_zones; ++i)
142061da546Spatrick    {
143061da546Spatrick        const malloc_zone_t *zone = (const malloc_zone_t *)zones[i];
144061da546Spatrick        if (zone && zone->introspect)
145061da546Spatrick            zone->introspect->enumerator (task,
146061da546Spatrick                                          &baton,
147061da546Spatrick                                          MALLOC_PTR_IN_USE_RANGE_TYPE,
148061da546Spatrick                                          (vm_address_t)zone,
149061da546Spatrick                                          task_peek,
150061da546Spatrick                                          [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void
151061da546Spatrick                                          {
152061da546Spatrick                                              range_callback_t callback = ((callback_baton_t *)baton)->callback;
153061da546Spatrick                                              for (unsigned i=0; i<size; ++i)
154061da546Spatrick                                              {
155061da546Spatrick                                                  callback (task, baton, type, ranges[i].address, ranges[i].size);
156061da546Spatrick                                              }
157061da546Spatrick                                          });
158061da546Spatrick    }
159061da546Spatrick}'''
160061da546Spatrick
161061da546Spatrick        if options.search_stack:
162061da546Spatrick            expr += '''
163061da546Spatrick#ifdef NUM_STACKS
164061da546Spatrick// Call the callback for the thread stack ranges
165061da546Spatrickfor (uint32_t i=0; i<NUM_STACKS; ++i) {
166061da546Spatrick    range_callback(task, &baton, 8, stacks[i].base, stacks[i].size);
167061da546Spatrick    if (STACK_RED_ZONE_SIZE > 0) {
168061da546Spatrick        range_callback(task, &baton, 16, stacks[i].base - STACK_RED_ZONE_SIZE, STACK_RED_ZONE_SIZE);
169061da546Spatrick    }
170061da546Spatrick}
171061da546Spatrick#endif'''
172061da546Spatrick
173061da546Spatrick        if options.search_segments:
174061da546Spatrick            expr += '''
175061da546Spatrick#ifdef NUM_SEGMENTS
176061da546Spatrick// Call the callback for all segments
177061da546Spatrickfor (uint32_t i=0; i<NUM_SEGMENTS; ++i)
178061da546Spatrick    range_callback(task, &baton, 32, segments[i].base, segments[i].size);
179061da546Spatrick#endif'''
180061da546Spatrick
181061da546Spatrick    if user_return_code:
182061da546Spatrick        expr += "\n%s" % (user_return_code,)
183061da546Spatrick
184061da546Spatrick    return expr
185061da546Spatrick
186061da546Spatrick
187061da546Spatrickdef get_member_types_for_offset(value_type, offset, member_list):
188061da546Spatrick    member = value_type.GetFieldAtIndex(0)
189061da546Spatrick    search_bases = False
190061da546Spatrick    if member:
191061da546Spatrick        if member.GetOffsetInBytes() <= offset:
192061da546Spatrick            for field_idx in range(value_type.GetNumberOfFields()):
193061da546Spatrick                member = value_type.GetFieldAtIndex(field_idx)
194061da546Spatrick                member_byte_offset = member.GetOffsetInBytes()
195061da546Spatrick                member_end_byte_offset = member_byte_offset + member.type.size
196061da546Spatrick                if member_byte_offset <= offset and offset < member_end_byte_offset:
197061da546Spatrick                    member_list.append(member)
198061da546Spatrick                    get_member_types_for_offset(
199061da546Spatrick                        member.type, offset - member_byte_offset, member_list)
200061da546Spatrick                    return
201061da546Spatrick        else:
202061da546Spatrick            search_bases = True
203061da546Spatrick    else:
204061da546Spatrick        search_bases = True
205061da546Spatrick    if search_bases:
206061da546Spatrick        for field_idx in range(value_type.GetNumberOfDirectBaseClasses()):
207061da546Spatrick            member = value_type.GetDirectBaseClassAtIndex(field_idx)
208061da546Spatrick            member_byte_offset = member.GetOffsetInBytes()
209061da546Spatrick            member_end_byte_offset = member_byte_offset + member.type.size
210061da546Spatrick            if member_byte_offset <= offset and offset < member_end_byte_offset:
211061da546Spatrick                member_list.append(member)
212061da546Spatrick                get_member_types_for_offset(
213061da546Spatrick                    member.type, offset - member_byte_offset, member_list)
214061da546Spatrick                return
215061da546Spatrick        for field_idx in range(value_type.GetNumberOfVirtualBaseClasses()):
216061da546Spatrick            member = value_type.GetVirtualBaseClassAtIndex(field_idx)
217061da546Spatrick            member_byte_offset = member.GetOffsetInBytes()
218061da546Spatrick            member_end_byte_offset = member_byte_offset + member.type.size
219061da546Spatrick            if member_byte_offset <= offset and offset < member_end_byte_offset:
220061da546Spatrick                member_list.append(member)
221061da546Spatrick                get_member_types_for_offset(
222061da546Spatrick                    member.type, offset - member_byte_offset, member_list)
223061da546Spatrick                return
224061da546Spatrick
225061da546Spatrick
226061da546Spatrickdef append_regex_callback(option, opt, value, parser):
227061da546Spatrick    try:
228061da546Spatrick        ivar_regex = re.compile(value)
229dda28197Spatrick        parser.values.ivar_regex_exclusions.append(ivar_regex)
230061da546Spatrick    except:
231061da546Spatrick        print('error: an exception was thrown when compiling the ivar regular expression for "%s"' % value)
232061da546Spatrick
233061da546Spatrick
234061da546Spatrickdef add_common_options(parser):
235061da546Spatrick    parser.add_option(
236061da546Spatrick        '-v',
237061da546Spatrick        '--verbose',
238061da546Spatrick        action='store_true',
239061da546Spatrick        dest='verbose',
240061da546Spatrick        help='display verbose debug info',
241061da546Spatrick        default=False)
242061da546Spatrick    parser.add_option(
243061da546Spatrick        '-t',
244061da546Spatrick        '--type',
245061da546Spatrick        action='store_true',
246061da546Spatrick        dest='print_type',
247061da546Spatrick        help='print the full value of the type for each matching malloc block',
248061da546Spatrick        default=False)
249061da546Spatrick    parser.add_option(
250061da546Spatrick        '-o',
251061da546Spatrick        '--po',
252061da546Spatrick        action='store_true',
253061da546Spatrick        dest='print_object_description',
254061da546Spatrick        help='print the object descriptions for any matches',
255061da546Spatrick        default=False)
256061da546Spatrick    parser.add_option(
257061da546Spatrick        '-z',
258061da546Spatrick        '--size',
259061da546Spatrick        action='store_true',
260061da546Spatrick        dest='show_size',
261061da546Spatrick        help='print the allocation size in bytes',
262061da546Spatrick        default=False)
263061da546Spatrick    parser.add_option(
264061da546Spatrick        '-r',
265061da546Spatrick        '--range',
266061da546Spatrick        action='store_true',
267061da546Spatrick        dest='show_range',
268061da546Spatrick        help='print the allocation address range instead of just the allocation base address',
269061da546Spatrick        default=False)
270061da546Spatrick    parser.add_option(
271061da546Spatrick        '-m',
272061da546Spatrick        '--memory',
273061da546Spatrick        action='store_true',
274061da546Spatrick        dest='memory',
275061da546Spatrick        help='dump the memory for each matching block',
276061da546Spatrick        default=False)
277061da546Spatrick    parser.add_option(
278061da546Spatrick        '-f',
279061da546Spatrick        '--format',
280061da546Spatrick        type='string',
281061da546Spatrick        dest='format',
282061da546Spatrick        help='the format to use when dumping memory if --memory is specified',
283061da546Spatrick        default=None)
284061da546Spatrick    parser.add_option(
285061da546Spatrick        '-I',
286061da546Spatrick        '--omit-ivar-regex',
287061da546Spatrick        type='string',
288061da546Spatrick        action='callback',
289061da546Spatrick        callback=append_regex_callback,
290dda28197Spatrick        dest='ivar_regex_exclusions',
291061da546Spatrick        default=[],
292061da546Spatrick        help='specify one or more regular expressions used to backlist any matches that are in ivars')
293061da546Spatrick    parser.add_option(
294061da546Spatrick        '-s',
295061da546Spatrick        '--stack',
296061da546Spatrick        action='store_true',
297061da546Spatrick        dest='stack',
298061da546Spatrick        help='gets the stack that allocated each malloc block if MallocStackLogging is enabled',
299061da546Spatrick        default=False)
300061da546Spatrick    parser.add_option(
301061da546Spatrick        '-S',
302061da546Spatrick        '--stack-history',
303061da546Spatrick        action='store_true',
304061da546Spatrick        dest='stack_history',
305061da546Spatrick        help='gets the stack history for all allocations whose start address matches each malloc block if MallocStackLogging is enabled',
306061da546Spatrick        default=False)
307061da546Spatrick    parser.add_option(
308061da546Spatrick        '-F',
309061da546Spatrick        '--max-frames',
310061da546Spatrick        type='int',
311061da546Spatrick        dest='max_frames',
312061da546Spatrick        help='the maximum number of stack frames to print when using the --stack or --stack-history options (default=128)',
313061da546Spatrick        default=128)
314061da546Spatrick    parser.add_option(
315061da546Spatrick        '-H',
316061da546Spatrick        '--max-history',
317061da546Spatrick        type='int',
318061da546Spatrick        dest='max_history',
319061da546Spatrick        help='the maximum number of stack history backtraces to print for each allocation when using the --stack-history option (default=16)',
320061da546Spatrick        default=16)
321061da546Spatrick    parser.add_option(
322061da546Spatrick        '-M',
323061da546Spatrick        '--max-matches',
324061da546Spatrick        type='int',
325061da546Spatrick        dest='max_matches',
326061da546Spatrick        help='the maximum number of matches to print',
327061da546Spatrick        default=32)
328061da546Spatrick    parser.add_option(
329061da546Spatrick        '-O',
330061da546Spatrick        '--offset',
331061da546Spatrick        type='int',
332061da546Spatrick        dest='offset',
333061da546Spatrick        help='the matching data must be at this offset',
334061da546Spatrick        default=-1)
335061da546Spatrick    parser.add_option(
336061da546Spatrick        '--ignore-stack',
337061da546Spatrick        action='store_false',
338061da546Spatrick        dest='search_stack',
339061da546Spatrick        help="Don't search the stack when enumerating memory",
340061da546Spatrick        default=True)
341061da546Spatrick    parser.add_option(
342061da546Spatrick        '--ignore-heap',
343061da546Spatrick        action='store_false',
344061da546Spatrick        dest='search_heap',
345061da546Spatrick        help="Don't search the heap allocations when enumerating memory",
346061da546Spatrick        default=True)
347061da546Spatrick    parser.add_option(
348061da546Spatrick        '--ignore-segments',
349061da546Spatrick        action='store_false',
350061da546Spatrick        dest='search_segments',
351061da546Spatrick        help="Don't search readable executable segments enumerating memory",
352061da546Spatrick        default=True)
353061da546Spatrick    parser.add_option(
354061da546Spatrick        '-V',
355061da546Spatrick        '--vm-regions',
356061da546Spatrick        action='store_true',
357061da546Spatrick        dest='search_vm_regions',
358061da546Spatrick        help='Check all VM regions instead of searching the heap, stack and segments',
359061da546Spatrick        default=False)
360061da546Spatrick
361061da546Spatrick
362061da546Spatrickdef type_flags_to_string(type_flags):
363061da546Spatrick    if type_flags == 0:
364061da546Spatrick        type_str = 'free'
365061da546Spatrick    elif type_flags & 2:
366061da546Spatrick        type_str = 'malloc'
367061da546Spatrick    elif type_flags & 4:
368061da546Spatrick        type_str = 'free'
369061da546Spatrick    elif type_flags & 1:
370061da546Spatrick        type_str = 'generic'
371061da546Spatrick    elif type_flags & 8:
372061da546Spatrick        type_str = 'stack'
373061da546Spatrick    elif type_flags & 16:
374061da546Spatrick        type_str = 'stack (red zone)'
375061da546Spatrick    elif type_flags & 32:
376061da546Spatrick        type_str = 'segment'
377061da546Spatrick    elif type_flags & 64:
378061da546Spatrick        type_str = 'vm_region'
379061da546Spatrick    else:
380061da546Spatrick        type_str = hex(type_flags)
381061da546Spatrick    return type_str
382061da546Spatrick
383061da546Spatrick
384061da546Spatrickdef find_variable_containing_address(verbose, frame, match_addr):
385061da546Spatrick    variables = frame.GetVariables(True, True, True, True)
386061da546Spatrick    matching_var = None
387061da546Spatrick    for var in variables:
388061da546Spatrick        var_addr = var.GetLoadAddress()
389061da546Spatrick        if var_addr != lldb.LLDB_INVALID_ADDRESS:
390061da546Spatrick            byte_size = var.GetType().GetByteSize()
391061da546Spatrick            if verbose:
392061da546Spatrick                print('frame #%u: [%#x - %#x) %s' % (frame.GetFrameID(), var.load_addr, var.load_addr + byte_size, var.name))
393061da546Spatrick            if var_addr == match_addr:
394061da546Spatrick                if verbose:
395061da546Spatrick                    print('match')
396061da546Spatrick                return var
397061da546Spatrick            else:
398061da546Spatrick                if byte_size > 0 and var_addr <= match_addr and match_addr < (
399061da546Spatrick                        var_addr + byte_size):
400061da546Spatrick                    if verbose:
401061da546Spatrick                        print('match')
402061da546Spatrick                    return var
403061da546Spatrick    return None
404061da546Spatrick
405061da546Spatrick
406061da546Spatrickdef find_frame_for_stack_address(process, addr):
407061da546Spatrick    closest_delta = sys.maxsize
408061da546Spatrick    closest_frame = None
409061da546Spatrick    # print 'find_frame_for_stack_address(%#x)' % (addr)
410061da546Spatrick    for thread in process:
411061da546Spatrick        prev_sp = lldb.LLDB_INVALID_ADDRESS
412061da546Spatrick        for frame in thread:
413061da546Spatrick            cfa = frame.GetCFA()
414061da546Spatrick            # print 'frame #%u: cfa = %#x' % (frame.GetFrameID(), cfa)
415061da546Spatrick            if addr < cfa:
416061da546Spatrick                delta = cfa - addr
417061da546Spatrick                # print '%#x < %#x, delta = %i' % (addr, cfa, delta)
418061da546Spatrick                if delta < closest_delta:
419061da546Spatrick                    # print 'closest'
420061da546Spatrick                    closest_delta = delta
421061da546Spatrick                    closest_frame = frame
422061da546Spatrick                # else:
423061da546Spatrick                #     print 'delta >= closest_delta'
424061da546Spatrick    return closest_frame
425061da546Spatrick
426061da546Spatrick
427061da546Spatrickdef type_flags_to_description(
428061da546Spatrick        process,
429061da546Spatrick        type_flags,
430061da546Spatrick        ptr_addr,
431061da546Spatrick        ptr_size,
432061da546Spatrick        offset,
433061da546Spatrick        match_addr):
434061da546Spatrick    show_offset = False
435061da546Spatrick    if type_flags == 0 or type_flags & 4:
436061da546Spatrick        type_str = 'free(%#x)' % (ptr_addr,)
437061da546Spatrick    elif type_flags & 2 or type_flags & 1:
438061da546Spatrick        type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr)
439061da546Spatrick        show_offset = True
440061da546Spatrick    elif type_flags & 8:
441061da546Spatrick        type_str = 'stack'
442061da546Spatrick        frame = find_frame_for_stack_address(process, match_addr)
443061da546Spatrick        if frame:
444061da546Spatrick            type_str += ' in frame #%u of thread #%u: tid %#x' % (frame.GetFrameID(
445061da546Spatrick            ), frame.GetThread().GetIndexID(), frame.GetThread().GetThreadID())
446061da546Spatrick        variables = frame.GetVariables(True, True, True, True)
447061da546Spatrick        matching_var = None
448061da546Spatrick        for var in variables:
449061da546Spatrick            var_addr = var.GetLoadAddress()
450061da546Spatrick            if var_addr != lldb.LLDB_INVALID_ADDRESS:
451061da546Spatrick                # print 'variable "%s" @ %#x (%#x)' % (var.name, var.load_addr,
452061da546Spatrick                # match_addr)
453061da546Spatrick                if var_addr == match_addr:
454061da546Spatrick                    matching_var = var
455061da546Spatrick                    break
456061da546Spatrick                else:
457061da546Spatrick                    byte_size = var.GetType().GetByteSize()
458061da546Spatrick                    if byte_size > 0 and var_addr <= match_addr and match_addr < (
459061da546Spatrick                            var_addr + byte_size):
460061da546Spatrick                        matching_var = var
461061da546Spatrick                        break
462061da546Spatrick        if matching_var:
463061da546Spatrick            type_str += ' in variable at %#x:\n    %s' % (
464061da546Spatrick                matching_var.GetLoadAddress(), matching_var)
465061da546Spatrick    elif type_flags & 16:
466061da546Spatrick        type_str = 'stack (red zone)'
467061da546Spatrick    elif type_flags & 32:
468061da546Spatrick        sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
469061da546Spatrick        type_str = 'segment [%#x - %#x), %s + %u, %s' % (
470061da546Spatrick            ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
471061da546Spatrick    elif type_flags & 64:
472061da546Spatrick        sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
473061da546Spatrick        type_str = 'vm_region [%#x - %#x), %s + %u, %s' % (
474061da546Spatrick            ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
475061da546Spatrick    else:
476061da546Spatrick        type_str = '%#x' % (ptr_addr,)
477061da546Spatrick        show_offset = True
478061da546Spatrick    if show_offset and offset != 0:
479061da546Spatrick        type_str += ' + %-6u' % (offset,)
480061da546Spatrick    return type_str
481061da546Spatrick
482061da546Spatrick
483061da546Spatrickdef dump_stack_history_entry(options, result, stack_history_entry, idx):
484061da546Spatrick    address = int(stack_history_entry.address)
485061da546Spatrick    if address:
486061da546Spatrick        type_flags = int(stack_history_entry.type_flags)
487061da546Spatrick        symbolicator = lldb.utils.symbolication.Symbolicator()
488061da546Spatrick        symbolicator.target = lldb.debugger.GetSelectedTarget()
489061da546Spatrick        type_str = type_flags_to_string(type_flags)
490061da546Spatrick        result.AppendMessage(
491061da546Spatrick            'stack[%u]: addr = 0x%x, type=%s, frames:' %
492061da546Spatrick            (idx, address, type_str))
493061da546Spatrick        frame_idx = 0
494061da546Spatrick        idx = 0
495061da546Spatrick        pc = int(stack_history_entry.frames[idx])
496061da546Spatrick        while pc != 0:
497061da546Spatrick            if pc >= 0x1000:
498061da546Spatrick                frames = symbolicator.symbolicate(pc)
499061da546Spatrick                if frames:
500061da546Spatrick                    for frame in frames:
501061da546Spatrick                        result.AppendMessage(
502061da546Spatrick                            '     [%u] %s' %
503061da546Spatrick                            (frame_idx, frame))
504061da546Spatrick                        frame_idx += 1
505061da546Spatrick                else:
506061da546Spatrick                    result.AppendMessage('     [%u] 0x%x' % (frame_idx, pc))
507061da546Spatrick                    frame_idx += 1
508061da546Spatrick                idx = idx + 1
509061da546Spatrick                pc = int(stack_history_entry.frames[idx])
510061da546Spatrick            else:
511061da546Spatrick                pc = 0
512061da546Spatrick        if idx >= options.max_frames:
513061da546Spatrick            result.AppendMessage(
514061da546Spatrick                'warning: the max number of stack frames (%u) was reached, use the "--max-frames=<COUNT>" option to see more frames' %
515061da546Spatrick                (options.max_frames))
516061da546Spatrick
517061da546Spatrick        result.AppendMessage('')
518061da546Spatrick
519061da546Spatrick
520061da546Spatrickdef dump_stack_history_entries(options, result, addr, history):
521061da546Spatrick    # malloc_stack_entry *get_stack_history_for_address (const void * addr)
522061da546Spatrick    expr_prefix = '''
523061da546Spatricktypedef int kern_return_t;
524061da546Spatricktypedef struct $malloc_stack_entry {
525061da546Spatrick    uint64_t address;
526061da546Spatrick    uint64_t argument;
527061da546Spatrick    uint32_t type_flags;
528061da546Spatrick    uint32_t num_frames;
529061da546Spatrick    uint64_t frames[512];
530061da546Spatrick    kern_return_t err;
531061da546Spatrick} $malloc_stack_entry;
532061da546Spatrick'''
533061da546Spatrick    single_expr = '''
534061da546Spatrick#define MAX_FRAMES %u
535061da546Spatricktypedef unsigned task_t;
536061da546Spatrick$malloc_stack_entry stack;
537061da546Spatrickstack.address = 0x%x;
538061da546Spatrickstack.type_flags = 2;
539061da546Spatrickstack.num_frames = 0;
540061da546Spatrickstack.frames[0] = 0;
541061da546Spatrickuint32_t max_stack_frames = MAX_FRAMES;
542061da546Spatrickstack.err = (kern_return_t)__mach_stack_logging_get_frames (
543061da546Spatrick    (task_t)mach_task_self(),
544061da546Spatrick    stack.address,
545061da546Spatrick    &stack.frames[0],
546061da546Spatrick    max_stack_frames,
547061da546Spatrick    &stack.num_frames);
548061da546Spatrickif (stack.num_frames < MAX_FRAMES)
549061da546Spatrick    stack.frames[stack.num_frames] = 0;
550061da546Spatrickelse
551061da546Spatrick    stack.frames[MAX_FRAMES-1] = 0;
552061da546Spatrickstack''' % (options.max_frames, addr)
553061da546Spatrick
554061da546Spatrick    history_expr = '''
555061da546Spatricktypedef int kern_return_t;
556061da546Spatricktypedef unsigned task_t;
557061da546Spatrick#define MAX_FRAMES %u
558061da546Spatrick#define MAX_HISTORY %u
559061da546Spatricktypedef struct mach_stack_logging_record_t {
560061da546Spatrick	uint32_t type_flags;
561061da546Spatrick	uint64_t stack_identifier;
562061da546Spatrick	uint64_t argument;
563061da546Spatrick	uint64_t address;
564061da546Spatrick} mach_stack_logging_record_t;
565061da546Spatricktypedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
566061da546Spatricktypedef struct malloc_stack_entry {
567061da546Spatrick    uint64_t address;
568061da546Spatrick    uint64_t argument;
569061da546Spatrick    uint32_t type_flags;
570061da546Spatrick    uint32_t num_frames;
571061da546Spatrick    uint64_t frames[MAX_FRAMES];
572061da546Spatrick    kern_return_t frames_err;
573061da546Spatrick} malloc_stack_entry;
574061da546Spatricktypedef struct $malloc_stack_history {
575061da546Spatrick    task_t task;
576061da546Spatrick    unsigned idx;
577061da546Spatrick    malloc_stack_entry entries[MAX_HISTORY];
578061da546Spatrick} $malloc_stack_history;
579061da546Spatrick$malloc_stack_history lldb_info = { (task_t)mach_task_self(), 0 };
580061da546Spatrickuint32_t max_stack_frames = MAX_FRAMES;
581061da546Spatrickenumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void {
582061da546Spatrick    $malloc_stack_history *lldb_info = ($malloc_stack_history *)baton;
583061da546Spatrick    if (lldb_info->idx < MAX_HISTORY) {
584061da546Spatrick        malloc_stack_entry *stack_entry = &(lldb_info->entries[lldb_info->idx]);
585061da546Spatrick        stack_entry->address = stack_record.address;
586061da546Spatrick        stack_entry->type_flags = stack_record.type_flags;
587061da546Spatrick        stack_entry->argument = stack_record.argument;
588061da546Spatrick        stack_entry->num_frames = 0;
589061da546Spatrick        stack_entry->frames[0] = 0;
590061da546Spatrick        stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack (
591061da546Spatrick            lldb_info->task,
592061da546Spatrick            stack_record.stack_identifier,
593061da546Spatrick            stack_entry->frames,
594061da546Spatrick            (uint32_t)MAX_FRAMES,
595061da546Spatrick            &stack_entry->num_frames);
596061da546Spatrick        // Terminate the frames with zero if there is room
597061da546Spatrick        if (stack_entry->num_frames < MAX_FRAMES)
598061da546Spatrick            stack_entry->frames[stack_entry->num_frames] = 0;
599061da546Spatrick    }
600061da546Spatrick    ++lldb_info->idx;
601061da546Spatrick};
602061da546Spatrick(kern_return_t)__mach_stack_logging_enumerate_records (lldb_info.task, (uint64_t)0x%x, callback, &lldb_info);
603061da546Spatricklldb_info''' % (options.max_frames, options.max_history, addr)
604061da546Spatrick
605061da546Spatrick    frame = lldb.debugger.GetSelectedTarget().GetProcess(
606061da546Spatrick    ).GetSelectedThread().GetSelectedFrame()
607061da546Spatrick    if history:
608061da546Spatrick        expr = history_expr
609061da546Spatrick    else:
610061da546Spatrick        expr = single_expr
611061da546Spatrick    expr_options = lldb.SBExpressionOptions()
612061da546Spatrick    expr_options.SetIgnoreBreakpoints(True)
613061da546Spatrick    expr_options.SetTimeoutInMicroSeconds(5 * 1000 * 1000)  # 5 second timeout
614061da546Spatrick    expr_options.SetTryAllThreads(True)
615061da546Spatrick    expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
616061da546Spatrick    expr_options.SetPrefix(expr_prefix)
617061da546Spatrick    expr_sbvalue = frame.EvaluateExpression(expr, expr_options)
618061da546Spatrick    if options.verbose:
619061da546Spatrick        print("expression:")
620061da546Spatrick        print(expr)
621061da546Spatrick        print("expression result:")
622061da546Spatrick        print(expr_sbvalue)
623061da546Spatrick    if expr_sbvalue.error.Success():
624061da546Spatrick        if history:
625061da546Spatrick            malloc_stack_history = lldb.value(expr_sbvalue)
626061da546Spatrick            num_stacks = int(malloc_stack_history.idx)
627061da546Spatrick            if num_stacks <= options.max_history:
628061da546Spatrick                i_max = num_stacks
629061da546Spatrick            else:
630061da546Spatrick                i_max = options.max_history
631061da546Spatrick            for i in range(i_max):
632061da546Spatrick                stack_history_entry = malloc_stack_history.entries[i]
633061da546Spatrick                dump_stack_history_entry(
634061da546Spatrick                    options, result, stack_history_entry, i)
635061da546Spatrick            if num_stacks > options.max_history:
636061da546Spatrick                result.AppendMessage(
637061da546Spatrick                    'warning: the max number of stacks (%u) was reached, use the "--max-history=%u" option to see all of the stacks' %
638061da546Spatrick                    (options.max_history, num_stacks))
639061da546Spatrick        else:
640061da546Spatrick            stack_history_entry = lldb.value(expr_sbvalue)
641061da546Spatrick            dump_stack_history_entry(options, result, stack_history_entry, 0)
642061da546Spatrick
643061da546Spatrick    else:
644061da546Spatrick        result.AppendMessage(
645061da546Spatrick            'error: expression failed "%s" => %s' %
646061da546Spatrick            (expr, expr_sbvalue.error))
647061da546Spatrick
648061da546Spatrick
649061da546Spatrickdef display_match_results(
650061da546Spatrick        process,
651061da546Spatrick        result,
652061da546Spatrick        options,
653061da546Spatrick        arg_str_description,
654061da546Spatrick        expr,
655061da546Spatrick        print_no_matches,
656061da546Spatrick        expr_prefix=None):
657061da546Spatrick    frame = lldb.debugger.GetSelectedTarget().GetProcess(
658061da546Spatrick    ).GetSelectedThread().GetSelectedFrame()
659061da546Spatrick    if not frame:
660061da546Spatrick        result.AppendMessage('error: invalid frame')
661061da546Spatrick        return 0
662061da546Spatrick    expr_options = lldb.SBExpressionOptions()
663061da546Spatrick    expr_options.SetIgnoreBreakpoints(True)
664061da546Spatrick    expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues)
665061da546Spatrick    expr_options.SetTimeoutInMicroSeconds(
666061da546Spatrick        30 * 1000 * 1000)  # 30 second timeout
667061da546Spatrick    expr_options.SetTryAllThreads(False)
668061da546Spatrick    expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
669061da546Spatrick    if expr_prefix:
670061da546Spatrick        expr_options.SetPrefix(expr_prefix)
671061da546Spatrick    expr_sbvalue = frame.EvaluateExpression(expr, expr_options)
672061da546Spatrick    if options.verbose:
673061da546Spatrick        print("expression:")
674061da546Spatrick        print(expr)
675061da546Spatrick        print("expression result:")
676061da546Spatrick        print(expr_sbvalue)
677061da546Spatrick    if expr_sbvalue.error.Success():
678061da546Spatrick        match_value = lldb.value(expr_sbvalue)
679061da546Spatrick        i = 0
680061da546Spatrick        match_idx = 0
681061da546Spatrick        while True:
682061da546Spatrick            print_entry = True
683061da546Spatrick            match_entry = match_value[i]
684061da546Spatrick            i += 1
685061da546Spatrick            if i > options.max_matches:
686061da546Spatrick                result.AppendMessage(
687061da546Spatrick                    'warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' %
688061da546Spatrick                    (options.max_matches))
689061da546Spatrick                break
690061da546Spatrick            malloc_addr = match_entry.addr.sbvalue.unsigned
691061da546Spatrick            if malloc_addr == 0:
692061da546Spatrick                break
693061da546Spatrick            malloc_size = int(match_entry.size)
694061da546Spatrick            offset = int(match_entry.offset)
695061da546Spatrick
696061da546Spatrick            if options.offset >= 0 and options.offset != offset:
697061da546Spatrick                print_entry = False
698061da546Spatrick            else:
699061da546Spatrick                match_addr = malloc_addr + offset
700061da546Spatrick                type_flags = int(match_entry.type)
701061da546Spatrick                #result.AppendMessage (hex(malloc_addr + offset))
702061da546Spatrick                if type_flags == 64:
703061da546Spatrick                    search_stack_old = options.search_stack
704061da546Spatrick                    search_segments_old = options.search_segments
705061da546Spatrick                    search_heap_old = options.search_heap
706061da546Spatrick                    search_vm_regions = options.search_vm_regions
707061da546Spatrick                    options.search_stack = True
708061da546Spatrick                    options.search_segments = True
709061da546Spatrick                    options.search_heap = True
710061da546Spatrick                    options.search_vm_regions = False
711061da546Spatrick                    if malloc_info_impl(lldb.debugger, result, options, [
712061da546Spatrick                                        hex(malloc_addr + offset)]):
713061da546Spatrick                        print_entry = False
714061da546Spatrick                    options.search_stack = search_stack_old
715061da546Spatrick                    options.search_segments = search_segments_old
716061da546Spatrick                    options.search_heap = search_heap_old
717061da546Spatrick                    options.search_vm_regions = search_vm_regions
718061da546Spatrick                if print_entry:
719061da546Spatrick                    description = '%#16.16x: %s' % (match_addr, type_flags_to_description(
720061da546Spatrick                        process, type_flags, malloc_addr, malloc_size, offset, match_addr))
721061da546Spatrick                    if options.show_size:
722061da546Spatrick                        description += ' <%5u>' % (malloc_size)
723061da546Spatrick                    if options.show_range:
724061da546Spatrick                        description += ' [%#x - %#x)' % (
725061da546Spatrick                            malloc_addr, malloc_addr + malloc_size)
726061da546Spatrick                    derefed_dynamic_value = None
727061da546Spatrick                    dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(
728061da546Spatrick                        lldb.eDynamicCanRunTarget)
729061da546Spatrick                    if dynamic_value.type.name == 'void *':
730061da546Spatrick                        if options.type == 'pointer' and malloc_size == 4096:
731061da546Spatrick                            error = lldb.SBError()
732061da546Spatrick                            process = expr_sbvalue.GetProcess()
733061da546Spatrick                            target = expr_sbvalue.GetTarget()
734061da546Spatrick                            data = bytearray(
735061da546Spatrick                                process.ReadMemory(
736061da546Spatrick                                    malloc_addr, 16, error))
737061da546Spatrick                            if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
738061da546Spatrick                                ptr_size = target.addr_size
739061da546Spatrick                                thread = process.ReadUnsignedFromMemory(
740061da546Spatrick                                    malloc_addr + 16 + ptr_size, ptr_size, error)
741061da546Spatrick                                #   4 bytes  0xa1a1a1a1
742061da546Spatrick                                #  12 bytes  'AUTORELEASE!'
743061da546Spatrick                                # ptr bytes  autorelease insertion point
744061da546Spatrick                                # ptr bytes  pthread_t
745061da546Spatrick                                # ptr bytes  next colder page
746061da546Spatrick                                # ptr bytes  next hotter page
747061da546Spatrick                                #   4 bytes  this page's depth in the list
748061da546Spatrick                                #   4 bytes  high-water mark
749061da546Spatrick                                description += ' AUTORELEASE! for pthread_t %#x' % (
750061da546Spatrick                                    thread)
751061da546Spatrick                        #     else:
752061da546Spatrick                        #         description += 'malloc(%u)' % (malloc_size)
753061da546Spatrick                        # else:
754061da546Spatrick                        #     description += 'malloc(%u)' % (malloc_size)
755061da546Spatrick                    else:
756061da546Spatrick                        derefed_dynamic_value = dynamic_value.deref
757061da546Spatrick                        if derefed_dynamic_value:
758061da546Spatrick                            derefed_dynamic_type = derefed_dynamic_value.type
759061da546Spatrick                            derefed_dynamic_type_size = derefed_dynamic_type.size
760061da546Spatrick                            derefed_dynamic_type_name = derefed_dynamic_type.name
761061da546Spatrick                            description += ' '
762061da546Spatrick                            description += derefed_dynamic_type_name
763061da546Spatrick                            if offset < derefed_dynamic_type_size:
764061da546Spatrick                                member_list = list()
765061da546Spatrick                                get_member_types_for_offset(
766061da546Spatrick                                    derefed_dynamic_type, offset, member_list)
767061da546Spatrick                                if member_list:
768061da546Spatrick                                    member_path = ''
769061da546Spatrick                                    for member in member_list:
770061da546Spatrick                                        member_name = member.name
771061da546Spatrick                                        if member_name:
772061da546Spatrick                                            if member_path:
773061da546Spatrick                                                member_path += '.'
774061da546Spatrick                                            member_path += member_name
775061da546Spatrick                                    if member_path:
776dda28197Spatrick                                        if options.ivar_regex_exclusions:
777dda28197Spatrick                                            for ivar_regex in options.ivar_regex_exclusions:
778061da546Spatrick                                                if ivar_regex.match(
779061da546Spatrick                                                        member_path):
780061da546Spatrick                                                    print_entry = False
781061da546Spatrick                                        description += '.%s' % (member_path)
782061da546Spatrick                            else:
783061da546Spatrick                                description += '%u bytes after %s' % (
784061da546Spatrick                                    offset - derefed_dynamic_type_size, derefed_dynamic_type_name)
785061da546Spatrick                        else:
786061da546Spatrick                            # strip the "*" from the end of the name since we
787061da546Spatrick                            # were unable to dereference this
788061da546Spatrick                            description += dynamic_value.type.name[0:-1]
789061da546Spatrick            if print_entry:
790061da546Spatrick                match_idx += 1
791061da546Spatrick                result_output = ''
792061da546Spatrick                if description:
793061da546Spatrick                    result_output += description
794061da546Spatrick                    if options.print_type and derefed_dynamic_value:
795061da546Spatrick                        result_output += ' %s' % (derefed_dynamic_value)
796061da546Spatrick                    if options.print_object_description and dynamic_value:
797061da546Spatrick                        desc = dynamic_value.GetObjectDescription()
798061da546Spatrick                        if desc:
799061da546Spatrick                            result_output += '\n%s' % (desc)
800061da546Spatrick                if result_output:
801061da546Spatrick                    result.AppendMessage(result_output)
802061da546Spatrick                if options.memory:
803061da546Spatrick                    cmd_result = lldb.SBCommandReturnObject()
804061da546Spatrick                    if options.format is None:
805061da546Spatrick                        memory_command = "memory read --force 0x%x 0x%x" % (
806061da546Spatrick                            malloc_addr, malloc_addr + malloc_size)
807061da546Spatrick                    else:
808061da546Spatrick                        memory_command = "memory read --force -f %s 0x%x 0x%x" % (
809061da546Spatrick                            options.format, malloc_addr, malloc_addr + malloc_size)
810061da546Spatrick                    if options.verbose:
811061da546Spatrick                        result.AppendMessage(memory_command)
812061da546Spatrick                    lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
813061da546Spatrick                    result.AppendMessage(cmd_result.GetOutput())
814061da546Spatrick                if options.stack_history:
815061da546Spatrick                    dump_stack_history_entries(options, result, malloc_addr, 1)
816061da546Spatrick                elif options.stack:
817061da546Spatrick                    dump_stack_history_entries(options, result, malloc_addr, 0)
818061da546Spatrick        return i
819061da546Spatrick    else:
820061da546Spatrick        result.AppendMessage(str(expr_sbvalue.error))
821061da546Spatrick    return 0
822061da546Spatrick
823061da546Spatrick
824061da546Spatrickdef get_ptr_refs_options():
825061da546Spatrick    usage = "usage: %prog [options] <EXPR> [EXPR ...]"
826061da546Spatrick    description = '''Searches all allocations on the heap for pointer values on
827061da546Spatrickdarwin user space programs. Any matches that were found will dump the malloc
828061da546Spatrickblocks that contain the pointers and might be able to print what kind of
829061da546Spatrickobjects the pointers are contained in using dynamic type information in the
830061da546Spatrickprogram.'''
831061da546Spatrick    parser = optparse.OptionParser(
832061da546Spatrick        description=description,
833061da546Spatrick        prog='ptr_refs',
834061da546Spatrick        usage=usage)
835061da546Spatrick    add_common_options(parser)
836061da546Spatrick    return parser
837061da546Spatrick
838061da546Spatrick
839061da546Spatrickdef find_variable(debugger, command, result, dict):
840061da546Spatrick    usage = "usage: %prog [options] <ADDR> [ADDR ...]"
841061da546Spatrick    description = '''Searches for a local variable in all frames that contains a hex ADDR.'''
842061da546Spatrick    command_args = shlex.split(command)
843061da546Spatrick    parser = optparse.OptionParser(
844061da546Spatrick        description=description,
845061da546Spatrick        prog='find_variable',
846061da546Spatrick        usage=usage)
847061da546Spatrick    parser.add_option(
848061da546Spatrick        '-v',
849061da546Spatrick        '--verbose',
850061da546Spatrick        action='store_true',
851061da546Spatrick        dest='verbose',
852061da546Spatrick        help='display verbose debug info',
853061da546Spatrick        default=False)
854061da546Spatrick    try:
855061da546Spatrick        (options, args) = parser.parse_args(command_args)
856061da546Spatrick    except:
857061da546Spatrick        return
858061da546Spatrick
859061da546Spatrick    process = debugger.GetSelectedTarget().GetProcess()
860061da546Spatrick    if not process:
861061da546Spatrick        result.AppendMessage('error: invalid process')
862061da546Spatrick        return
863061da546Spatrick
864061da546Spatrick    for arg in args:
865061da546Spatrick        var_addr = int(arg, 16)
866061da546Spatrick        print("Finding a variable with address %#x..." % (var_addr), file=result)
867061da546Spatrick        done = False
868061da546Spatrick        for thread in process:
869061da546Spatrick            for frame in thread:
870061da546Spatrick                var = find_variable_containing_address(
871061da546Spatrick                    options.verbose, frame, var_addr)
872061da546Spatrick                if var:
873061da546Spatrick                    print(var)
874061da546Spatrick                    done = True
875061da546Spatrick                    break
876061da546Spatrick            if done:
877061da546Spatrick                break
878061da546Spatrick
879061da546Spatrick
880061da546Spatrickdef ptr_refs(debugger, command, result, dict):
881061da546Spatrick    command_args = shlex.split(command)
882061da546Spatrick    parser = get_ptr_refs_options()
883061da546Spatrick    try:
884061da546Spatrick        (options, args) = parser.parse_args(command_args)
885061da546Spatrick    except:
886061da546Spatrick        return
887061da546Spatrick
888061da546Spatrick    process = debugger.GetSelectedTarget().GetProcess()
889061da546Spatrick    if not process:
890061da546Spatrick        result.AppendMessage('error: invalid process')
891061da546Spatrick        return
892061da546Spatrick    frame = process.GetSelectedThread().GetSelectedFrame()
893061da546Spatrick    if not frame:
894061da546Spatrick        result.AppendMessage('error: invalid frame')
895061da546Spatrick        return
896061da546Spatrick
897061da546Spatrick    options.type = 'pointer'
898061da546Spatrick    if options.format is None:
899061da546Spatrick        options.format = "A"  # 'A' is "address" format
900061da546Spatrick
901061da546Spatrick    if args:
902061da546Spatrick        # When we initialize the expression, we must define any types that
903061da546Spatrick        # we will need when looking at every allocation. We must also define
904061da546Spatrick        # a type named callback_baton_t and make an instance named "baton"
905061da546Spatrick        # and initialize it how ever we want to. The address of "baton" will
906061da546Spatrick        # be passed into our range callback. callback_baton_t must contain
907061da546Spatrick        # a member named "callback" whose type is "range_callback_t". This
908061da546Spatrick        # will be used by our zone callbacks to call the range callback for
909061da546Spatrick        # each malloc range.
910061da546Spatrick        expr_prefix = '''
911061da546Spatrickstruct $malloc_match {
912061da546Spatrick    void *addr;
913061da546Spatrick    uintptr_t size;
914061da546Spatrick    uintptr_t offset;
915061da546Spatrick    uintptr_t type;
916061da546Spatrick};
917061da546Spatrick'''
918061da546Spatrick        user_init_code_format = '''
919061da546Spatrick#define MAX_MATCHES %u
920061da546Spatricktypedef struct callback_baton_t {
921061da546Spatrick    range_callback_t callback;
922061da546Spatrick    unsigned num_matches;
923061da546Spatrick    $malloc_match matches[MAX_MATCHES];
924061da546Spatrick    void *ptr;
925061da546Spatrick} callback_baton_t;
926061da546Spatrickrange_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
927061da546Spatrick    callback_baton_t *lldb_info = (callback_baton_t *)baton;
928061da546Spatrick    typedef void* T;
929061da546Spatrick    const unsigned size = sizeof(T);
930061da546Spatrick    T *array = (T*)ptr_addr;
931061da546Spatrick    for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) {
932061da546Spatrick        if (array[idx] == lldb_info->ptr) {
933061da546Spatrick            if (lldb_info->num_matches < MAX_MATCHES) {
934061da546Spatrick                lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
935061da546Spatrick                lldb_info->matches[lldb_info->num_matches].size = ptr_size;
936061da546Spatrick                lldb_info->matches[lldb_info->num_matches].offset = idx*sizeof(T);
937061da546Spatrick                lldb_info->matches[lldb_info->num_matches].type = type;
938061da546Spatrick                ++lldb_info->num_matches;
939061da546Spatrick            }
940061da546Spatrick        }
941061da546Spatrick    }
942061da546Spatrick};
943061da546Spatrickcallback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
944061da546Spatrick'''
945061da546Spatrick        # We must also define a snippet of code to be run that returns
946061da546Spatrick        # the result of the expression we run.
947061da546Spatrick        # Here we return NULL if our pointer was not found in any malloc blocks,
948061da546Spatrick        # and we return the address of the matches array so we can then access
949061da546Spatrick        # the matching results
950061da546Spatrick        user_return_code = '''if (baton.num_matches < MAX_MATCHES)
951061da546Spatrick    baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
952061da546Spatrickbaton.matches'''
953061da546Spatrick        # Iterate through all of our pointer expressions and display the
954061da546Spatrick        # results
955061da546Spatrick        for ptr_expr in args:
956061da546Spatrick            user_init_code = user_init_code_format % (
957061da546Spatrick                options.max_matches, ptr_expr)
958061da546Spatrick            expr = get_iterate_memory_expr(
959061da546Spatrick                options, process, user_init_code, user_return_code)
960061da546Spatrick            arg_str_description = 'malloc block containing pointer %s' % ptr_expr
961061da546Spatrick            display_match_results(
962061da546Spatrick                process,
963061da546Spatrick                result,
964061da546Spatrick                options,
965061da546Spatrick                arg_str_description,
966061da546Spatrick                expr,
967061da546Spatrick                True,
968061da546Spatrick                expr_prefix)
969061da546Spatrick    else:
970061da546Spatrick        result.AppendMessage('error: no pointer arguments were given')
971061da546Spatrick
972061da546Spatrick
973061da546Spatrickdef get_cstr_refs_options():
974061da546Spatrick    usage = "usage: %prog [options] <CSTR> [CSTR ...]"
975061da546Spatrick    description = '''Searches all allocations on the heap for C string values on
976061da546Spatrickdarwin user space programs. Any matches that were found will dump the malloc
977061da546Spatrickblocks that contain the C strings and might be able to print what kind of
978061da546Spatrickobjects the pointers are contained in using dynamic type information in the
979061da546Spatrickprogram.'''
980061da546Spatrick    parser = optparse.OptionParser(
981061da546Spatrick        description=description,
982061da546Spatrick        prog='cstr_refs',
983061da546Spatrick        usage=usage)
984061da546Spatrick    add_common_options(parser)
985061da546Spatrick    return parser
986061da546Spatrick
987061da546Spatrick
988061da546Spatrickdef cstr_refs(debugger, command, result, dict):
989061da546Spatrick    command_args = shlex.split(command)
990061da546Spatrick    parser = get_cstr_refs_options()
991061da546Spatrick    try:
992061da546Spatrick        (options, args) = parser.parse_args(command_args)
993061da546Spatrick    except:
994061da546Spatrick        return
995061da546Spatrick
996061da546Spatrick    process = debugger.GetSelectedTarget().GetProcess()
997061da546Spatrick    if not process:
998061da546Spatrick        result.AppendMessage('error: invalid process')
999061da546Spatrick        return
1000061da546Spatrick    frame = process.GetSelectedThread().GetSelectedFrame()
1001061da546Spatrick    if not frame:
1002061da546Spatrick        result.AppendMessage('error: invalid frame')
1003061da546Spatrick        return
1004061da546Spatrick
1005061da546Spatrick    options.type = 'cstr'
1006061da546Spatrick    if options.format is None:
1007061da546Spatrick        options.format = "Y"  # 'Y' is "bytes with ASCII" format
1008061da546Spatrick
1009061da546Spatrick    if args:
1010061da546Spatrick        # When we initialize the expression, we must define any types that
1011061da546Spatrick        # we will need when looking at every allocation. We must also define
1012061da546Spatrick        # a type named callback_baton_t and make an instance named "baton"
1013061da546Spatrick        # and initialize it how ever we want to. The address of "baton" will
1014061da546Spatrick        # be passed into our range callback. callback_baton_t must contain
1015061da546Spatrick        # a member named "callback" whose type is "range_callback_t". This
1016061da546Spatrick        # will be used by our zone callbacks to call the range callback for
1017061da546Spatrick        # each malloc range.
1018061da546Spatrick        expr_prefix = '''
1019061da546Spatrickstruct $malloc_match {
1020061da546Spatrick    void *addr;
1021061da546Spatrick    uintptr_t size;
1022061da546Spatrick    uintptr_t offset;
1023061da546Spatrick    uintptr_t type;
1024061da546Spatrick};
1025061da546Spatrick'''
1026061da546Spatrick        user_init_code_format = '''
1027061da546Spatrick#define MAX_MATCHES %u
1028061da546Spatricktypedef struct callback_baton_t {
1029061da546Spatrick    range_callback_t callback;
1030061da546Spatrick    unsigned num_matches;
1031061da546Spatrick    $malloc_match matches[MAX_MATCHES];
1032061da546Spatrick    const char *cstr;
1033061da546Spatrick    unsigned cstr_len;
1034061da546Spatrick} callback_baton_t;
1035061da546Spatrickrange_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
1036061da546Spatrick    callback_baton_t *lldb_info = (callback_baton_t *)baton;
1037061da546Spatrick    if (lldb_info->cstr_len < ptr_size) {
1038061da546Spatrick        const char *begin = (const char *)ptr_addr;
1039061da546Spatrick        const char *end = begin + ptr_size - lldb_info->cstr_len;
1040061da546Spatrick        for (const char *s = begin; s < end; ++s) {
1041061da546Spatrick            if ((int)memcmp(s, lldb_info->cstr, lldb_info->cstr_len) == 0) {
1042061da546Spatrick                if (lldb_info->num_matches < MAX_MATCHES) {
1043061da546Spatrick                    lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
1044061da546Spatrick                    lldb_info->matches[lldb_info->num_matches].size = ptr_size;
1045061da546Spatrick                    lldb_info->matches[lldb_info->num_matches].offset = s - begin;
1046061da546Spatrick                    lldb_info->matches[lldb_info->num_matches].type = type;
1047061da546Spatrick                    ++lldb_info->num_matches;
1048061da546Spatrick                }
1049061da546Spatrick            }
1050061da546Spatrick        }
1051061da546Spatrick    }
1052061da546Spatrick};
1053061da546Spatrickconst char *cstr = "%s";
1054061da546Spatrickcallback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };'''
1055061da546Spatrick        # We must also define a snippet of code to be run that returns
1056061da546Spatrick        # the result of the expression we run.
1057061da546Spatrick        # Here we return NULL if our pointer was not found in any malloc blocks,
1058061da546Spatrick        # and we return the address of the matches array so we can then access
1059061da546Spatrick        # the matching results
1060061da546Spatrick        user_return_code = '''if (baton.num_matches < MAX_MATCHES)
1061061da546Spatrick    baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
1062061da546Spatrickbaton.matches'''
1063061da546Spatrick        # Iterate through all of our pointer expressions and display the
1064061da546Spatrick        # results
1065061da546Spatrick        for cstr in args:
1066061da546Spatrick            user_init_code = user_init_code_format % (
1067061da546Spatrick                options.max_matches, cstr)
1068061da546Spatrick            expr = get_iterate_memory_expr(
1069061da546Spatrick                options, process, user_init_code, user_return_code)
1070061da546Spatrick            arg_str_description = 'malloc block containing "%s"' % cstr
1071061da546Spatrick            display_match_results(
1072061da546Spatrick                process,
1073061da546Spatrick                result,
1074061da546Spatrick                options,
1075061da546Spatrick                arg_str_description,
1076061da546Spatrick                expr,
1077061da546Spatrick                True,
1078061da546Spatrick                expr_prefix)
1079061da546Spatrick    else:
1080061da546Spatrick        result.AppendMessage(
1081061da546Spatrick            'error: command takes one or more C string arguments')
1082061da546Spatrick
1083061da546Spatrick
1084061da546Spatrickdef get_malloc_info_options():
1085061da546Spatrick    usage = "usage: %prog [options] <EXPR> [EXPR ...]"
1086061da546Spatrick    description = '''Searches the heap a malloc block that contains the addresses
1087061da546Spatrickspecified as one or more address expressions. Any matches that were found will
1088061da546Spatrickdump the malloc blocks that match or contain the specified address. The matching
1089061da546Spatrickblocks might be able to show what kind of objects they are using dynamic type
1090061da546Spatrickinformation in the program.'''
1091061da546Spatrick    parser = optparse.OptionParser(
1092061da546Spatrick        description=description,
1093061da546Spatrick        prog='malloc_info',
1094061da546Spatrick        usage=usage)
1095061da546Spatrick    add_common_options(parser)
1096061da546Spatrick    return parser
1097061da546Spatrick
1098061da546Spatrick
1099061da546Spatrickdef malloc_info(debugger, command, result, dict):
1100061da546Spatrick    command_args = shlex.split(command)
1101061da546Spatrick    parser = get_malloc_info_options()
1102061da546Spatrick    try:
1103061da546Spatrick        (options, args) = parser.parse_args(command_args)
1104061da546Spatrick    except:
1105061da546Spatrick        return
1106061da546Spatrick    malloc_info_impl(debugger, result, options, args)
1107061da546Spatrick
1108061da546Spatrick
1109061da546Spatrickdef malloc_info_impl(debugger, result, options, args):
1110061da546Spatrick    # We are specifically looking for something on the heap only
1111061da546Spatrick    options.type = 'malloc_info'
1112061da546Spatrick
1113061da546Spatrick    process = debugger.GetSelectedTarget().GetProcess()
1114061da546Spatrick    if not process:
1115061da546Spatrick        result.AppendMessage('error: invalid process')
1116061da546Spatrick        return
1117061da546Spatrick    frame = process.GetSelectedThread().GetSelectedFrame()
1118061da546Spatrick    if not frame:
1119061da546Spatrick        result.AppendMessage('error: invalid frame')
1120061da546Spatrick        return
1121061da546Spatrick    expr_prefix = '''
1122061da546Spatrickstruct $malloc_match {
1123061da546Spatrick    void *addr;
1124061da546Spatrick    uintptr_t size;
1125061da546Spatrick    uintptr_t offset;
1126061da546Spatrick    uintptr_t type;
1127061da546Spatrick};
1128061da546Spatrick'''
1129061da546Spatrick
1130061da546Spatrick    user_init_code_format = '''
1131061da546Spatricktypedef struct callback_baton_t {
1132061da546Spatrick    range_callback_t callback;
1133061da546Spatrick    unsigned num_matches;
1134061da546Spatrick    $malloc_match matches[2]; // Two items so they can be NULL terminated
1135061da546Spatrick    void *ptr;
1136061da546Spatrick} callback_baton_t;
1137061da546Spatrickrange_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
1138061da546Spatrick    callback_baton_t *lldb_info = (callback_baton_t *)baton;
1139061da546Spatrick    if (lldb_info->num_matches == 0) {
1140061da546Spatrick        uint8_t *p = (uint8_t *)lldb_info->ptr;
1141061da546Spatrick        uint8_t *lo = (uint8_t *)ptr_addr;
1142061da546Spatrick        uint8_t *hi = lo + ptr_size;
1143061da546Spatrick        if (lo <= p && p < hi) {
1144061da546Spatrick            lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
1145061da546Spatrick            lldb_info->matches[lldb_info->num_matches].size = ptr_size;
1146061da546Spatrick            lldb_info->matches[lldb_info->num_matches].offset = p - lo;
1147061da546Spatrick            lldb_info->matches[lldb_info->num_matches].type = type;
1148061da546Spatrick            lldb_info->num_matches = 1;
1149061da546Spatrick        }
1150061da546Spatrick    }
1151061da546Spatrick};
1152061da546Spatrickcallback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
1153061da546Spatrickbaton.matches[0].addr = 0;
1154061da546Spatrickbaton.matches[1].addr = 0;'''
1155061da546Spatrick    if args:
1156061da546Spatrick        total_matches = 0
1157061da546Spatrick        for ptr_expr in args:
1158061da546Spatrick            user_init_code = user_init_code_format % (ptr_expr)
1159061da546Spatrick            expr = get_iterate_memory_expr(
1160061da546Spatrick                options, process, user_init_code, 'baton.matches')
1161061da546Spatrick            arg_str_description = 'malloc block that contains %s' % ptr_expr
1162061da546Spatrick            total_matches += display_match_results(
1163061da546Spatrick                process, result, options, arg_str_description, expr, True, expr_prefix)
1164061da546Spatrick        return total_matches
1165061da546Spatrick    else:
1166061da546Spatrick        result.AppendMessage(
1167061da546Spatrick            'error: command takes one or more pointer expressions')
1168061da546Spatrick        return 0
1169061da546Spatrick
1170061da546Spatrick
1171061da546Spatrickdef get_thread_stack_ranges_struct(process):
1172061da546Spatrick    '''Create code that defines a structure that represents threads stack bounds
1173061da546Spatrick    for all  threads. It returns a static sized array initialized with all of
1174061da546Spatrick    the tid, base, size structs for all the threads.'''
1175061da546Spatrick    stack_dicts = list()
1176061da546Spatrick    if process:
1177061da546Spatrick        i = 0
1178061da546Spatrick        for thread in process:
1179061da546Spatrick            min_sp = thread.frame[0].sp
1180061da546Spatrick            max_sp = min_sp
1181061da546Spatrick            for frame in thread.frames:
1182061da546Spatrick                sp = frame.sp
1183061da546Spatrick                if sp < min_sp:
1184061da546Spatrick                    min_sp = sp
1185061da546Spatrick                if sp > max_sp:
1186061da546Spatrick                    max_sp = sp
1187061da546Spatrick            if min_sp < max_sp:
1188061da546Spatrick                stack_dicts.append({'tid': thread.GetThreadID(
1189061da546Spatrick                ), 'base': min_sp, 'size': max_sp - min_sp, 'index': i})
1190061da546Spatrick                i += 1
1191061da546Spatrick    stack_dicts_len = len(stack_dicts)
1192061da546Spatrick    if stack_dicts_len > 0:
1193061da546Spatrick        result = '''
1194061da546Spatrick#define NUM_STACKS %u
1195061da546Spatrick#define STACK_RED_ZONE_SIZE %u
1196061da546Spatricktypedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
1197061da546Spatrickthread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize())
1198061da546Spatrick        for stack_dict in stack_dicts:
1199061da546Spatrick            result += '''
1200061da546Spatrickstacks[%(index)u].tid  = 0x%(tid)x;
1201061da546Spatrickstacks[%(index)u].base = 0x%(base)x;
1202061da546Spatrickstacks[%(index)u].size = 0x%(size)x;''' % stack_dict
1203061da546Spatrick        return result
1204061da546Spatrick    else:
1205061da546Spatrick        return ''
1206061da546Spatrick
1207061da546Spatrick
1208061da546Spatrickdef get_sections_ranges_struct(process):
1209061da546Spatrick    '''Create code that defines a structure that represents all segments that
1210061da546Spatrick    can contain data for all images in "target". It returns a static sized
1211061da546Spatrick    array initialized with all of base, size structs for all the threads.'''
1212061da546Spatrick    target = process.target
1213061da546Spatrick    segment_dicts = list()
1214061da546Spatrick    for (module_idx, module) in enumerate(target.modules):
1215061da546Spatrick        for sect_idx in range(module.GetNumSections()):
1216061da546Spatrick            section = module.GetSectionAtIndex(sect_idx)
1217061da546Spatrick            if not section:
1218061da546Spatrick                break
1219061da546Spatrick            name = section.name
1220061da546Spatrick            if name != '__TEXT' and name != '__LINKEDIT' and name != '__PAGEZERO':
1221061da546Spatrick                base = section.GetLoadAddress(target)
1222061da546Spatrick                size = section.GetByteSize()
1223061da546Spatrick                if base != lldb.LLDB_INVALID_ADDRESS and size > 0:
1224061da546Spatrick                    segment_dicts.append({'base': base, 'size': size})
1225061da546Spatrick    segment_dicts_len = len(segment_dicts)
1226061da546Spatrick    if segment_dicts_len > 0:
1227061da546Spatrick        result = '''
1228061da546Spatrick#define NUM_SEGMENTS %u
1229061da546Spatricktypedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t;
1230061da546Spatricksegment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,)
1231061da546Spatrick        for (idx, segment_dict) in enumerate(segment_dicts):
1232061da546Spatrick            segment_dict['index'] = idx
1233061da546Spatrick            result += '''
1234061da546Spatricksegments[%(index)u].base = 0x%(base)x;
1235061da546Spatricksegments[%(index)u].size = 0x%(size)x;''' % segment_dict
1236061da546Spatrick        return result
1237061da546Spatrick    else:
1238061da546Spatrick        return ''
1239061da546Spatrick
1240061da546Spatrick
1241061da546Spatrickdef section_ptr_refs(debugger, command, result, dict):
1242061da546Spatrick    command_args = shlex.split(command)
1243061da546Spatrick    usage = "usage: %prog [options] <EXPR> [EXPR ...]"
1244061da546Spatrick    description = '''Searches section contents for pointer values in darwin user space programs.'''
1245061da546Spatrick    parser = optparse.OptionParser(
1246061da546Spatrick        description=description,
1247061da546Spatrick        prog='section_ptr_refs',
1248061da546Spatrick        usage=usage)
1249061da546Spatrick    add_common_options(parser)
1250061da546Spatrick    parser.add_option(
1251061da546Spatrick        '--section',
1252061da546Spatrick        action='append',
1253061da546Spatrick        type='string',
1254061da546Spatrick        dest='section_names',
1255061da546Spatrick        help='section name to search',
1256061da546Spatrick        default=list())
1257061da546Spatrick    try:
1258061da546Spatrick        (options, args) = parser.parse_args(command_args)
1259061da546Spatrick    except:
1260061da546Spatrick        return
1261061da546Spatrick
1262061da546Spatrick    options.type = 'pointer'
1263061da546Spatrick
1264061da546Spatrick    sections = list()
1265061da546Spatrick    section_modules = list()
1266061da546Spatrick    if not options.section_names:
1267061da546Spatrick        result.AppendMessage(
1268061da546Spatrick            'error: at least one section must be specified with the --section option')
1269061da546Spatrick        return
1270061da546Spatrick
1271061da546Spatrick    target = debugger.GetSelectedTarget()
1272061da546Spatrick    for module in target.modules:
1273061da546Spatrick        for section_name in options.section_names:
1274061da546Spatrick            section = module.section[section_name]
1275061da546Spatrick            if section:
1276061da546Spatrick                sections.append(section)
1277061da546Spatrick                section_modules.append(module)
1278061da546Spatrick    if sections:
1279061da546Spatrick        dylid_load_err = load_dylib()
1280061da546Spatrick        if dylid_load_err:
1281061da546Spatrick            result.AppendMessage(dylid_load_err)
1282061da546Spatrick            return
1283061da546Spatrick        frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
1284061da546Spatrick        for expr_str in args:
1285061da546Spatrick            for (idx, section) in enumerate(sections):
1286061da546Spatrick                expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (
1287061da546Spatrick                    section.addr.load_addr, section.size, expr_str)
1288061da546Spatrick                arg_str_description = 'section %s.%s containing "%s"' % (
1289061da546Spatrick                    section_modules[idx].file.fullpath, section.name, expr_str)
1290061da546Spatrick                num_matches = display_match_results(
1291061da546Spatrick                    target.GetProcess(), result, options, arg_str_description, expr, False)
1292061da546Spatrick                if num_matches:
1293061da546Spatrick                    if num_matches < options.max_matches:
1294061da546Spatrick                        options.max_matches = options.max_matches - num_matches
1295061da546Spatrick                    else:
1296061da546Spatrick                        options.max_matches = 0
1297061da546Spatrick                if options.max_matches == 0:
1298061da546Spatrick                    return
1299061da546Spatrick    else:
1300061da546Spatrick        result.AppendMessage(
1301061da546Spatrick            'error: no sections were found that match any of %s' %
1302061da546Spatrick            (', '.join(
1303061da546Spatrick                options.section_names)))
1304061da546Spatrick
1305061da546Spatrick
1306061da546Spatrickdef get_objc_refs_options():
1307061da546Spatrick    usage = "usage: %prog [options] <CLASS> [CLASS ...]"
1308061da546Spatrick    description = '''Searches all allocations on the heap for instances of
1309061da546Spatrickobjective C classes, or any classes that inherit from the specified classes
1310061da546Spatrickin darwin user space programs. Any matches that were found will dump the malloc
1311061da546Spatrickblocks that contain the C strings and might be able to print what kind of
1312061da546Spatrickobjects the pointers are contained in using dynamic type information in the
1313061da546Spatrickprogram.'''
1314061da546Spatrick    parser = optparse.OptionParser(
1315061da546Spatrick        description=description,
1316061da546Spatrick        prog='objc_refs',
1317061da546Spatrick        usage=usage)
1318061da546Spatrick    add_common_options(parser)
1319061da546Spatrick    return parser
1320061da546Spatrick
1321061da546Spatrick
1322061da546Spatrickdef objc_refs(debugger, command, result, dict):
1323061da546Spatrick    command_args = shlex.split(command)
1324061da546Spatrick    parser = get_objc_refs_options()
1325061da546Spatrick    try:
1326061da546Spatrick        (options, args) = parser.parse_args(command_args)
1327061da546Spatrick    except:
1328061da546Spatrick        return
1329061da546Spatrick
1330061da546Spatrick    process = debugger.GetSelectedTarget().GetProcess()
1331061da546Spatrick    if not process:
1332061da546Spatrick        result.AppendMessage('error: invalid process')
1333061da546Spatrick        return
1334061da546Spatrick    frame = process.GetSelectedThread().GetSelectedFrame()
1335061da546Spatrick    if not frame:
1336061da546Spatrick        result.AppendMessage('error: invalid frame')
1337061da546Spatrick        return
1338061da546Spatrick
1339061da546Spatrick    options.type = 'isa'
1340061da546Spatrick    if options.format is None:
1341061da546Spatrick        options.format = "A"  # 'A' is "address" format
1342061da546Spatrick
1343061da546Spatrick    expr_options = lldb.SBExpressionOptions()
1344061da546Spatrick    expr_options.SetIgnoreBreakpoints(True)
1345061da546Spatrick    expr_options.SetTimeoutInMicroSeconds(
1346061da546Spatrick        3 * 1000 * 1000)  # 3 second infinite timeout
1347061da546Spatrick    expr_options.SetTryAllThreads(True)
1348061da546Spatrick    expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
1349061da546Spatrick    num_objc_classes_value = frame.EvaluateExpression(
1350061da546Spatrick        "(int)objc_getClassList((void *)0, (int)0)", expr_options)
1351061da546Spatrick    if not num_objc_classes_value.error.Success():
1352061da546Spatrick        result.AppendMessage('error: %s' %
1353061da546Spatrick                             num_objc_classes_value.error.GetCString())
1354061da546Spatrick        return
1355061da546Spatrick
1356061da546Spatrick    num_objc_classes = num_objc_classes_value.GetValueAsUnsigned()
1357061da546Spatrick    if num_objc_classes == 0:
1358061da546Spatrick        result.AppendMessage('error: no objective C classes in program')
1359061da546Spatrick        return
1360061da546Spatrick
1361061da546Spatrick    if args:
1362061da546Spatrick        # When we initialize the expression, we must define any types that
1363061da546Spatrick        # we will need when looking at every allocation. We must also define
1364061da546Spatrick        # a type named callback_baton_t and make an instance named "baton"
1365061da546Spatrick        # and initialize it how ever we want to. The address of "baton" will
1366061da546Spatrick        # be passed into our range callback. callback_baton_t must contain
1367061da546Spatrick        # a member named "callback" whose type is "range_callback_t". This
1368061da546Spatrick        # will be used by our zone callbacks to call the range callback for
1369061da546Spatrick        # each malloc range.
1370061da546Spatrick        expr_prefix = '''
1371061da546Spatrickstruct $malloc_match {
1372061da546Spatrick    void *addr;
1373061da546Spatrick    uintptr_t size;
1374061da546Spatrick    uintptr_t offset;
1375061da546Spatrick    uintptr_t type;
1376061da546Spatrick};
1377061da546Spatrick'''
1378061da546Spatrick
1379061da546Spatrick        user_init_code_format = '''
1380061da546Spatrick#define MAX_MATCHES %u
1381061da546Spatricktypedef int (*compare_callback_t)(const void *a, const void *b);
1382061da546Spatricktypedef struct callback_baton_t {
1383061da546Spatrick    range_callback_t callback;
1384061da546Spatrick    compare_callback_t compare_callback;
1385061da546Spatrick    unsigned num_matches;
1386061da546Spatrick    $malloc_match matches[MAX_MATCHES];
1387061da546Spatrick    void *isa;
1388061da546Spatrick    Class classes[%u];
1389061da546Spatrick} callback_baton_t;
1390061da546Spatrickcompare_callback_t compare_callback = [](const void *a, const void *b) -> int {
1391061da546Spatrick     Class a_ptr = *(Class *)a;
1392061da546Spatrick     Class b_ptr = *(Class *)b;
1393061da546Spatrick     if (a_ptr < b_ptr) return -1;
1394061da546Spatrick     if (a_ptr > b_ptr) return +1;
1395061da546Spatrick     return 0;
1396061da546Spatrick};
1397061da546Spatricktypedef Class (*class_getSuperclass_type)(void *isa);
1398061da546Spatrickrange_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
1399061da546Spatrick    class_getSuperclass_type class_getSuperclass_impl = (class_getSuperclass_type)class_getSuperclass;
1400061da546Spatrick    callback_baton_t *lldb_info = (callback_baton_t *)baton;
1401061da546Spatrick    if (sizeof(Class) <= ptr_size) {
1402061da546Spatrick        Class *curr_class_ptr = (Class *)ptr_addr;
1403061da546Spatrick        Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr,
1404061da546Spatrick                                                      (const void *)lldb_info->classes,
1405061da546Spatrick                                                      sizeof(lldb_info->classes)/sizeof(Class),
1406061da546Spatrick                                                      sizeof(Class),
1407061da546Spatrick                                                      lldb_info->compare_callback);
1408061da546Spatrick        if (matching_class_ptr) {
1409061da546Spatrick            bool match = false;
1410061da546Spatrick            if (lldb_info->isa) {
1411061da546Spatrick                Class isa = *curr_class_ptr;
1412061da546Spatrick                if (lldb_info->isa == isa)
1413061da546Spatrick                    match = true;
1414061da546Spatrick                else { // if (lldb_info->objc.match_superclasses) {
1415061da546Spatrick                    Class super = class_getSuperclass_impl(isa);
1416061da546Spatrick                    while (super) {
1417061da546Spatrick                        if (super == lldb_info->isa) {
1418061da546Spatrick                            match = true;
1419061da546Spatrick                            break;
1420061da546Spatrick                        }
1421061da546Spatrick                        super = class_getSuperclass_impl(super);
1422061da546Spatrick                    }
1423061da546Spatrick                }
1424061da546Spatrick            }
1425061da546Spatrick            else
1426061da546Spatrick                match = true;
1427061da546Spatrick            if (match) {
1428061da546Spatrick                if (lldb_info->num_matches < MAX_MATCHES) {
1429061da546Spatrick                    lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
1430061da546Spatrick                    lldb_info->matches[lldb_info->num_matches].size = ptr_size;
1431061da546Spatrick                    lldb_info->matches[lldb_info->num_matches].offset = 0;
1432061da546Spatrick                    lldb_info->matches[lldb_info->num_matches].type = type;
1433061da546Spatrick                    ++lldb_info->num_matches;
1434061da546Spatrick                }
1435061da546Spatrick            }
1436061da546Spatrick        }
1437061da546Spatrick    }
1438061da546Spatrick};
1439061da546Spatrickcallback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
1440061da546Spatrickint nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class));
1441061da546Spatrick(void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);'''
1442061da546Spatrick        # We must also define a snippet of code to be run that returns
1443061da546Spatrick        # the result of the expression we run.
1444061da546Spatrick        # Here we return NULL if our pointer was not found in any malloc blocks,
1445061da546Spatrick        # and we return the address of the matches array so we can then access
1446061da546Spatrick        # the matching results
1447061da546Spatrick        user_return_code = '''if (baton.num_matches < MAX_MATCHES)
1448061da546Spatrick    baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
1449061da546Spatrick        baton.matches'''
1450061da546Spatrick        # Iterate through all of our ObjC class name arguments
1451061da546Spatrick        for class_name in args:
1452061da546Spatrick            addr_expr_str = "(void *)[%s class]" % class_name
1453061da546Spatrick            expr_options = lldb.SBExpressionOptions()
1454061da546Spatrick            expr_options.SetIgnoreBreakpoints(True)
1455061da546Spatrick            expr_options.SetTimeoutInMicroSeconds(
1456061da546Spatrick                1 * 1000 * 1000)  # 1 second timeout
1457061da546Spatrick            expr_options.SetTryAllThreads(True)
1458061da546Spatrick            expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
1459061da546Spatrick            expr_sbvalue = frame.EvaluateExpression(
1460061da546Spatrick                addr_expr_str, expr_options)
1461061da546Spatrick            if expr_sbvalue.error.Success():
1462061da546Spatrick                isa = expr_sbvalue.unsigned
1463061da546Spatrick                if isa:
1464061da546Spatrick                    options.type = 'isa'
1465061da546Spatrick                    result.AppendMessage(
1466061da546Spatrick                        'Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' %
1467061da546Spatrick                        (class_name, isa))
1468061da546Spatrick                    user_init_code = user_init_code_format % (
1469061da546Spatrick                        options.max_matches, num_objc_classes, isa)
1470061da546Spatrick                    expr = get_iterate_memory_expr(
1471061da546Spatrick                        options, process, user_init_code, user_return_code)
1472061da546Spatrick                    arg_str_description = 'objective C classes with isa 0x%x' % isa
1473061da546Spatrick                    display_match_results(
1474061da546Spatrick                        process,
1475061da546Spatrick                        result,
1476061da546Spatrick                        options,
1477061da546Spatrick                        arg_str_description,
1478061da546Spatrick                        expr,
1479061da546Spatrick                        True,
1480061da546Spatrick                        expr_prefix)
1481061da546Spatrick                else:
1482061da546Spatrick                    result.AppendMessage(
1483061da546Spatrick                        'error: Can\'t find isa for an ObjC class named "%s"' %
1484061da546Spatrick                        (class_name))
1485061da546Spatrick            else:
1486061da546Spatrick                result.AppendMessage(
1487061da546Spatrick                    'error: expression error for "%s": %s' %
1488061da546Spatrick                    (addr_expr_str, expr_sbvalue.error))
1489061da546Spatrick    else:
1490061da546Spatrick        result.AppendMessage(
1491061da546Spatrick            'error: command takes one or more C string arguments')
1492061da546Spatrick
1493061da546Spatrickif __name__ == '__main__':
1494061da546Spatrick    lldb.debugger = lldb.SBDebugger.Create()
1495061da546Spatrick
1496*f6aab3d8Srobertdef __lldb_init_module(debugger, internal_dict):
1497061da546Spatrick    # Make the options so we can generate the help text for the new LLDB
1498061da546Spatrick    # command line command prior to registering it with LLDB below. This way
1499061da546Spatrick    # if clients in LLDB type "help malloc_info", they will see the exact same
1500061da546Spatrick    # output as typing "malloc_info --help".
1501061da546Spatrick    ptr_refs.__doc__ = get_ptr_refs_options().format_help()
1502061da546Spatrick    cstr_refs.__doc__ = get_cstr_refs_options().format_help()
1503061da546Spatrick    malloc_info.__doc__ = get_malloc_info_options().format_help()
1504061da546Spatrick    objc_refs.__doc__ = get_objc_refs_options().format_help()
1505*f6aab3d8Srobert    debugger.HandleCommand(
1506*f6aab3d8Srobert        'command script add -o -f %s.ptr_refs ptr_refs' %
1507061da546Spatrick        __name__)
1508*f6aab3d8Srobert    debugger.HandleCommand(
1509*f6aab3d8Srobert        'command script add -o -f %s.cstr_refs cstr_refs' %
1510061da546Spatrick        __name__)
1511*f6aab3d8Srobert    debugger.HandleCommand(
1512*f6aab3d8Srobert        'command script add -o -f %s.malloc_info malloc_info' %
1513061da546Spatrick        __name__)
1514*f6aab3d8Srobert    debugger.HandleCommand(
1515*f6aab3d8Srobert        'command script add -o -f %s.find_variable find_variable' %
1516061da546Spatrick        __name__)
1517*f6aab3d8Srobert    # debugger.HandleCommand('command script add -o -f %s.section_ptr_refs section_ptr_refs' % package_name)
1518*f6aab3d8Srobert    debugger.HandleCommand(
1519*f6aab3d8Srobert        'command script add -o -f %s.objc_refs objc_refs' %
1520061da546Spatrick        __name__)
1521061da546Spatrick    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