blob: 2155683b88286151bb51afe727fd8ed1412c9441 [file] [log] [blame]
Greg Clayton804de012012-04-11 16:27:06 +00001#!/usr/bin/python
2
3#----------------------------------------------------------------------
Greg Claytond712ef02012-04-25 18:40:20 +00004# This module is designed to live inside the "lldb" python package
5# in the "lldb.macosx" package. To use this in the embedded python
6# interpreter using "lldb" just import it:
Greg Clayton804de012012-04-11 16:27:06 +00007#
Greg Claytond712ef02012-04-25 18:40:20 +00008# (lldb) script import lldb.macosx.heap
Greg Clayton804de012012-04-11 16:27:06 +00009#----------------------------------------------------------------------
10
11import lldb
12import commands
13import optparse
Greg Clayton7fb671b2012-04-11 18:30:53 +000014import os
Greg Claytond712ef02012-04-25 18:40:20 +000015import os.path
Greg Claytone04cfd22012-07-11 22:13:18 +000016import re
Greg Clayton804de012012-04-11 16:27:06 +000017import shlex
Greg Claytond712ef02012-04-25 18:40:20 +000018import string
19import tempfile
Greg Claytoned3eee62012-04-25 01:49:50 +000020import lldb.utils.symbolication
Greg Clayton804de012012-04-11 16:27:06 +000021
Greg Claytond712ef02012-04-25 18:40:20 +000022g_libheap_dylib_dir = None
23g_libheap_dylib_dict = dict()
24
Greg Claytone7fc9cc2013-01-31 06:38:09 +000025def get_iterate_memory_expr(process, user_init_code, user_return_code):
26 return '''
27typedef unsigned natural_t;
Greg Clayton85e62ec2013-01-30 22:57:34 +000028typedef uintptr_t vm_size_t;
29typedef uintptr_t vm_address_t;
30typedef natural_t task_t;
31typedef int kern_return_t;
32#define KERN_SUCCESS 0
33#define MALLOC_PTR_IN_USE_RANGE_TYPE 1
34typedef struct vm_range_t {
35 vm_address_t address;
36 vm_size_t size;
37} vm_range_t;
38typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory);
39typedef void (*vm_range_recorder_t)(task_t task, void *baton, unsigned type, vm_range_t *range, unsigned size);
40typedef void (*range_callback_t)(task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size);
41typedef struct malloc_introspection_t {
42 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 */
43} malloc_introspection_t;
44typedef struct malloc_zone_t {
45 void *reserved1[12];
46 struct malloc_introspection_t *introspect;
47} malloc_zone_t;
48memory_reader_t task_peek = [](task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) -> kern_return_t {
49 *local_memory = (void*) remote_address;
50 return KERN_SUCCESS;
51};
52vm_address_t *zones = 0;
53unsigned int num_zones = 0;
54%s
Greg Claytone7fc9cc2013-01-31 06:38:09 +000055%s
56%s
Greg Clayton85e62ec2013-01-30 22:57:34 +000057task_t task = 0;
58kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones);
59if (KERN_SUCCESS == err)
60{
61 for (unsigned int i=0; i<num_zones; ++i)
62 {
63 const malloc_zone_t *zone = (const malloc_zone_t *)zones[i];
64 if (zone && zone->introspect)
65 zone->introspect->enumerator (task,
66 &baton,
67 MALLOC_PTR_IN_USE_RANGE_TYPE,
68 (vm_address_t)zone,
69 task_peek,
70 [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void
71 {
72 range_callback_t callback = ((callback_baton_t *)baton)->callback;
73 for (unsigned i=0; i<size; ++i)
74 {
75 callback (task, baton, type, ranges[i].address, ranges[i].size);
76 }
77 });
78 }
79}
Greg Claytone7fc9cc2013-01-31 06:38:09 +000080// Call the callback for the thread stack ranges
81for (uint32_t i=0; i<NUM_STACKS; ++i)
82 range_callback(task, &baton, 8, stacks[i].base, stacks[i].size);
83// Call the callback for all segments
84for (uint32_t i=0; i<NUM_SEGMENTS; ++i)
85 range_callback(task, &baton, 16, segments[i].base, segments[i].size);
86%s''' % (get_thread_stack_ranges_struct (process), get_sections_ranges_struct(process), user_init_code, user_return_code)
Greg Clayton94073022012-07-07 01:22:45 +000087
88def get_member_types_for_offset(value_type, offset, member_list):
89 member = value_type.GetFieldAtIndex(0)
90 search_bases = False
91 if member:
92 if member.GetOffsetInBytes() <= offset:
93 for field_idx in range (value_type.GetNumberOfFields()):
94 member = value_type.GetFieldAtIndex(field_idx)
95 member_byte_offset = member.GetOffsetInBytes()
96 member_end_byte_offset = member_byte_offset + member.type.size
97 if member_byte_offset <= offset and offset < member_end_byte_offset:
98 member_list.append(member)
99 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
100 return
101 else:
102 search_bases = True
103 else:
104 search_bases = True
105 if search_bases:
106 for field_idx in range (value_type.GetNumberOfDirectBaseClasses()):
107 member = value_type.GetDirectBaseClassAtIndex(field_idx)
108 member_byte_offset = member.GetOffsetInBytes()
109 member_end_byte_offset = member_byte_offset + member.type.size
110 if member_byte_offset <= offset and offset < member_end_byte_offset:
111 member_list.append(member)
112 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
113 return
114 for field_idx in range (value_type.GetNumberOfVirtualBaseClasses()):
115 member = value_type.GetVirtualBaseClassAtIndex(field_idx)
116 member_byte_offset = member.GetOffsetInBytes()
117 member_end_byte_offset = member_byte_offset + member.type.size
118 if member_byte_offset <= offset and offset < member_end_byte_offset:
119 member_list.append(member)
120 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
121 return
Greg Claytone04cfd22012-07-11 22:13:18 +0000122
123def append_regex_callback(option, opt, value, parser):
124 try:
125 ivar_regex = re.compile(value)
126 parser.values.ivar_regex_blacklist.append(ivar_regex)
127 except:
128 print 'error: an exception was thrown when compiling the ivar regular expression for "%s"' % value
Greg Claytonb403a152012-04-21 00:11:26 +0000129
Greg Claytond84bb482012-04-13 16:24:09 +0000130def add_common_options(parser):
131 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
Greg Claytone04cfd22012-07-11 22:13:18 +0000132 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)
Greg Claytond84bb482012-04-13 16:24:09 +0000133 parser.add_option('-o', '--po', action='store_true', dest='print_object_description', help='print the object descriptions for any matches', default=False)
Greg Claytona3606ad2012-09-12 02:02:32 +0000134 parser.add_option('-z', '--size', action='store_true', dest='show_size', help='print the allocation size in bytes', default=False)
135 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)
Greg Claytond84bb482012-04-13 16:24:09 +0000136 parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False)
137 parser.add_option('-f', '--format', type='string', dest='format', help='the format to use when dumping memory if --memory is specified', default=None)
Greg Claytone04cfd22012-07-11 22:13:18 +0000138 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')
Greg Claytonb403a152012-04-21 00:11:26 +0000139 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)
Greg Claytond9fc5352012-05-10 23:17:28 +0000140 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)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000141 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)
142 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)
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000143 parser.add_option('-M', '--max-matches', type='int', dest='max_matches', help='the maximum number of matches to print', default=32)
Greg Clayton774ebdf2012-08-11 02:26:26 +0000144 parser.add_option('-O', '--offset', type='int', dest='offset', help='the matching data must be at this offset', default=-1)
Greg Claytonae23ed32012-10-08 22:39:38 +0000145 parser.add_option('-V', '--vm-regions', action='store_true', dest='check_vm_regions', help='Also check the VM regions', default=False)
Greg Claytond9fc5352012-05-10 23:17:28 +0000146
Greg Clayton85e62ec2013-01-30 22:57:34 +0000147def type_flags_to_string(type_flags):
148 if type_flags == 0:
149 type_str = 'free'
150 elif type_flags & 2:
151 type_str = 'malloc'
152 elif type_flags & 4:
153 type_str = 'free'
154 elif type_flags & 1:
155 type_str = 'generic'
156 elif type_flags & 8:
157 type_str = 'stack'
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000158 elif type_flags & 16:
159 type_str = 'segment'
Greg Clayton85e62ec2013-01-30 22:57:34 +0000160 else:
161 type_str = hex(type_flags)
162 return type_str
163
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000164def type_flags_to_description(type_flags, ptr_addr, ptr_size, offset):
165 show_offset = False
Greg Clayton85e62ec2013-01-30 22:57:34 +0000166 if type_flags == 0 or type_flags & 4:
167 type_str = 'free(%#x)' % (ptr_addr,)
168 elif type_flags & 2 or type_flags & 1:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000169 type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr)
170 show_offset = True
Greg Clayton85e62ec2013-01-30 22:57:34 +0000171 elif type_flags & 8:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000172 type_str = 'stack'
173 elif type_flags & 16:
174 sb_addr = lldb.debugger.GetSelectedTarget().ResolveLoadAddress(ptr_addr + offset)
175 type_str = 'segment [%#x - %#x), %s + %u, %s' % (ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000176 else:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000177 type_str = '%#x' % (ptr_addr,)
178 show_offset = True
179 if show_offset and offset != 0:
180 type_str += ' + %-6u' % (offset,)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000181 return type_str
182
183def dump_stack_history_entry(options, result, stack_history_entry, idx):
Greg Clayton7200ed12012-05-10 23:37:52 +0000184 address = int(stack_history_entry.address)
185 if address:
186 type_flags = int(stack_history_entry.type_flags)
Greg Claytond9fc5352012-05-10 23:17:28 +0000187 symbolicator = lldb.utils.symbolication.Symbolicator()
Greg Clayton85e62ec2013-01-30 22:57:34 +0000188 symbolicator.target = lldb.debugger.GetSelectedTarget()
189 type_str = type_flags_to_string(type_flags)
Greg Claytonc373ca52012-09-01 00:34:35 +0000190 result.AppendMessage('stack[%u]: addr = 0x%x, type=%s, frames:' % (idx, address, type_str))
Greg Claytond9fc5352012-05-10 23:17:28 +0000191 frame_idx = 0
Greg Clayton7200ed12012-05-10 23:37:52 +0000192 idx = 0
193 pc = int(stack_history_entry.frames[idx])
Greg Claytond9fc5352012-05-10 23:17:28 +0000194 while pc != 0:
195 if pc >= 0x1000:
196 frames = symbolicator.symbolicate(pc)
197 if frames:
198 for frame in frames:
Greg Claytonc373ca52012-09-01 00:34:35 +0000199 result.AppendMessage(' [%u] %s' % (frame_idx, frame))
Greg Claytond9fc5352012-05-10 23:17:28 +0000200 frame_idx += 1
201 else:
Greg Claytonc373ca52012-09-01 00:34:35 +0000202 result.AppendMessage(' [%u] 0x%x' % (frame_idx, pc))
Greg Claytond9fc5352012-05-10 23:17:28 +0000203 frame_idx += 1
Greg Clayton7200ed12012-05-10 23:37:52 +0000204 idx = idx + 1
205 pc = int(stack_history_entry.frames[idx])
Greg Claytond9fc5352012-05-10 23:17:28 +0000206 else:
207 pc = 0
Greg Clayton85e62ec2013-01-30 22:57:34 +0000208 if idx >= options.max_frames:
209 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))
210
Greg Claytonc373ca52012-09-01 00:34:35 +0000211 result.AppendMessage('')
Greg Claytond9fc5352012-05-10 23:17:28 +0000212
Greg Clayton85e62ec2013-01-30 22:57:34 +0000213def dump_stack_history_entries(options, result, addr, history):
Greg Claytond9fc5352012-05-10 23:17:28 +0000214 # malloc_stack_entry *get_stack_history_for_address (const void * addr)
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000215 single_expr = '''
216typedef int kern_return_t;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000217#define MAX_FRAMES %u
218typedef struct $malloc_stack_entry {
219 uint64_t address;
220 uint64_t argument;
221 uint32_t type_flags;
222 uint32_t num_frames;
223 uint64_t frames[512];
224 kern_return_t err;
225} $malloc_stack_entry;
226typedef unsigned task_t;
227$malloc_stack_entry stack;
228stack.address = 0x%x;
229stack.type_flags = 2;
230stack.num_frames = 0;
231stack.frames[0] = 0;
232uint32_t max_stack_frames = MAX_FRAMES;
233stack.err = (kern_return_t)__mach_stack_logging_get_frames (
234 (task_t)mach_task_self(),
235 stack.address,
236 &stack.frames[0],
237 max_stack_frames,
238 &stack.num_frames);
239if (stack.num_frames < MAX_FRAMES)
240 stack.frames[stack.num_frames] = 0;
241else
242 stack.frames[MAX_FRAMES-1] = 0;
243stack''' % (options.max_frames, addr);
244
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000245 history_expr = '''
246typedef int kern_return_t;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000247typedef unsigned task_t;
248#define MAX_FRAMES %u
249#define MAX_HISTORY %u
250typedef struct mach_stack_logging_record_t {
251 uint32_t type_flags;
252 uint64_t stack_identifier;
253 uint64_t argument;
254 uint64_t address;
255} mach_stack_logging_record_t;
256typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
257typedef struct malloc_stack_entry {
258 uint64_t address;
259 uint64_t argument;
260 uint32_t type_flags;
261 uint32_t num_frames;
262 uint64_t frames[MAX_FRAMES];
263 kern_return_t frames_err;
264} malloc_stack_entry;
265typedef struct $malloc_stack_history {
266 task_t task;
267 unsigned idx;
268 malloc_stack_entry entries[MAX_HISTORY];
269} $malloc_stack_history;
270$malloc_stack_history info = { (task_t)mach_task_self(), 0 };
271uint32_t max_stack_frames = MAX_FRAMES;
272enumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void {
273 $malloc_stack_history *info = ($malloc_stack_history *)baton;
274 if (info->idx < MAX_HISTORY) {
275 malloc_stack_entry *stack_entry = &(info->entries[info->idx]);
276 stack_entry->address = stack_record.address;
277 stack_entry->type_flags = stack_record.type_flags;
278 stack_entry->argument = stack_record.argument;
279 stack_entry->num_frames = 0;
280 stack_entry->frames[0] = 0;
281 stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack (
282 info->task,
283 stack_record.stack_identifier,
284 stack_entry->frames,
285 (uint32_t)MAX_FRAMES,
286 &stack_entry->num_frames);
287 // Terminate the frames with zero if there is room
288 if (stack_entry->num_frames < MAX_FRAMES)
289 stack_entry->frames[stack_entry->num_frames] = 0;
290 }
291 ++info->idx;
292};
293(kern_return_t)__mach_stack_logging_enumerate_records (info.task, (uint64_t)0x%x, callback, &info);
294info''' % (options.max_frames, options.max_history, addr);
295
296 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
297 if history:
298 expr = history_expr
299 else:
300 expr = single_expr
301 expr_sbvalue = frame.EvaluateExpression (expr)
302 if options.verbose:
303 print "expression:"
304 print expr
305 print "expression result:"
306 print expr_sbvalue
307 if expr_sbvalue.error.Success():
308 if history:
309 malloc_stack_history = lldb.value(expr_sbvalue)
310 num_stacks = int(malloc_stack_history.idx)
311 if num_stacks <= options.max_history:
312 i_max = num_stacks
313 else:
314 i_max = options.max_history
315 for i in range(i_max):
316 stack_history_entry = malloc_stack_history.entries[i]
317 dump_stack_history_entry(options, result, stack_history_entry, i)
318 if num_stacks > options.max_history:
319 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))
320 else:
321 stack_history_entry = lldb.value(expr_sbvalue)
322 dump_stack_history_entry(options, result, stack_history_entry, 0)
323
324 else:
325 result.AppendMessage('error: expression failed "%s" => %s' % (expr, expr_sbvalue.error))
326
327
328def display_match_results (result, options, arg_str_description, expr, print_no_matches = True):
329 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
330 if not frame:
331 result.AppendMessage('error: invalid frame')
332 return 0
333 expr_sbvalue = frame.EvaluateExpression (expr)
334 if options.verbose:
335 print "expression:"
336 print expr
337 print "expression result:"
338 print expr_sbvalue
Greg Clayton7fb671b2012-04-11 18:30:53 +0000339 if expr_sbvalue.error.Success():
340 if expr_sbvalue.unsigned:
341 match_value = lldb.value(expr_sbvalue)
342 i = 0
Greg Clayton774ebdf2012-08-11 02:26:26 +0000343 match_idx = 0
Greg Clayton7fb671b2012-04-11 18:30:53 +0000344 while 1:
Greg Claytone04cfd22012-07-11 22:13:18 +0000345 print_entry = True
Greg Clayton7fb671b2012-04-11 18:30:53 +0000346 match_entry = match_value[i]; i += 1
Greg Clayton85e62ec2013-01-30 22:57:34 +0000347 if i > options.max_matches:
348 result.AppendMessage('warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' % (options.max_matches))
Greg Claytone04cfd22012-07-11 22:13:18 +0000349 break
Greg Clayton7fb671b2012-04-11 18:30:53 +0000350 malloc_addr = match_entry.addr.sbvalue.unsigned
351 if malloc_addr == 0:
352 break
353 malloc_size = int(match_entry.size)
354 offset = int(match_entry.offset)
Greg Clayton774ebdf2012-08-11 02:26:26 +0000355
356 if options.offset >= 0 and options.offset != offset:
357 print_entry = False
358 else:
359 match_addr = malloc_addr + offset
360 dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000361 type_flags = int(match_entry.type)
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000362 description = '%#16.16x: %s' % (match_addr, type_flags_to_description(type_flags, malloc_addr, malloc_size, offset))
Greg Claytona3606ad2012-09-12 02:02:32 +0000363 if options.show_size:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000364 description += ' <%5u>' % (malloc_size)
Greg Claytona3606ad2012-09-12 02:02:32 +0000365 if options.show_range:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000366 description += ' [%#x - %#x)' % (malloc_addr, malloc_addr + malloc_size)
Greg Clayton774ebdf2012-08-11 02:26:26 +0000367 derefed_dynamic_value = None
368 if dynamic_value.type.name == 'void *':
369 if options.type == 'pointer' and malloc_size == 4096:
370 error = lldb.SBError()
Greg Clayton85e62ec2013-01-30 22:57:34 +0000371 process = expr_sbvalue.GetProcess()
372 target = expr_sbvalue.GetTarget()
373 data = bytearray(process.ReadMemory(malloc_addr, 16, error))
Greg Clayton774ebdf2012-08-11 02:26:26 +0000374 if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
Greg Clayton85e62ec2013-01-30 22:57:34 +0000375 ptr_size = target.addr_size
376 thread = process.ReadUnsignedFromMemory (malloc_addr + 16 + ptr_size, ptr_size, error)
Greg Claytona3606ad2012-09-12 02:02:32 +0000377 # 4 bytes 0xa1a1a1a1
378 # 12 bytes 'AUTORELEASE!'
379 # ptr bytes autorelease insertion point
380 # ptr bytes pthread_t
381 # ptr bytes next colder page
382 # ptr bytes next hotter page
383 # 4 bytes this page's depth in the list
384 # 4 bytes high-water mark
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000385 description += ' AUTORELEASE! for pthread_t %#x' % (thread)
386 # else:
387 # description += 'malloc(%u)' % (malloc_size)
388 # else:
389 # description += 'malloc(%u)' % (malloc_size)
Greg Clayton774ebdf2012-08-11 02:26:26 +0000390 else:
391 derefed_dynamic_value = dynamic_value.deref
392 if derefed_dynamic_value:
393 derefed_dynamic_type = derefed_dynamic_value.type
394 derefed_dynamic_type_size = derefed_dynamic_type.size
395 derefed_dynamic_type_name = derefed_dynamic_type.name
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000396 description += ' '
Greg Claytona3606ad2012-09-12 02:02:32 +0000397 description += derefed_dynamic_type_name
Greg Clayton774ebdf2012-08-11 02:26:26 +0000398 if offset < derefed_dynamic_type_size:
399 member_list = list();
400 get_member_types_for_offset (derefed_dynamic_type, offset, member_list)
401 if member_list:
402 member_path = ''
403 for member in member_list:
404 member_name = member.name
405 if member_name:
406 if member_path:
407 member_path += '.'
408 member_path += member_name
409 if member_path:
410 if options.ivar_regex_blacklist:
411 for ivar_regex in options.ivar_regex_blacklist:
412 if ivar_regex.match(member_path):
413 print_entry = False
Greg Claytona3606ad2012-09-12 02:02:32 +0000414 description += '.%s' % (member_path)
415 else:
416 description += '%u bytes after %s' % (offset - derefed_dynamic_type_size, derefed_dynamic_type_name)
417 else:
418 # strip the "*" from the end of the name since we were unable to dereference this
419 description += dynamic_value.type.name[0:-1]
Greg Claytone04cfd22012-07-11 22:13:18 +0000420 if print_entry:
Greg Clayton774ebdf2012-08-11 02:26:26 +0000421 match_idx += 1
Greg Clayton7143f002012-09-04 14:09:21 +0000422 result_output = ''
Greg Claytone04cfd22012-07-11 22:13:18 +0000423 if description:
Greg Clayton7143f002012-09-04 14:09:21 +0000424 result_output += description
Greg Claytone04cfd22012-07-11 22:13:18 +0000425 if options.print_type and derefed_dynamic_value:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000426 result_output += ' %s' % (derefed_dynamic_value)
Greg Claytone04cfd22012-07-11 22:13:18 +0000427 if options.print_object_description and dynamic_value:
428 desc = dynamic_value.GetObjectDescription()
429 if desc:
Greg Claytona3606ad2012-09-12 02:02:32 +0000430 result_output += '\n%s' % (desc)
Greg Clayton7143f002012-09-04 14:09:21 +0000431 if result_output:
432 result.AppendMessage(result_output)
Greg Claytone04cfd22012-07-11 22:13:18 +0000433 if options.memory:
434 cmd_result = lldb.SBCommandReturnObject()
Greg Clayton85e62ec2013-01-30 22:57:34 +0000435 if options.format == None:
436 memory_command = "memory read --force 0x%x 0x%x" % (malloc_addr, malloc_addr + malloc_size)
437 else:
438 memory_command = "memory read --force -f %s 0x%x 0x%x" % (options.format, malloc_addr, malloc_addr + malloc_size)
439 if options.verbose:
440 result.AppendMessage(memory_command)
Greg Claytone04cfd22012-07-11 22:13:18 +0000441 lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
Greg Claytonc373ca52012-09-01 00:34:35 +0000442 result.AppendMessage(cmd_result.GetOutput())
Greg Claytone04cfd22012-07-11 22:13:18 +0000443 if options.stack_history:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000444 dump_stack_history_entries(options, result, malloc_addr, 1)
Greg Claytone04cfd22012-07-11 22:13:18 +0000445 elif options.stack:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000446 dump_stack_history_entries(options, result, malloc_addr, 0)
Greg Claytone04cfd22012-07-11 22:13:18 +0000447 return i
448 elif print_no_matches:
Greg Claytonc373ca52012-09-01 00:34:35 +0000449 result.AppendMessage('no matches found for %s' % (arg_str_description))
Greg Clayton7fb671b2012-04-11 18:30:53 +0000450 else:
Greg Clayton7143f002012-09-04 14:09:21 +0000451 result.AppendMessage(str(expr_sbvalue.error))
Greg Claytone04cfd22012-07-11 22:13:18 +0000452 return 0
453
Greg Clayton85e62ec2013-01-30 22:57:34 +0000454def get_ptr_refs_options ():
455 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
456 description='''Searches all allocations on the heap for pointer values on
457darwin user space programs. Any matches that were found will dump the malloc
458blocks that contain the pointers and might be able to print what kind of
459objects the pointers are contained in using dynamic type information in the
460program.'''
461 parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
462 add_common_options(parser)
463 return parser
Greg Clayton7fb671b2012-04-11 18:30:53 +0000464
Greg Clayton77677162012-04-12 18:57:36 +0000465def ptr_refs(debugger, command, result, dict):
Greg Clayton804de012012-04-11 16:27:06 +0000466 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000467 parser = get_ptr_refs_options()
Greg Clayton804de012012-04-11 16:27:06 +0000468 try:
469 (options, args) = parser.parse_args(command_args)
470 except:
471 return
Greg Clayton7fb671b2012-04-11 18:30:53 +0000472
Greg Clayton85e62ec2013-01-30 22:57:34 +0000473 process = lldb.debugger.GetSelectedTarget().GetProcess()
474 if not process:
475 result.AppendMessage('error: invalid process')
476 return
477 frame = process.GetSelectedThread().GetSelectedFrame()
478 if not frame:
479 result.AppendMessage('error: invalid frame')
480 return
481
Greg Clayton7fb671b2012-04-11 18:30:53 +0000482 options.type = 'pointer'
Greg Clayton85e62ec2013-01-30 22:57:34 +0000483 if options.format == None:
484 options.format = "A" # 'A' is "address" format
485
Greg Clayton804de012012-04-11 16:27:06 +0000486 if args:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000487 # When we initialize the expression, we must define any types that
488 # we will need when looking at every allocation. We must also define
489 # a type named callback_baton_t and make an instance named "baton"
490 # and initialize it how ever we want to. The address of "baton" will
491 # be passed into our range callback. callback_baton_t must contain
492 # a member named "callback" whose type is "range_callback_t". This
493 # will be used by our zone callbacks to call the range callback for
494 # each malloc range.
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000495 user_init_code_format = '''
496#define MAX_MATCHES %u
Greg Clayton85e62ec2013-01-30 22:57:34 +0000497struct $malloc_match {
498 void *addr;
499 uintptr_t size;
500 uintptr_t offset;
501 uintptr_t type;
502};
503typedef struct callback_baton_t {
504 range_callback_t callback;
505 unsigned num_matches;
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000506 $malloc_match matches[MAX_MATCHES];
Greg Clayton85e62ec2013-01-30 22:57:34 +0000507 void *ptr;
508} callback_baton_t;
509range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
510 callback_baton_t *info = (callback_baton_t *)baton;
511 typedef void* T;
512 const unsigned size = sizeof(T);
513 T *array = (T*)ptr_addr;
514 for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) {
515 if (array[idx] == info->ptr) {
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000516 if (info->num_matches < MAX_MATCHES) {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000517 info->matches[info->num_matches].addr = (void*)ptr_addr;
518 info->matches[info->num_matches].size = ptr_size;
519 info->matches[info->num_matches].offset = idx*sizeof(T);
520 info->matches[info->num_matches].type = type;
521 ++info->num_matches;
522 }
523 }
524 }
525};
526callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
527'''
528 # We must also define a snippet of code to be run that returns
529 # the result of the expression we run.
530 # Here we return NULL if our pointer was not found in any malloc blocks,
531 # and we return the address of the matches array so we can then access
532 # the matching results
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000533 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
534 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
Greg Clayton85e62ec2013-01-30 22:57:34 +0000535baton.matches'''
536 # Iterate through all of our pointer expressions and display the results
537 for ptr_expr in args:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000538 user_init_code = user_init_code_format % (options.max_matches, ptr_expr)
539 expr = get_iterate_memory_expr(process, user_init_code, user_return_code)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000540 arg_str_description = 'malloc block containing pointer %s' % ptr_expr
541 display_match_results (result, options, arg_str_description, expr)
Greg Clayton804de012012-04-11 16:27:06 +0000542 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000543 result.AppendMessage('error: no pointer arguments were given')
544
545def get_cstr_refs_options():
546 usage = "usage: %prog [options] <CSTR> [CSTR ...]"
547 description='''Searches all allocations on the heap for C string values on
548darwin user space programs. Any matches that were found will dump the malloc
549blocks that contain the C strings and might be able to print what kind of
550objects the pointers are contained in using dynamic type information in the
551program.'''
552 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
553 add_common_options(parser)
554 return parser
Greg Clayton804de012012-04-11 16:27:06 +0000555
Greg Clayton77677162012-04-12 18:57:36 +0000556def cstr_refs(debugger, command, result, dict):
Greg Clayton7fb671b2012-04-11 18:30:53 +0000557 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000558 parser = get_cstr_refs_options();
Greg Clayton7fb671b2012-04-11 18:30:53 +0000559 try:
560 (options, args) = parser.parse_args(command_args)
561 except:
562 return
563
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000564 process = lldb.debugger.GetSelectedTarget().GetProcess()
565 if not process:
566 result.AppendMessage('error: invalid process')
567 return
568 frame = process.GetSelectedThread().GetSelectedFrame()
Greg Clayton85e62ec2013-01-30 22:57:34 +0000569 if not frame:
570 result.AppendMessage('error: invalid frame')
571 return
572
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000573
Greg Clayton7fb671b2012-04-11 18:30:53 +0000574 options.type = 'cstr'
Greg Clayton85e62ec2013-01-30 22:57:34 +0000575 if options.format == None:
576 options.format = "Y" # 'Y' is "bytes with ASCII" format
Greg Clayton7fb671b2012-04-11 18:30:53 +0000577
578 if args:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000579 # When we initialize the expression, we must define any types that
580 # we will need when looking at every allocation. We must also define
581 # a type named callback_baton_t and make an instance named "baton"
582 # and initialize it how ever we want to. The address of "baton" will
583 # be passed into our range callback. callback_baton_t must contain
584 # a member named "callback" whose type is "range_callback_t". This
585 # will be used by our zone callbacks to call the range callback for
586 # each malloc range.
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000587 user_init_code_format = '''
588#define MAX_MATCHES %u
589struct $malloc_match {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000590 void *addr;
591 uintptr_t size;
592 uintptr_t offset;
593 uintptr_t type;
594};
595typedef struct callback_baton_t {
596 range_callback_t callback;
597 unsigned num_matches;
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000598 $malloc_match matches[MAX_MATCHES];
Greg Clayton85e62ec2013-01-30 22:57:34 +0000599 const char *cstr;
600 unsigned cstr_len;
601} callback_baton_t;
602range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
603 callback_baton_t *info = (callback_baton_t *)baton;
604 if (info->cstr_len < ptr_size) {
605 const char *begin = (const char *)ptr_addr;
606 const char *end = begin + ptr_size - info->cstr_len;
607 for (const char *s = begin; s < end; ++s) {
608 if ((int)memcmp(s, info->cstr, info->cstr_len) == 0) {
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000609 if (info->num_matches < MAX_MATCHES) {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000610 info->matches[info->num_matches].addr = (void*)ptr_addr;
611 info->matches[info->num_matches].size = ptr_size;
612 info->matches[info->num_matches].offset = s - begin;
613 info->matches[info->num_matches].type = type;
614 ++info->num_matches;
615 }
616 }
617 }
618 }
619};
620const char *cstr = "%s";
621callback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };'''
622 # We must also define a snippet of code to be run that returns
623 # the result of the expression we run.
624 # Here we return NULL if our pointer was not found in any malloc blocks,
625 # and we return the address of the matches array so we can then access
626 # the matching results
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000627 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
628 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
629baton.matches'''
Greg Clayton85e62ec2013-01-30 22:57:34 +0000630 # Iterate through all of our pointer expressions and display the results
631 for cstr in args:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000632 user_init_code = user_init_code_format % (options.max_matches, cstr)
633 expr = get_iterate_memory_expr(process, user_init_code, user_return_code)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000634 arg_str_description = 'malloc block containing "%s"' % cstr
635 display_match_results (result, options, arg_str_description, expr)
Greg Clayton7fb671b2012-04-11 18:30:53 +0000636 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000637 result.AppendMessage('error: command takes one or more C string arguments')
638
639
640def get_malloc_info_options():
641 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
642 description='''Searches the heap a malloc block that contains the addresses
643specified as one or more address expressions. Any matches that were found will
644dump the malloc blocks that match or contain the specified address. The matching
645blocks might be able to show what kind of objects they are using dynamic type
646information in the program.'''
647 parser = optparse.OptionParser(description=description, prog='malloc_info',usage=usage)
648 add_common_options(parser)
649 return parser
Greg Clayton804de012012-04-11 16:27:06 +0000650
Greg Clayton77677162012-04-12 18:57:36 +0000651def malloc_info(debugger, command, result, dict):
652 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000653 parser = get_malloc_info_options()
Greg Clayton77677162012-04-12 18:57:36 +0000654 try:
655 (options, args) = parser.parse_args(command_args)
656 except:
657 return
Greg Clayton77677162012-04-12 18:57:36 +0000658 options.type = 'addr'
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000659
660 process = lldb.debugger.GetSelectedTarget().GetProcess()
661 if not process:
662 result.AppendMessage('error: invalid process')
663 return
664 frame = process.GetSelectedThread().GetSelectedFrame()
665 if not frame:
666 result.AppendMessage('error: invalid frame')
667 return
Greg Clayton85e62ec2013-01-30 22:57:34 +0000668
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000669 user_init_code_format = '''
670struct $malloc_match {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000671 void *addr;
672 uintptr_t size;
673 uintptr_t offset;
674 uintptr_t type;
675};
676typedef struct callback_baton_t {
677 range_callback_t callback;
678 unsigned num_matches;
679 $malloc_match matches[2]; // Two items so they can be NULL terminated
680 void *ptr;
681} callback_baton_t;
682range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
683 callback_baton_t *info = (callback_baton_t *)baton;
684 if (info->num_matches == 0) {
685 uint8_t *p = (uint8_t *)info->ptr;
686 uint8_t *lo = (uint8_t *)ptr_addr;
687 uint8_t *hi = lo + ptr_size;
688 if (lo <= p && p < hi) {
689 info->matches[info->num_matches].addr = (void*)ptr_addr;
690 info->matches[info->num_matches].size = ptr_size;
691 info->matches[info->num_matches].offset = p - lo;
692 info->matches[info->num_matches].type = type;
693 info->num_matches = 1;
694 }
695 }
696};
697callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000698baton.matches[0].addr = 0;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000699baton.matches[1].addr = 0;'''
Greg Clayton77677162012-04-12 18:57:36 +0000700 if args:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000701 for ptr_expr in args:
702 user_init_code = user_init_code_format % (ptr_expr)
703 expr = get_iterate_memory_expr(process, user_init_code, 'baton.matches')
704 arg_str_description = 'malloc block that contains %s' % ptr_expr
705 display_match_results (result, options, arg_str_description, expr)
Greg Clayton77677162012-04-12 18:57:36 +0000706 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000707 result.AppendMessage('error: command takes one or more pointer expressions')
Greg Clayton77677162012-04-12 18:57:36 +0000708
Greg Clayton85e62ec2013-01-30 22:57:34 +0000709def get_thread_stack_ranges_struct (process):
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000710 '''Create code that defines a structure that represents threads stack bounds
711 for all threads. It returns a static sized array initialized with all of
712 the tid, base, size structs for all the threads.'''
Greg Clayton85e62ec2013-01-30 22:57:34 +0000713 stack_dicts = list()
714 if process:
715 i = 0;
716 for thread in process:
717 min_sp = thread.frame[0].sp
718 max_sp = min_sp
719 for frame in thread.frames:
720 sp = frame.sp
721 if sp < min_sp: min_sp = sp
722 if sp > max_sp: max_sp = sp
723 if min_sp < max_sp:
724 stack_dicts.append ({ 'tid' : thread.GetThreadID(), 'base' : min_sp , 'size' : max_sp-min_sp, 'index' : i })
725 i += 1
726 stack_dicts_len = len(stack_dicts)
727 if stack_dicts_len > 0:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000728 result = '''
729#define NUM_STACKS %u
Greg Clayton85e62ec2013-01-30 22:57:34 +0000730typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000731thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len,)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000732 for stack_dict in stack_dicts:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000733 result += '''
734stacks[%(index)u].tid = 0x%(tid)x;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000735stacks[%(index)u].base = 0x%(base)x;
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000736stacks[%(index)u].size = 0x%(size)x;''' % stack_dict
Greg Clayton85e62ec2013-01-30 22:57:34 +0000737 return result
738 else:
739 return None
Greg Claytonae23ed32012-10-08 22:39:38 +0000740
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000741def get_sections_ranges_struct (process):
742 '''Create code that defines a structure that represents all segments that
743 can contain data for all images in "target". It returns a static sized
744 array initialized with all of base, size structs for all the threads.'''
745 target = process.target
746 segment_dicts = list()
747 for (module_idx, module) in enumerate(target.modules):
748 for sect_idx in range(module.GetNumSections()):
749 section = module.GetSectionAtIndex(sect_idx)
750 if not section:
751 break
752 name = section.name
753 if name != '__TEXT' and name != '__LINKEDIT' and name != '__PAGEZERO':
754 base = section.GetLoadAddress(target)
755 size = section.GetByteSize()
756 if base != lldb.LLDB_INVALID_ADDRESS and size > 0:
757 segment_dicts.append ({ 'base' : base, 'size' : size })
758 segment_dicts_len = len(segment_dicts)
759 if segment_dicts_len > 0:
760 result = '''
761#define NUM_SEGMENTS %u
762typedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t;
763segment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,)
764 for (idx, segment_dict) in enumerate(segment_dicts):
765 segment_dict['index'] = idx
766 result += '''
767segments[%(index)u].base = 0x%(base)x;
768segments[%(index)u].size = 0x%(size)x;''' % segment_dict
769 return result
Greg Claytonae23ed32012-10-08 22:39:38 +0000770 else:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000771 return None
Greg Claytonae23ed32012-10-08 22:39:38 +0000772
Greg Claytone04cfd22012-07-11 22:13:18 +0000773def section_ptr_refs(debugger, command, result, dict):
774 command_args = shlex.split(command)
775 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
776 description='''Searches section contents for pointer values in darwin user space programs.'''
777 parser = optparse.OptionParser(description=description, prog='section_ptr_refs',usage=usage)
778 add_common_options(parser)
779 parser.add_option('--section', action='append', type='string', dest='section_names', help='section name to search', default=list())
780 try:
781 (options, args) = parser.parse_args(command_args)
782 except:
783 return
784
785 options.type = 'pointer'
Greg Claytonae23ed32012-10-08 22:39:38 +0000786
Greg Claytone04cfd22012-07-11 22:13:18 +0000787 sections = list()
788 section_modules = list()
789 if not options.section_names:
Greg Claytonc373ca52012-09-01 00:34:35 +0000790 result.AppendMessage('error: at least one section must be specified with the --section option')
Greg Claytone04cfd22012-07-11 22:13:18 +0000791 return
792
Greg Clayton85e62ec2013-01-30 22:57:34 +0000793 target = lldb.debugger.GetSelectedTarget()
794 for module in target.modules:
Greg Claytone04cfd22012-07-11 22:13:18 +0000795 for section_name in options.section_names:
796 section = module.section[section_name]
797 if section:
798 sections.append (section)
799 section_modules.append (module)
800 if sections:
801 dylid_load_err = load_dylib()
802 if dylid_load_err:
Greg Claytonc373ca52012-09-01 00:34:35 +0000803 result.AppendMessage(dylid_load_err)
Greg Claytone04cfd22012-07-11 22:13:18 +0000804 return
Greg Clayton85e62ec2013-01-30 22:57:34 +0000805 frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
Greg Claytone04cfd22012-07-11 22:13:18 +0000806 for expr_str in args:
807 for (idx, section) in enumerate(sections):
808 expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (section.addr.load_addr, section.size, expr_str)
809 arg_str_description = 'section %s.%s containing "%s"' % (section_modules[idx].file.fullpath, section.name, expr_str)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000810 num_matches = display_match_results (result, options, arg_str_description, expr, False)
Greg Claytone04cfd22012-07-11 22:13:18 +0000811 if num_matches:
812 if num_matches < options.max_matches:
813 options.max_matches = options.max_matches - num_matches
814 else:
815 options.max_matches = 0
816 if options.max_matches == 0:
817 return
818 else:
Greg Claytonc373ca52012-09-01 00:34:35 +0000819 result.AppendMessage('error: no sections were found that match any of %s' % (', '.join(options.section_names)))
Greg Claytone04cfd22012-07-11 22:13:18 +0000820
Greg Clayton85e62ec2013-01-30 22:57:34 +0000821def get_objc_refs_options():
822 usage = "usage: %prog [options] <CLASS> [CLASS ...]"
823 description='''Searches all allocations on the heap for instances of
824objective C classes, or any classes that inherit from the specified classes
825in darwin user space programs. Any matches that were found will dump the malloc
826blocks that contain the C strings and might be able to print what kind of
827objects the pointers are contained in using dynamic type information in the
828program.'''
829 parser = optparse.OptionParser(description=description, prog='objc_refs',usage=usage)
830 add_common_options(parser)
831 return parser
832
Greg Clayton774ebdf2012-08-11 02:26:26 +0000833def objc_refs(debugger, command, result, dict):
834 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000835 parser = get_objc_refs_options()
Greg Clayton774ebdf2012-08-11 02:26:26 +0000836 try:
837 (options, args) = parser.parse_args(command_args)
838 except:
839 return
840
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000841 process = lldb.debugger.GetSelectedTarget().GetProcess()
842 if not process:
843 result.AppendMessage('error: invalid process')
844 return
845 frame = process.GetSelectedThread().GetSelectedFrame()
Greg Clayton85e62ec2013-01-30 22:57:34 +0000846 if not frame:
847 result.AppendMessage('error: invalid frame')
848 return
849
850 options.type = 'isa'
851 if options.format == None:
852 options.format = "A" # 'A' is "address" format
853
854 num_objc_classes_value = frame.EvaluateExpression("(int)objc_getClassList((void *)0, (int)0)")
855 if not num_objc_classes_value.error.Success():
856 result.AppendMessage('error: %s' % num_objc_classes_value.error.GetCString())
857 return
858
859 num_objc_classes = num_objc_classes_value.GetValueAsUnsigned()
860 if num_objc_classes == 0:
861 result.AppendMessage('error: no objective C classes in program')
862 return
863
864 if args:
865 # When we initialize the expression, we must define any types that
866 # we will need when looking at every allocation. We must also define
867 # a type named callback_baton_t and make an instance named "baton"
868 # and initialize it how ever we want to. The address of "baton" will
869 # be passed into our range callback. callback_baton_t must contain
870 # a member named "callback" whose type is "range_callback_t". This
871 # will be used by our zone callbacks to call the range callback for
872 # each malloc range.
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000873 user_init_code_format = '''#define MAX_MATCHES %u
874struct $malloc_match {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000875 void *addr;
876 uintptr_t size;
877 uintptr_t offset;
878 uintptr_t type;
879};
880typedef int (*compare_callback_t)(const void *a, const void *b);
881typedef struct callback_baton_t {
882 range_callback_t callback;
883 compare_callback_t compare_callback;
884 unsigned num_matches;
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000885 $malloc_match matches[MAX_MATCHES];
Greg Clayton85e62ec2013-01-30 22:57:34 +0000886 void *isa;
887 Class classes[%u];
888} callback_baton_t;
889compare_callback_t compare_callback = [](const void *a, const void *b) -> int {
890 Class a_ptr = *(Class *)a;
891 Class b_ptr = *(Class *)b;
892 if (a_ptr < b_ptr) return -1;
893 if (a_ptr > b_ptr) return +1;
894 return 0;
895};
896range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
897 callback_baton_t *info = (callback_baton_t *)baton;
898 if (sizeof(Class) <= ptr_size) {
899 Class *curr_class_ptr = (Class *)ptr_addr;
900 Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr,
901 (const void *)info->classes,
902 sizeof(info->classes)/sizeof(Class),
903 sizeof(Class),
904 info->compare_callback);
905 if (matching_class_ptr) {
906 bool match = false;
907 if (info->isa) {
908 Class isa = *curr_class_ptr;
909 if (info->isa == isa)
910 match = true;
911 else { // if (info->objc.match_superclasses) {
912 Class super = (Class)class_getSuperclass(isa);
913 while (super) {
914 if (super == info->isa) {
915 match = true;
916 break;
917 }
918 super = (Class)class_getSuperclass(super);
919 }
920 }
921 }
922 else
923 match = true;
924 if (match) {
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000925 if (info->num_matches < MAX_MATCHES) {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000926 info->matches[info->num_matches].addr = (void*)ptr_addr;
927 info->matches[info->num_matches].size = ptr_size;
928 info->matches[info->num_matches].offset = 0;
929 info->matches[info->num_matches].type = type;
930 ++info->num_matches;
931 }
932 }
933 }
934 }
935};
936callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
937int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class));
938(void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);'''
939 # We must also define a snippet of code to be run that returns
940 # the result of the expression we run.
941 # Here we return NULL if our pointer was not found in any malloc blocks,
942 # and we return the address of the matches array so we can then access
943 # the matching results
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000944 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
945 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
946 baton.matches'''
Greg Clayton85e62ec2013-01-30 22:57:34 +0000947 # Iterate through all of our ObjC class name arguments
948 for class_name in args:
949 addr_expr_str = "(void *)[%s class]" % class_name
950 expr_sbvalue = frame.EvaluateExpression (addr_expr_str)
951 if expr_sbvalue.error.Success():
952 isa = expr_sbvalue.unsigned
953 if isa:
954 options.type = 'isa'
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000955 result.AppendMessage('Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' % (class_name, isa))
956 user_init_code = user_init_code_format % (options.max_matches, num_objc_classes, isa)
957 expr = get_iterate_memory_expr(process, user_init_code, user_return_code)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000958 arg_str_description = 'objective C classes with isa 0x%x' % isa
959 display_match_results (result, options, arg_str_description, expr)
Greg Clayton774ebdf2012-08-11 02:26:26 +0000960 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000961 result.AppendMessage('error: Can\'t find isa for an ObjC class named "%s"' % (class_name))
962 else:
963 result.AppendMessage('error: expression error for "%s": %s' % (addr_expr_str, expr_sbvalue.error))
964 else:
965 result.AppendMessage('error: command takes one or more C string arguments');
Greg Clayton774ebdf2012-08-11 02:26:26 +0000966
Greg Claytond712ef02012-04-25 18:40:20 +0000967if __name__ == '__main__':
968 lldb.debugger = lldb.SBDebugger.Create()
969
970# This initializer is being run from LLDB in the embedded command interpreter
971# Add any commands contained in this module to LLDB
Greg Clayton85e62ec2013-01-30 22:57:34 +0000972if __package__:
973 package_name = __package__ + '.' + __name__
974else:
975 package_name = __name__
976
977# Make the options so we can generate the help text for the new LLDB
978# command line command prior to registering it with LLDB below. This way
979# if clients in LLDB type "help malloc_info", they will see the exact same
980# output as typing "malloc_info --help".
981ptr_refs.__doc__ = get_ptr_refs_options().format_help()
982cstr_refs.__doc__ = get_cstr_refs_options().format_help()
983malloc_info.__doc__ = get_malloc_info_options().format_help()
984objc_refs.__doc__ = get_objc_refs_options().format_help()
985lldb.debugger.HandleCommand('command script add -f %s.ptr_refs ptr_refs' % package_name)
986lldb.debugger.HandleCommand('command script add -f %s.cstr_refs cstr_refs' % package_name)
987lldb.debugger.HandleCommand('command script add -f %s.malloc_info malloc_info' % package_name)
988# lldb.debugger.HandleCommand('command script add -f %s.heap heap' % package_name)
989# lldb.debugger.HandleCommand('command script add -f %s.section_ptr_refs section_ptr_refs' % package_name)
990# lldb.debugger.HandleCommand('command script add -f %s.stack_ptr_refs stack_ptr_refs' % package_name)
991lldb.debugger.HandleCommand('command script add -f %s.objc_refs objc_refs' % package_name)
992print '"malloc_info", "ptr_refs", "cstr_refs", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help.'
Greg Clayton804de012012-04-11 16:27:06 +0000993
994
995
996