blob: a7273758816142b74f09ae60381eee1325997bf9 [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 Clayton13fbb992013-02-01 00:47:49 +000025def get_iterate_memory_expr(options, process, user_init_code, user_return_code):
26 expr = '''
Greg Claytone7fc9cc2013-01-31 06:38:09 +000027typedef 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
Greg Clayton13fbb992013-02-01 00:47:49 +000033typedef void (*range_callback_t)(task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size);
34''';
35 if options.search_vm_regions:
36 expr += '''
37typedef int vm_prot_t;
38typedef unsigned int vm_inherit_t;
39typedef unsigned long long memory_object_offset_t;
40typedef unsigned int boolean_t;
41typedef int vm_behavior_t;
42typedef uint32_t vm32_object_id_t;
43typedef natural_t mach_msg_type_number_t;
44typedef uint64_t mach_vm_address_t;
45typedef uint64_t mach_vm_offset_t;
46typedef uint64_t mach_vm_size_t;
47typedef uint64_t vm_map_offset_t;
48typedef uint64_t vm_map_address_t;
49typedef uint64_t vm_map_size_t;
50#define VM_PROT_NONE ((vm_prot_t) 0x00)
51#define VM_PROT_READ ((vm_prot_t) 0x01)
52#define VM_PROT_WRITE ((vm_prot_t) 0x02)
53#define VM_PROT_EXECUTE ((vm_prot_t) 0x04)
54typedef struct vm_region_submap_short_info_data_64_t {
55 vm_prot_t protection;
56 vm_prot_t max_protection;
57 vm_inherit_t inheritance;
58 memory_object_offset_t offset; // offset into object/map
59 unsigned int user_tag; // user tag on map entry
60 unsigned int ref_count; // obj/map mappers, etc
61 unsigned short shadow_depth; // only for obj
62 unsigned char external_pager; // only for obj
63 unsigned char share_mode; // see enumeration
64 boolean_t is_submap; // submap vs obj
65 vm_behavior_t behavior; // access behavior hint
66 vm32_object_id_t object_id; // obj/map name, not a handle
67 unsigned short user_wired_count;
68} vm_region_submap_short_info_data_64_t;
69#define VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ((mach_msg_type_number_t)(sizeof(vm_region_submap_short_info_data_64_t)/sizeof(int)))''';
70 if user_init_code:
71 expr += user_init_code;
72 expr += '''
73task_t task = (task_t)mach_task_self();
74mach_vm_address_t vm_region_base_addr;
75mach_vm_size_t vm_region_size;
76natural_t vm_region_depth;
77vm_region_submap_short_info_data_64_t vm_region_info;
78kern_return_t err;
79for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size)
80{
81 mach_msg_type_number_t vm_region_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
82 err = (kern_return_t)mach_vm_region_recurse (task,
83 &vm_region_base_addr,
84 &vm_region_size,
85 &vm_region_depth,
86 &vm_region_info,
87 &vm_region_info_size);
88 if (err)
89 break;
90 // Check all read + write regions. This will cover the thread stacks
91 // and any regions of memory like __DATA segments, that might contain
92 // data we are looking for
93 if (vm_region_info.protection & VM_PROT_WRITE &&
94 vm_region_info.protection & VM_PROT_READ)
95 {
96 baton.callback (task,
97 &baton,
98 64,
99 vm_region_base_addr,
100 vm_region_size);
101 }
102}'''
103 else:
104 if options.search_stack:
105 expr += get_thread_stack_ranges_struct (process)
106 if options.search_segments:
107 expr += get_sections_ranges_struct (process)
108 if user_init_code:
109 expr += user_init_code
110 if options.search_heap:
111 expr += '''
Greg Clayton85e62ec2013-01-30 22:57:34 +0000112#define MALLOC_PTR_IN_USE_RANGE_TYPE 1
113typedef struct vm_range_t {
114 vm_address_t address;
115 vm_size_t size;
116} vm_range_t;
117typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory);
118typedef void (*vm_range_recorder_t)(task_t task, void *baton, unsigned type, vm_range_t *range, unsigned size);
Greg Clayton85e62ec2013-01-30 22:57:34 +0000119typedef struct malloc_introspection_t {
120 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 */
121} malloc_introspection_t;
122typedef struct malloc_zone_t {
123 void *reserved1[12];
124 struct malloc_introspection_t *introspect;
125} malloc_zone_t;
126memory_reader_t task_peek = [](task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) -> kern_return_t {
127 *local_memory = (void*) remote_address;
128 return KERN_SUCCESS;
129};
130vm_address_t *zones = 0;
Greg Clayton13fbb992013-02-01 00:47:49 +0000131unsigned int num_zones = 0;task_t task = 0;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000132kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones);
133if (KERN_SUCCESS == err)
134{
135 for (unsigned int i=0; i<num_zones; ++i)
136 {
137 const malloc_zone_t *zone = (const malloc_zone_t *)zones[i];
138 if (zone && zone->introspect)
139 zone->introspect->enumerator (task,
140 &baton,
141 MALLOC_PTR_IN_USE_RANGE_TYPE,
142 (vm_address_t)zone,
143 task_peek,
144 [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void
145 {
146 range_callback_t callback = ((callback_baton_t *)baton)->callback;
147 for (unsigned i=0; i<size; ++i)
148 {
149 callback (task, baton, type, ranges[i].address, ranges[i].size);
150 }
151 });
152 }
Greg Clayton13fbb992013-02-01 00:47:49 +0000153}'''
154
155 if options.search_stack:
156 expr += '''
Greg Clayton966c6f62014-01-07 21:55:00 +0000157#ifdef NUM_STACKS
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000158// Call the callback for the thread stack ranges
Greg Clayton13fbb992013-02-01 00:47:49 +0000159for (uint32_t i=0; i<NUM_STACKS; ++i) {
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000160 range_callback(task, &baton, 8, stacks[i].base, stacks[i].size);
Greg Clayton13fbb992013-02-01 00:47:49 +0000161 if (STACK_RED_ZONE_SIZE > 0) {
162 range_callback(task, &baton, 16, stacks[i].base - STACK_RED_ZONE_SIZE, STACK_RED_ZONE_SIZE);
163 }
164}
Greg Clayton966c6f62014-01-07 21:55:00 +0000165#endif'''
Greg Clayton13fbb992013-02-01 00:47:49 +0000166
167 if options.search_segments:
168 expr += '''
Greg Clayton966c6f62014-01-07 21:55:00 +0000169#ifdef NUM_SEGMENTS
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000170// Call the callback for all segments
171for (uint32_t i=0; i<NUM_SEGMENTS; ++i)
Greg Clayton966c6f62014-01-07 21:55:00 +0000172 range_callback(task, &baton, 32, segments[i].base, segments[i].size);
173#endif'''
Greg Clayton13fbb992013-02-01 00:47:49 +0000174
175 if user_return_code:
176 expr += "\n%s" % (user_return_code,)
177
178 return expr
Greg Clayton94073022012-07-07 01:22:45 +0000179
180def get_member_types_for_offset(value_type, offset, member_list):
181 member = value_type.GetFieldAtIndex(0)
182 search_bases = False
183 if member:
184 if member.GetOffsetInBytes() <= offset:
185 for field_idx in range (value_type.GetNumberOfFields()):
186 member = value_type.GetFieldAtIndex(field_idx)
187 member_byte_offset = member.GetOffsetInBytes()
188 member_end_byte_offset = member_byte_offset + member.type.size
189 if member_byte_offset <= offset and offset < member_end_byte_offset:
190 member_list.append(member)
191 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
192 return
193 else:
194 search_bases = True
195 else:
196 search_bases = True
197 if search_bases:
198 for field_idx in range (value_type.GetNumberOfDirectBaseClasses()):
199 member = value_type.GetDirectBaseClassAtIndex(field_idx)
200 member_byte_offset = member.GetOffsetInBytes()
201 member_end_byte_offset = member_byte_offset + member.type.size
202 if member_byte_offset <= offset and offset < member_end_byte_offset:
203 member_list.append(member)
204 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
205 return
206 for field_idx in range (value_type.GetNumberOfVirtualBaseClasses()):
207 member = value_type.GetVirtualBaseClassAtIndex(field_idx)
208 member_byte_offset = member.GetOffsetInBytes()
209 member_end_byte_offset = member_byte_offset + member.type.size
210 if member_byte_offset <= offset and offset < member_end_byte_offset:
211 member_list.append(member)
212 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
213 return
Greg Claytone04cfd22012-07-11 22:13:18 +0000214
215def append_regex_callback(option, opt, value, parser):
216 try:
217 ivar_regex = re.compile(value)
218 parser.values.ivar_regex_blacklist.append(ivar_regex)
219 except:
220 print 'error: an exception was thrown when compiling the ivar regular expression for "%s"' % value
Greg Claytonb403a152012-04-21 00:11:26 +0000221
Greg Claytond84bb482012-04-13 16:24:09 +0000222def add_common_options(parser):
223 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
Greg Claytone04cfd22012-07-11 22:13:18 +0000224 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 +0000225 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 +0000226 parser.add_option('-z', '--size', action='store_true', dest='show_size', help='print the allocation size in bytes', default=False)
227 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 +0000228 parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False)
229 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 +0000230 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 +0000231 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 +0000232 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 +0000233 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)
234 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 +0000235 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 +0000236 parser.add_option('-O', '--offset', type='int', dest='offset', help='the matching data must be at this offset', default=-1)
Greg Clayton13fbb992013-02-01 00:47:49 +0000237 parser.add_option('--ignore-stack', action='store_false', dest='search_stack', help="Don't search the stack when enumerating memory", default=True)
238 parser.add_option('--ignore-heap', action='store_false', dest='search_heap', help="Don't search the heap allocations when enumerating memory", default=True)
239 parser.add_option('--ignore-segments', action='store_false', dest='search_segments', help="Don't search readable executable segments enumerating memory", default=True)
240 parser.add_option('-V', '--vm-regions', action='store_true', dest='search_vm_regions', help='Check all VM regions instead of searching the heap, stack and segments', default=False)
Greg Claytond9fc5352012-05-10 23:17:28 +0000241
Greg Clayton85e62ec2013-01-30 22:57:34 +0000242def type_flags_to_string(type_flags):
243 if type_flags == 0:
244 type_str = 'free'
245 elif type_flags & 2:
246 type_str = 'malloc'
247 elif type_flags & 4:
248 type_str = 'free'
249 elif type_flags & 1:
250 type_str = 'generic'
251 elif type_flags & 8:
252 type_str = 'stack'
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000253 elif type_flags & 16:
Greg Clayton13fbb992013-02-01 00:47:49 +0000254 type_str = 'stack (red zone)'
255 elif type_flags & 32:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000256 type_str = 'segment'
Greg Clayton13fbb992013-02-01 00:47:49 +0000257 elif type_flags & 64:
258 type_str = 'vm_region'
Greg Clayton85e62ec2013-01-30 22:57:34 +0000259 else:
260 type_str = hex(type_flags)
261 return type_str
262
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000263def type_flags_to_description(type_flags, ptr_addr, ptr_size, offset):
264 show_offset = False
Greg Clayton85e62ec2013-01-30 22:57:34 +0000265 if type_flags == 0 or type_flags & 4:
266 type_str = 'free(%#x)' % (ptr_addr,)
267 elif type_flags & 2 or type_flags & 1:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000268 type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr)
269 show_offset = True
Greg Clayton85e62ec2013-01-30 22:57:34 +0000270 elif type_flags & 8:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000271 type_str = 'stack'
272 elif type_flags & 16:
Greg Clayton13fbb992013-02-01 00:47:49 +0000273 type_str = 'stack (red zone)'
274 elif type_flags & 32:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000275 sb_addr = lldb.debugger.GetSelectedTarget().ResolveLoadAddress(ptr_addr + offset)
276 type_str = 'segment [%#x - %#x), %s + %u, %s' % (ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
Greg Clayton13fbb992013-02-01 00:47:49 +0000277 elif type_flags & 64:
278 sb_addr = lldb.debugger.GetSelectedTarget().ResolveLoadAddress(ptr_addr + offset)
279 type_str = 'vm_region [%#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 +0000280 else:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000281 type_str = '%#x' % (ptr_addr,)
282 show_offset = True
283 if show_offset and offset != 0:
284 type_str += ' + %-6u' % (offset,)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000285 return type_str
286
287def dump_stack_history_entry(options, result, stack_history_entry, idx):
Greg Clayton7200ed12012-05-10 23:37:52 +0000288 address = int(stack_history_entry.address)
289 if address:
290 type_flags = int(stack_history_entry.type_flags)
Greg Claytond9fc5352012-05-10 23:17:28 +0000291 symbolicator = lldb.utils.symbolication.Symbolicator()
Greg Clayton85e62ec2013-01-30 22:57:34 +0000292 symbolicator.target = lldb.debugger.GetSelectedTarget()
293 type_str = type_flags_to_string(type_flags)
Greg Claytonc373ca52012-09-01 00:34:35 +0000294 result.AppendMessage('stack[%u]: addr = 0x%x, type=%s, frames:' % (idx, address, type_str))
Greg Claytond9fc5352012-05-10 23:17:28 +0000295 frame_idx = 0
Greg Clayton7200ed12012-05-10 23:37:52 +0000296 idx = 0
297 pc = int(stack_history_entry.frames[idx])
Greg Claytond9fc5352012-05-10 23:17:28 +0000298 while pc != 0:
299 if pc >= 0x1000:
300 frames = symbolicator.symbolicate(pc)
301 if frames:
302 for frame in frames:
Greg Claytonc373ca52012-09-01 00:34:35 +0000303 result.AppendMessage(' [%u] %s' % (frame_idx, frame))
Greg Claytond9fc5352012-05-10 23:17:28 +0000304 frame_idx += 1
305 else:
Greg Claytonc373ca52012-09-01 00:34:35 +0000306 result.AppendMessage(' [%u] 0x%x' % (frame_idx, pc))
Greg Claytond9fc5352012-05-10 23:17:28 +0000307 frame_idx += 1
Greg Clayton7200ed12012-05-10 23:37:52 +0000308 idx = idx + 1
309 pc = int(stack_history_entry.frames[idx])
Greg Claytond9fc5352012-05-10 23:17:28 +0000310 else:
311 pc = 0
Greg Clayton85e62ec2013-01-30 22:57:34 +0000312 if idx >= options.max_frames:
313 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))
314
Greg Claytonc373ca52012-09-01 00:34:35 +0000315 result.AppendMessage('')
Greg Claytond9fc5352012-05-10 23:17:28 +0000316
Greg Clayton85e62ec2013-01-30 22:57:34 +0000317def dump_stack_history_entries(options, result, addr, history):
Greg Claytond9fc5352012-05-10 23:17:28 +0000318 # malloc_stack_entry *get_stack_history_for_address (const void * addr)
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000319 single_expr = '''
320typedef int kern_return_t;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000321#define MAX_FRAMES %u
322typedef struct $malloc_stack_entry {
323 uint64_t address;
324 uint64_t argument;
325 uint32_t type_flags;
326 uint32_t num_frames;
327 uint64_t frames[512];
328 kern_return_t err;
329} $malloc_stack_entry;
330typedef unsigned task_t;
331$malloc_stack_entry stack;
332stack.address = 0x%x;
333stack.type_flags = 2;
334stack.num_frames = 0;
335stack.frames[0] = 0;
336uint32_t max_stack_frames = MAX_FRAMES;
337stack.err = (kern_return_t)__mach_stack_logging_get_frames (
338 (task_t)mach_task_self(),
339 stack.address,
340 &stack.frames[0],
341 max_stack_frames,
342 &stack.num_frames);
343if (stack.num_frames < MAX_FRAMES)
344 stack.frames[stack.num_frames] = 0;
345else
346 stack.frames[MAX_FRAMES-1] = 0;
347stack''' % (options.max_frames, addr);
348
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000349 history_expr = '''
350typedef int kern_return_t;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000351typedef unsigned task_t;
352#define MAX_FRAMES %u
353#define MAX_HISTORY %u
354typedef struct mach_stack_logging_record_t {
355 uint32_t type_flags;
356 uint64_t stack_identifier;
357 uint64_t argument;
358 uint64_t address;
359} mach_stack_logging_record_t;
360typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
361typedef struct malloc_stack_entry {
362 uint64_t address;
363 uint64_t argument;
364 uint32_t type_flags;
365 uint32_t num_frames;
366 uint64_t frames[MAX_FRAMES];
367 kern_return_t frames_err;
368} malloc_stack_entry;
369typedef struct $malloc_stack_history {
370 task_t task;
371 unsigned idx;
372 malloc_stack_entry entries[MAX_HISTORY];
373} $malloc_stack_history;
374$malloc_stack_history info = { (task_t)mach_task_self(), 0 };
375uint32_t max_stack_frames = MAX_FRAMES;
376enumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void {
377 $malloc_stack_history *info = ($malloc_stack_history *)baton;
378 if (info->idx < MAX_HISTORY) {
379 malloc_stack_entry *stack_entry = &(info->entries[info->idx]);
380 stack_entry->address = stack_record.address;
381 stack_entry->type_flags = stack_record.type_flags;
382 stack_entry->argument = stack_record.argument;
383 stack_entry->num_frames = 0;
384 stack_entry->frames[0] = 0;
385 stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack (
386 info->task,
387 stack_record.stack_identifier,
388 stack_entry->frames,
389 (uint32_t)MAX_FRAMES,
390 &stack_entry->num_frames);
391 // Terminate the frames with zero if there is room
392 if (stack_entry->num_frames < MAX_FRAMES)
393 stack_entry->frames[stack_entry->num_frames] = 0;
394 }
395 ++info->idx;
396};
397(kern_return_t)__mach_stack_logging_enumerate_records (info.task, (uint64_t)0x%x, callback, &info);
398info''' % (options.max_frames, options.max_history, addr);
399
400 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
401 if history:
402 expr = history_expr
403 else:
404 expr = single_expr
Greg Clayton927fa012013-04-03 07:25:30 +0000405 expr_options = lldb.SBExpressionOptions()
406 expr_options.SetIgnoreBreakpoints(True);
407 expr_options.SetTimeoutInMicroSeconds (5*1000*1000) # 5 second timeout
408 expr_options.SetTryAllThreads (True)
409 expr_sbvalue = frame.EvaluateExpression (expr, expr_options)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000410 if options.verbose:
411 print "expression:"
412 print expr
413 print "expression result:"
414 print expr_sbvalue
415 if expr_sbvalue.error.Success():
416 if history:
417 malloc_stack_history = lldb.value(expr_sbvalue)
418 num_stacks = int(malloc_stack_history.idx)
419 if num_stacks <= options.max_history:
420 i_max = num_stacks
421 else:
422 i_max = options.max_history
423 for i in range(i_max):
424 stack_history_entry = malloc_stack_history.entries[i]
425 dump_stack_history_entry(options, result, stack_history_entry, i)
426 if num_stacks > options.max_history:
427 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))
428 else:
429 stack_history_entry = lldb.value(expr_sbvalue)
430 dump_stack_history_entry(options, result, stack_history_entry, 0)
431
432 else:
433 result.AppendMessage('error: expression failed "%s" => %s' % (expr, expr_sbvalue.error))
434
435
436def display_match_results (result, options, arg_str_description, expr, print_no_matches = True):
437 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
438 if not frame:
439 result.AppendMessage('error: invalid frame')
440 return 0
Greg Clayton927fa012013-04-03 07:25:30 +0000441 expr_options = lldb.SBExpressionOptions()
442 expr_options.SetIgnoreBreakpoints(True);
443 expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues);
444 expr_options.SetTimeoutInMicroSeconds (30*1000*1000) # 30 second timeout
445 expr_options.SetTryAllThreads (False)
446 expr_sbvalue = frame.EvaluateExpression (expr, expr_options)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000447 if options.verbose:
448 print "expression:"
449 print expr
450 print "expression result:"
451 print expr_sbvalue
Greg Clayton7fb671b2012-04-11 18:30:53 +0000452 if expr_sbvalue.error.Success():
Greg Clayton966c6f62014-01-07 21:55:00 +0000453 match_value = lldb.value(expr_sbvalue)
454 i = 0
455 match_idx = 0
456 while 1:
457 print_entry = True
458 match_entry = match_value[i]; i += 1
459 if i > options.max_matches:
460 result.AppendMessage('warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' % (options.max_matches))
461 break
462 malloc_addr = match_entry.addr.sbvalue.unsigned
463 if malloc_addr == 0:
464 break
465 malloc_size = int(match_entry.size)
466 offset = int(match_entry.offset)
467
468 if options.offset >= 0 and options.offset != offset:
469 print_entry = False
470 else:
471 match_addr = malloc_addr + offset
472 type_flags = int(match_entry.type)
473 #result.AppendMessage (hex(malloc_addr + offset))
474 if type_flags == 64:
475 search_stack_old = options.search_stack
476 search_segments_old = options.search_segments
477 search_heap_old = options.search_heap
478 search_vm_regions = options.search_vm_regions
479 options.search_stack = True
480 options.search_segments = True
481 options.search_heap = True
482 options.search_vm_regions = False
483 if malloc_info_impl (lldb.debugger, result, options, [hex(malloc_addr + offset)]):
484 print_entry = False
485 options.search_stack = search_stack_old
486 options.search_segments = search_segments_old
487 options.search_heap = search_heap_old
488 options.search_vm_regions = search_vm_regions
Greg Claytone04cfd22012-07-11 22:13:18 +0000489 if print_entry:
Greg Clayton966c6f62014-01-07 21:55:00 +0000490 description = '%#16.16x: %s' % (match_addr, type_flags_to_description(type_flags, malloc_addr, malloc_size, offset))
491 if options.show_size:
492 description += ' <%5u>' % (malloc_size)
493 if options.show_range:
494 description += ' [%#x - %#x)' % (malloc_addr, malloc_addr + malloc_size)
495 derefed_dynamic_value = None
496 dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
497 if dynamic_value.type.name == 'void *':
498 if options.type == 'pointer' and malloc_size == 4096:
499 error = lldb.SBError()
500 process = expr_sbvalue.GetProcess()
501 target = expr_sbvalue.GetTarget()
502 data = bytearray(process.ReadMemory(malloc_addr, 16, error))
503 if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
504 ptr_size = target.addr_size
505 thread = process.ReadUnsignedFromMemory (malloc_addr + 16 + ptr_size, ptr_size, error)
506 # 4 bytes 0xa1a1a1a1
507 # 12 bytes 'AUTORELEASE!'
508 # ptr bytes autorelease insertion point
509 # ptr bytes pthread_t
510 # ptr bytes next colder page
511 # ptr bytes next hotter page
512 # 4 bytes this page's depth in the list
513 # 4 bytes high-water mark
514 description += ' AUTORELEASE! for pthread_t %#x' % (thread)
515 # else:
516 # description += 'malloc(%u)' % (malloc_size)
517 # else:
518 # description += 'malloc(%u)' % (malloc_size)
519 else:
520 derefed_dynamic_value = dynamic_value.deref
521 if derefed_dynamic_value:
522 derefed_dynamic_type = derefed_dynamic_value.type
523 derefed_dynamic_type_size = derefed_dynamic_type.size
524 derefed_dynamic_type_name = derefed_dynamic_type.name
525 description += ' '
526 description += derefed_dynamic_type_name
527 if offset < derefed_dynamic_type_size:
528 member_list = list();
529 get_member_types_for_offset (derefed_dynamic_type, offset, member_list)
530 if member_list:
531 member_path = ''
532 for member in member_list:
533 member_name = member.name
534 if member_name:
535 if member_path:
536 member_path += '.'
537 member_path += member_name
538 if member_path:
539 if options.ivar_regex_blacklist:
540 for ivar_regex in options.ivar_regex_blacklist:
541 if ivar_regex.match(member_path):
542 print_entry = False
543 description += '.%s' % (member_path)
544 else:
545 description += '%u bytes after %s' % (offset - derefed_dynamic_type_size, derefed_dynamic_type_name)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000546 else:
Greg Clayton966c6f62014-01-07 21:55:00 +0000547 # strip the "*" from the end of the name since we were unable to dereference this
548 description += dynamic_value.type.name[0:-1]
549 if print_entry:
550 match_idx += 1
551 result_output = ''
552 if description:
553 result_output += description
554 if options.print_type and derefed_dynamic_value:
555 result_output += ' %s' % (derefed_dynamic_value)
556 if options.print_object_description and dynamic_value:
557 desc = dynamic_value.GetObjectDescription()
558 if desc:
559 result_output += '\n%s' % (desc)
560 if result_output:
561 result.AppendMessage(result_output)
562 if options.memory:
563 cmd_result = lldb.SBCommandReturnObject()
564 if options.format == None:
565 memory_command = "memory read --force 0x%x 0x%x" % (malloc_addr, malloc_addr + malloc_size)
566 else:
567 memory_command = "memory read --force -f %s 0x%x 0x%x" % (options.format, malloc_addr, malloc_addr + malloc_size)
568 if options.verbose:
569 result.AppendMessage(memory_command)
570 lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
571 result.AppendMessage(cmd_result.GetOutput())
572 if options.stack_history:
573 dump_stack_history_entries(options, result, malloc_addr, 1)
574 elif options.stack:
575 dump_stack_history_entries(options, result, malloc_addr, 0)
576 return i
Greg Clayton7fb671b2012-04-11 18:30:53 +0000577 else:
Greg Clayton7143f002012-09-04 14:09:21 +0000578 result.AppendMessage(str(expr_sbvalue.error))
Greg Claytone04cfd22012-07-11 22:13:18 +0000579 return 0
580
Greg Clayton85e62ec2013-01-30 22:57:34 +0000581def get_ptr_refs_options ():
582 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
583 description='''Searches all allocations on the heap for pointer values on
584darwin user space programs. Any matches that were found will dump the malloc
585blocks that contain the pointers and might be able to print what kind of
586objects the pointers are contained in using dynamic type information in the
587program.'''
588 parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
589 add_common_options(parser)
590 return parser
Greg Clayton7fb671b2012-04-11 18:30:53 +0000591
Greg Clayton77677162012-04-12 18:57:36 +0000592def ptr_refs(debugger, command, result, dict):
Greg Clayton804de012012-04-11 16:27:06 +0000593 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000594 parser = get_ptr_refs_options()
Greg Clayton804de012012-04-11 16:27:06 +0000595 try:
596 (options, args) = parser.parse_args(command_args)
597 except:
598 return
Greg Clayton7fb671b2012-04-11 18:30:53 +0000599
Greg Clayton85e62ec2013-01-30 22:57:34 +0000600 process = lldb.debugger.GetSelectedTarget().GetProcess()
601 if not process:
602 result.AppendMessage('error: invalid process')
603 return
604 frame = process.GetSelectedThread().GetSelectedFrame()
605 if not frame:
606 result.AppendMessage('error: invalid frame')
607 return
608
Greg Clayton7fb671b2012-04-11 18:30:53 +0000609 options.type = 'pointer'
Greg Clayton85e62ec2013-01-30 22:57:34 +0000610 if options.format == None:
611 options.format = "A" # 'A' is "address" format
612
Greg Clayton804de012012-04-11 16:27:06 +0000613 if args:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000614 # When we initialize the expression, we must define any types that
615 # we will need when looking at every allocation. We must also define
616 # a type named callback_baton_t and make an instance named "baton"
617 # and initialize it how ever we want to. The address of "baton" will
618 # be passed into our range callback. callback_baton_t must contain
619 # a member named "callback" whose type is "range_callback_t". This
620 # will be used by our zone callbacks to call the range callback for
621 # each malloc range.
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000622 user_init_code_format = '''
623#define MAX_MATCHES %u
Greg Clayton85e62ec2013-01-30 22:57:34 +0000624struct $malloc_match {
625 void *addr;
626 uintptr_t size;
627 uintptr_t offset;
628 uintptr_t type;
629};
630typedef struct callback_baton_t {
631 range_callback_t callback;
632 unsigned num_matches;
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000633 $malloc_match matches[MAX_MATCHES];
Greg Clayton85e62ec2013-01-30 22:57:34 +0000634 void *ptr;
635} callback_baton_t;
636range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
637 callback_baton_t *info = (callback_baton_t *)baton;
638 typedef void* T;
639 const unsigned size = sizeof(T);
640 T *array = (T*)ptr_addr;
641 for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) {
642 if (array[idx] == info->ptr) {
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000643 if (info->num_matches < MAX_MATCHES) {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000644 info->matches[info->num_matches].addr = (void*)ptr_addr;
645 info->matches[info->num_matches].size = ptr_size;
646 info->matches[info->num_matches].offset = idx*sizeof(T);
647 info->matches[info->num_matches].type = type;
648 ++info->num_matches;
649 }
650 }
651 }
652};
653callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
654'''
655 # We must also define a snippet of code to be run that returns
656 # the result of the expression we run.
657 # Here we return NULL if our pointer was not found in any malloc blocks,
658 # and we return the address of the matches array so we can then access
659 # the matching results
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000660 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
661 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
Greg Clayton85e62ec2013-01-30 22:57:34 +0000662baton.matches'''
663 # Iterate through all of our pointer expressions and display the results
664 for ptr_expr in args:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000665 user_init_code = user_init_code_format % (options.max_matches, ptr_expr)
Greg Clayton13fbb992013-02-01 00:47:49 +0000666 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000667 arg_str_description = 'malloc block containing pointer %s' % ptr_expr
668 display_match_results (result, options, arg_str_description, expr)
Greg Clayton804de012012-04-11 16:27:06 +0000669 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000670 result.AppendMessage('error: no pointer arguments were given')
671
672def get_cstr_refs_options():
673 usage = "usage: %prog [options] <CSTR> [CSTR ...]"
674 description='''Searches all allocations on the heap for C string values on
675darwin user space programs. Any matches that were found will dump the malloc
676blocks that contain the C strings and might be able to print what kind of
677objects the pointers are contained in using dynamic type information in the
678program.'''
679 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
680 add_common_options(parser)
681 return parser
Greg Clayton804de012012-04-11 16:27:06 +0000682
Greg Clayton77677162012-04-12 18:57:36 +0000683def cstr_refs(debugger, command, result, dict):
Greg Clayton7fb671b2012-04-11 18:30:53 +0000684 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000685 parser = get_cstr_refs_options();
Greg Clayton7fb671b2012-04-11 18:30:53 +0000686 try:
687 (options, args) = parser.parse_args(command_args)
688 except:
689 return
690
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000691 process = lldb.debugger.GetSelectedTarget().GetProcess()
692 if not process:
693 result.AppendMessage('error: invalid process')
694 return
695 frame = process.GetSelectedThread().GetSelectedFrame()
Greg Clayton85e62ec2013-01-30 22:57:34 +0000696 if not frame:
697 result.AppendMessage('error: invalid frame')
698 return
699
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000700
Greg Clayton7fb671b2012-04-11 18:30:53 +0000701 options.type = 'cstr'
Greg Clayton85e62ec2013-01-30 22:57:34 +0000702 if options.format == None:
703 options.format = "Y" # 'Y' is "bytes with ASCII" format
Greg Clayton7fb671b2012-04-11 18:30:53 +0000704
705 if args:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000706 # When we initialize the expression, we must define any types that
707 # we will need when looking at every allocation. We must also define
708 # a type named callback_baton_t and make an instance named "baton"
709 # and initialize it how ever we want to. The address of "baton" will
710 # be passed into our range callback. callback_baton_t must contain
711 # a member named "callback" whose type is "range_callback_t". This
712 # will be used by our zone callbacks to call the range callback for
713 # each malloc range.
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000714 user_init_code_format = '''
715#define MAX_MATCHES %u
716struct $malloc_match {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000717 void *addr;
718 uintptr_t size;
719 uintptr_t offset;
720 uintptr_t type;
721};
722typedef struct callback_baton_t {
723 range_callback_t callback;
724 unsigned num_matches;
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000725 $malloc_match matches[MAX_MATCHES];
Greg Clayton85e62ec2013-01-30 22:57:34 +0000726 const char *cstr;
727 unsigned cstr_len;
728} callback_baton_t;
729range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
730 callback_baton_t *info = (callback_baton_t *)baton;
731 if (info->cstr_len < ptr_size) {
732 const char *begin = (const char *)ptr_addr;
733 const char *end = begin + ptr_size - info->cstr_len;
734 for (const char *s = begin; s < end; ++s) {
735 if ((int)memcmp(s, info->cstr, info->cstr_len) == 0) {
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000736 if (info->num_matches < MAX_MATCHES) {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000737 info->matches[info->num_matches].addr = (void*)ptr_addr;
738 info->matches[info->num_matches].size = ptr_size;
739 info->matches[info->num_matches].offset = s - begin;
740 info->matches[info->num_matches].type = type;
741 ++info->num_matches;
742 }
743 }
744 }
745 }
746};
747const char *cstr = "%s";
748callback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };'''
749 # We must also define a snippet of code to be run that returns
750 # the result of the expression we run.
751 # Here we return NULL if our pointer was not found in any malloc blocks,
752 # and we return the address of the matches array so we can then access
753 # the matching results
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000754 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
755 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
756baton.matches'''
Greg Clayton85e62ec2013-01-30 22:57:34 +0000757 # Iterate through all of our pointer expressions and display the results
758 for cstr in args:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000759 user_init_code = user_init_code_format % (options.max_matches, cstr)
Greg Clayton13fbb992013-02-01 00:47:49 +0000760 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000761 arg_str_description = 'malloc block containing "%s"' % cstr
762 display_match_results (result, options, arg_str_description, expr)
Greg Clayton7fb671b2012-04-11 18:30:53 +0000763 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000764 result.AppendMessage('error: command takes one or more C string arguments')
765
766
767def get_malloc_info_options():
768 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
769 description='''Searches the heap a malloc block that contains the addresses
770specified as one or more address expressions. Any matches that were found will
771dump the malloc blocks that match or contain the specified address. The matching
772blocks might be able to show what kind of objects they are using dynamic type
773information in the program.'''
774 parser = optparse.OptionParser(description=description, prog='malloc_info',usage=usage)
775 add_common_options(parser)
776 return parser
Greg Clayton804de012012-04-11 16:27:06 +0000777
Greg Clayton77677162012-04-12 18:57:36 +0000778def malloc_info(debugger, command, result, dict):
779 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000780 parser = get_malloc_info_options()
Greg Clayton77677162012-04-12 18:57:36 +0000781 try:
782 (options, args) = parser.parse_args(command_args)
783 except:
784 return
Greg Clayton13fbb992013-02-01 00:47:49 +0000785 malloc_info_impl (debugger, result, options, args)
786
787def malloc_info_impl (debugger, result, options, args):
788 # We are specifically looking for something on the heap only
789 options.type = 'malloc_info'
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000790
791 process = lldb.debugger.GetSelectedTarget().GetProcess()
792 if not process:
793 result.AppendMessage('error: invalid process')
794 return
795 frame = process.GetSelectedThread().GetSelectedFrame()
796 if not frame:
797 result.AppendMessage('error: invalid frame')
798 return
Greg Clayton85e62ec2013-01-30 22:57:34 +0000799
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000800 user_init_code_format = '''
801struct $malloc_match {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000802 void *addr;
803 uintptr_t size;
804 uintptr_t offset;
805 uintptr_t type;
806};
807typedef struct callback_baton_t {
808 range_callback_t callback;
809 unsigned num_matches;
810 $malloc_match matches[2]; // Two items so they can be NULL terminated
811 void *ptr;
812} callback_baton_t;
813range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
814 callback_baton_t *info = (callback_baton_t *)baton;
815 if (info->num_matches == 0) {
816 uint8_t *p = (uint8_t *)info->ptr;
817 uint8_t *lo = (uint8_t *)ptr_addr;
818 uint8_t *hi = lo + ptr_size;
819 if (lo <= p && p < hi) {
820 info->matches[info->num_matches].addr = (void*)ptr_addr;
821 info->matches[info->num_matches].size = ptr_size;
822 info->matches[info->num_matches].offset = p - lo;
823 info->matches[info->num_matches].type = type;
824 info->num_matches = 1;
825 }
826 }
827};
828callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000829baton.matches[0].addr = 0;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000830baton.matches[1].addr = 0;'''
Greg Clayton77677162012-04-12 18:57:36 +0000831 if args:
Greg Clayton13fbb992013-02-01 00:47:49 +0000832 total_matches = 0
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000833 for ptr_expr in args:
834 user_init_code = user_init_code_format % (ptr_expr)
Greg Clayton13fbb992013-02-01 00:47:49 +0000835 expr = get_iterate_memory_expr(options, process, user_init_code, 'baton.matches')
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000836 arg_str_description = 'malloc block that contains %s' % ptr_expr
Greg Clayton13fbb992013-02-01 00:47:49 +0000837 total_matches += display_match_results (result, options, arg_str_description, expr)
838 return total_matches
Greg Clayton77677162012-04-12 18:57:36 +0000839 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000840 result.AppendMessage('error: command takes one or more pointer expressions')
Greg Clayton13fbb992013-02-01 00:47:49 +0000841 return 0
Greg Clayton77677162012-04-12 18:57:36 +0000842
Greg Clayton85e62ec2013-01-30 22:57:34 +0000843def get_thread_stack_ranges_struct (process):
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000844 '''Create code that defines a structure that represents threads stack bounds
845 for all threads. It returns a static sized array initialized with all of
846 the tid, base, size structs for all the threads.'''
Greg Clayton85e62ec2013-01-30 22:57:34 +0000847 stack_dicts = list()
848 if process:
849 i = 0;
850 for thread in process:
851 min_sp = thread.frame[0].sp
852 max_sp = min_sp
853 for frame in thread.frames:
854 sp = frame.sp
855 if sp < min_sp: min_sp = sp
856 if sp > max_sp: max_sp = sp
857 if min_sp < max_sp:
858 stack_dicts.append ({ 'tid' : thread.GetThreadID(), 'base' : min_sp , 'size' : max_sp-min_sp, 'index' : i })
859 i += 1
860 stack_dicts_len = len(stack_dicts)
861 if stack_dicts_len > 0:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000862 result = '''
863#define NUM_STACKS %u
Greg Clayton13fbb992013-02-01 00:47:49 +0000864#define STACK_RED_ZONE_SIZE %u
Greg Clayton85e62ec2013-01-30 22:57:34 +0000865typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
Greg Clayton13fbb992013-02-01 00:47:49 +0000866thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize())
Greg Clayton85e62ec2013-01-30 22:57:34 +0000867 for stack_dict in stack_dicts:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000868 result += '''
869stacks[%(index)u].tid = 0x%(tid)x;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000870stacks[%(index)u].base = 0x%(base)x;
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000871stacks[%(index)u].size = 0x%(size)x;''' % stack_dict
Greg Clayton85e62ec2013-01-30 22:57:34 +0000872 return result
873 else:
Greg Clayton966c6f62014-01-07 21:55:00 +0000874 return ''
Greg Claytonae23ed32012-10-08 22:39:38 +0000875
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000876def get_sections_ranges_struct (process):
877 '''Create code that defines a structure that represents all segments that
878 can contain data for all images in "target". It returns a static sized
879 array initialized with all of base, size structs for all the threads.'''
880 target = process.target
881 segment_dicts = list()
882 for (module_idx, module) in enumerate(target.modules):
883 for sect_idx in range(module.GetNumSections()):
884 section = module.GetSectionAtIndex(sect_idx)
885 if not section:
886 break
887 name = section.name
888 if name != '__TEXT' and name != '__LINKEDIT' and name != '__PAGEZERO':
889 base = section.GetLoadAddress(target)
890 size = section.GetByteSize()
891 if base != lldb.LLDB_INVALID_ADDRESS and size > 0:
892 segment_dicts.append ({ 'base' : base, 'size' : size })
893 segment_dicts_len = len(segment_dicts)
894 if segment_dicts_len > 0:
895 result = '''
896#define NUM_SEGMENTS %u
897typedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t;
898segment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,)
899 for (idx, segment_dict) in enumerate(segment_dicts):
900 segment_dict['index'] = idx
901 result += '''
902segments[%(index)u].base = 0x%(base)x;
903segments[%(index)u].size = 0x%(size)x;''' % segment_dict
904 return result
Greg Claytonae23ed32012-10-08 22:39:38 +0000905 else:
Greg Clayton966c6f62014-01-07 21:55:00 +0000906 return ''
Greg Claytonae23ed32012-10-08 22:39:38 +0000907
Greg Claytone04cfd22012-07-11 22:13:18 +0000908def section_ptr_refs(debugger, command, result, dict):
909 command_args = shlex.split(command)
910 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
911 description='''Searches section contents for pointer values in darwin user space programs.'''
912 parser = optparse.OptionParser(description=description, prog='section_ptr_refs',usage=usage)
913 add_common_options(parser)
914 parser.add_option('--section', action='append', type='string', dest='section_names', help='section name to search', default=list())
915 try:
916 (options, args) = parser.parse_args(command_args)
917 except:
918 return
919
920 options.type = 'pointer'
Greg Claytonae23ed32012-10-08 22:39:38 +0000921
Greg Claytone04cfd22012-07-11 22:13:18 +0000922 sections = list()
923 section_modules = list()
924 if not options.section_names:
Greg Claytonc373ca52012-09-01 00:34:35 +0000925 result.AppendMessage('error: at least one section must be specified with the --section option')
Greg Claytone04cfd22012-07-11 22:13:18 +0000926 return
927
Greg Clayton85e62ec2013-01-30 22:57:34 +0000928 target = lldb.debugger.GetSelectedTarget()
929 for module in target.modules:
Greg Claytone04cfd22012-07-11 22:13:18 +0000930 for section_name in options.section_names:
931 section = module.section[section_name]
932 if section:
933 sections.append (section)
934 section_modules.append (module)
935 if sections:
936 dylid_load_err = load_dylib()
937 if dylid_load_err:
Greg Claytonc373ca52012-09-01 00:34:35 +0000938 result.AppendMessage(dylid_load_err)
Greg Claytone04cfd22012-07-11 22:13:18 +0000939 return
Greg Clayton85e62ec2013-01-30 22:57:34 +0000940 frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
Greg Claytone04cfd22012-07-11 22:13:18 +0000941 for expr_str in args:
942 for (idx, section) in enumerate(sections):
943 expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (section.addr.load_addr, section.size, expr_str)
944 arg_str_description = 'section %s.%s containing "%s"' % (section_modules[idx].file.fullpath, section.name, expr_str)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000945 num_matches = display_match_results (result, options, arg_str_description, expr, False)
Greg Claytone04cfd22012-07-11 22:13:18 +0000946 if num_matches:
947 if num_matches < options.max_matches:
948 options.max_matches = options.max_matches - num_matches
949 else:
950 options.max_matches = 0
951 if options.max_matches == 0:
952 return
953 else:
Greg Claytonc373ca52012-09-01 00:34:35 +0000954 result.AppendMessage('error: no sections were found that match any of %s' % (', '.join(options.section_names)))
Greg Claytone04cfd22012-07-11 22:13:18 +0000955
Greg Clayton85e62ec2013-01-30 22:57:34 +0000956def get_objc_refs_options():
957 usage = "usage: %prog [options] <CLASS> [CLASS ...]"
958 description='''Searches all allocations on the heap for instances of
959objective C classes, or any classes that inherit from the specified classes
960in darwin user space programs. Any matches that were found will dump the malloc
961blocks that contain the C strings and might be able to print what kind of
962objects the pointers are contained in using dynamic type information in the
963program.'''
964 parser = optparse.OptionParser(description=description, prog='objc_refs',usage=usage)
965 add_common_options(parser)
966 return parser
967
Greg Clayton774ebdf2012-08-11 02:26:26 +0000968def objc_refs(debugger, command, result, dict):
969 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000970 parser = get_objc_refs_options()
Greg Clayton774ebdf2012-08-11 02:26:26 +0000971 try:
972 (options, args) = parser.parse_args(command_args)
973 except:
974 return
975
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000976 process = lldb.debugger.GetSelectedTarget().GetProcess()
977 if not process:
978 result.AppendMessage('error: invalid process')
979 return
980 frame = process.GetSelectedThread().GetSelectedFrame()
Greg Clayton85e62ec2013-01-30 22:57:34 +0000981 if not frame:
982 result.AppendMessage('error: invalid frame')
983 return
984
985 options.type = 'isa'
986 if options.format == None:
987 options.format = "A" # 'A' is "address" format
988
Greg Clayton927fa012013-04-03 07:25:30 +0000989 expr_options = lldb.SBExpressionOptions()
990 expr_options.SetIgnoreBreakpoints(True);
991 expr_options.SetTimeoutInMicroSeconds (3*1000*1000) # 3 second infinite timeout
992 expr_options.SetTryAllThreads (True)
993 num_objc_classes_value = frame.EvaluateExpression("(int)objc_getClassList((void *)0, (int)0)", expr_options)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000994 if not num_objc_classes_value.error.Success():
995 result.AppendMessage('error: %s' % num_objc_classes_value.error.GetCString())
996 return
997
998 num_objc_classes = num_objc_classes_value.GetValueAsUnsigned()
999 if num_objc_classes == 0:
1000 result.AppendMessage('error: no objective C classes in program')
1001 return
1002
1003 if args:
1004 # When we initialize the expression, we must define any types that
1005 # we will need when looking at every allocation. We must also define
1006 # a type named callback_baton_t and make an instance named "baton"
1007 # and initialize it how ever we want to. The address of "baton" will
1008 # be passed into our range callback. callback_baton_t must contain
1009 # a member named "callback" whose type is "range_callback_t". This
1010 # will be used by our zone callbacks to call the range callback for
1011 # each malloc range.
Greg Clayton13fbb992013-02-01 00:47:49 +00001012 user_init_code_format = '''
1013#define MAX_MATCHES %u
Greg Claytone7fc9cc2013-01-31 06:38:09 +00001014struct $malloc_match {
Greg Clayton85e62ec2013-01-30 22:57:34 +00001015 void *addr;
1016 uintptr_t size;
1017 uintptr_t offset;
1018 uintptr_t type;
1019};
1020typedef int (*compare_callback_t)(const void *a, const void *b);
1021typedef struct callback_baton_t {
1022 range_callback_t callback;
1023 compare_callback_t compare_callback;
1024 unsigned num_matches;
Greg Claytone7fc9cc2013-01-31 06:38:09 +00001025 $malloc_match matches[MAX_MATCHES];
Greg Clayton85e62ec2013-01-30 22:57:34 +00001026 void *isa;
1027 Class classes[%u];
1028} callback_baton_t;
1029compare_callback_t compare_callback = [](const void *a, const void *b) -> int {
1030 Class a_ptr = *(Class *)a;
1031 Class b_ptr = *(Class *)b;
1032 if (a_ptr < b_ptr) return -1;
1033 if (a_ptr > b_ptr) return +1;
1034 return 0;
1035};
Sean Callananf09a3db2013-08-13 00:53:33 +00001036typedef Class (*class_getSuperclass_type)(void *isa);
Greg Clayton85e62ec2013-01-30 22:57:34 +00001037range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
Sean Callananf09a3db2013-08-13 00:53:33 +00001038 class_getSuperclass_type class_getSuperclass_impl = (class_getSuperclass_type)class_getSuperclass;
Greg Clayton85e62ec2013-01-30 22:57:34 +00001039 callback_baton_t *info = (callback_baton_t *)baton;
1040 if (sizeof(Class) <= ptr_size) {
1041 Class *curr_class_ptr = (Class *)ptr_addr;
1042 Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr,
1043 (const void *)info->classes,
1044 sizeof(info->classes)/sizeof(Class),
1045 sizeof(Class),
1046 info->compare_callback);
1047 if (matching_class_ptr) {
1048 bool match = false;
1049 if (info->isa) {
1050 Class isa = *curr_class_ptr;
1051 if (info->isa == isa)
1052 match = true;
1053 else { // if (info->objc.match_superclasses) {
Sean Callananf09a3db2013-08-13 00:53:33 +00001054 Class super = class_getSuperclass_impl(isa);
Greg Clayton85e62ec2013-01-30 22:57:34 +00001055 while (super) {
1056 if (super == info->isa) {
1057 match = true;
1058 break;
1059 }
Sean Callananf09a3db2013-08-13 00:53:33 +00001060 super = class_getSuperclass_impl(super);
Greg Clayton85e62ec2013-01-30 22:57:34 +00001061 }
1062 }
1063 }
1064 else
1065 match = true;
1066 if (match) {
Greg Claytone7fc9cc2013-01-31 06:38:09 +00001067 if (info->num_matches < MAX_MATCHES) {
Greg Clayton85e62ec2013-01-30 22:57:34 +00001068 info->matches[info->num_matches].addr = (void*)ptr_addr;
1069 info->matches[info->num_matches].size = ptr_size;
1070 info->matches[info->num_matches].offset = 0;
1071 info->matches[info->num_matches].type = type;
1072 ++info->num_matches;
1073 }
1074 }
1075 }
1076 }
1077};
1078callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
1079int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class));
1080(void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);'''
1081 # We must also define a snippet of code to be run that returns
1082 # the result of the expression we run.
1083 # Here we return NULL if our pointer was not found in any malloc blocks,
1084 # and we return the address of the matches array so we can then access
1085 # the matching results
Greg Claytone7fc9cc2013-01-31 06:38:09 +00001086 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
1087 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
1088 baton.matches'''
Greg Clayton85e62ec2013-01-30 22:57:34 +00001089 # Iterate through all of our ObjC class name arguments
1090 for class_name in args:
1091 addr_expr_str = "(void *)[%s class]" % class_name
Greg Clayton927fa012013-04-03 07:25:30 +00001092 expr_options = lldb.SBExpressionOptions()
1093 expr_options.SetIgnoreBreakpoints(True);
1094 expr_options.SetTimeoutInMicroSeconds (1*1000*1000) # 1 second timeout
1095 expr_options.SetTryAllThreads (True)
1096 expr_sbvalue = frame.EvaluateExpression (addr_expr_str, expr_options)
Greg Clayton85e62ec2013-01-30 22:57:34 +00001097 if expr_sbvalue.error.Success():
1098 isa = expr_sbvalue.unsigned
1099 if isa:
1100 options.type = 'isa'
Greg Claytone7fc9cc2013-01-31 06:38:09 +00001101 result.AppendMessage('Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' % (class_name, isa))
1102 user_init_code = user_init_code_format % (options.max_matches, num_objc_classes, isa)
Greg Clayton13fbb992013-02-01 00:47:49 +00001103 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
Greg Clayton85e62ec2013-01-30 22:57:34 +00001104 arg_str_description = 'objective C classes with isa 0x%x' % isa
1105 display_match_results (result, options, arg_str_description, expr)
Greg Clayton774ebdf2012-08-11 02:26:26 +00001106 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +00001107 result.AppendMessage('error: Can\'t find isa for an ObjC class named "%s"' % (class_name))
1108 else:
1109 result.AppendMessage('error: expression error for "%s": %s' % (addr_expr_str, expr_sbvalue.error))
1110 else:
1111 result.AppendMessage('error: command takes one or more C string arguments');
Greg Clayton774ebdf2012-08-11 02:26:26 +00001112
Greg Claytond712ef02012-04-25 18:40:20 +00001113if __name__ == '__main__':
1114 lldb.debugger = lldb.SBDebugger.Create()
1115
Greg Clayton85e62ec2013-01-30 22:57:34 +00001116# Make the options so we can generate the help text for the new LLDB
1117# command line command prior to registering it with LLDB below. This way
1118# if clients in LLDB type "help malloc_info", they will see the exact same
1119# output as typing "malloc_info --help".
1120ptr_refs.__doc__ = get_ptr_refs_options().format_help()
1121cstr_refs.__doc__ = get_cstr_refs_options().format_help()
1122malloc_info.__doc__ = get_malloc_info_options().format_help()
1123objc_refs.__doc__ = get_objc_refs_options().format_help()
Enrico Granatacd4218f2013-02-22 02:21:10 +00001124lldb.debugger.HandleCommand('command script add -f %s.ptr_refs ptr_refs' % __name__)
1125lldb.debugger.HandleCommand('command script add -f %s.cstr_refs cstr_refs' % __name__)
1126lldb.debugger.HandleCommand('command script add -f %s.malloc_info malloc_info' % __name__)
Greg Clayton85e62ec2013-01-30 22:57:34 +00001127# lldb.debugger.HandleCommand('command script add -f %s.heap heap' % package_name)
1128# lldb.debugger.HandleCommand('command script add -f %s.section_ptr_refs section_ptr_refs' % package_name)
1129# lldb.debugger.HandleCommand('command script add -f %s.stack_ptr_refs stack_ptr_refs' % package_name)
Enrico Granatacd4218f2013-02-22 02:21:10 +00001130lldb.debugger.HandleCommand('command script add -f %s.objc_refs objc_refs' % __name__)
Greg Clayton85e62ec2013-01-30 22:57:34 +00001131print '"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 +00001132
1133
1134
1135