[lldb/examples] Add missing declaration in heap.py
[lldb.git] / lldb / examples / darwin / heap_find / heap.py
1 #!/usr/bin/env python
2
3 #----------------------------------------------------------------------
4 # This module is designed to live inside the "lldb" python package
5 # in the "lldb.macosx" package. To use this in the embedded python
6 # interpreter using "lldb" just import it:
7 #
8 #   (lldb) script import lldb.macosx.heap
9 #----------------------------------------------------------------------
10
11 from __future__ import print_function
12 import lldb
13 import optparse
14 import os
15 import os.path
16 import re
17 import shlex
18 import string
19 import sys
20 import tempfile
21 import lldb.utils.symbolication
22
23 g_libheap_dylib_dir = None
24 g_libheap_dylib_dict = dict()
25
26
27 def get_iterate_memory_expr(
28         options,
29         process,
30         user_init_code,
31         user_return_code):
32     expr = '''
33 typedef unsigned natural_t;
34 typedef uintptr_t vm_size_t;
35 typedef uintptr_t vm_address_t;
36 typedef natural_t task_t;
37 typedef int kern_return_t;
38 #define KERN_SUCCESS 0
39 typedef void (*range_callback_t)(task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size);
40 '''
41     if options.search_vm_regions:
42         expr += '''
43 typedef int vm_prot_t;
44 typedef unsigned int vm_inherit_t;
45 typedef unsigned long long      memory_object_offset_t;
46 typedef unsigned int boolean_t;
47 typedef int vm_behavior_t;
48 typedef uint32_t vm32_object_id_t;
49 typedef natural_t mach_msg_type_number_t;
50 typedef uint64_t mach_vm_address_t;
51 typedef uint64_t mach_vm_offset_t;
52 typedef uint64_t mach_vm_size_t;
53 typedef uint64_t vm_map_offset_t;
54 typedef uint64_t vm_map_address_t;
55 typedef 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)
60 typedef 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 += '''
79 task_t task = (task_t)mach_task_self();
80 mach_vm_address_t vm_region_base_addr;
81 mach_vm_size_t vm_region_size;
82 natural_t vm_region_depth;
83 vm_region_submap_short_info_data_64_t vm_region_info;
84 kern_return_t err;
85 for (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
119 typedef struct vm_range_t {
120     vm_address_t        address;
121     vm_size_t           size;
122 } vm_range_t;
123 typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory);
124 typedef void (*vm_range_recorder_t)(task_t task, void *baton, unsigned type, vm_range_t *range, unsigned size);
125 typedef 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;
128 typedef struct malloc_zone_t {
129     void *reserved1[12];
130     struct malloc_introspection_t       *introspect;
131 } malloc_zone_t;
132 kern_return_t malloc_get_all_zones(task_t task, memory_reader_t reader, vm_address_t **addresses, unsigned *count);
133 memory_reader_t task_peek = [](task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) -> kern_return_t {
134     *local_memory = (void*) remote_address;
135     return KERN_SUCCESS;
136 };
137 vm_address_t *zones = 0;
138 unsigned int num_zones = 0;task_t task = 0;
139 kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones);
140 if (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
166 for (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
178 for (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
188 def 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
227 def 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
235 def 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
363 def 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
385 def 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
407 def 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
428 def 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
484 def 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
521 def dump_stack_history_entries(options, result, addr, history):
522     # malloc_stack_entry *get_stack_history_for_address (const void * addr)
523     expr_prefix = '''
524 typedef int kern_return_t;
525 typedef 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
536 typedef unsigned task_t;
537 $malloc_stack_entry stack;
538 stack.address = 0x%x;
539 stack.type_flags = 2;
540 stack.num_frames = 0;
541 stack.frames[0] = 0;
542 uint32_t max_stack_frames = MAX_FRAMES;
543 stack.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);
549 if (stack.num_frames < MAX_FRAMES)
550     stack.frames[stack.num_frames] = 0;
551 else
552     stack.frames[MAX_FRAMES-1] = 0;
553 stack''' % (options.max_frames, addr)
554
555     history_expr = '''
556 typedef int kern_return_t;
557 typedef unsigned task_t;
558 #define MAX_FRAMES %u
559 #define MAX_HISTORY %u
560 typedef 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;
566 typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
567 typedef 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;
575 typedef 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 };
581 uint32_t max_stack_frames = MAX_FRAMES;
582 enumerate_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);
604 lldb_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
650 def 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
825 def get_ptr_refs_options():
826     usage = "usage: %prog [options] <EXPR> [EXPR ...]"
827     description = '''Searches all allocations on the heap for pointer values on
828 darwin user space programs. Any matches that were found will dump the malloc
829 blocks that contain the pointers and might be able to print what kind of
830 objects the pointers are contained in using dynamic type information in the
831 program.'''
832     parser = optparse.OptionParser(
833         description=description,
834         prog='ptr_refs',
835         usage=usage)
836     add_common_options(parser)
837     return parser
838
839
840 def 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
881 def 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 = '''
912 struct $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
921 typedef 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;
927 range_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 };
944 callback_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
953 baton.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
974 def get_cstr_refs_options():
975     usage = "usage: %prog [options] <CSTR> [CSTR ...]"
976     description = '''Searches all allocations on the heap for C string values on
977 darwin user space programs. Any matches that were found will dump the malloc
978 blocks that contain the C strings and might be able to print what kind of
979 objects the pointers are contained in using dynamic type information in the
980 program.'''
981     parser = optparse.OptionParser(
982         description=description,
983         prog='cstr_refs',
984         usage=usage)
985     add_common_options(parser)
986     return parser
987
988
989 def 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 = '''
1020 struct $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
1029 typedef 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;
1036 range_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 };
1054 const char *cstr = "%s";
1055 callback_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
1063 baton.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
1085 def get_malloc_info_options():
1086     usage = "usage: %prog [options] <EXPR> [EXPR ...]"
1087     description = '''Searches the heap a malloc block that contains the addresses
1088 specified as one or more address expressions. Any matches that were found will
1089 dump the malloc blocks that match or contain the specified address. The matching
1090 blocks might be able to show what kind of objects they are using dynamic type
1091 information 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
1100 def 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
1110 def 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 = '''
1123 struct $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 = '''
1132 typedef 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;
1138 range_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 };
1153 callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
1154 baton.matches[0].addr = 0;
1155 baton.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
1172 def 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
1197 typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
1198 thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize())
1199         for stack_dict in stack_dicts:
1200             result += '''
1201 stacks[%(index)u].tid  = 0x%(tid)x;
1202 stacks[%(index)u].base = 0x%(base)x;
1203 stacks[%(index)u].size = 0x%(size)x;''' % stack_dict
1204         return result
1205     else:
1206         return ''
1207
1208
1209 def 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
1230 typedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t;
1231 segment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,)
1232         for (idx, segment_dict) in enumerate(segment_dicts):
1233             segment_dict['index'] = idx
1234             result += '''
1235 segments[%(index)u].base = 0x%(base)x;
1236 segments[%(index)u].size = 0x%(size)x;''' % segment_dict
1237         return result
1238     else:
1239         return ''
1240
1241
1242 def 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
1307 def get_objc_refs_options():
1308     usage = "usage: %prog [options] <CLASS> [CLASS ...]"
1309     description = '''Searches all allocations on the heap for instances of
1310 objective C classes, or any classes that inherit from the specified classes
1311 in darwin user space programs. Any matches that were found will dump the malloc
1312 blocks that contain the C strings and might be able to print what kind of
1313 objects the pointers are contained in using dynamic type information in the
1314 program.'''
1315     parser = optparse.OptionParser(
1316         description=description,
1317         prog='objc_refs',
1318         usage=usage)
1319     add_common_options(parser)
1320     return parser
1321
1322
1323 def 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 = '''
1372 struct $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
1382 typedef int (*compare_callback_t)(const void *a, const void *b);
1383 typedef 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;
1391 compare_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 };
1398 typedef Class (*class_getSuperclass_type)(void *isa);
1399 range_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 };
1440 callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
1441 int 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
1494 if __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".
1501 ptr_refs.__doc__ = get_ptr_refs_options().format_help()
1502 cstr_refs.__doc__ = get_cstr_refs_options().format_help()
1503 malloc_info.__doc__ = get_malloc_info_options().format_help()
1504 objc_refs.__doc__ = get_objc_refs_options().format_help()
1505 lldb.debugger.HandleCommand(
1506     'command script add -f %s.ptr_refs ptr_refs' %
1507     __name__)
1508 lldb.debugger.HandleCommand(
1509     'command script add -f %s.cstr_refs cstr_refs' %
1510     __name__)
1511 lldb.debugger.HandleCommand(
1512     'command script add -f %s.malloc_info malloc_info' %
1513     __name__)
1514 lldb.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)
1520 lldb.debugger.HandleCommand(
1521     'command script add -f %s.objc_refs objc_refs' %
1522     __name__)
1523 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.')