summaryrefslogtreecommitdiff
path: root/examples/darwin/heap_find/heap.py
diff options
context:
space:
mode:
Diffstat (limited to 'examples/darwin/heap_find/heap.py')
-rw-r--r--examples/darwin/heap_find/heap.py1244
1 files changed, 1244 insertions, 0 deletions
diff --git a/examples/darwin/heap_find/heap.py b/examples/darwin/heap_find/heap.py
new file mode 100644
index 0000000000000..fb3394323890e
--- /dev/null
+++ b/examples/darwin/heap_find/heap.py
@@ -0,0 +1,1244 @@
+#!/usr/bin/python
+
+#----------------------------------------------------------------------
+# This module is designed to live inside the "lldb" python package
+# in the "lldb.macosx" package. To use this in the embedded python
+# interpreter using "lldb" just import it:
+#
+# (lldb) script import lldb.macosx.heap
+#----------------------------------------------------------------------
+
+import lldb
+import commands
+import optparse
+import os
+import os.path
+import re
+import shlex
+import string
+import sys
+import tempfile
+import lldb.utils.symbolication
+
+g_libheap_dylib_dir = None
+g_libheap_dylib_dict = dict()
+
+def get_iterate_memory_expr(options, process, user_init_code, user_return_code):
+ expr = '''
+typedef unsigned natural_t;
+typedef uintptr_t vm_size_t;
+typedef uintptr_t vm_address_t;
+typedef natural_t task_t;
+typedef int kern_return_t;
+#define KERN_SUCCESS 0
+typedef void (*range_callback_t)(task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size);
+''';
+ if options.search_vm_regions:
+ expr += '''
+typedef int vm_prot_t;
+typedef unsigned int vm_inherit_t;
+typedef unsigned long long memory_object_offset_t;
+typedef unsigned int boolean_t;
+typedef int vm_behavior_t;
+typedef uint32_t vm32_object_id_t;
+typedef natural_t mach_msg_type_number_t;
+typedef uint64_t mach_vm_address_t;
+typedef uint64_t mach_vm_offset_t;
+typedef uint64_t mach_vm_size_t;
+typedef uint64_t vm_map_offset_t;
+typedef uint64_t vm_map_address_t;
+typedef uint64_t vm_map_size_t;
+#define VM_PROT_NONE ((vm_prot_t) 0x00)
+#define VM_PROT_READ ((vm_prot_t) 0x01)
+#define VM_PROT_WRITE ((vm_prot_t) 0x02)
+#define VM_PROT_EXECUTE ((vm_prot_t) 0x04)
+typedef struct vm_region_submap_short_info_data_64_t {
+ vm_prot_t protection;
+ vm_prot_t max_protection;
+ vm_inherit_t inheritance;
+ memory_object_offset_t offset; // offset into object/map
+ unsigned int user_tag; // user tag on map entry
+ unsigned int ref_count; // obj/map mappers, etc
+ unsigned short shadow_depth; // only for obj
+ unsigned char external_pager; // only for obj
+ unsigned char share_mode; // see enumeration
+ boolean_t is_submap; // submap vs obj
+ vm_behavior_t behavior; // access behavior hint
+ vm32_object_id_t object_id; // obj/map name, not a handle
+ unsigned short user_wired_count;
+} vm_region_submap_short_info_data_64_t;
+#define VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ((mach_msg_type_number_t)(sizeof(vm_region_submap_short_info_data_64_t)/sizeof(int)))''';
+ if user_init_code:
+ expr += user_init_code;
+ expr += '''
+task_t task = (task_t)mach_task_self();
+mach_vm_address_t vm_region_base_addr;
+mach_vm_size_t vm_region_size;
+natural_t vm_region_depth;
+vm_region_submap_short_info_data_64_t vm_region_info;
+kern_return_t err;
+for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size)
+{
+ mach_msg_type_number_t vm_region_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
+ err = (kern_return_t)mach_vm_region_recurse (task,
+ &vm_region_base_addr,
+ &vm_region_size,
+ &vm_region_depth,
+ &vm_region_info,
+ &vm_region_info_size);
+ if (err)
+ break;
+ // Check all read + write regions. This will cover the thread stacks
+ // and any regions of memory like __DATA segments, that might contain
+ // data we are looking for
+ if (vm_region_info.protection & VM_PROT_WRITE &&
+ vm_region_info.protection & VM_PROT_READ)
+ {
+ baton.callback (task,
+ &baton,
+ 64,
+ vm_region_base_addr,
+ vm_region_size);
+ }
+}'''
+ else:
+ if options.search_stack:
+ expr += get_thread_stack_ranges_struct (process)
+ if options.search_segments:
+ expr += get_sections_ranges_struct (process)
+ if user_init_code:
+ expr += user_init_code
+ if options.search_heap:
+ expr += '''
+#define MALLOC_PTR_IN_USE_RANGE_TYPE 1
+typedef struct vm_range_t {
+ vm_address_t address;
+ vm_size_t size;
+} vm_range_t;
+typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory);
+typedef void (*vm_range_recorder_t)(task_t task, void *baton, unsigned type, vm_range_t *range, unsigned size);
+typedef struct malloc_introspection_t {
+ 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 */
+} malloc_introspection_t;
+typedef struct malloc_zone_t {
+ void *reserved1[12];
+ struct malloc_introspection_t *introspect;
+} malloc_zone_t;
+memory_reader_t task_peek = [](task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) -> kern_return_t {
+ *local_memory = (void*) remote_address;
+ return KERN_SUCCESS;
+};
+vm_address_t *zones = 0;
+unsigned int num_zones = 0;task_t task = 0;
+kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones);
+if (KERN_SUCCESS == err)
+{
+ for (unsigned int i=0; i<num_zones; ++i)
+ {
+ const malloc_zone_t *zone = (const malloc_zone_t *)zones[i];
+ if (zone && zone->introspect)
+ zone->introspect->enumerator (task,
+ &baton,
+ MALLOC_PTR_IN_USE_RANGE_TYPE,
+ (vm_address_t)zone,
+ task_peek,
+ [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void
+ {
+ range_callback_t callback = ((callback_baton_t *)baton)->callback;
+ for (unsigned i=0; i<size; ++i)
+ {
+ callback (task, baton, type, ranges[i].address, ranges[i].size);
+ }
+ });
+ }
+}'''
+
+ if options.search_stack:
+ expr += '''
+#ifdef NUM_STACKS
+// Call the callback for the thread stack ranges
+for (uint32_t i=0; i<NUM_STACKS; ++i) {
+ range_callback(task, &baton, 8, stacks[i].base, stacks[i].size);
+ if (STACK_RED_ZONE_SIZE > 0) {
+ range_callback(task, &baton, 16, stacks[i].base - STACK_RED_ZONE_SIZE, STACK_RED_ZONE_SIZE);
+ }
+}
+#endif'''
+
+ if options.search_segments:
+ expr += '''
+#ifdef NUM_SEGMENTS
+// Call the callback for all segments
+for (uint32_t i=0; i<NUM_SEGMENTS; ++i)
+ range_callback(task, &baton, 32, segments[i].base, segments[i].size);
+#endif'''
+
+ if user_return_code:
+ expr += "\n%s" % (user_return_code,)
+
+ return expr
+
+def get_member_types_for_offset(value_type, offset, member_list):
+ member = value_type.GetFieldAtIndex(0)
+ search_bases = False
+ if member:
+ if member.GetOffsetInBytes() <= offset:
+ for field_idx in range (value_type.GetNumberOfFields()):
+ member = value_type.GetFieldAtIndex(field_idx)
+ member_byte_offset = member.GetOffsetInBytes()
+ member_end_byte_offset = member_byte_offset + member.type.size
+ if member_byte_offset <= offset and offset < member_end_byte_offset:
+ member_list.append(member)
+ get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
+ return
+ else:
+ search_bases = True
+ else:
+ search_bases = True
+ if search_bases:
+ for field_idx in range (value_type.GetNumberOfDirectBaseClasses()):
+ member = value_type.GetDirectBaseClassAtIndex(field_idx)
+ member_byte_offset = member.GetOffsetInBytes()
+ member_end_byte_offset = member_byte_offset + member.type.size
+ if member_byte_offset <= offset and offset < member_end_byte_offset:
+ member_list.append(member)
+ get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
+ return
+ for field_idx in range (value_type.GetNumberOfVirtualBaseClasses()):
+ member = value_type.GetVirtualBaseClassAtIndex(field_idx)
+ member_byte_offset = member.GetOffsetInBytes()
+ member_end_byte_offset = member_byte_offset + member.type.size
+ if member_byte_offset <= offset and offset < member_end_byte_offset:
+ member_list.append(member)
+ get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
+ return
+
+def append_regex_callback(option, opt, value, parser):
+ try:
+ ivar_regex = re.compile(value)
+ parser.values.ivar_regex_blacklist.append(ivar_regex)
+ except:
+ print 'error: an exception was thrown when compiling the ivar regular expression for "%s"' % value
+
+def add_common_options(parser):
+ parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
+ parser.add_option('-t', '--type', action='store_true', dest='print_type', help='print the full value of the type for each matching malloc block', default=False)
+ parser.add_option('-o', '--po', action='store_true', dest='print_object_description', help='print the object descriptions for any matches', default=False)
+ parser.add_option('-z', '--size', action='store_true', dest='show_size', help='print the allocation size in bytes', default=False)
+ parser.add_option('-r', '--range', action='store_true', dest='show_range', help='print the allocation address range instead of just the allocation base address', default=False)
+ parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False)
+ parser.add_option('-f', '--format', type='string', dest='format', help='the format to use when dumping memory if --memory is specified', default=None)
+ parser.add_option('-I', '--omit-ivar-regex', type='string', action='callback', callback=append_regex_callback, dest='ivar_regex_blacklist', default=[], help='specify one or more regular expressions used to backlist any matches that are in ivars')
+ parser.add_option('-s', '--stack', action='store_true', dest='stack', help='gets the stack that allocated each malloc block if MallocStackLogging is enabled', default=False)
+ parser.add_option('-S', '--stack-history', action='store_true', dest='stack_history', help='gets the stack history for all allocations whose start address matches each malloc block if MallocStackLogging is enabled', default=False)
+ parser.add_option('-F', '--max-frames', type='int', dest='max_frames', help='the maximum number of stack frames to print when using the --stack or --stack-history options (default=128)', default=128)
+ parser.add_option('-H', '--max-history', type='int', dest='max_history', help='the maximum number of stack history backtraces to print for each allocation when using the --stack-history option (default=16)', default=16)
+ parser.add_option('-M', '--max-matches', type='int', dest='max_matches', help='the maximum number of matches to print', default=32)
+ parser.add_option('-O', '--offset', type='int', dest='offset', help='the matching data must be at this offset', default=-1)
+ parser.add_option('--ignore-stack', action='store_false', dest='search_stack', help="Don't search the stack when enumerating memory", default=True)
+ parser.add_option('--ignore-heap', action='store_false', dest='search_heap', help="Don't search the heap allocations when enumerating memory", default=True)
+ parser.add_option('--ignore-segments', action='store_false', dest='search_segments', help="Don't search readable executable segments enumerating memory", default=True)
+ parser.add_option('-V', '--vm-regions', action='store_true', dest='search_vm_regions', help='Check all VM regions instead of searching the heap, stack and segments', default=False)
+
+def type_flags_to_string(type_flags):
+ if type_flags == 0:
+ type_str = 'free'
+ elif type_flags & 2:
+ type_str = 'malloc'
+ elif type_flags & 4:
+ type_str = 'free'
+ elif type_flags & 1:
+ type_str = 'generic'
+ elif type_flags & 8:
+ type_str = 'stack'
+ elif type_flags & 16:
+ type_str = 'stack (red zone)'
+ elif type_flags & 32:
+ type_str = 'segment'
+ elif type_flags & 64:
+ type_str = 'vm_region'
+ else:
+ type_str = hex(type_flags)
+ return type_str
+
+def find_variable_containing_address(verbose, frame, match_addr):
+ variables = frame.GetVariables(True,True,True,True)
+ matching_var = None
+ for var in variables:
+ var_addr = var.GetLoadAddress()
+ if var_addr != lldb.LLDB_INVALID_ADDRESS:
+ byte_size = var.GetType().GetByteSize()
+ if verbose:
+ print 'frame #%u: [%#x - %#x) %s' % (frame.GetFrameID(), var.load_addr, var.load_addr + byte_size, var.name)
+ if var_addr == match_addr:
+ if verbose:
+ print 'match'
+ return var
+ else:
+ if byte_size > 0 and var_addr <= match_addr and match_addr < (var_addr + byte_size):
+ if verbose:
+ print 'match'
+ return var
+ return None
+
+def find_frame_for_stack_address(process, addr):
+ closest_delta = sys.maxint
+ closest_frame = None
+ #print 'find_frame_for_stack_address(%#x)' % (addr)
+ for thread in process:
+ prev_sp = lldb.LLDB_INVALID_ADDRESS
+ for frame in thread:
+ cfa = frame.GetCFA()
+ #print 'frame #%u: cfa = %#x' % (frame.GetFrameID(), cfa)
+ if addr < cfa:
+ delta = cfa - addr
+ #print '%#x < %#x, delta = %i' % (addr, cfa, delta)
+ if delta < closest_delta:
+ #print 'closest'
+ closest_delta = delta
+ closest_frame = frame
+ # else:
+ # print 'delta >= closest_delta'
+ return closest_frame
+
+def type_flags_to_description(process, type_flags, ptr_addr, ptr_size, offset, match_addr):
+ show_offset = False
+ if type_flags == 0 or type_flags & 4:
+ type_str = 'free(%#x)' % (ptr_addr,)
+ elif type_flags & 2 or type_flags & 1:
+ type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr)
+ show_offset = True
+ elif type_flags & 8:
+ type_str = 'stack'
+ frame = find_frame_for_stack_address(process, match_addr)
+ if frame:
+ type_str += ' in frame #%u of thread #%u: tid %#x' % (frame.GetFrameID(), frame.GetThread().GetIndexID(), frame.GetThread().GetThreadID())
+ variables = frame.GetVariables(True,True,True,True)
+ matching_var = None
+ for var in variables:
+ var_addr = var.GetLoadAddress()
+ if var_addr != lldb.LLDB_INVALID_ADDRESS:
+ #print 'variable "%s" @ %#x (%#x)' % (var.name, var.load_addr, match_addr)
+ if var_addr == match_addr:
+ matching_var = var
+ break
+ else:
+ byte_size = var.GetType().GetByteSize()
+ if byte_size > 0 and var_addr <= match_addr and match_addr < (var_addr + byte_size):
+ matching_var = var
+ break
+ if matching_var:
+ type_str += ' in variable at %#x:\n %s' % (matching_var.GetLoadAddress(), matching_var)
+ elif type_flags & 16:
+ type_str = 'stack (red zone)'
+ elif type_flags & 32:
+ sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
+ type_str = 'segment [%#x - %#x), %s + %u, %s' % (ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
+ elif type_flags & 64:
+ sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
+ type_str = 'vm_region [%#x - %#x), %s + %u, %s' % (ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
+ else:
+ type_str = '%#x' % (ptr_addr,)
+ show_offset = True
+ if show_offset and offset != 0:
+ type_str += ' + %-6u' % (offset,)
+ return type_str
+
+def dump_stack_history_entry(options, result, stack_history_entry, idx):
+ address = int(stack_history_entry.address)
+ if address:
+ type_flags = int(stack_history_entry.type_flags)
+ symbolicator = lldb.utils.symbolication.Symbolicator()
+ symbolicator.target = lldb.debugger.GetSelectedTarget()
+ type_str = type_flags_to_string(type_flags)
+ result.AppendMessage('stack[%u]: addr = 0x%x, type=%s, frames:' % (idx, address, type_str))
+ frame_idx = 0
+ idx = 0
+ pc = int(stack_history_entry.frames[idx])
+ while pc != 0:
+ if pc >= 0x1000:
+ frames = symbolicator.symbolicate(pc)
+ if frames:
+ for frame in frames:
+ result.AppendMessage(' [%u] %s' % (frame_idx, frame))
+ frame_idx += 1
+ else:
+ result.AppendMessage(' [%u] 0x%x' % (frame_idx, pc))
+ frame_idx += 1
+ idx = idx + 1
+ pc = int(stack_history_entry.frames[idx])
+ else:
+ pc = 0
+ if idx >= options.max_frames:
+ result.AppendMessage('warning: the max number of stack frames (%u) was reached, use the "--max-frames=<COUNT>" option to see more frames' % (options.max_frames))
+
+ result.AppendMessage('')
+
+def dump_stack_history_entries(options, result, addr, history):
+ # malloc_stack_entry *get_stack_history_for_address (const void * addr)
+ expr_prefix = '''
+typedef int kern_return_t;
+typedef struct $malloc_stack_entry {
+ uint64_t address;
+ uint64_t argument;
+ uint32_t type_flags;
+ uint32_t num_frames;
+ uint64_t frames[512];
+ kern_return_t err;
+} $malloc_stack_entry;
+'''
+ single_expr = '''
+#define MAX_FRAMES %u
+typedef unsigned task_t;
+$malloc_stack_entry stack;
+stack.address = 0x%x;
+stack.type_flags = 2;
+stack.num_frames = 0;
+stack.frames[0] = 0;
+uint32_t max_stack_frames = MAX_FRAMES;
+stack.err = (kern_return_t)__mach_stack_logging_get_frames (
+ (task_t)mach_task_self(),
+ stack.address,
+ &stack.frames[0],
+ max_stack_frames,
+ &stack.num_frames);
+if (stack.num_frames < MAX_FRAMES)
+ stack.frames[stack.num_frames] = 0;
+else
+ stack.frames[MAX_FRAMES-1] = 0;
+stack''' % (options.max_frames, addr);
+
+ history_expr = '''
+typedef int kern_return_t;
+typedef unsigned task_t;
+#define MAX_FRAMES %u
+#define MAX_HISTORY %u
+typedef struct mach_stack_logging_record_t {
+ uint32_t type_flags;
+ uint64_t stack_identifier;
+ uint64_t argument;
+ uint64_t address;
+} mach_stack_logging_record_t;
+typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
+typedef struct malloc_stack_entry {
+ uint64_t address;
+ uint64_t argument;
+ uint32_t type_flags;
+ uint32_t num_frames;
+ uint64_t frames[MAX_FRAMES];
+ kern_return_t frames_err;
+} malloc_stack_entry;
+typedef struct $malloc_stack_history {
+ task_t task;
+ unsigned idx;
+ malloc_stack_entry entries[MAX_HISTORY];
+} $malloc_stack_history;
+$malloc_stack_history info = { (task_t)mach_task_self(), 0 };
+uint32_t max_stack_frames = MAX_FRAMES;
+enumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void {
+ $malloc_stack_history *info = ($malloc_stack_history *)baton;
+ if (info->idx < MAX_HISTORY) {
+ malloc_stack_entry *stack_entry = &(info->entries[info->idx]);
+ stack_entry->address = stack_record.address;
+ stack_entry->type_flags = stack_record.type_flags;
+ stack_entry->argument = stack_record.argument;
+ stack_entry->num_frames = 0;
+ stack_entry->frames[0] = 0;
+ stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack (
+ info->task,
+ stack_record.stack_identifier,
+ stack_entry->frames,
+ (uint32_t)MAX_FRAMES,
+ &stack_entry->num_frames);
+ // Terminate the frames with zero if there is room
+ if (stack_entry->num_frames < MAX_FRAMES)
+ stack_entry->frames[stack_entry->num_frames] = 0;
+ }
+ ++info->idx;
+};
+(kern_return_t)__mach_stack_logging_enumerate_records (info.task, (uint64_t)0x%x, callback, &info);
+info''' % (options.max_frames, options.max_history, addr);
+
+ frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
+ if history:
+ expr = history_expr
+ else:
+ expr = single_expr
+ expr_options = lldb.SBExpressionOptions()
+ expr_options.SetIgnoreBreakpoints(True);
+ expr_options.SetTimeoutInMicroSeconds (5*1000*1000) # 5 second timeout
+ expr_options.SetTryAllThreads (True)
+ expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
+ expr_options.SetPrefix(expr_prefix)
+ expr_sbvalue = frame.EvaluateExpression (expr, expr_options)
+ if options.verbose:
+ print "expression:"
+ print expr
+ print "expression result:"
+ print expr_sbvalue
+ if expr_sbvalue.error.Success():
+ if history:
+ malloc_stack_history = lldb.value(expr_sbvalue)
+ num_stacks = int(malloc_stack_history.idx)
+ if num_stacks <= options.max_history:
+ i_max = num_stacks
+ else:
+ i_max = options.max_history
+ for i in range(i_max):
+ stack_history_entry = malloc_stack_history.entries[i]
+ dump_stack_history_entry(options, result, stack_history_entry, i)
+ if num_stacks > options.max_history:
+ result.AppendMessage('warning: the max number of stacks (%u) was reached, use the "--max-history=%u" option to see all of the stacks' % (options.max_history, num_stacks))
+ else:
+ stack_history_entry = lldb.value(expr_sbvalue)
+ dump_stack_history_entry(options, result, stack_history_entry, 0)
+
+ else:
+ result.AppendMessage('error: expression failed "%s" => %s' % (expr, expr_sbvalue.error))
+
+
+def display_match_results (process, result, options, arg_str_description, expr, print_no_matches, expr_prefix = None):
+ frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
+ if not frame:
+ result.AppendMessage('error: invalid frame')
+ return 0
+ expr_options = lldb.SBExpressionOptions()
+ expr_options.SetIgnoreBreakpoints(True);
+ expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues);
+ expr_options.SetTimeoutInMicroSeconds (30*1000*1000) # 30 second timeout
+ expr_options.SetTryAllThreads (False)
+ expr_options.SetLanguage (lldb.eLanguageTypeObjC_plus_plus)
+ if expr_prefix:
+ expr_options.SetPrefix (expr_prefix)
+ expr_sbvalue = frame.EvaluateExpression (expr, expr_options)
+ if options.verbose:
+ print "expression:"
+ print expr
+ print "expression result:"
+ print expr_sbvalue
+ if expr_sbvalue.error.Success():
+ match_value = lldb.value(expr_sbvalue)
+ i = 0
+ match_idx = 0
+ while 1:
+ print_entry = True
+ match_entry = match_value[i]; i += 1
+ if i > options.max_matches:
+ result.AppendMessage('warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' % (options.max_matches))
+ break
+ malloc_addr = match_entry.addr.sbvalue.unsigned
+ if malloc_addr == 0:
+ break
+ malloc_size = int(match_entry.size)
+ offset = int(match_entry.offset)
+
+ if options.offset >= 0 and options.offset != offset:
+ print_entry = False
+ else:
+ match_addr = malloc_addr + offset
+ type_flags = int(match_entry.type)
+ #result.AppendMessage (hex(malloc_addr + offset))
+ if type_flags == 64:
+ search_stack_old = options.search_stack
+ search_segments_old = options.search_segments
+ search_heap_old = options.search_heap
+ search_vm_regions = options.search_vm_regions
+ options.search_stack = True
+ options.search_segments = True
+ options.search_heap = True
+ options.search_vm_regions = False
+ if malloc_info_impl (lldb.debugger, result, options, [hex(malloc_addr + offset)]):
+ print_entry = False
+ options.search_stack = search_stack_old
+ options.search_segments = search_segments_old
+ options.search_heap = search_heap_old
+ options.search_vm_regions = search_vm_regions
+ if print_entry:
+ description = '%#16.16x: %s' % (match_addr, type_flags_to_description(process, type_flags, malloc_addr, malloc_size, offset, match_addr))
+ if options.show_size:
+ description += ' <%5u>' % (malloc_size)
+ if options.show_range:
+ description += ' [%#x - %#x)' % (malloc_addr, malloc_addr + malloc_size)
+ derefed_dynamic_value = None
+ dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
+ if dynamic_value.type.name == 'void *':
+ if options.type == 'pointer' and malloc_size == 4096:
+ error = lldb.SBError()
+ process = expr_sbvalue.GetProcess()
+ target = expr_sbvalue.GetTarget()
+ data = bytearray(process.ReadMemory(malloc_addr, 16, error))
+ if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
+ ptr_size = target.addr_size
+ thread = process.ReadUnsignedFromMemory (malloc_addr + 16 + ptr_size, ptr_size, error)
+ # 4 bytes 0xa1a1a1a1
+ # 12 bytes 'AUTORELEASE!'
+ # ptr bytes autorelease insertion point
+ # ptr bytes pthread_t
+ # ptr bytes next colder page
+ # ptr bytes next hotter page
+ # 4 bytes this page's depth in the list
+ # 4 bytes high-water mark
+ description += ' AUTORELEASE! for pthread_t %#x' % (thread)
+ # else:
+ # description += 'malloc(%u)' % (malloc_size)
+ # else:
+ # description += 'malloc(%u)' % (malloc_size)
+ else:
+ derefed_dynamic_value = dynamic_value.deref
+ if derefed_dynamic_value:
+ derefed_dynamic_type = derefed_dynamic_value.type
+ derefed_dynamic_type_size = derefed_dynamic_type.size
+ derefed_dynamic_type_name = derefed_dynamic_type.name
+ description += ' '
+ description += derefed_dynamic_type_name
+ if offset < derefed_dynamic_type_size:
+ member_list = list();
+ get_member_types_for_offset (derefed_dynamic_type, offset, member_list)
+ if member_list:
+ member_path = ''
+ for member in member_list:
+ member_name = member.name
+ if member_name:
+ if member_path:
+ member_path += '.'
+ member_path += member_name
+ if member_path:
+ if options.ivar_regex_blacklist:
+ for ivar_regex in options.ivar_regex_blacklist:
+ if ivar_regex.match(member_path):
+ print_entry = False
+ description += '.%s' % (member_path)
+ else:
+ description += '%u bytes after %s' % (offset - derefed_dynamic_type_size, derefed_dynamic_type_name)
+ else:
+ # strip the "*" from the end of the name since we were unable to dereference this
+ description += dynamic_value.type.name[0:-1]
+ if print_entry:
+ match_idx += 1
+ result_output = ''
+ if description:
+ result_output += description
+ if options.print_type and derefed_dynamic_value:
+ result_output += ' %s' % (derefed_dynamic_value)
+ if options.print_object_description and dynamic_value:
+ desc = dynamic_value.GetObjectDescription()
+ if desc:
+ result_output += '\n%s' % (desc)
+ if result_output:
+ result.AppendMessage(result_output)
+ if options.memory:
+ cmd_result = lldb.SBCommandReturnObject()
+ if options.format == None:
+ memory_command = "memory read --force 0x%x 0x%x" % (malloc_addr, malloc_addr + malloc_size)
+ else:
+ memory_command = "memory read --force -f %s 0x%x 0x%x" % (options.format, malloc_addr, malloc_addr + malloc_size)
+ if options.verbose:
+ result.AppendMessage(memory_command)
+ lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
+ result.AppendMessage(cmd_result.GetOutput())
+ if options.stack_history:
+ dump_stack_history_entries(options, result, malloc_addr, 1)
+ elif options.stack:
+ dump_stack_history_entries(options, result, malloc_addr, 0)
+ return i
+ else:
+ result.AppendMessage(str(expr_sbvalue.error))
+ return 0
+
+def get_ptr_refs_options ():
+ usage = "usage: %prog [options] <EXPR> [EXPR ...]"
+ description='''Searches all allocations on the heap for pointer values on
+darwin user space programs. Any matches that were found will dump the malloc
+blocks that contain the pointers and might be able to print what kind of
+objects the pointers are contained in using dynamic type information in the
+program.'''
+ parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
+ add_common_options(parser)
+ return parser
+
+def find_variable(debugger, command, result, dict):
+ usage = "usage: %prog [options] <ADDR> [ADDR ...]"
+ description='''Searches for a local variable in all frames that contains a hex ADDR.'''
+ command_args = shlex.split(command)
+ parser = optparse.OptionParser(description=description, prog='find_variable',usage=usage)
+ parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
+ try:
+ (options, args) = parser.parse_args(command_args)
+ except:
+ return
+
+ process = debugger.GetSelectedTarget().GetProcess()
+ if not process:
+ result.AppendMessage('error: invalid process')
+ return
+
+ for arg in args:
+ var_addr = int(arg, 16)
+ print >>result, "Finding a variable with address %#x..." % (var_addr)
+ done = False
+ for thread in process:
+ for frame in thread:
+ var = find_variable_containing_address(options.verbose, frame, var_addr)
+ if var:
+ print var
+ done = True
+ break
+ if done:
+ break
+
+def ptr_refs(debugger, command, result, dict):
+ command_args = shlex.split(command)
+ parser = get_ptr_refs_options()
+ try:
+ (options, args) = parser.parse_args(command_args)
+ except:
+ return
+
+ process = debugger.GetSelectedTarget().GetProcess()
+ if not process:
+ result.AppendMessage('error: invalid process')
+ return
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ if not frame:
+ result.AppendMessage('error: invalid frame')
+ return
+
+ options.type = 'pointer'
+ if options.format == None:
+ options.format = "A" # 'A' is "address" format
+
+ if args:
+ # When we initialize the expression, we must define any types that
+ # we will need when looking at every allocation. We must also define
+ # a type named callback_baton_t and make an instance named "baton"
+ # and initialize it how ever we want to. The address of "baton" will
+ # be passed into our range callback. callback_baton_t must contain
+ # a member named "callback" whose type is "range_callback_t". This
+ # will be used by our zone callbacks to call the range callback for
+ # each malloc range.
+ expr_prefix = '''
+struct $malloc_match {
+ void *addr;
+ uintptr_t size;
+ uintptr_t offset;
+ uintptr_t type;
+};
+'''
+ user_init_code_format = '''
+#define MAX_MATCHES %u
+typedef struct callback_baton_t {
+ range_callback_t callback;
+ unsigned num_matches;
+ $malloc_match matches[MAX_MATCHES];
+ void *ptr;
+} callback_baton_t;
+range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
+ callback_baton_t *info = (callback_baton_t *)baton;
+ typedef void* T;
+ const unsigned size = sizeof(T);
+ T *array = (T*)ptr_addr;
+ for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) {
+ if (array[idx] == info->ptr) {
+ if (info->num_matches < MAX_MATCHES) {
+ info->matches[info->num_matches].addr = (void*)ptr_addr;
+ info->matches[info->num_matches].size = ptr_size;
+ info->matches[info->num_matches].offset = idx*sizeof(T);
+ info->matches[info->num_matches].type = type;
+ ++info->num_matches;
+ }
+ }
+ }
+};
+callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
+'''
+ # We must also define a snippet of code to be run that returns
+ # the result of the expression we run.
+ # Here we return NULL if our pointer was not found in any malloc blocks,
+ # and we return the address of the matches array so we can then access
+ # the matching results
+ user_return_code = '''if (baton.num_matches < MAX_MATCHES)
+ baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
+baton.matches'''
+ # Iterate through all of our pointer expressions and display the results
+ for ptr_expr in args:
+ user_init_code = user_init_code_format % (options.max_matches, ptr_expr)
+ expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
+ arg_str_description = 'malloc block containing pointer %s' % ptr_expr
+ display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
+ else:
+ result.AppendMessage('error: no pointer arguments were given')
+
+def get_cstr_refs_options():
+ usage = "usage: %prog [options] <CSTR> [CSTR ...]"
+ description='''Searches all allocations on the heap for C string values on
+darwin user space programs. Any matches that were found will dump the malloc
+blocks that contain the C strings and might be able to print what kind of
+objects the pointers are contained in using dynamic type information in the
+program.'''
+ parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
+ add_common_options(parser)
+ return parser
+
+def cstr_refs(debugger, command, result, dict):
+ command_args = shlex.split(command)
+ parser = get_cstr_refs_options();
+ try:
+ (options, args) = parser.parse_args(command_args)
+ except:
+ return
+
+ process = debugger.GetSelectedTarget().GetProcess()
+ if not process:
+ result.AppendMessage('error: invalid process')
+ return
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ if not frame:
+ result.AppendMessage('error: invalid frame')
+ return
+
+
+ options.type = 'cstr'
+ if options.format == None:
+ options.format = "Y" # 'Y' is "bytes with ASCII" format
+
+ if args:
+ # When we initialize the expression, we must define any types that
+ # we will need when looking at every allocation. We must also define
+ # a type named callback_baton_t and make an instance named "baton"
+ # and initialize it how ever we want to. The address of "baton" will
+ # be passed into our range callback. callback_baton_t must contain
+ # a member named "callback" whose type is "range_callback_t". This
+ # will be used by our zone callbacks to call the range callback for
+ # each malloc range.
+ expr_prefix = '''
+struct $malloc_match {
+ void *addr;
+ uintptr_t size;
+ uintptr_t offset;
+ uintptr_t type;
+};
+'''
+ user_init_code_format = '''
+#define MAX_MATCHES %u
+typedef struct callback_baton_t {
+ range_callback_t callback;
+ unsigned num_matches;
+ $malloc_match matches[MAX_MATCHES];
+ const char *cstr;
+ unsigned cstr_len;
+} callback_baton_t;
+range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
+ callback_baton_t *info = (callback_baton_t *)baton;
+ if (info->cstr_len < ptr_size) {
+ const char *begin = (const char *)ptr_addr;
+ const char *end = begin + ptr_size - info->cstr_len;
+ for (const char *s = begin; s < end; ++s) {
+ if ((int)memcmp(s, info->cstr, info->cstr_len) == 0) {
+ if (info->num_matches < MAX_MATCHES) {
+ info->matches[info->num_matches].addr = (void*)ptr_addr;
+ info->matches[info->num_matches].size = ptr_size;
+ info->matches[info->num_matches].offset = s - begin;
+ info->matches[info->num_matches].type = type;
+ ++info->num_matches;
+ }
+ }
+ }
+ }
+};
+const char *cstr = "%s";
+callback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };'''
+ # We must also define a snippet of code to be run that returns
+ # the result of the expression we run.
+ # Here we return NULL if our pointer was not found in any malloc blocks,
+ # and we return the address of the matches array so we can then access
+ # the matching results
+ user_return_code = '''if (baton.num_matches < MAX_MATCHES)
+ baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
+baton.matches'''
+ # Iterate through all of our pointer expressions and display the results
+ for cstr in args:
+ user_init_code = user_init_code_format % (options.max_matches, cstr)
+ expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
+ arg_str_description = 'malloc block containing "%s"' % cstr
+ display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
+ else:
+ result.AppendMessage('error: command takes one or more C string arguments')
+
+
+def get_malloc_info_options():
+ usage = "usage: %prog [options] <EXPR> [EXPR ...]"
+ description='''Searches the heap a malloc block that contains the addresses
+specified as one or more address expressions. Any matches that were found will
+dump the malloc blocks that match or contain the specified address. The matching
+blocks might be able to show what kind of objects they are using dynamic type
+information in the program.'''
+ parser = optparse.OptionParser(description=description, prog='malloc_info',usage=usage)
+ add_common_options(parser)
+ return parser
+
+def malloc_info(debugger, command, result, dict):
+ command_args = shlex.split(command)
+ parser = get_malloc_info_options()
+ try:
+ (options, args) = parser.parse_args(command_args)
+ except:
+ return
+ malloc_info_impl (debugger, result, options, args)
+
+def malloc_info_impl (debugger, result, options, args):
+ # We are specifically looking for something on the heap only
+ options.type = 'malloc_info'
+
+ process = debugger.GetSelectedTarget().GetProcess()
+ if not process:
+ result.AppendMessage('error: invalid process')
+ return
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ if not frame:
+ result.AppendMessage('error: invalid frame')
+ return
+ expr_prefix = '''
+struct $malloc_match {
+ void *addr;
+ uintptr_t size;
+ uintptr_t offset;
+ uintptr_t type;
+};
+'''
+
+ user_init_code_format = '''
+typedef struct callback_baton_t {
+ range_callback_t callback;
+ unsigned num_matches;
+ $malloc_match matches[2]; // Two items so they can be NULL terminated
+ void *ptr;
+} callback_baton_t;
+range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
+ callback_baton_t *info = (callback_baton_t *)baton;
+ if (info->num_matches == 0) {
+ uint8_t *p = (uint8_t *)info->ptr;
+ uint8_t *lo = (uint8_t *)ptr_addr;
+ uint8_t *hi = lo + ptr_size;
+ if (lo <= p && p < hi) {
+ info->matches[info->num_matches].addr = (void*)ptr_addr;
+ info->matches[info->num_matches].size = ptr_size;
+ info->matches[info->num_matches].offset = p - lo;
+ info->matches[info->num_matches].type = type;
+ info->num_matches = 1;
+ }
+ }
+};
+callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
+baton.matches[0].addr = 0;
+baton.matches[1].addr = 0;'''
+ if args:
+ total_matches = 0
+ for ptr_expr in args:
+ user_init_code = user_init_code_format % (ptr_expr)
+ expr = get_iterate_memory_expr(options, process, user_init_code, 'baton.matches')
+ arg_str_description = 'malloc block that contains %s' % ptr_expr
+ total_matches += display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
+ return total_matches
+ else:
+ result.AppendMessage('error: command takes one or more pointer expressions')
+ return 0
+
+def get_thread_stack_ranges_struct (process):
+ '''Create code that defines a structure that represents threads stack bounds
+ for all threads. It returns a static sized array initialized with all of
+ the tid, base, size structs for all the threads.'''
+ stack_dicts = list()
+ if process:
+ i = 0;
+ for thread in process:
+ min_sp = thread.frame[0].sp
+ max_sp = min_sp
+ for frame in thread.frames:
+ sp = frame.sp
+ if sp < min_sp: min_sp = sp
+ if sp > max_sp: max_sp = sp
+ if min_sp < max_sp:
+ stack_dicts.append ({ 'tid' : thread.GetThreadID(), 'base' : min_sp , 'size' : max_sp-min_sp, 'index' : i })
+ i += 1
+ stack_dicts_len = len(stack_dicts)
+ if stack_dicts_len > 0:
+ result = '''
+#define NUM_STACKS %u
+#define STACK_RED_ZONE_SIZE %u
+typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
+thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize())
+ for stack_dict in stack_dicts:
+ result += '''
+stacks[%(index)u].tid = 0x%(tid)x;
+stacks[%(index)u].base = 0x%(base)x;
+stacks[%(index)u].size = 0x%(size)x;''' % stack_dict
+ return result
+ else:
+ return ''
+
+def get_sections_ranges_struct (process):
+ '''Create code that defines a structure that represents all segments that
+ can contain data for all images in "target". It returns a static sized
+ array initialized with all of base, size structs for all the threads.'''
+ target = process.target
+ segment_dicts = list()
+ for (module_idx, module) in enumerate(target.modules):
+ for sect_idx in range(module.GetNumSections()):
+ section = module.GetSectionAtIndex(sect_idx)
+ if not section:
+ break
+ name = section.name
+ if name != '__TEXT' and name != '__LINKEDIT' and name != '__PAGEZERO':
+ base = section.GetLoadAddress(target)
+ size = section.GetByteSize()
+ if base != lldb.LLDB_INVALID_ADDRESS and size > 0:
+ segment_dicts.append ({ 'base' : base, 'size' : size })
+ segment_dicts_len = len(segment_dicts)
+ if segment_dicts_len > 0:
+ result = '''
+#define NUM_SEGMENTS %u
+typedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t;
+segment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,)
+ for (idx, segment_dict) in enumerate(segment_dicts):
+ segment_dict['index'] = idx
+ result += '''
+segments[%(index)u].base = 0x%(base)x;
+segments[%(index)u].size = 0x%(size)x;''' % segment_dict
+ return result
+ else:
+ return ''
+
+def section_ptr_refs(debugger, command, result, dict):
+ command_args = shlex.split(command)
+ usage = "usage: %prog [options] <EXPR> [EXPR ...]"
+ description='''Searches section contents for pointer values in darwin user space programs.'''
+ parser = optparse.OptionParser(description=description, prog='section_ptr_refs',usage=usage)
+ add_common_options(parser)
+ parser.add_option('--section', action='append', type='string', dest='section_names', help='section name to search', default=list())
+ try:
+ (options, args) = parser.parse_args(command_args)
+ except:
+ return
+
+ options.type = 'pointer'
+
+ sections = list()
+ section_modules = list()
+ if not options.section_names:
+ result.AppendMessage('error: at least one section must be specified with the --section option')
+ return
+
+ target = debugger.GetSelectedTarget()
+ for module in target.modules:
+ for section_name in options.section_names:
+ section = module.section[section_name]
+ if section:
+ sections.append (section)
+ section_modules.append (module)
+ if sections:
+ dylid_load_err = load_dylib()
+ if dylid_load_err:
+ result.AppendMessage(dylid_load_err)
+ return
+ frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
+ for expr_str in args:
+ for (idx, section) in enumerate(sections):
+ expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (section.addr.load_addr, section.size, expr_str)
+ arg_str_description = 'section %s.%s containing "%s"' % (section_modules[idx].file.fullpath, section.name, expr_str)
+ num_matches = display_match_results (target.GetProcess(), result, options, arg_str_description, expr, False)
+ if num_matches:
+ if num_matches < options.max_matches:
+ options.max_matches = options.max_matches - num_matches
+ else:
+ options.max_matches = 0
+ if options.max_matches == 0:
+ return
+ else:
+ result.AppendMessage('error: no sections were found that match any of %s' % (', '.join(options.section_names)))
+
+def get_objc_refs_options():
+ usage = "usage: %prog [options] <CLASS> [CLASS ...]"
+ description='''Searches all allocations on the heap for instances of
+objective C classes, or any classes that inherit from the specified classes
+in darwin user space programs. Any matches that were found will dump the malloc
+blocks that contain the C strings and might be able to print what kind of
+objects the pointers are contained in using dynamic type information in the
+program.'''
+ parser = optparse.OptionParser(description=description, prog='objc_refs',usage=usage)
+ add_common_options(parser)
+ return parser
+
+def objc_refs(debugger, command, result, dict):
+ command_args = shlex.split(command)
+ parser = get_objc_refs_options()
+ try:
+ (options, args) = parser.parse_args(command_args)
+ except:
+ return
+
+ process = debugger.GetSelectedTarget().GetProcess()
+ if not process:
+ result.AppendMessage('error: invalid process')
+ return
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ if not frame:
+ result.AppendMessage('error: invalid frame')
+ return
+
+ options.type = 'isa'
+ if options.format == None:
+ options.format = "A" # 'A' is "address" format
+
+ expr_options = lldb.SBExpressionOptions()
+ expr_options.SetIgnoreBreakpoints(True);
+ expr_options.SetTimeoutInMicroSeconds (3*1000*1000) # 3 second infinite timeout
+ expr_options.SetTryAllThreads (True)
+ expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
+ num_objc_classes_value = frame.EvaluateExpression("(int)objc_getClassList((void *)0, (int)0)", expr_options)
+ if not num_objc_classes_value.error.Success():
+ result.AppendMessage('error: %s' % num_objc_classes_value.error.GetCString())
+ return
+
+ num_objc_classes = num_objc_classes_value.GetValueAsUnsigned()
+ if num_objc_classes == 0:
+ result.AppendMessage('error: no objective C classes in program')
+ return
+
+ if args:
+ # When we initialize the expression, we must define any types that
+ # we will need when looking at every allocation. We must also define
+ # a type named callback_baton_t and make an instance named "baton"
+ # and initialize it how ever we want to. The address of "baton" will
+ # be passed into our range callback. callback_baton_t must contain
+ # a member named "callback" whose type is "range_callback_t". This
+ # will be used by our zone callbacks to call the range callback for
+ # each malloc range.
+ expr_prefix = '''
+struct $malloc_match {
+ void *addr;
+ uintptr_t size;
+ uintptr_t offset;
+ uintptr_t type;
+};
+'''
+
+ user_init_code_format = '''
+#define MAX_MATCHES %u
+typedef int (*compare_callback_t)(const void *a, const void *b);
+typedef struct callback_baton_t {
+ range_callback_t callback;
+ compare_callback_t compare_callback;
+ unsigned num_matches;
+ $malloc_match matches[MAX_MATCHES];
+ void *isa;
+ Class classes[%u];
+} callback_baton_t;
+compare_callback_t compare_callback = [](const void *a, const void *b) -> int {
+ Class a_ptr = *(Class *)a;
+ Class b_ptr = *(Class *)b;
+ if (a_ptr < b_ptr) return -1;
+ if (a_ptr > b_ptr) return +1;
+ return 0;
+};
+typedef Class (*class_getSuperclass_type)(void *isa);
+range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
+ class_getSuperclass_type class_getSuperclass_impl = (class_getSuperclass_type)class_getSuperclass;
+ callback_baton_t *info = (callback_baton_t *)baton;
+ if (sizeof(Class) <= ptr_size) {
+ Class *curr_class_ptr = (Class *)ptr_addr;
+ Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr,
+ (const void *)info->classes,
+ sizeof(info->classes)/sizeof(Class),
+ sizeof(Class),
+ info->compare_callback);
+ if (matching_class_ptr) {
+ bool match = false;
+ if (info->isa) {
+ Class isa = *curr_class_ptr;
+ if (info->isa == isa)
+ match = true;
+ else { // if (info->objc.match_superclasses) {
+ Class super = class_getSuperclass_impl(isa);
+ while (super) {
+ if (super == info->isa) {
+ match = true;
+ break;
+ }
+ super = class_getSuperclass_impl(super);
+ }
+ }
+ }
+ else
+ match = true;
+ if (match) {
+ if (info->num_matches < MAX_MATCHES) {
+ info->matches[info->num_matches].addr = (void*)ptr_addr;
+ info->matches[info->num_matches].size = ptr_size;
+ info->matches[info->num_matches].offset = 0;
+ info->matches[info->num_matches].type = type;
+ ++info->num_matches;
+ }
+ }
+ }
+ }
+};
+callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
+int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class));
+(void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);'''
+ # We must also define a snippet of code to be run that returns
+ # the result of the expression we run.
+ # Here we return NULL if our pointer was not found in any malloc blocks,
+ # and we return the address of the matches array so we can then access
+ # the matching results
+ user_return_code = '''if (baton.num_matches < MAX_MATCHES)
+ baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
+ baton.matches'''
+ # Iterate through all of our ObjC class name arguments
+ for class_name in args:
+ addr_expr_str = "(void *)[%s class]" % class_name
+ expr_options = lldb.SBExpressionOptions()
+ expr_options.SetIgnoreBreakpoints(True);
+ expr_options.SetTimeoutInMicroSeconds (1*1000*1000) # 1 second timeout
+ expr_options.SetTryAllThreads (True)
+ expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
+ expr_sbvalue = frame.EvaluateExpression (addr_expr_str, expr_options)
+ if expr_sbvalue.error.Success():
+ isa = expr_sbvalue.unsigned
+ if isa:
+ options.type = 'isa'
+ result.AppendMessage('Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' % (class_name, isa))
+ user_init_code = user_init_code_format % (options.max_matches, num_objc_classes, isa)
+ expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
+ arg_str_description = 'objective C classes with isa 0x%x' % isa
+ display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
+ else:
+ result.AppendMessage('error: Can\'t find isa for an ObjC class named "%s"' % (class_name))
+ else:
+ result.AppendMessage('error: expression error for "%s": %s' % (addr_expr_str, expr_sbvalue.error))
+ else:
+ result.AppendMessage('error: command takes one or more C string arguments');
+
+if __name__ == '__main__':
+ lldb.debugger = lldb.SBDebugger.Create()
+
+# Make the options so we can generate the help text for the new LLDB
+# command line command prior to registering it with LLDB below. This way
+# if clients in LLDB type "help malloc_info", they will see the exact same
+# output as typing "malloc_info --help".
+ptr_refs.__doc__ = get_ptr_refs_options().format_help()
+cstr_refs.__doc__ = get_cstr_refs_options().format_help()
+malloc_info.__doc__ = get_malloc_info_options().format_help()
+objc_refs.__doc__ = get_objc_refs_options().format_help()
+lldb.debugger.HandleCommand('command script add -f %s.ptr_refs ptr_refs' % __name__)
+lldb.debugger.HandleCommand('command script add -f %s.cstr_refs cstr_refs' % __name__)
+lldb.debugger.HandleCommand('command script add -f %s.malloc_info malloc_info' % __name__)
+lldb.debugger.HandleCommand('command script add -f %s.find_variable find_variable' % __name__)
+# lldb.debugger.HandleCommand('command script add -f %s.heap heap' % package_name)
+# lldb.debugger.HandleCommand('command script add -f %s.section_ptr_refs section_ptr_refs' % package_name)
+# lldb.debugger.HandleCommand('command script add -f %s.stack_ptr_refs stack_ptr_refs' % package_name)
+lldb.debugger.HandleCommand('command script add -f %s.objc_refs objc_refs' % __name__)
+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.'
+
+
+
+