blob: 3bcd52902abdb3cbb6f81b7ff16eecdf41081613 [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 Claytonad72e522013-02-01 00:47:49 +000025def get_iterate_memory_expr(options, process, user_init_code, user_return_code):
26 expr = '''
Greg Clayton549294a2013-01-31 06:38:09 +000027typedef unsigned natural_t;
Greg Clayton849acc82013-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 Claytonad72e522013-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 Clayton849acc82013-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 Clayton849acc82013-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 Claytonad72e522013-02-01 00:47:49 +0000131unsigned int num_zones = 0;task_t task = 0;
Greg Clayton849acc82013-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 Claytonad72e522013-02-01 00:47:49 +0000153}'''
154
155 if options.search_stack:
156 expr += '''
Greg Clayton549294a2013-01-31 06:38:09 +0000157// Call the callback for the thread stack ranges
Greg Claytonad72e522013-02-01 00:47:49 +0000158for (uint32_t i=0; i<NUM_STACKS; ++i) {
Greg Clayton549294a2013-01-31 06:38:09 +0000159 range_callback(task, &baton, 8, stacks[i].base, stacks[i].size);
Greg Claytonad72e522013-02-01 00:47:49 +0000160 if (STACK_RED_ZONE_SIZE > 0) {
161 range_callback(task, &baton, 16, stacks[i].base - STACK_RED_ZONE_SIZE, STACK_RED_ZONE_SIZE);
162 }
163}
164 '''
165
166 if options.search_segments:
167 expr += '''
Greg Clayton549294a2013-01-31 06:38:09 +0000168// Call the callback for all segments
169for (uint32_t i=0; i<NUM_SEGMENTS; ++i)
Greg Claytonad72e522013-02-01 00:47:49 +0000170 range_callback(task, &baton, 32, segments[i].base, segments[i].size);'''
171
172 if user_return_code:
173 expr += "\n%s" % (user_return_code,)
174
175 return expr
Greg Clayton0d0f56d2012-07-07 01:22:45 +0000176
177def get_member_types_for_offset(value_type, offset, member_list):
178 member = value_type.GetFieldAtIndex(0)
179 search_bases = False
180 if member:
181 if member.GetOffsetInBytes() <= offset:
182 for field_idx in range (value_type.GetNumberOfFields()):
183 member = value_type.GetFieldAtIndex(field_idx)
184 member_byte_offset = member.GetOffsetInBytes()
185 member_end_byte_offset = member_byte_offset + member.type.size
186 if member_byte_offset <= offset and offset < member_end_byte_offset:
187 member_list.append(member)
188 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
189 return
190 else:
191 search_bases = True
192 else:
193 search_bases = True
194 if search_bases:
195 for field_idx in range (value_type.GetNumberOfDirectBaseClasses()):
196 member = value_type.GetDirectBaseClassAtIndex(field_idx)
197 member_byte_offset = member.GetOffsetInBytes()
198 member_end_byte_offset = member_byte_offset + member.type.size
199 if member_byte_offset <= offset and offset < member_end_byte_offset:
200 member_list.append(member)
201 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
202 return
203 for field_idx in range (value_type.GetNumberOfVirtualBaseClasses()):
204 member = value_type.GetVirtualBaseClassAtIndex(field_idx)
205 member_byte_offset = member.GetOffsetInBytes()
206 member_end_byte_offset = member_byte_offset + member.type.size
207 if member_byte_offset <= offset and offset < member_end_byte_offset:
208 member_list.append(member)
209 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
210 return
Greg Clayton4c5c4292012-07-11 22:13:18 +0000211
212def append_regex_callback(option, opt, value, parser):
213 try:
214 ivar_regex = re.compile(value)
215 parser.values.ivar_regex_blacklist.append(ivar_regex)
216 except:
217 print 'error: an exception was thrown when compiling the ivar regular expression for "%s"' % value
Greg Clayton9098fee2012-04-21 00:11:26 +0000218
Greg Clayton93e5ba52012-04-13 16:24:09 +0000219def add_common_options(parser):
220 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000221 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 +0000222 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 +0000223 parser.add_option('-z', '--size', action='store_true', dest='show_size', help='print the allocation size in bytes', default=False)
224 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 +0000225 parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False)
226 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 +0000227 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 +0000228 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 +0000229 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 +0000230 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)
231 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 Clayton549294a2013-01-31 06:38:09 +0000232 parser.add_option('-M', '--max-matches', type='int', dest='max_matches', help='the maximum number of matches to print', default=32)
Greg Claytonab20f292012-08-11 02:26:26 +0000233 parser.add_option('-O', '--offset', type='int', dest='offset', help='the matching data must be at this offset', default=-1)
Greg Claytonad72e522013-02-01 00:47:49 +0000234 parser.add_option('--ignore-stack', action='store_false', dest='search_stack', help="Don't search the stack when enumerating memory", default=True)
235 parser.add_option('--ignore-heap', action='store_false', dest='search_heap', help="Don't search the heap allocations when enumerating memory", default=True)
236 parser.add_option('--ignore-segments', action='store_false', dest='search_segments', help="Don't search readable executable segments enumerating memory", default=True)
237 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 Clayton7a245762012-05-10 23:17:28 +0000238
Greg Clayton849acc82013-01-30 22:57:34 +0000239def type_flags_to_string(type_flags):
240 if type_flags == 0:
241 type_str = 'free'
242 elif type_flags & 2:
243 type_str = 'malloc'
244 elif type_flags & 4:
245 type_str = 'free'
246 elif type_flags & 1:
247 type_str = 'generic'
248 elif type_flags & 8:
249 type_str = 'stack'
Greg Clayton549294a2013-01-31 06:38:09 +0000250 elif type_flags & 16:
Greg Claytonad72e522013-02-01 00:47:49 +0000251 type_str = 'stack (red zone)'
252 elif type_flags & 32:
Greg Clayton549294a2013-01-31 06:38:09 +0000253 type_str = 'segment'
Greg Claytonad72e522013-02-01 00:47:49 +0000254 elif type_flags & 64:
255 type_str = 'vm_region'
Greg Clayton849acc82013-01-30 22:57:34 +0000256 else:
257 type_str = hex(type_flags)
258 return type_str
259
Greg Clayton549294a2013-01-31 06:38:09 +0000260def type_flags_to_description(type_flags, ptr_addr, ptr_size, offset):
261 show_offset = False
Greg Clayton849acc82013-01-30 22:57:34 +0000262 if type_flags == 0 or type_flags & 4:
263 type_str = 'free(%#x)' % (ptr_addr,)
264 elif type_flags & 2 or type_flags & 1:
Greg Clayton549294a2013-01-31 06:38:09 +0000265 type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr)
266 show_offset = True
Greg Clayton849acc82013-01-30 22:57:34 +0000267 elif type_flags & 8:
Greg Clayton549294a2013-01-31 06:38:09 +0000268 type_str = 'stack'
269 elif type_flags & 16:
Greg Claytonad72e522013-02-01 00:47:49 +0000270 type_str = 'stack (red zone)'
271 elif type_flags & 32:
Greg Clayton549294a2013-01-31 06:38:09 +0000272 sb_addr = lldb.debugger.GetSelectedTarget().ResolveLoadAddress(ptr_addr + offset)
273 type_str = 'segment [%#x - %#x), %s + %u, %s' % (ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
Greg Claytonad72e522013-02-01 00:47:49 +0000274 elif type_flags & 64:
275 sb_addr = lldb.debugger.GetSelectedTarget().ResolveLoadAddress(ptr_addr + offset)
276 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 Clayton849acc82013-01-30 22:57:34 +0000277 else:
Greg Clayton549294a2013-01-31 06:38:09 +0000278 type_str = '%#x' % (ptr_addr,)
279 show_offset = True
280 if show_offset and offset != 0:
281 type_str += ' + %-6u' % (offset,)
Greg Clayton849acc82013-01-30 22:57:34 +0000282 return type_str
283
284def dump_stack_history_entry(options, result, stack_history_entry, idx):
Greg Clayton6f446f32012-05-10 23:37:52 +0000285 address = int(stack_history_entry.address)
286 if address:
287 type_flags = int(stack_history_entry.type_flags)
Greg Clayton7a245762012-05-10 23:17:28 +0000288 symbolicator = lldb.utils.symbolication.Symbolicator()
Greg Clayton849acc82013-01-30 22:57:34 +0000289 symbolicator.target = lldb.debugger.GetSelectedTarget()
290 type_str = type_flags_to_string(type_flags)
Greg Clayton85df60c2012-09-01 00:34:35 +0000291 result.AppendMessage('stack[%u]: addr = 0x%x, type=%s, frames:' % (idx, address, type_str))
Greg Clayton7a245762012-05-10 23:17:28 +0000292 frame_idx = 0
Greg Clayton6f446f32012-05-10 23:37:52 +0000293 idx = 0
294 pc = int(stack_history_entry.frames[idx])
Greg Clayton7a245762012-05-10 23:17:28 +0000295 while pc != 0:
296 if pc >= 0x1000:
297 frames = symbolicator.symbolicate(pc)
298 if frames:
299 for frame in frames:
Greg Clayton85df60c2012-09-01 00:34:35 +0000300 result.AppendMessage(' [%u] %s' % (frame_idx, frame))
Greg Clayton7a245762012-05-10 23:17:28 +0000301 frame_idx += 1
302 else:
Greg Clayton85df60c2012-09-01 00:34:35 +0000303 result.AppendMessage(' [%u] 0x%x' % (frame_idx, pc))
Greg Clayton7a245762012-05-10 23:17:28 +0000304 frame_idx += 1
Greg Clayton6f446f32012-05-10 23:37:52 +0000305 idx = idx + 1
306 pc = int(stack_history_entry.frames[idx])
Greg Clayton7a245762012-05-10 23:17:28 +0000307 else:
308 pc = 0
Greg Clayton849acc82013-01-30 22:57:34 +0000309 if idx >= options.max_frames:
310 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))
311
Greg Clayton85df60c2012-09-01 00:34:35 +0000312 result.AppendMessage('')
Greg Clayton7a245762012-05-10 23:17:28 +0000313
Greg Clayton849acc82013-01-30 22:57:34 +0000314def dump_stack_history_entries(options, result, addr, history):
Greg Clayton7a245762012-05-10 23:17:28 +0000315 # malloc_stack_entry *get_stack_history_for_address (const void * addr)
Greg Clayton549294a2013-01-31 06:38:09 +0000316 single_expr = '''
317typedef int kern_return_t;
Greg Clayton849acc82013-01-30 22:57:34 +0000318#define MAX_FRAMES %u
319typedef struct $malloc_stack_entry {
320 uint64_t address;
321 uint64_t argument;
322 uint32_t type_flags;
323 uint32_t num_frames;
324 uint64_t frames[512];
325 kern_return_t err;
326} $malloc_stack_entry;
327typedef unsigned task_t;
328$malloc_stack_entry stack;
329stack.address = 0x%x;
330stack.type_flags = 2;
331stack.num_frames = 0;
332stack.frames[0] = 0;
333uint32_t max_stack_frames = MAX_FRAMES;
334stack.err = (kern_return_t)__mach_stack_logging_get_frames (
335 (task_t)mach_task_self(),
336 stack.address,
337 &stack.frames[0],
338 max_stack_frames,
339 &stack.num_frames);
340if (stack.num_frames < MAX_FRAMES)
341 stack.frames[stack.num_frames] = 0;
342else
343 stack.frames[MAX_FRAMES-1] = 0;
344stack''' % (options.max_frames, addr);
345
Greg Clayton549294a2013-01-31 06:38:09 +0000346 history_expr = '''
347typedef int kern_return_t;
Greg Clayton849acc82013-01-30 22:57:34 +0000348typedef unsigned task_t;
349#define MAX_FRAMES %u
350#define MAX_HISTORY %u
351typedef struct mach_stack_logging_record_t {
352 uint32_t type_flags;
353 uint64_t stack_identifier;
354 uint64_t argument;
355 uint64_t address;
356} mach_stack_logging_record_t;
357typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
358typedef struct malloc_stack_entry {
359 uint64_t address;
360 uint64_t argument;
361 uint32_t type_flags;
362 uint32_t num_frames;
363 uint64_t frames[MAX_FRAMES];
364 kern_return_t frames_err;
365} malloc_stack_entry;
366typedef struct $malloc_stack_history {
367 task_t task;
368 unsigned idx;
369 malloc_stack_entry entries[MAX_HISTORY];
370} $malloc_stack_history;
371$malloc_stack_history info = { (task_t)mach_task_self(), 0 };
372uint32_t max_stack_frames = MAX_FRAMES;
373enumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void {
374 $malloc_stack_history *info = ($malloc_stack_history *)baton;
375 if (info->idx < MAX_HISTORY) {
376 malloc_stack_entry *stack_entry = &(info->entries[info->idx]);
377 stack_entry->address = stack_record.address;
378 stack_entry->type_flags = stack_record.type_flags;
379 stack_entry->argument = stack_record.argument;
380 stack_entry->num_frames = 0;
381 stack_entry->frames[0] = 0;
382 stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack (
383 info->task,
384 stack_record.stack_identifier,
385 stack_entry->frames,
386 (uint32_t)MAX_FRAMES,
387 &stack_entry->num_frames);
388 // Terminate the frames with zero if there is room
389 if (stack_entry->num_frames < MAX_FRAMES)
390 stack_entry->frames[stack_entry->num_frames] = 0;
391 }
392 ++info->idx;
393};
394(kern_return_t)__mach_stack_logging_enumerate_records (info.task, (uint64_t)0x%x, callback, &info);
395info''' % (options.max_frames, options.max_history, addr);
396
397 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
398 if history:
399 expr = history_expr
400 else:
401 expr = single_expr
Greg Clayton5b578232013-04-03 07:25:30 +0000402 expr_options = lldb.SBExpressionOptions()
403 expr_options.SetIgnoreBreakpoints(True);
404 expr_options.SetTimeoutInMicroSeconds (5*1000*1000) # 5 second timeout
405 expr_options.SetTryAllThreads (True)
406 expr_sbvalue = frame.EvaluateExpression (expr, expr_options)
Greg Clayton849acc82013-01-30 22:57:34 +0000407 if options.verbose:
408 print "expression:"
409 print expr
410 print "expression result:"
411 print expr_sbvalue
412 if expr_sbvalue.error.Success():
413 if history:
414 malloc_stack_history = lldb.value(expr_sbvalue)
415 num_stacks = int(malloc_stack_history.idx)
416 if num_stacks <= options.max_history:
417 i_max = num_stacks
418 else:
419 i_max = options.max_history
420 for i in range(i_max):
421 stack_history_entry = malloc_stack_history.entries[i]
422 dump_stack_history_entry(options, result, stack_history_entry, i)
423 if num_stacks > options.max_history:
424 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))
425 else:
426 stack_history_entry = lldb.value(expr_sbvalue)
427 dump_stack_history_entry(options, result, stack_history_entry, 0)
428
429 else:
430 result.AppendMessage('error: expression failed "%s" => %s' % (expr, expr_sbvalue.error))
431
432
433def display_match_results (result, options, arg_str_description, expr, print_no_matches = True):
434 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
435 if not frame:
436 result.AppendMessage('error: invalid frame')
437 return 0
Greg Clayton5b578232013-04-03 07:25:30 +0000438 expr_options = lldb.SBExpressionOptions()
439 expr_options.SetIgnoreBreakpoints(True);
440 expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues);
441 expr_options.SetTimeoutInMicroSeconds (30*1000*1000) # 30 second timeout
442 expr_options.SetTryAllThreads (False)
443 expr_sbvalue = frame.EvaluateExpression (expr, expr_options)
Greg Clayton849acc82013-01-30 22:57:34 +0000444 if options.verbose:
445 print "expression:"
446 print expr
447 print "expression result:"
448 print expr_sbvalue
Greg Clayton96666442012-04-11 18:30:53 +0000449 if expr_sbvalue.error.Success():
450 if expr_sbvalue.unsigned:
451 match_value = lldb.value(expr_sbvalue)
452 i = 0
Greg Claytonab20f292012-08-11 02:26:26 +0000453 match_idx = 0
Greg Clayton96666442012-04-11 18:30:53 +0000454 while 1:
Greg Clayton4c5c4292012-07-11 22:13:18 +0000455 print_entry = True
Greg Clayton96666442012-04-11 18:30:53 +0000456 match_entry = match_value[i]; i += 1
Greg Clayton849acc82013-01-30 22:57:34 +0000457 if i > options.max_matches:
458 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 +0000459 break
Greg Clayton96666442012-04-11 18:30:53 +0000460 malloc_addr = match_entry.addr.sbvalue.unsigned
461 if malloc_addr == 0:
462 break
463 malloc_size = int(match_entry.size)
464 offset = int(match_entry.offset)
Greg Claytonab20f292012-08-11 02:26:26 +0000465
466 if options.offset >= 0 and options.offset != offset:
467 print_entry = False
468 else:
469 match_addr = malloc_addr + offset
Greg Clayton849acc82013-01-30 22:57:34 +0000470 type_flags = int(match_entry.type)
Greg Claytonad72e522013-02-01 00:47:49 +0000471 #result.AppendMessage (hex(malloc_addr + offset))
472 if type_flags == 64:
473 search_stack_old = options.search_stack
474 search_segments_old = options.search_segments
475 search_heap_old = options.search_heap
476 search_vm_regions = options.search_vm_regions
477 options.search_stack = True
478 options.search_segments = True
479 options.search_heap = True
480 options.search_vm_regions = False
481 if malloc_info_impl (lldb.debugger, result, options, [hex(malloc_addr + offset)]):
482 print_entry = False
483 options.search_stack = search_stack_old
484 options.search_segments = search_segments_old
485 options.search_heap = search_heap_old
486 options.search_vm_regions = search_vm_regions
487 if print_entry:
488 description = '%#16.16x: %s' % (match_addr, type_flags_to_description(type_flags, malloc_addr, malloc_size, offset))
489 if options.show_size:
490 description += ' <%5u>' % (malloc_size)
491 if options.show_range:
492 description += ' [%#x - %#x)' % (malloc_addr, malloc_addr + malloc_size)
493 derefed_dynamic_value = None
494 dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
495 if dynamic_value.type.name == 'void *':
496 if options.type == 'pointer' and malloc_size == 4096:
497 error = lldb.SBError()
498 process = expr_sbvalue.GetProcess()
499 target = expr_sbvalue.GetTarget()
500 data = bytearray(process.ReadMemory(malloc_addr, 16, error))
501 if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
502 ptr_size = target.addr_size
503 thread = process.ReadUnsignedFromMemory (malloc_addr + 16 + ptr_size, ptr_size, error)
504 # 4 bytes 0xa1a1a1a1
505 # 12 bytes 'AUTORELEASE!'
506 # ptr bytes autorelease insertion point
507 # ptr bytes pthread_t
508 # ptr bytes next colder page
509 # ptr bytes next hotter page
510 # 4 bytes this page's depth in the list
511 # 4 bytes high-water mark
512 description += ' AUTORELEASE! for pthread_t %#x' % (thread)
513 # else:
514 # description += 'malloc(%u)' % (malloc_size)
515 # else:
516 # description += 'malloc(%u)' % (malloc_size)
Greg Claytona936c6e2012-09-12 02:02:32 +0000517 else:
Greg Claytonad72e522013-02-01 00:47:49 +0000518 derefed_dynamic_value = dynamic_value.deref
519 if derefed_dynamic_value:
520 derefed_dynamic_type = derefed_dynamic_value.type
521 derefed_dynamic_type_size = derefed_dynamic_type.size
522 derefed_dynamic_type_name = derefed_dynamic_type.name
523 description += ' '
524 description += derefed_dynamic_type_name
525 if offset < derefed_dynamic_type_size:
526 member_list = list();
527 get_member_types_for_offset (derefed_dynamic_type, offset, member_list)
528 if member_list:
529 member_path = ''
530 for member in member_list:
531 member_name = member.name
532 if member_name:
533 if member_path:
534 member_path += '.'
535 member_path += member_name
536 if member_path:
537 if options.ivar_regex_blacklist:
538 for ivar_regex in options.ivar_regex_blacklist:
539 if ivar_regex.match(member_path):
540 print_entry = False
541 description += '.%s' % (member_path)
542 else:
543 description += '%u bytes after %s' % (offset - derefed_dynamic_type_size, derefed_dynamic_type_name)
544 else:
545 # strip the "*" from the end of the name since we were unable to dereference this
546 description += dynamic_value.type.name[0:-1]
Greg Clayton4c5c4292012-07-11 22:13:18 +0000547 if print_entry:
Greg Claytonab20f292012-08-11 02:26:26 +0000548 match_idx += 1
Greg Claytonaaf7fad2012-09-04 14:09:21 +0000549 result_output = ''
Greg Clayton4c5c4292012-07-11 22:13:18 +0000550 if description:
Greg Claytonaaf7fad2012-09-04 14:09:21 +0000551 result_output += description
Greg Clayton4c5c4292012-07-11 22:13:18 +0000552 if options.print_type and derefed_dynamic_value:
Greg Clayton549294a2013-01-31 06:38:09 +0000553 result_output += ' %s' % (derefed_dynamic_value)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000554 if options.print_object_description and dynamic_value:
555 desc = dynamic_value.GetObjectDescription()
556 if desc:
Greg Claytona936c6e2012-09-12 02:02:32 +0000557 result_output += '\n%s' % (desc)
Greg Claytonaaf7fad2012-09-04 14:09:21 +0000558 if result_output:
559 result.AppendMessage(result_output)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000560 if options.memory:
561 cmd_result = lldb.SBCommandReturnObject()
Greg Clayton849acc82013-01-30 22:57:34 +0000562 if options.format == None:
563 memory_command = "memory read --force 0x%x 0x%x" % (malloc_addr, malloc_addr + malloc_size)
564 else:
565 memory_command = "memory read --force -f %s 0x%x 0x%x" % (options.format, malloc_addr, malloc_addr + malloc_size)
566 if options.verbose:
567 result.AppendMessage(memory_command)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000568 lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
Greg Clayton85df60c2012-09-01 00:34:35 +0000569 result.AppendMessage(cmd_result.GetOutput())
Greg Clayton4c5c4292012-07-11 22:13:18 +0000570 if options.stack_history:
Greg Clayton549294a2013-01-31 06:38:09 +0000571 dump_stack_history_entries(options, result, malloc_addr, 1)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000572 elif options.stack:
Greg Clayton549294a2013-01-31 06:38:09 +0000573 dump_stack_history_entries(options, result, malloc_addr, 0)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000574 return i
575 elif print_no_matches:
Greg Clayton85df60c2012-09-01 00:34:35 +0000576 result.AppendMessage('no matches found for %s' % (arg_str_description))
Greg Clayton96666442012-04-11 18:30:53 +0000577 else:
Greg Claytonaaf7fad2012-09-04 14:09:21 +0000578 result.AppendMessage(str(expr_sbvalue.error))
Greg Clayton4c5c4292012-07-11 22:13:18 +0000579 return 0
580
Greg Clayton849acc82013-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 Clayton96666442012-04-11 18:30:53 +0000591
Greg Claytonbff78412012-04-12 18:57:36 +0000592def ptr_refs(debugger, command, result, dict):
Greg Claytone93e24f2012-04-11 16:27:06 +0000593 command_args = shlex.split(command)
Greg Clayton849acc82013-01-30 22:57:34 +0000594 parser = get_ptr_refs_options()
Greg Claytone93e24f2012-04-11 16:27:06 +0000595 try:
596 (options, args) = parser.parse_args(command_args)
597 except:
598 return
Greg Clayton96666442012-04-11 18:30:53 +0000599
Greg Clayton849acc82013-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 Clayton96666442012-04-11 18:30:53 +0000609 options.type = 'pointer'
Greg Clayton849acc82013-01-30 22:57:34 +0000610 if options.format == None:
611 options.format = "A" # 'A' is "address" format
612
Greg Claytone93e24f2012-04-11 16:27:06 +0000613 if args:
Greg Clayton849acc82013-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 Clayton549294a2013-01-31 06:38:09 +0000622 user_init_code_format = '''
623#define MAX_MATCHES %u
Greg Clayton849acc82013-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 Clayton549294a2013-01-31 06:38:09 +0000633 $malloc_match matches[MAX_MATCHES];
Greg Clayton849acc82013-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 Clayton549294a2013-01-31 06:38:09 +0000643 if (info->num_matches < MAX_MATCHES) {
Greg Clayton849acc82013-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 Clayton549294a2013-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 Clayton849acc82013-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 Clayton549294a2013-01-31 06:38:09 +0000665 user_init_code = user_init_code_format % (options.max_matches, ptr_expr)
Greg Claytonad72e522013-02-01 00:47:49 +0000666 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
Greg Clayton849acc82013-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 Claytone93e24f2012-04-11 16:27:06 +0000669 else:
Greg Clayton849acc82013-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 Claytone93e24f2012-04-11 16:27:06 +0000682
Greg Claytonbff78412012-04-12 18:57:36 +0000683def cstr_refs(debugger, command, result, dict):
Greg Clayton96666442012-04-11 18:30:53 +0000684 command_args = shlex.split(command)
Greg Clayton849acc82013-01-30 22:57:34 +0000685 parser = get_cstr_refs_options();
Greg Clayton96666442012-04-11 18:30:53 +0000686 try:
687 (options, args) = parser.parse_args(command_args)
688 except:
689 return
690
Greg Clayton549294a2013-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 Clayton849acc82013-01-30 22:57:34 +0000696 if not frame:
697 result.AppendMessage('error: invalid frame')
698 return
699
Greg Clayton549294a2013-01-31 06:38:09 +0000700
Greg Clayton96666442012-04-11 18:30:53 +0000701 options.type = 'cstr'
Greg Clayton849acc82013-01-30 22:57:34 +0000702 if options.format == None:
703 options.format = "Y" # 'Y' is "bytes with ASCII" format
Greg Clayton96666442012-04-11 18:30:53 +0000704
705 if args:
Greg Clayton849acc82013-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 Clayton549294a2013-01-31 06:38:09 +0000714 user_init_code_format = '''
715#define MAX_MATCHES %u
716struct $malloc_match {
Greg Clayton849acc82013-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 Clayton549294a2013-01-31 06:38:09 +0000725 $malloc_match matches[MAX_MATCHES];
Greg Clayton849acc82013-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 Clayton549294a2013-01-31 06:38:09 +0000736 if (info->num_matches < MAX_MATCHES) {
Greg Clayton849acc82013-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 Clayton549294a2013-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 Clayton849acc82013-01-30 22:57:34 +0000757 # Iterate through all of our pointer expressions and display the results
758 for cstr in args:
Greg Clayton549294a2013-01-31 06:38:09 +0000759 user_init_code = user_init_code_format % (options.max_matches, cstr)
Greg Claytonad72e522013-02-01 00:47:49 +0000760 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
Greg Clayton849acc82013-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 Clayton96666442012-04-11 18:30:53 +0000763 else:
Greg Clayton849acc82013-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 Claytone93e24f2012-04-11 16:27:06 +0000777
Greg Claytonbff78412012-04-12 18:57:36 +0000778def malloc_info(debugger, command, result, dict):
779 command_args = shlex.split(command)
Greg Clayton849acc82013-01-30 22:57:34 +0000780 parser = get_malloc_info_options()
Greg Claytonbff78412012-04-12 18:57:36 +0000781 try:
782 (options, args) = parser.parse_args(command_args)
783 except:
784 return
Greg Claytonad72e522013-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 Clayton549294a2013-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 Clayton849acc82013-01-30 22:57:34 +0000799
Greg Clayton549294a2013-01-31 06:38:09 +0000800 user_init_code_format = '''
801struct $malloc_match {
Greg Clayton849acc82013-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 Clayton549294a2013-01-31 06:38:09 +0000829baton.matches[0].addr = 0;
Greg Clayton849acc82013-01-30 22:57:34 +0000830baton.matches[1].addr = 0;'''
Greg Claytonbff78412012-04-12 18:57:36 +0000831 if args:
Greg Claytonad72e522013-02-01 00:47:49 +0000832 total_matches = 0
Greg Clayton549294a2013-01-31 06:38:09 +0000833 for ptr_expr in args:
834 user_init_code = user_init_code_format % (ptr_expr)
Greg Claytonad72e522013-02-01 00:47:49 +0000835 expr = get_iterate_memory_expr(options, process, user_init_code, 'baton.matches')
Greg Clayton549294a2013-01-31 06:38:09 +0000836 arg_str_description = 'malloc block that contains %s' % ptr_expr
Greg Claytonad72e522013-02-01 00:47:49 +0000837 total_matches += display_match_results (result, options, arg_str_description, expr)
838 return total_matches
Greg Claytonbff78412012-04-12 18:57:36 +0000839 else:
Greg Clayton849acc82013-01-30 22:57:34 +0000840 result.AppendMessage('error: command takes one or more pointer expressions')
Greg Claytonad72e522013-02-01 00:47:49 +0000841 return 0
Greg Claytonbff78412012-04-12 18:57:36 +0000842
Greg Clayton849acc82013-01-30 22:57:34 +0000843def get_thread_stack_ranges_struct (process):
Greg Clayton549294a2013-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 Clayton849acc82013-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 Clayton549294a2013-01-31 06:38:09 +0000862 result = '''
863#define NUM_STACKS %u
Greg Claytonad72e522013-02-01 00:47:49 +0000864#define STACK_RED_ZONE_SIZE %u
Greg Clayton849acc82013-01-30 22:57:34 +0000865typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
Greg Claytonad72e522013-02-01 00:47:49 +0000866thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize())
Greg Clayton849acc82013-01-30 22:57:34 +0000867 for stack_dict in stack_dicts:
Greg Clayton549294a2013-01-31 06:38:09 +0000868 result += '''
869stacks[%(index)u].tid = 0x%(tid)x;
Greg Clayton849acc82013-01-30 22:57:34 +0000870stacks[%(index)u].base = 0x%(base)x;
Greg Clayton549294a2013-01-31 06:38:09 +0000871stacks[%(index)u].size = 0x%(size)x;''' % stack_dict
Greg Clayton849acc82013-01-30 22:57:34 +0000872 return result
873 else:
874 return None
Greg Clayton130a3122012-10-08 22:39:38 +0000875
Greg Clayton549294a2013-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 Clayton130a3122012-10-08 22:39:38 +0000905 else:
Greg Clayton549294a2013-01-31 06:38:09 +0000906 return None
Greg Clayton130a3122012-10-08 22:39:38 +0000907
Greg Clayton4c5c4292012-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 Clayton130a3122012-10-08 22:39:38 +0000921
Greg Clayton4c5c4292012-07-11 22:13:18 +0000922 sections = list()
923 section_modules = list()
924 if not options.section_names:
Greg Clayton85df60c2012-09-01 00:34:35 +0000925 result.AppendMessage('error: at least one section must be specified with the --section option')
Greg Clayton4c5c4292012-07-11 22:13:18 +0000926 return
927
Greg Clayton849acc82013-01-30 22:57:34 +0000928 target = lldb.debugger.GetSelectedTarget()
929 for module in target.modules:
Greg Clayton4c5c4292012-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 Clayton85df60c2012-09-01 00:34:35 +0000938 result.AppendMessage(dylid_load_err)
Greg Clayton4c5c4292012-07-11 22:13:18 +0000939 return
Greg Clayton849acc82013-01-30 22:57:34 +0000940 frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
Greg Clayton4c5c4292012-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 Clayton849acc82013-01-30 22:57:34 +0000945 num_matches = display_match_results (result, options, arg_str_description, expr, False)
Greg Clayton4c5c4292012-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 Clayton85df60c2012-09-01 00:34:35 +0000954 result.AppendMessage('error: no sections were found that match any of %s' % (', '.join(options.section_names)))
Greg Clayton4c5c4292012-07-11 22:13:18 +0000955
Greg Clayton849acc82013-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 Claytonab20f292012-08-11 02:26:26 +0000968def objc_refs(debugger, command, result, dict):
969 command_args = shlex.split(command)
Greg Clayton849acc82013-01-30 22:57:34 +0000970 parser = get_objc_refs_options()
Greg Claytonab20f292012-08-11 02:26:26 +0000971 try:
972 (options, args) = parser.parse_args(command_args)
973 except:
974 return
975
Greg Clayton549294a2013-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 Clayton849acc82013-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 Clayton5b578232013-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 Clayton849acc82013-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 Claytonad72e522013-02-01 00:47:49 +00001012 user_init_code_format = '''
1013#define MAX_MATCHES %u
Greg Clayton549294a2013-01-31 06:38:09 +00001014struct $malloc_match {
Greg Clayton849acc82013-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 Clayton549294a2013-01-31 06:38:09 +00001025 $malloc_match matches[MAX_MATCHES];
Greg Clayton849acc82013-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};
1036range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
1037 callback_baton_t *info = (callback_baton_t *)baton;
1038 if (sizeof(Class) <= ptr_size) {
1039 Class *curr_class_ptr = (Class *)ptr_addr;
1040 Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr,
1041 (const void *)info->classes,
1042 sizeof(info->classes)/sizeof(Class),
1043 sizeof(Class),
1044 info->compare_callback);
1045 if (matching_class_ptr) {
1046 bool match = false;
1047 if (info->isa) {
1048 Class isa = *curr_class_ptr;
1049 if (info->isa == isa)
1050 match = true;
1051 else { // if (info->objc.match_superclasses) {
1052 Class super = (Class)class_getSuperclass(isa);
1053 while (super) {
1054 if (super == info->isa) {
1055 match = true;
1056 break;
1057 }
1058 super = (Class)class_getSuperclass(super);
1059 }
1060 }
1061 }
1062 else
1063 match = true;
1064 if (match) {
Greg Clayton549294a2013-01-31 06:38:09 +00001065 if (info->num_matches < MAX_MATCHES) {
Greg Clayton849acc82013-01-30 22:57:34 +00001066 info->matches[info->num_matches].addr = (void*)ptr_addr;
1067 info->matches[info->num_matches].size = ptr_size;
1068 info->matches[info->num_matches].offset = 0;
1069 info->matches[info->num_matches].type = type;
1070 ++info->num_matches;
1071 }
1072 }
1073 }
1074 }
1075};
1076callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
1077int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class));
1078(void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);'''
1079 # We must also define a snippet of code to be run that returns
1080 # the result of the expression we run.
1081 # Here we return NULL if our pointer was not found in any malloc blocks,
1082 # and we return the address of the matches array so we can then access
1083 # the matching results
Greg Clayton549294a2013-01-31 06:38:09 +00001084 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
1085 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
1086 baton.matches'''
Greg Clayton849acc82013-01-30 22:57:34 +00001087 # Iterate through all of our ObjC class name arguments
1088 for class_name in args:
1089 addr_expr_str = "(void *)[%s class]" % class_name
Greg Clayton5b578232013-04-03 07:25:30 +00001090 expr_options = lldb.SBExpressionOptions()
1091 expr_options.SetIgnoreBreakpoints(True);
1092 expr_options.SetTimeoutInMicroSeconds (1*1000*1000) # 1 second timeout
1093 expr_options.SetTryAllThreads (True)
1094 expr_sbvalue = frame.EvaluateExpression (addr_expr_str, expr_options)
Greg Clayton849acc82013-01-30 22:57:34 +00001095 if expr_sbvalue.error.Success():
1096 isa = expr_sbvalue.unsigned
1097 if isa:
1098 options.type = 'isa'
Greg Clayton549294a2013-01-31 06:38:09 +00001099 result.AppendMessage('Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' % (class_name, isa))
1100 user_init_code = user_init_code_format % (options.max_matches, num_objc_classes, isa)
Greg Claytonad72e522013-02-01 00:47:49 +00001101 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
Greg Clayton849acc82013-01-30 22:57:34 +00001102 arg_str_description = 'objective C classes with isa 0x%x' % isa
1103 display_match_results (result, options, arg_str_description, expr)
Greg Claytonab20f292012-08-11 02:26:26 +00001104 else:
Greg Clayton849acc82013-01-30 22:57:34 +00001105 result.AppendMessage('error: Can\'t find isa for an ObjC class named "%s"' % (class_name))
1106 else:
1107 result.AppendMessage('error: expression error for "%s": %s' % (addr_expr_str, expr_sbvalue.error))
1108 else:
1109 result.AppendMessage('error: command takes one or more C string arguments');
Greg Claytonab20f292012-08-11 02:26:26 +00001110
Greg Clayton1dae6f32012-04-25 18:40:20 +00001111if __name__ == '__main__':
1112 lldb.debugger = lldb.SBDebugger.Create()
1113
Greg Clayton849acc82013-01-30 22:57:34 +00001114# Make the options so we can generate the help text for the new LLDB
1115# command line command prior to registering it with LLDB below. This way
1116# if clients in LLDB type "help malloc_info", they will see the exact same
1117# output as typing "malloc_info --help".
1118ptr_refs.__doc__ = get_ptr_refs_options().format_help()
1119cstr_refs.__doc__ = get_cstr_refs_options().format_help()
1120malloc_info.__doc__ = get_malloc_info_options().format_help()
1121objc_refs.__doc__ = get_objc_refs_options().format_help()
Enrico Granata3801be72013-02-22 02:21:10 +00001122lldb.debugger.HandleCommand('command script add -f %s.ptr_refs ptr_refs' % __name__)
1123lldb.debugger.HandleCommand('command script add -f %s.cstr_refs cstr_refs' % __name__)
1124lldb.debugger.HandleCommand('command script add -f %s.malloc_info malloc_info' % __name__)
Greg Clayton849acc82013-01-30 22:57:34 +00001125# lldb.debugger.HandleCommand('command script add -f %s.heap heap' % package_name)
1126# lldb.debugger.HandleCommand('command script add -f %s.section_ptr_refs section_ptr_refs' % package_name)
1127# lldb.debugger.HandleCommand('command script add -f %s.stack_ptr_refs stack_ptr_refs' % package_name)
Enrico Granata3801be72013-02-22 02:21:10 +00001128lldb.debugger.HandleCommand('command script add -f %s.objc_refs objc_refs' % __name__)
Greg Clayton849acc82013-01-30 22:57:34 +00001129print '"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 +00001130
1131
1132
1133