blob: 92dbf67c4197ec33b54934d3f5e70f62f4ca54be [file] [log] [blame]
Greg Claytone93e24f2012-04-11 16:27:06 +00001#!/usr/bin/python
2
3#----------------------------------------------------------------------
Greg Clayton1dae6f32012-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 Claytone93e24f2012-04-11 16:27:06 +00007#
Greg Clayton1dae6f32012-04-25 18:40:20 +00008# (lldb) script import lldb.macosx.heap
Greg Claytone93e24f2012-04-11 16:27:06 +00009#----------------------------------------------------------------------
10
11import lldb
12import commands
13import optparse
Greg Clayton96666442012-04-11 18:30:53 +000014import os
Greg Clayton1dae6f32012-04-25 18:40:20 +000015import os.path
Greg Clayton4c5c4292012-07-11 22:13:18 +000016import re
Greg Claytone93e24f2012-04-11 16:27:06 +000017import shlex
Greg Clayton1dae6f32012-04-25 18:40:20 +000018import string
19import tempfile
Greg Clayton6f2f0ab2012-04-25 01:49:50 +000020import lldb.utils.symbolication
Greg Claytone93e24f2012-04-11 16:27:06 +000021
Greg Clayton1dae6f32012-04-25 18:40:20 +000022g_libheap_dylib_dir = None
23g_libheap_dylib_dict = dict()
24
Greg Clayton849acc82013-01-30 22:57:34 +000025g_iterate_malloc_blocks_expr = '''typedef unsigned natural_t;
26typedef uintptr_t vm_size_t;
27typedef uintptr_t vm_address_t;
28typedef natural_t task_t;
29typedef int kern_return_t;
30#define KERN_SUCCESS 0
31#define MALLOC_PTR_IN_USE_RANGE_TYPE 1
32typedef struct vm_range_t {
33 vm_address_t address;
34 vm_size_t size;
35} vm_range_t;
36typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory);
37typedef void (*vm_range_recorder_t)(task_t task, void *baton, unsigned type, vm_range_t *range, unsigned size);
38typedef void (*range_callback_t)(task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size);
39typedef struct malloc_introspection_t {
40 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 */
41} malloc_introspection_t;
42typedef struct malloc_zone_t {
43 void *reserved1[12];
44 struct malloc_introspection_t *introspect;
45} malloc_zone_t;
46memory_reader_t task_peek = [](task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) -> kern_return_t {
47 *local_memory = (void*) remote_address;
48 return KERN_SUCCESS;
49};
50vm_address_t *zones = 0;
51unsigned int num_zones = 0;
52%s
53task_t task = 0;
54kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones);
55if (KERN_SUCCESS == err)
56{
57 for (unsigned int i=0; i<num_zones; ++i)
58 {
59 const malloc_zone_t *zone = (const malloc_zone_t *)zones[i];
60 if (zone && zone->introspect)
61 zone->introspect->enumerator (task,
62 &baton,
63 MALLOC_PTR_IN_USE_RANGE_TYPE,
64 (vm_address_t)zone,
65 task_peek,
66 [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void
67 {
68 range_callback_t callback = ((callback_baton_t *)baton)->callback;
69 for (unsigned i=0; i<size; ++i)
70 {
71 callback (task, baton, type, ranges[i].address, ranges[i].size);
72 }
73 });
74 }
75}
76%s
77'''
Greg Clayton9098fee2012-04-21 00:11:26 +000078def load_dylib():
Greg Clayton849acc82013-01-30 22:57:34 +000079 target = lldb.debugger.GetSelectedTarget()
80 if target:
Greg Clayton1dae6f32012-04-25 18:40:20 +000081 global g_libheap_dylib_dir
82 global g_libheap_dylib_dict
Greg Clayton849acc82013-01-30 22:57:34 +000083 triple = target.triple
Greg Clayton3e339792012-04-25 21:23:07 +000084 if triple in g_libheap_dylib_dict:
85 libheap_dylib_path = g_libheap_dylib_dict[triple]
86 else:
Greg Clayton1dae6f32012-04-25 18:40:20 +000087 if not g_libheap_dylib_dir:
Greg Clayton3e339792012-04-25 21:23:07 +000088 g_libheap_dylib_dir = tempfile.gettempdir() + '/lldb-dylibs'
89 triple_dir = g_libheap_dylib_dir + '/' + triple + '/' + __name__
Greg Clayton1dae6f32012-04-25 18:40:20 +000090 if not os.path.exists(triple_dir):
Greg Clayton3e339792012-04-25 21:23:07 +000091 os.makedirs(triple_dir)
Greg Clayton1dae6f32012-04-25 18:40:20 +000092 libheap_dylib_path = triple_dir + '/libheap.dylib'
93 g_libheap_dylib_dict[triple] = libheap_dylib_path
Greg Clayton3e339792012-04-25 21:23:07 +000094 heap_code_directory = os.path.dirname(__file__) + '/heap'
95 heap_source_file = heap_code_directory + '/heap_find.cpp'
96 # Check if the dylib doesn't exist, or if "heap_find.cpp" is newer than the dylib
97 if not os.path.exists(libheap_dylib_path) or os.stat(heap_source_file).st_mtime > os.stat(libheap_dylib_path).st_mtime:
98 # Remake the dylib
Greg Clayton1dae6f32012-04-25 18:40:20 +000099 make_command = '(cd "%s" ; make EXE="%s" ARCH=%s)' % (heap_code_directory, libheap_dylib_path, string.split(triple, '-')[0])
Greg Clayton0cae0632012-06-27 19:57:59 +0000100 (make_exit_status, make_output) = commands.getstatusoutput(make_command)
101 if make_exit_status != 0:
Greg Clayton85df60c2012-09-01 00:34:35 +0000102 return 'error: make failed: %s' % (make_output)
Greg Clayton9098fee2012-04-21 00:11:26 +0000103 if os.path.exists(libheap_dylib_path):
104 libheap_dylib_spec = lldb.SBFileSpec(libheap_dylib_path)
Greg Clayton849acc82013-01-30 22:57:34 +0000105 if target.FindModule(libheap_dylib_spec):
Greg Clayton9098fee2012-04-21 00:11:26 +0000106 return None # success, 'libheap.dylib' already loaded
Greg Clayton849acc82013-01-30 22:57:34 +0000107 process = target.GetProcess()
108 if process:
109 state = process.state
Greg Clayton9098fee2012-04-21 00:11:26 +0000110 if state == lldb.eStateStopped:
111 (libheap_dylib_path)
112 error = lldb.SBError()
Greg Clayton849acc82013-01-30 22:57:34 +0000113 image_idx = process.LoadImage(libheap_dylib_spec, error)
Greg Clayton9098fee2012-04-21 00:11:26 +0000114 if error.Success():
115 return None
116 else:
117 if error:
118 return 'error: %s' % error
119 else:
120 return 'error: "process load \'%s\'" failed' % libheap_dylib_spec
121 else:
122 return 'error: process is not stopped'
123 else:
124 return 'error: invalid process'
125 else:
126 return 'error: file does not exist "%s"' % libheap_dylib_path
127 else:
128 return 'error: invalid target'
129
130 debugger.HandleCommand('process load "%s"' % libheap_dylib_path)
Greg Clayton849acc82013-01-30 22:57:34 +0000131 if target.FindModule(libheap_dylib_spec):
Greg Claytonbf479652012-05-11 02:42:36 +0000132 return None # success, 'libheap.dylib' already loaded
133 return 'error: failed to load "%s"' % libheap_dylib_path
Greg Clayton0d0f56d2012-07-07 01:22:45 +0000134
135def get_member_types_for_offset(value_type, offset, member_list):
136 member = value_type.GetFieldAtIndex(0)
137 search_bases = False
138 if member:
139 if member.GetOffsetInBytes() <= offset:
140 for field_idx in range (value_type.GetNumberOfFields()):
141 member = value_type.GetFieldAtIndex(field_idx)
142 member_byte_offset = member.GetOffsetInBytes()
143 member_end_byte_offset = member_byte_offset + member.type.size
144 if member_byte_offset <= offset and offset < member_end_byte_offset:
145 member_list.append(member)
146 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
147 return
148 else:
149 search_bases = True
150 else:
151 search_bases = True
152 if search_bases:
153 for field_idx in range (value_type.GetNumberOfDirectBaseClasses()):
154 member = value_type.GetDirectBaseClassAtIndex(field_idx)
155 member_byte_offset = member.GetOffsetInBytes()
156 member_end_byte_offset = member_byte_offset + member.type.size
157 if member_byte_offset <= offset and offset < member_end_byte_offset:
158 member_list.append(member)
159 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
160 return
161 for field_idx in range (value_type.GetNumberOfVirtualBaseClasses()):
162 member = value_type.GetVirtualBaseClassAtIndex(field_idx)
163 member_byte_offset = member.GetOffsetInBytes()
164 member_end_byte_offset = member_byte_offset + member.type.size
165 if member_byte_offset <= offset and offset < member_end_byte_offset:
166 member_list.append(member)
167 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
168 return
Greg Clayton4c5c4292012-07-11 22:13:18 +0000169
170def append_regex_callback(option, opt, value, parser):
171 try:
172 ivar_regex = re.compile(value)
173 parser.values.ivar_regex_blacklist.append(ivar_regex)
174 except:
175 print 'error: an exception was thrown when compiling the ivar regular expression for "%s"' % value
Greg Clayton9098fee2012-04-21 00:11:26 +0000176
Greg Clayton93e5ba52012-04-13 16:24:09 +0000177def add_common_options(parser):
178 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000179 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 Clayton93e5ba52012-04-13 16:24:09 +0000180 parser.add_option('-o', '--po', action='store_true', dest='print_object_description', help='print the object descriptions for any matches', default=False)
Greg Claytona936c6e2012-09-12 02:02:32 +0000181 parser.add_option('-z', '--size', action='store_true', dest='show_size', help='print the allocation size in bytes', default=False)
182 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 Clayton93e5ba52012-04-13 16:24:09 +0000183 parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False)
184 parser.add_option('-f', '--format', type='string', dest='format', help='the format to use when dumping memory if --memory is specified', default=None)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000185 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 Clayton9098fee2012-04-21 00:11:26 +0000186 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 Clayton7a245762012-05-10 23:17:28 +0000187 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 Clayton849acc82013-01-30 22:57:34 +0000188 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)
189 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 Clayton4c5c4292012-07-11 22:13:18 +0000190 parser.add_option('-M', '--max-matches', type='int', dest='max_matches', help='the maximum number of matches to print', default=256)
Greg Claytonab20f292012-08-11 02:26:26 +0000191 parser.add_option('-O', '--offset', type='int', dest='offset', help='the matching data must be at this offset', default=-1)
Greg Clayton130a3122012-10-08 22:39:38 +0000192 parser.add_option('-V', '--vm-regions', action='store_true', dest='check_vm_regions', help='Also check the VM regions', default=False)
Greg Clayton7a245762012-05-10 23:17:28 +0000193
Greg Clayton849acc82013-01-30 22:57:34 +0000194def type_flags_to_string(type_flags):
195 if type_flags == 0:
196 type_str = 'free'
197 elif type_flags & 2:
198 type_str = 'malloc'
199 elif type_flags & 4:
200 type_str = 'free'
201 elif type_flags & 1:
202 type_str = 'generic'
203 elif type_flags & 8:
204 type_str = 'stack'
205 else:
206 type_str = hex(type_flags)
207 return type_str
208
209def type_flags_to_description(type_flags, addr, ptr_addr, ptr_size):
210 if type_flags == 0 or type_flags & 4:
211 type_str = 'free(%#x)' % (ptr_addr,)
212 elif type_flags & 2 or type_flags & 1:
213 type_str = 'malloc(%5u) -> %#x' % (ptr_size, ptr_addr)
214 elif type_flags & 8:
215 type_str = 'stack @ %#x' % (addr,)
216 else:
217 type_str = hex(type_flags)
218 return type_str
219
220def dump_stack_history_entry(options, result, stack_history_entry, idx):
Greg Clayton6f446f32012-05-10 23:37:52 +0000221 address = int(stack_history_entry.address)
222 if address:
223 type_flags = int(stack_history_entry.type_flags)
Greg Clayton7a245762012-05-10 23:17:28 +0000224 symbolicator = lldb.utils.symbolication.Symbolicator()
Greg Clayton849acc82013-01-30 22:57:34 +0000225 symbolicator.target = lldb.debugger.GetSelectedTarget()
226 type_str = type_flags_to_string(type_flags)
Greg Clayton85df60c2012-09-01 00:34:35 +0000227 result.AppendMessage('stack[%u]: addr = 0x%x, type=%s, frames:' % (idx, address, type_str))
Greg Clayton7a245762012-05-10 23:17:28 +0000228 frame_idx = 0
Greg Clayton6f446f32012-05-10 23:37:52 +0000229 idx = 0
230 pc = int(stack_history_entry.frames[idx])
Greg Clayton7a245762012-05-10 23:17:28 +0000231 while pc != 0:
232 if pc >= 0x1000:
233 frames = symbolicator.symbolicate(pc)
234 if frames:
235 for frame in frames:
Greg Clayton85df60c2012-09-01 00:34:35 +0000236 result.AppendMessage(' [%u] %s' % (frame_idx, frame))
Greg Clayton7a245762012-05-10 23:17:28 +0000237 frame_idx += 1
238 else:
Greg Clayton85df60c2012-09-01 00:34:35 +0000239 result.AppendMessage(' [%u] 0x%x' % (frame_idx, pc))
Greg Clayton7a245762012-05-10 23:17:28 +0000240 frame_idx += 1
Greg Clayton6f446f32012-05-10 23:37:52 +0000241 idx = idx + 1
242 pc = int(stack_history_entry.frames[idx])
Greg Clayton7a245762012-05-10 23:17:28 +0000243 else:
244 pc = 0
Greg Clayton849acc82013-01-30 22:57:34 +0000245 if idx >= options.max_frames:
246 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))
247
Greg Clayton85df60c2012-09-01 00:34:35 +0000248 result.AppendMessage('')
Greg Clayton7a245762012-05-10 23:17:28 +0000249
Greg Clayton849acc82013-01-30 22:57:34 +0000250def dump_stack_history_entries(options, result, addr, history):
Greg Clayton7a245762012-05-10 23:17:28 +0000251 # malloc_stack_entry *get_stack_history_for_address (const void * addr)
Greg Claytonbf479652012-05-11 02:42:36 +0000252 expr = 'get_stack_history_for_address((void *)0x%x, %u)' % (addr, history)
Greg Clayton849acc82013-01-30 22:57:34 +0000253 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
254 expr_sbvalue = frame.EvaluateExpression (expr)
Greg Clayton7a245762012-05-10 23:17:28 +0000255 if expr_sbvalue.error.Success():
256 if expr_sbvalue.unsigned:
257 expr_value = lldb.value(expr_sbvalue)
258 idx = 0;
259 stack_history_entry = expr_value[idx]
Greg Clayton6f446f32012-05-10 23:37:52 +0000260 while int(stack_history_entry.address) != 0:
Greg Clayton849acc82013-01-30 22:57:34 +0000261 dump_stack_history_entry(options, result, stack_history_entry, idx)
Greg Clayton7a245762012-05-10 23:17:28 +0000262 idx = idx + 1
263 stack_history_entry = expr_value[idx]
Greg Clayton85df60c2012-09-01 00:34:35 +0000264 else:
265 result.AppendMessage('"%s" returned zero' % (expr))
Greg Claytonbf479652012-05-11 02:42:36 +0000266 else:
Greg Clayton85df60c2012-09-01 00:34:35 +0000267 result.AppendMessage('error: expression failed "%s" => %s' % (expr, expr_sbvalue.error))
Greg Clayton4c5c4292012-07-11 22:13:18 +0000268
Greg Clayton849acc82013-01-30 22:57:34 +0000269def dump_stack_history_entries2(options, result, addr, history):
270 # malloc_stack_entry *get_stack_history_for_address (const void * addr)
271 single_expr = '''typedef int kern_return_t;
272#define MAX_FRAMES %u
273typedef struct $malloc_stack_entry {
274 uint64_t address;
275 uint64_t argument;
276 uint32_t type_flags;
277 uint32_t num_frames;
278 uint64_t frames[512];
279 kern_return_t err;
280} $malloc_stack_entry;
281typedef unsigned task_t;
282$malloc_stack_entry stack;
283stack.address = 0x%x;
284stack.type_flags = 2;
285stack.num_frames = 0;
286stack.frames[0] = 0;
287uint32_t max_stack_frames = MAX_FRAMES;
288stack.err = (kern_return_t)__mach_stack_logging_get_frames (
289 (task_t)mach_task_self(),
290 stack.address,
291 &stack.frames[0],
292 max_stack_frames,
293 &stack.num_frames);
294if (stack.num_frames < MAX_FRAMES)
295 stack.frames[stack.num_frames] = 0;
296else
297 stack.frames[MAX_FRAMES-1] = 0;
298stack''' % (options.max_frames, addr);
299
300 history_expr = '''typedef int kern_return_t;
301typedef unsigned task_t;
302#define MAX_FRAMES %u
303#define MAX_HISTORY %u
304typedef struct mach_stack_logging_record_t {
305 uint32_t type_flags;
306 uint64_t stack_identifier;
307 uint64_t argument;
308 uint64_t address;
309} mach_stack_logging_record_t;
310typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
311typedef struct malloc_stack_entry {
312 uint64_t address;
313 uint64_t argument;
314 uint32_t type_flags;
315 uint32_t num_frames;
316 uint64_t frames[MAX_FRAMES];
317 kern_return_t frames_err;
318} malloc_stack_entry;
319typedef struct $malloc_stack_history {
320 task_t task;
321 unsigned idx;
322 malloc_stack_entry entries[MAX_HISTORY];
323} $malloc_stack_history;
324$malloc_stack_history info = { (task_t)mach_task_self(), 0 };
325uint32_t max_stack_frames = MAX_FRAMES;
326enumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void {
327 $malloc_stack_history *info = ($malloc_stack_history *)baton;
328 if (info->idx < MAX_HISTORY) {
329 malloc_stack_entry *stack_entry = &(info->entries[info->idx]);
330 stack_entry->address = stack_record.address;
331 stack_entry->type_flags = stack_record.type_flags;
332 stack_entry->argument = stack_record.argument;
333 stack_entry->num_frames = 0;
334 stack_entry->frames[0] = 0;
335 stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack (
336 info->task,
337 stack_record.stack_identifier,
338 stack_entry->frames,
339 (uint32_t)MAX_FRAMES,
340 &stack_entry->num_frames);
341 // Terminate the frames with zero if there is room
342 if (stack_entry->num_frames < MAX_FRAMES)
343 stack_entry->frames[stack_entry->num_frames] = 0;
344 }
345 ++info->idx;
346};
347(kern_return_t)__mach_stack_logging_enumerate_records (info.task, (uint64_t)0x%x, callback, &info);
348info''' % (options.max_frames, options.max_history, addr);
349
350 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
351 if history:
352 expr = history_expr
353 else:
354 expr = single_expr
355 expr_sbvalue = frame.EvaluateExpression (expr)
356 if options.verbose:
357 print "expression:"
358 print expr
359 print "expression result:"
360 print expr_sbvalue
361 if expr_sbvalue.error.Success():
362 if history:
363 malloc_stack_history = lldb.value(expr_sbvalue)
364 num_stacks = int(malloc_stack_history.idx)
365 if num_stacks <= options.max_history:
366 i_max = num_stacks
367 else:
368 i_max = options.max_history
369 for i in range(i_max):
370 stack_history_entry = malloc_stack_history.entries[i]
371 dump_stack_history_entry(options, result, stack_history_entry, i)
372 if num_stacks > options.max_history:
373 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))
374 else:
375 stack_history_entry = lldb.value(expr_sbvalue)
376 dump_stack_history_entry(options, result, stack_history_entry, 0)
377
378 else:
379 result.AppendMessage('error: expression failed "%s" => %s' % (expr, expr_sbvalue.error))
380
381
382def display_match_results (result, options, arg_str_description, expr, print_no_matches = True):
383 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
384 if not frame:
385 result.AppendMessage('error: invalid frame')
386 return 0
387 expr_sbvalue = frame.EvaluateExpression (expr)
388 if options.verbose:
389 print "expression:"
390 print expr
391 print "expression result:"
392 print expr_sbvalue
Greg Clayton96666442012-04-11 18:30:53 +0000393 if expr_sbvalue.error.Success():
394 if expr_sbvalue.unsigned:
395 match_value = lldb.value(expr_sbvalue)
396 i = 0
Greg Claytonab20f292012-08-11 02:26:26 +0000397 match_idx = 0
Greg Clayton96666442012-04-11 18:30:53 +0000398 while 1:
Greg Clayton4c5c4292012-07-11 22:13:18 +0000399 print_entry = True
Greg Clayton96666442012-04-11 18:30:53 +0000400 match_entry = match_value[i]; i += 1
Greg Clayton849acc82013-01-30 22:57:34 +0000401 if i > options.max_matches:
402 result.AppendMessage('warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' % (options.max_matches))
Greg Clayton4c5c4292012-07-11 22:13:18 +0000403 break
Greg Clayton96666442012-04-11 18:30:53 +0000404 malloc_addr = match_entry.addr.sbvalue.unsigned
405 if malloc_addr == 0:
406 break
407 malloc_size = int(match_entry.size)
408 offset = int(match_entry.offset)
Greg Claytonab20f292012-08-11 02:26:26 +0000409
410 if options.offset >= 0 and options.offset != offset:
411 print_entry = False
412 else:
413 match_addr = malloc_addr + offset
414 dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
Greg Clayton849acc82013-01-30 22:57:34 +0000415 type_flags = int(match_entry.type)
416 description = '%#16.16x: %s ' % (match_addr, type_flags_to_description(type_flags, match_addr, malloc_addr, malloc_size))
Greg Claytona936c6e2012-09-12 02:02:32 +0000417 if options.show_size:
418 description += '<%5u> ' % (malloc_size)
419 if options.show_range:
420 if offset > 0:
421 description += '[%#x - %#x) + %-6u ' % (malloc_addr, malloc_addr + malloc_size, offset)
422 else:
423 description += '[%#x - %#x)' % (malloc_addr, malloc_addr + malloc_size)
424 else:
425 if options.type != 'isa':
Greg Clayton849acc82013-01-30 22:57:34 +0000426 description += ' + %-6u ' % (offset,)
Greg Claytonab20f292012-08-11 02:26:26 +0000427 derefed_dynamic_value = None
428 if dynamic_value.type.name == 'void *':
429 if options.type == 'pointer' and malloc_size == 4096:
430 error = lldb.SBError()
Greg Clayton849acc82013-01-30 22:57:34 +0000431 process = expr_sbvalue.GetProcess()
432 target = expr_sbvalue.GetTarget()
433 data = bytearray(process.ReadMemory(malloc_addr, 16, error))
Greg Claytonab20f292012-08-11 02:26:26 +0000434 if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
Greg Clayton849acc82013-01-30 22:57:34 +0000435 ptr_size = target.addr_size
436 thread = process.ReadUnsignedFromMemory (malloc_addr + 16 + ptr_size, ptr_size, error)
Greg Claytona936c6e2012-09-12 02:02:32 +0000437 # 4 bytes 0xa1a1a1a1
438 # 12 bytes 'AUTORELEASE!'
439 # ptr bytes autorelease insertion point
440 # ptr bytes pthread_t
441 # ptr bytes next colder page
442 # ptr bytes next hotter page
443 # 4 bytes this page's depth in the list
444 # 4 bytes high-water mark
445 description += 'AUTORELEASE! for pthread_t %#x' % (thread)
446 else:
447 description += 'malloc(%u)' % (malloc_size)
448 else:
449 description += 'malloc(%u)' % (malloc_size)
Greg Claytonab20f292012-08-11 02:26:26 +0000450 else:
451 derefed_dynamic_value = dynamic_value.deref
452 if derefed_dynamic_value:
453 derefed_dynamic_type = derefed_dynamic_value.type
454 derefed_dynamic_type_size = derefed_dynamic_type.size
455 derefed_dynamic_type_name = derefed_dynamic_type.name
Greg Claytona936c6e2012-09-12 02:02:32 +0000456 description += derefed_dynamic_type_name
Greg Claytonab20f292012-08-11 02:26:26 +0000457 if offset < derefed_dynamic_type_size:
458 member_list = list();
459 get_member_types_for_offset (derefed_dynamic_type, offset, member_list)
460 if member_list:
461 member_path = ''
462 for member in member_list:
463 member_name = member.name
464 if member_name:
465 if member_path:
466 member_path += '.'
467 member_path += member_name
468 if member_path:
469 if options.ivar_regex_blacklist:
470 for ivar_regex in options.ivar_regex_blacklist:
471 if ivar_regex.match(member_path):
472 print_entry = False
Greg Claytona936c6e2012-09-12 02:02:32 +0000473 description += '.%s' % (member_path)
474 else:
475 description += '%u bytes after %s' % (offset - derefed_dynamic_type_size, derefed_dynamic_type_name)
476 else:
477 # strip the "*" from the end of the name since we were unable to dereference this
478 description += dynamic_value.type.name[0:-1]
Greg Clayton4c5c4292012-07-11 22:13:18 +0000479 if print_entry:
Greg Claytonab20f292012-08-11 02:26:26 +0000480 match_idx += 1
Greg Claytonaaf7fad2012-09-04 14:09:21 +0000481 result_output = ''
Greg Clayton4c5c4292012-07-11 22:13:18 +0000482 if description:
Greg Claytonaaf7fad2012-09-04 14:09:21 +0000483 result_output += description
Greg Clayton4c5c4292012-07-11 22:13:18 +0000484 if options.print_type and derefed_dynamic_value:
Greg Claytonaaf7fad2012-09-04 14:09:21 +0000485 result_output += '%s' % (derefed_dynamic_value)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000486 if options.print_object_description and dynamic_value:
487 desc = dynamic_value.GetObjectDescription()
488 if desc:
Greg Claytona936c6e2012-09-12 02:02:32 +0000489 result_output += '\n%s' % (desc)
Greg Claytonaaf7fad2012-09-04 14:09:21 +0000490 if result_output:
491 result.AppendMessage(result_output)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000492 if options.memory:
493 cmd_result = lldb.SBCommandReturnObject()
Greg Clayton849acc82013-01-30 22:57:34 +0000494 if options.format == None:
495 memory_command = "memory read --force 0x%x 0x%x" % (malloc_addr, malloc_addr + malloc_size)
496 else:
497 memory_command = "memory read --force -f %s 0x%x 0x%x" % (options.format, malloc_addr, malloc_addr + malloc_size)
498 if options.verbose:
499 result.AppendMessage(memory_command)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000500 lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
Greg Clayton85df60c2012-09-01 00:34:35 +0000501 result.AppendMessage(cmd_result.GetOutput())
Greg Clayton4c5c4292012-07-11 22:13:18 +0000502 if options.stack_history:
Greg Clayton849acc82013-01-30 22:57:34 +0000503 dump_stack_history_entries2(options, result, malloc_addr, 1)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000504 elif options.stack:
Greg Clayton849acc82013-01-30 22:57:34 +0000505 dump_stack_history_entries2(options, result, malloc_addr, 0)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000506 return i
507 elif print_no_matches:
Greg Clayton85df60c2012-09-01 00:34:35 +0000508 result.AppendMessage('no matches found for %s' % (arg_str_description))
Greg Clayton96666442012-04-11 18:30:53 +0000509 else:
Greg Claytonaaf7fad2012-09-04 14:09:21 +0000510 result.AppendMessage(str(expr_sbvalue.error))
Greg Clayton4c5c4292012-07-11 22:13:18 +0000511 return 0
512
Greg Clayton85df60c2012-09-01 00:34:35 +0000513def heap_search(result, options, arg_str):
Greg Clayton4c5c4292012-07-11 22:13:18 +0000514 dylid_load_err = load_dylib()
515 if dylid_load_err:
Greg Clayton85df60c2012-09-01 00:34:35 +0000516 result.AppendMessage(dylid_load_err)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000517 return
518 expr = None
Greg Clayton393fc5f2012-09-11 18:10:27 +0000519 print_no_matches = True
Greg Clayton4c5c4292012-07-11 22:13:18 +0000520 arg_str_description = arg_str
Greg Clayton4c5c4292012-07-11 22:13:18 +0000521 if options.type == 'pointer':
Greg Clayton130a3122012-10-08 22:39:38 +0000522 expr = 'find_pointer_in_heap((void *)%s, (int)%u)' % (arg_str, options.check_vm_regions)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000523 arg_str_description = 'malloc block containing pointer %s' % arg_str
524 if options.format == None:
525 options.format = "A" # 'A' is "address" format
Greg Claytonab20f292012-08-11 02:26:26 +0000526 elif options.type == 'isa':
Greg Clayton130a3122012-10-08 22:39:38 +0000527 expr = 'find_objc_objects_in_memory ((void *)%s, (int)%u)' % (arg_str, options.check_vm_regions)
Greg Claytonaaf7fad2012-09-04 14:09:21 +0000528 #result.AppendMessage ('expr -u0 -- %s' % expr) # REMOVE THIS LINE
Greg Claytonab20f292012-08-11 02:26:26 +0000529 arg_str_description = 'objective C classes with isa %s' % arg_str
530 options.offset = 0
531 if options.format == None:
532 options.format = "A" # 'A' is "address" format
Greg Clayton4c5c4292012-07-11 22:13:18 +0000533 elif options.type == 'cstr':
Greg Clayton130a3122012-10-08 22:39:38 +0000534 expr = 'find_cstring_in_heap("%s", (int)%u)' % (arg_str, options.check_vm_regions)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000535 arg_str_description = 'malloc block containing "%s"' % arg_str
536 elif options.type == 'addr':
Greg Clayton130a3122012-10-08 22:39:38 +0000537 expr = 'find_block_for_address((void *)%s, (int)%u)' % (arg_str, options.check_vm_regions)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000538 arg_str_description = 'malloc block for %s' % arg_str
Greg Clayton393fc5f2012-09-11 18:10:27 +0000539 elif options.type == 'all':
540 expr = 'get_heap_info(1)'
541 arg_str_description = None
542 print_no_matches = False
Greg Clayton4c5c4292012-07-11 22:13:18 +0000543 else:
Greg Clayton85df60c2012-09-01 00:34:35 +0000544 result.AppendMessage('error: invalid type "%s"\nvalid values are "pointer", "cstr"' % options.type)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000545 return
Greg Clayton85df60c2012-09-01 00:34:35 +0000546 if options.format == None:
547 options.format = "Y" # 'Y' is "bytes with ASCII" format
Greg Clayton4c5c4292012-07-11 22:13:18 +0000548
Greg Clayton849acc82013-01-30 22:57:34 +0000549 display_match_results (result, options, arg_str_description, expr)
550
551def get_ptr_refs_options ():
552 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
553 description='''Searches all allocations on the heap for pointer values on
554darwin user space programs. Any matches that were found will dump the malloc
555blocks that contain the pointers and might be able to print what kind of
556objects the pointers are contained in using dynamic type information in the
557program.'''
558 parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
559 add_common_options(parser)
560 return parser
Greg Clayton96666442012-04-11 18:30:53 +0000561
Greg Claytonbff78412012-04-12 18:57:36 +0000562def ptr_refs(debugger, command, result, dict):
Greg Claytone93e24f2012-04-11 16:27:06 +0000563 command_args = shlex.split(command)
Greg Clayton849acc82013-01-30 22:57:34 +0000564 parser = get_ptr_refs_options()
Greg Claytone93e24f2012-04-11 16:27:06 +0000565 try:
566 (options, args) = parser.parse_args(command_args)
567 except:
568 return
Greg Clayton96666442012-04-11 18:30:53 +0000569
Greg Clayton849acc82013-01-30 22:57:34 +0000570 process = lldb.debugger.GetSelectedTarget().GetProcess()
571 if not process:
572 result.AppendMessage('error: invalid process')
573 return
574 frame = process.GetSelectedThread().GetSelectedFrame()
575 if not frame:
576 result.AppendMessage('error: invalid frame')
577 return
578
Greg Clayton96666442012-04-11 18:30:53 +0000579 options.type = 'pointer'
Greg Clayton849acc82013-01-30 22:57:34 +0000580 if options.format == None:
581 options.format = "A" # 'A' is "address" format
582
Greg Claytone93e24f2012-04-11 16:27:06 +0000583 if args:
Greg Clayton849acc82013-01-30 22:57:34 +0000584 # When we initialize the expression, we must define any types that
585 # we will need when looking at every allocation. We must also define
586 # a type named callback_baton_t and make an instance named "baton"
587 # and initialize it how ever we want to. The address of "baton" will
588 # be passed into our range callback. callback_baton_t must contain
589 # a member named "callback" whose type is "range_callback_t". This
590 # will be used by our zone callbacks to call the range callback for
591 # each malloc range.
592 init_expr_format = '''
593struct $malloc_match {
594 void *addr;
595 uintptr_t size;
596 uintptr_t offset;
597 uintptr_t type;
598};
599typedef struct callback_baton_t {
600 range_callback_t callback;
601 unsigned num_matches;
602 $malloc_match matches[%u];
603 void *ptr;
604} callback_baton_t;
605range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
606 callback_baton_t *info = (callback_baton_t *)baton;
607 typedef void* T;
608 const unsigned size = sizeof(T);
609 T *array = (T*)ptr_addr;
610 for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) {
611 if (array[idx] == info->ptr) {
612 if (info->num_matches < sizeof(info->matches)/sizeof($malloc_match)) {
613 info->matches[info->num_matches].addr = (void*)ptr_addr;
614 info->matches[info->num_matches].size = ptr_size;
615 info->matches[info->num_matches].offset = idx*sizeof(T);
616 info->matches[info->num_matches].type = type;
617 ++info->num_matches;
618 }
619 }
620 }
621};
622callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
623'''
624 # We must also define a snippet of code to be run that returns
625 # the result of the expression we run.
626 # Here we return NULL if our pointer was not found in any malloc blocks,
627 # and we return the address of the matches array so we can then access
628 # the matching results
629 return_expr = '''
630for (uint32_t i=0; i<NUM_STACKS; ++i)
631 range_callback (task, &baton, 8, stacks[i].base, stacks[i].size);
632baton.matches'''
633 # Iterate through all of our pointer expressions and display the results
634 for ptr_expr in args:
635 init_expr = init_expr_format % (options.max_matches, ptr_expr)
636 stack_info = get_thread_stack_ranges_struct (process)
637 if stack_info:
638 init_expr += stack_info
639 expr = g_iterate_malloc_blocks_expr % (init_expr, return_expr)
640 arg_str_description = 'malloc block containing pointer %s' % ptr_expr
641 display_match_results (result, options, arg_str_description, expr)
Greg Claytone93e24f2012-04-11 16:27:06 +0000642 else:
Greg Clayton849acc82013-01-30 22:57:34 +0000643 result.AppendMessage('error: no pointer arguments were given')
644
645def get_cstr_refs_options():
646 usage = "usage: %prog [options] <CSTR> [CSTR ...]"
647 description='''Searches all allocations on the heap for C string values on
648darwin user space programs. Any matches that were found will dump the malloc
649blocks that contain the C strings and might be able to print what kind of
650objects the pointers are contained in using dynamic type information in the
651program.'''
652 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
653 add_common_options(parser)
654 return parser
Greg Claytone93e24f2012-04-11 16:27:06 +0000655
Greg Claytonbff78412012-04-12 18:57:36 +0000656def cstr_refs(debugger, command, result, dict):
Greg Clayton96666442012-04-11 18:30:53 +0000657 command_args = shlex.split(command)
Greg Clayton849acc82013-01-30 22:57:34 +0000658 parser = get_cstr_refs_options();
Greg Clayton96666442012-04-11 18:30:53 +0000659 try:
660 (options, args) = parser.parse_args(command_args)
661 except:
662 return
663
Greg Clayton849acc82013-01-30 22:57:34 +0000664 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
665 if not frame:
666 result.AppendMessage('error: invalid frame')
667 return
668
Greg Clayton96666442012-04-11 18:30:53 +0000669 options.type = 'cstr'
Greg Clayton849acc82013-01-30 22:57:34 +0000670 if options.format == None:
671 options.format = "Y" # 'Y' is "bytes with ASCII" format
Greg Clayton96666442012-04-11 18:30:53 +0000672
673 if args:
Greg Clayton849acc82013-01-30 22:57:34 +0000674 # When we initialize the expression, we must define any types that
675 # we will need when looking at every allocation. We must also define
676 # a type named callback_baton_t and make an instance named "baton"
677 # and initialize it how ever we want to. The address of "baton" will
678 # be passed into our range callback. callback_baton_t must contain
679 # a member named "callback" whose type is "range_callback_t". This
680 # will be used by our zone callbacks to call the range callback for
681 # each malloc range.
682 init_expr_format = '''struct $malloc_match {
683 void *addr;
684 uintptr_t size;
685 uintptr_t offset;
686 uintptr_t type;
687};
688typedef struct callback_baton_t {
689 range_callback_t callback;
690 unsigned num_matches;
691 $malloc_match matches[%u];
692 const char *cstr;
693 unsigned cstr_len;
694} callback_baton_t;
695range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
696 callback_baton_t *info = (callback_baton_t *)baton;
697 if (info->cstr_len < ptr_size) {
698 const char *begin = (const char *)ptr_addr;
699 const char *end = begin + ptr_size - info->cstr_len;
700 for (const char *s = begin; s < end; ++s) {
701 if ((int)memcmp(s, info->cstr, info->cstr_len) == 0) {
702 if (info->num_matches < sizeof(info->matches)/sizeof($malloc_match)) {
703 info->matches[info->num_matches].addr = (void*)ptr_addr;
704 info->matches[info->num_matches].size = ptr_size;
705 info->matches[info->num_matches].offset = s - begin;
706 info->matches[info->num_matches].type = type;
707 ++info->num_matches;
708 }
709 }
710 }
711 }
712};
713const char *cstr = "%s";
714callback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };'''
715 # We must also define a snippet of code to be run that returns
716 # the result of the expression we run.
717 # Here we return NULL if our pointer was not found in any malloc blocks,
718 # and we return the address of the matches array so we can then access
719 # the matching results
720 return_expr = '$malloc_match *result = baton.num_matches ? baton.matches : ($malloc_match *)0; result'
721 # Iterate through all of our pointer expressions and display the results
722 for cstr in args:
723 init_expr = init_expr_format % (options.max_matches, cstr)
724 expr = g_iterate_malloc_blocks_expr % (init_expr, return_expr)
725 arg_str_description = 'malloc block containing "%s"' % cstr
726 display_match_results (result, options, arg_str_description, expr)
Greg Clayton96666442012-04-11 18:30:53 +0000727 else:
Greg Clayton849acc82013-01-30 22:57:34 +0000728 result.AppendMessage('error: command takes one or more C string arguments')
729
730
731def get_malloc_info_options():
732 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
733 description='''Searches the heap a malloc block that contains the addresses
734specified as one or more address expressions. Any matches that were found will
735dump the malloc blocks that match or contain the specified address. The matching
736blocks might be able to show what kind of objects they are using dynamic type
737information in the program.'''
738 parser = optparse.OptionParser(description=description, prog='malloc_info',usage=usage)
739 add_common_options(parser)
740 return parser
Greg Claytone93e24f2012-04-11 16:27:06 +0000741
Greg Claytonbff78412012-04-12 18:57:36 +0000742def malloc_info(debugger, command, result, dict):
743 command_args = shlex.split(command)
Greg Clayton849acc82013-01-30 22:57:34 +0000744 parser = get_malloc_info_options()
Greg Claytonbff78412012-04-12 18:57:36 +0000745 try:
746 (options, args) = parser.parse_args(command_args)
747 except:
748 return
Greg Claytonbff78412012-04-12 18:57:36 +0000749 options.type = 'addr'
Greg Clayton849acc82013-01-30 22:57:34 +0000750
751 init_expr_format = '''struct $malloc_match {
752 void *addr;
753 uintptr_t size;
754 uintptr_t offset;
755 uintptr_t type;
756};
757typedef struct callback_baton_t {
758 range_callback_t callback;
759 unsigned num_matches;
760 $malloc_match matches[2]; // Two items so they can be NULL terminated
761 void *ptr;
762} callback_baton_t;
763range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
764 callback_baton_t *info = (callback_baton_t *)baton;
765 if (info->num_matches == 0) {
766 uint8_t *p = (uint8_t *)info->ptr;
767 uint8_t *lo = (uint8_t *)ptr_addr;
768 uint8_t *hi = lo + ptr_size;
769 if (lo <= p && p < hi) {
770 info->matches[info->num_matches].addr = (void*)ptr_addr;
771 info->matches[info->num_matches].size = ptr_size;
772 info->matches[info->num_matches].offset = p - lo;
773 info->matches[info->num_matches].type = type;
774 info->num_matches = 1;
775 }
776 }
777};
778callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
779baton.matches[1].addr = 0;'''
Greg Claytonbff78412012-04-12 18:57:36 +0000780 if args:
Greg Clayton849acc82013-01-30 22:57:34 +0000781 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
782 if frame:
783 for ptr_expr in args:
784 init_expr = init_expr_format % (ptr_expr)
785 expr = g_iterate_malloc_blocks_expr % (init_expr, 'baton.matches')
786 arg_str_description = 'malloc block that contains %s' % ptr_expr
787 display_match_results (result, options, arg_str_description, expr)
788 else:
789 result.AppendMessage('error: invalid frame')
Greg Claytonbff78412012-04-12 18:57:36 +0000790 else:
Greg Clayton849acc82013-01-30 22:57:34 +0000791 result.AppendMessage('error: command takes one or more pointer expressions')
Greg Claytonbff78412012-04-12 18:57:36 +0000792
Greg Clayton393fc5f2012-09-11 18:10:27 +0000793def heap(debugger, command, result, dict):
Greg Claytonbf479652012-05-11 02:42:36 +0000794 command_args = shlex.split(command)
795 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
Greg Clayton393fc5f2012-09-11 18:10:27 +0000796 description='''Traverse all allocations on the heap and report statistics.
Greg Claytonbf479652012-05-11 02:42:36 +0000797
Greg Clayton393fc5f2012-09-11 18:10:27 +0000798 If programs set the MallocStackLogging=1 in the environment, then stack
799 history is available for any allocations. '''
Greg Clayton849acc82013-01-30 22:57:34 +0000800 parser = optparse.OptionParser(description=description, prog='heap',usage=usage)
Greg Clayton393fc5f2012-09-11 18:10:27 +0000801 add_common_options(parser)
802 try:
803 (options, args) = parser.parse_args(command_args)
804 except:
805 return
806 options.type = 'all'
807 if args:
808 result.AppendMessage('error: heap command takes no arguments, only options')
Greg Claytonbf479652012-05-11 02:42:36 +0000809 else:
Greg Clayton393fc5f2012-09-11 18:10:27 +0000810 heap_search (result, options, None)
Greg Claytonbf479652012-05-11 02:42:36 +0000811
Greg Clayton849acc82013-01-30 22:57:34 +0000812def get_thread_stack_ranges_struct (process):
813 stack_dicts = list()
814 if process:
815 i = 0;
816 for thread in process:
817 min_sp = thread.frame[0].sp
818 max_sp = min_sp
819 for frame in thread.frames:
820 sp = frame.sp
821 if sp < min_sp: min_sp = sp
822 if sp > max_sp: max_sp = sp
823 if min_sp < max_sp:
824 stack_dicts.append ({ 'tid' : thread.GetThreadID(), 'base' : min_sp , 'size' : max_sp-min_sp, 'index' : i })
825 i += 1
826 stack_dicts_len = len(stack_dicts)
827 if stack_dicts_len > 0:
828 result = '''#define NUM_STACKS %u
829typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
830thread_stack_t stacks[NUM_STACKS];
831''' % (stack_dicts_len,)
832 for stack_dict in stack_dicts:
833 result += '''stacks[%(index)u].tid = 0x%(tid)x;
834stacks[%(index)u].base = 0x%(base)x;
835stacks[%(index)u].size = 0x%(size)x;
836''' % stack_dict
837 return result
838 else:
839 return None
840
Greg Clayton130a3122012-10-08 22:39:38 +0000841def stack_ptr_refs(debugger, command, result, dict):
842 command_args = shlex.split(command)
843 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
844 description='''Searches thread stack contents for pointer values in darwin user space programs.'''
Greg Clayton849acc82013-01-30 22:57:34 +0000845 parser = optparse.OptionParser(description=description, prog='stack_ptr_refs',usage=usage)
Greg Clayton130a3122012-10-08 22:39:38 +0000846 add_common_options(parser)
847 try:
848 (options, args) = parser.parse_args(command_args)
849 except:
850 return
851
852 options.type = 'pointer'
853
854 stack_threads = list()
855 stack_bases = list()
856 stack_sizes = list()
Greg Clayton849acc82013-01-30 22:57:34 +0000857 process = lldb.debugger.GetSelectedTarget().GetProcess()
858 for thread in process:
Greg Clayton130a3122012-10-08 22:39:38 +0000859 min_sp = thread.frame[0].sp
860 max_sp = min_sp
861 for frame in thread.frames:
862 sp = frame.sp
863 if sp < min_sp: min_sp = sp
864 if sp > max_sp: max_sp = sp
865 result.AppendMessage ('%s stack [%#x - %#x)' % (thread, min_sp, max_sp))
866 if min_sp < max_sp:
867 stack_threads.append (thread)
868 stack_bases.append (min_sp)
869 stack_sizes.append (max_sp-min_sp)
870
871 if stack_bases:
872 dylid_load_err = load_dylib()
873 if dylid_load_err:
874 result.AppendMessage(dylid_load_err)
875 return
Greg Clayton849acc82013-01-30 22:57:34 +0000876 frame = process.GetSelectedThread().GetSelectedFrame()
Greg Clayton130a3122012-10-08 22:39:38 +0000877 for expr_str in args:
878 for (idx, stack_base) in enumerate(stack_bases):
879 stack_size = stack_sizes[idx]
880 expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (stack_base, stack_size, expr_str)
881 arg_str_description = 'thead %s stack containing "%s"' % (stack_threads[idx], expr_str)
Greg Clayton849acc82013-01-30 22:57:34 +0000882 num_matches = display_match_results (result, options, arg_str_description, expr, False)
Greg Clayton130a3122012-10-08 22:39:38 +0000883 if num_matches:
884 if num_matches < options.max_matches:
885 options.max_matches = options.max_matches - num_matches
886 else:
887 options.max_matches = 0
888 if options.max_matches == 0:
889 return
890 else:
891 result.AppendMessage('error: no thread stacks were found that match any of %s' % (', '.join(options.section_names)))
892
Greg Clayton4c5c4292012-07-11 22:13:18 +0000893def section_ptr_refs(debugger, command, result, dict):
894 command_args = shlex.split(command)
895 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
896 description='''Searches section contents for pointer values in darwin user space programs.'''
897 parser = optparse.OptionParser(description=description, prog='section_ptr_refs',usage=usage)
898 add_common_options(parser)
899 parser.add_option('--section', action='append', type='string', dest='section_names', help='section name to search', default=list())
900 try:
901 (options, args) = parser.parse_args(command_args)
902 except:
903 return
904
905 options.type = 'pointer'
Greg Clayton130a3122012-10-08 22:39:38 +0000906
Greg Clayton4c5c4292012-07-11 22:13:18 +0000907 sections = list()
908 section_modules = list()
909 if not options.section_names:
Greg Clayton85df60c2012-09-01 00:34:35 +0000910 result.AppendMessage('error: at least one section must be specified with the --section option')
Greg Clayton4c5c4292012-07-11 22:13:18 +0000911 return
912
Greg Clayton849acc82013-01-30 22:57:34 +0000913 target = lldb.debugger.GetSelectedTarget()
914 for module in target.modules:
Greg Clayton4c5c4292012-07-11 22:13:18 +0000915 for section_name in options.section_names:
916 section = module.section[section_name]
917 if section:
918 sections.append (section)
919 section_modules.append (module)
920 if sections:
921 dylid_load_err = load_dylib()
922 if dylid_load_err:
Greg Clayton85df60c2012-09-01 00:34:35 +0000923 result.AppendMessage(dylid_load_err)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000924 return
Greg Clayton849acc82013-01-30 22:57:34 +0000925 frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
Greg Clayton4c5c4292012-07-11 22:13:18 +0000926 for expr_str in args:
927 for (idx, section) in enumerate(sections):
928 expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (section.addr.load_addr, section.size, expr_str)
929 arg_str_description = 'section %s.%s containing "%s"' % (section_modules[idx].file.fullpath, section.name, expr_str)
Greg Clayton849acc82013-01-30 22:57:34 +0000930 num_matches = display_match_results (result, options, arg_str_description, expr, False)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000931 if num_matches:
932 if num_matches < options.max_matches:
933 options.max_matches = options.max_matches - num_matches
934 else:
935 options.max_matches = 0
936 if options.max_matches == 0:
937 return
938 else:
Greg Clayton85df60c2012-09-01 00:34:35 +0000939 result.AppendMessage('error: no sections were found that match any of %s' % (', '.join(options.section_names)))
Greg Clayton4c5c4292012-07-11 22:13:18 +0000940
Greg Clayton849acc82013-01-30 22:57:34 +0000941def get_objc_refs_options():
942 usage = "usage: %prog [options] <CLASS> [CLASS ...]"
943 description='''Searches all allocations on the heap for instances of
944objective C classes, or any classes that inherit from the specified classes
945in darwin user space programs. Any matches that were found will dump the malloc
946blocks that contain the C strings and might be able to print what kind of
947objects the pointers are contained in using dynamic type information in the
948program.'''
949 parser = optparse.OptionParser(description=description, prog='objc_refs',usage=usage)
950 add_common_options(parser)
951 return parser
952
Greg Claytonab20f292012-08-11 02:26:26 +0000953def objc_refs(debugger, command, result, dict):
954 command_args = shlex.split(command)
Greg Clayton849acc82013-01-30 22:57:34 +0000955 parser = get_objc_refs_options()
Greg Claytonab20f292012-08-11 02:26:26 +0000956 try:
957 (options, args) = parser.parse_args(command_args)
958 except:
959 return
960
Greg Clayton849acc82013-01-30 22:57:34 +0000961 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
962 if not frame:
963 result.AppendMessage('error: invalid frame')
964 return
965
966 options.type = 'isa'
967 if options.format == None:
968 options.format = "A" # 'A' is "address" format
969
970 num_objc_classes_value = frame.EvaluateExpression("(int)objc_getClassList((void *)0, (int)0)")
971 if not num_objc_classes_value.error.Success():
972 result.AppendMessage('error: %s' % num_objc_classes_value.error.GetCString())
973 return
974
975 num_objc_classes = num_objc_classes_value.GetValueAsUnsigned()
976 if num_objc_classes == 0:
977 result.AppendMessage('error: no objective C classes in program')
978 return
979
980 if args:
981 # When we initialize the expression, we must define any types that
982 # we will need when looking at every allocation. We must also define
983 # a type named callback_baton_t and make an instance named "baton"
984 # and initialize it how ever we want to. The address of "baton" will
985 # be passed into our range callback. callback_baton_t must contain
986 # a member named "callback" whose type is "range_callback_t". This
987 # will be used by our zone callbacks to call the range callback for
988 # each malloc range.
989 init_expr_format = '''struct $malloc_match {
990 void *addr;
991 uintptr_t size;
992 uintptr_t offset;
993 uintptr_t type;
994};
995typedef int (*compare_callback_t)(const void *a, const void *b);
996typedef struct callback_baton_t {
997 range_callback_t callback;
998 compare_callback_t compare_callback;
999 unsigned num_matches;
1000 $malloc_match matches[%u];
1001 void *isa;
1002 Class classes[%u];
1003} callback_baton_t;
1004compare_callback_t compare_callback = [](const void *a, const void *b) -> int {
1005 Class a_ptr = *(Class *)a;
1006 Class b_ptr = *(Class *)b;
1007 if (a_ptr < b_ptr) return -1;
1008 if (a_ptr > b_ptr) return +1;
1009 return 0;
1010};
1011range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
1012 callback_baton_t *info = (callback_baton_t *)baton;
1013 if (sizeof(Class) <= ptr_size) {
1014 Class *curr_class_ptr = (Class *)ptr_addr;
1015 Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr,
1016 (const void *)info->classes,
1017 sizeof(info->classes)/sizeof(Class),
1018 sizeof(Class),
1019 info->compare_callback);
1020 if (matching_class_ptr) {
1021 bool match = false;
1022 if (info->isa) {
1023 Class isa = *curr_class_ptr;
1024 if (info->isa == isa)
1025 match = true;
1026 else { // if (info->objc.match_superclasses) {
1027 Class super = (Class)class_getSuperclass(isa);
1028 while (super) {
1029 if (super == info->isa) {
1030 match = true;
1031 break;
1032 }
1033 super = (Class)class_getSuperclass(super);
1034 }
1035 }
1036 }
1037 else
1038 match = true;
1039 if (match) {
1040 if (info->num_matches < sizeof(info->matches)/sizeof($malloc_match)) {
1041 info->matches[info->num_matches].addr = (void*)ptr_addr;
1042 info->matches[info->num_matches].size = ptr_size;
1043 info->matches[info->num_matches].offset = 0;
1044 info->matches[info->num_matches].type = type;
1045 ++info->num_matches;
1046 }
1047 }
1048 }
1049 }
1050};
1051callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
1052int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class));
1053(void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);'''
1054 # We must also define a snippet of code to be run that returns
1055 # the result of the expression we run.
1056 # Here we return NULL if our pointer was not found in any malloc blocks,
1057 # and we return the address of the matches array so we can then access
1058 # the matching results
1059 return_expr = '$malloc_match *result = baton.num_matches ? baton.matches : ($malloc_match *)0; result'
1060 # Iterate through all of our ObjC class name arguments
1061 for class_name in args:
1062 addr_expr_str = "(void *)[%s class]" % class_name
1063 expr_sbvalue = frame.EvaluateExpression (addr_expr_str)
1064 if expr_sbvalue.error.Success():
1065 isa = expr_sbvalue.unsigned
1066 if isa:
1067 options.type = 'isa'
1068 result.AppendMessage('Searching for all instances of classes or subclasses of %s (isa=0x%x)' % (class_name, isa))
1069 init_expr = init_expr_format % (options.max_matches, num_objc_classes, isa)
1070 expr = g_iterate_malloc_blocks_expr % (init_expr, return_expr)
1071 arg_str_description = 'objective C classes with isa 0x%x' % isa
1072 display_match_results (result, options, arg_str_description, expr)
Greg Claytonab20f292012-08-11 02:26:26 +00001073 else:
Greg Clayton849acc82013-01-30 22:57:34 +00001074 result.AppendMessage('error: Can\'t find isa for an ObjC class named "%s"' % (class_name))
1075 else:
1076 result.AppendMessage('error: expression error for "%s": %s' % (addr_expr_str, expr_sbvalue.error))
1077 else:
1078 result.AppendMessage('error: command takes one or more C string arguments');
Greg Claytonab20f292012-08-11 02:26:26 +00001079
Greg Clayton1dae6f32012-04-25 18:40:20 +00001080if __name__ == '__main__':
1081 lldb.debugger = lldb.SBDebugger.Create()
1082
1083# This initializer is being run from LLDB in the embedded command interpreter
1084# Add any commands contained in this module to LLDB
Greg Clayton849acc82013-01-30 22:57:34 +00001085if __package__:
1086 package_name = __package__ + '.' + __name__
1087else:
1088 package_name = __name__
1089
1090# Make the options so we can generate the help text for the new LLDB
1091# command line command prior to registering it with LLDB below. This way
1092# if clients in LLDB type "help malloc_info", they will see the exact same
1093# output as typing "malloc_info --help".
1094ptr_refs.__doc__ = get_ptr_refs_options().format_help()
1095cstr_refs.__doc__ = get_cstr_refs_options().format_help()
1096malloc_info.__doc__ = get_malloc_info_options().format_help()
1097objc_refs.__doc__ = get_objc_refs_options().format_help()
1098lldb.debugger.HandleCommand('command script add -f %s.ptr_refs ptr_refs' % package_name)
1099lldb.debugger.HandleCommand('command script add -f %s.cstr_refs cstr_refs' % package_name)
1100lldb.debugger.HandleCommand('command script add -f %s.malloc_info malloc_info' % package_name)
1101# lldb.debugger.HandleCommand('command script add -f %s.heap heap' % package_name)
1102# lldb.debugger.HandleCommand('command script add -f %s.section_ptr_refs section_ptr_refs' % package_name)
1103# lldb.debugger.HandleCommand('command script add -f %s.stack_ptr_refs stack_ptr_refs' % package_name)
1104lldb.debugger.HandleCommand('command script add -f %s.objc_refs objc_refs' % package_name)
1105print '"malloc_info", "ptr_refs", "cstr_refs", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help.'
Greg Claytone93e24f2012-04-11 16:27:06 +00001106
1107
1108
1109