aboutsummaryrefslogtreecommitdiff
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.py1522
1 files changed, 0 insertions, 1522 deletions
diff --git a/examples/darwin/heap_find/heap.py b/examples/darwin/heap_find/heap.py
deleted file mode 100644
index c463380bf49f..000000000000
--- a/examples/darwin/heap_find/heap.py
+++ /dev/null
@@ -1,1522 +0,0 @@
-#!/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.maxsize
- 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 lldb_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 *lldb_info = ($malloc_stack_history *)baton;
- if (lldb_info->idx < MAX_HISTORY) {
- malloc_stack_entry *stack_entry = &(lldb_info->entries[lldb_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 (
- lldb_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;
- }
- ++lldb_info->idx;
-};
-(kern_return_t)__mach_stack_logging_enumerate_records (lldb_info.task, (uint64_t)0x%x, callback, &lldb_info);
-lldb_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 True:
- 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 is 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 is 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 *lldb_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] == lldb_info->ptr) {
- if (lldb_info->num_matches < MAX_MATCHES) {
- lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
- lldb_info->matches[lldb_info->num_matches].size = ptr_size;
- lldb_info->matches[lldb_info->num_matches].offset = idx*sizeof(T);
- lldb_info->matches[lldb_info->num_matches].type = type;
- ++lldb_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 is 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 *lldb_info = (callback_baton_t *)baton;
- if (lldb_info->cstr_len < ptr_size) {
- const char *begin = (const char *)ptr_addr;
- const char *end = begin + ptr_size - lldb_info->cstr_len;
- for (const char *s = begin; s < end; ++s) {
- if ((int)memcmp(s, lldb_info->cstr, lldb_info->cstr_len) == 0) {
- if (lldb_info->num_matches < MAX_MATCHES) {
- lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
- lldb_info->matches[lldb_info->num_matches].size = ptr_size;
- lldb_info->matches[lldb_info->num_matches].offset = s - begin;
- lldb_info->matches[lldb_info->num_matches].type = type;
- ++lldb_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 *lldb_info = (callback_baton_t *)baton;
- if (lldb_info->num_matches == 0) {
- uint8_t *p = (uint8_t *)lldb_info->ptr;
- uint8_t *lo = (uint8_t *)ptr_addr;
- uint8_t *hi = lo + ptr_size;
- if (lo <= p && p < hi) {
- lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
- lldb_info->matches[lldb_info->num_matches].size = ptr_size;
- lldb_info->matches[lldb_info->num_matches].offset = p - lo;
- lldb_info->matches[lldb_info->num_matches].type = type;
- lldb_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 is 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 *lldb_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 *)lldb_info->classes,
- sizeof(lldb_info->classes)/sizeof(Class),
- sizeof(Class),
- lldb_info->compare_callback);
- if (matching_class_ptr) {
- bool match = false;
- if (lldb_info->isa) {
- Class isa = *curr_class_ptr;
- if (lldb_info->isa == isa)
- match = true;
- else { // if (lldb_info->objc.match_superclasses) {
- Class super = class_getSuperclass_impl(isa);
- while (super) {
- if (super == lldb_info->isa) {
- match = true;
- break;
- }
- super = class_getSuperclass_impl(super);
- }
- }
- }
- else
- match = true;
- if (match) {
- if (lldb_info->num_matches < MAX_MATCHES) {
- lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
- lldb_info->matches[lldb_info->num_matches].size = ptr_size;
- lldb_info->matches[lldb_info->num_matches].offset = 0;
- lldb_info->matches[lldb_info->num_matches].type = type;
- ++lldb_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.'