blob: fb3394323890ebb000daf3119e9b5be825ffea44 [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
Greg Clayton424a5db2015-05-28 03:27:22 +000019import sys
Greg Claytond712ef02012-04-25 18:40:20 +000020import tempfile
Greg Claytoned3eee62012-04-25 01:49:50 +000021import lldb.utils.symbolication
Greg Clayton804de012012-04-11 16:27:06 +000022
Greg Claytond712ef02012-04-25 18:40:20 +000023g_libheap_dylib_dir = None
24g_libheap_dylib_dict = dict()
25
Greg Clayton13fbb992013-02-01 00:47:49 +000026def get_iterate_memory_expr(options, process, user_init_code, user_return_code):
27 expr = '''
Greg Claytone7fc9cc2013-01-31 06:38:09 +000028typedef unsigned natural_t;
Greg Clayton85e62ec2013-01-30 22:57:34 +000029typedef uintptr_t vm_size_t;
30typedef uintptr_t vm_address_t;
31typedef natural_t task_t;
32typedef int kern_return_t;
33#define KERN_SUCCESS 0
Greg Clayton13fbb992013-02-01 00:47:49 +000034typedef void (*range_callback_t)(task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size);
35''';
36 if options.search_vm_regions:
37 expr += '''
38typedef int vm_prot_t;
39typedef unsigned int vm_inherit_t;
40typedef unsigned long long memory_object_offset_t;
41typedef unsigned int boolean_t;
42typedef int vm_behavior_t;
43typedef uint32_t vm32_object_id_t;
44typedef natural_t mach_msg_type_number_t;
45typedef uint64_t mach_vm_address_t;
46typedef uint64_t mach_vm_offset_t;
47typedef uint64_t mach_vm_size_t;
48typedef uint64_t vm_map_offset_t;
49typedef uint64_t vm_map_address_t;
50typedef uint64_t vm_map_size_t;
51#define VM_PROT_NONE ((vm_prot_t) 0x00)
52#define VM_PROT_READ ((vm_prot_t) 0x01)
53#define VM_PROT_WRITE ((vm_prot_t) 0x02)
54#define VM_PROT_EXECUTE ((vm_prot_t) 0x04)
55typedef struct vm_region_submap_short_info_data_64_t {
56 vm_prot_t protection;
57 vm_prot_t max_protection;
58 vm_inherit_t inheritance;
59 memory_object_offset_t offset; // offset into object/map
60 unsigned int user_tag; // user tag on map entry
61 unsigned int ref_count; // obj/map mappers, etc
62 unsigned short shadow_depth; // only for obj
63 unsigned char external_pager; // only for obj
64 unsigned char share_mode; // see enumeration
65 boolean_t is_submap; // submap vs obj
66 vm_behavior_t behavior; // access behavior hint
67 vm32_object_id_t object_id; // obj/map name, not a handle
68 unsigned short user_wired_count;
69} vm_region_submap_short_info_data_64_t;
70#define VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ((mach_msg_type_number_t)(sizeof(vm_region_submap_short_info_data_64_t)/sizeof(int)))''';
71 if user_init_code:
72 expr += user_init_code;
73 expr += '''
74task_t task = (task_t)mach_task_self();
75mach_vm_address_t vm_region_base_addr;
76mach_vm_size_t vm_region_size;
77natural_t vm_region_depth;
78vm_region_submap_short_info_data_64_t vm_region_info;
79kern_return_t err;
80for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size)
81{
82 mach_msg_type_number_t vm_region_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
83 err = (kern_return_t)mach_vm_region_recurse (task,
84 &vm_region_base_addr,
85 &vm_region_size,
86 &vm_region_depth,
87 &vm_region_info,
88 &vm_region_info_size);
89 if (err)
90 break;
91 // Check all read + write regions. This will cover the thread stacks
92 // and any regions of memory like __DATA segments, that might contain
93 // data we are looking for
94 if (vm_region_info.protection & VM_PROT_WRITE &&
95 vm_region_info.protection & VM_PROT_READ)
96 {
97 baton.callback (task,
98 &baton,
99 64,
100 vm_region_base_addr,
101 vm_region_size);
102 }
103}'''
104 else:
105 if options.search_stack:
106 expr += get_thread_stack_ranges_struct (process)
107 if options.search_segments:
108 expr += get_sections_ranges_struct (process)
109 if user_init_code:
110 expr += user_init_code
111 if options.search_heap:
112 expr += '''
Greg Clayton85e62ec2013-01-30 22:57:34 +0000113#define MALLOC_PTR_IN_USE_RANGE_TYPE 1
114typedef struct vm_range_t {
115 vm_address_t address;
116 vm_size_t size;
117} vm_range_t;
118typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory);
119typedef 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 +0000120typedef struct malloc_introspection_t {
121 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 */
122} malloc_introspection_t;
123typedef struct malloc_zone_t {
124 void *reserved1[12];
125 struct malloc_introspection_t *introspect;
126} malloc_zone_t;
127memory_reader_t task_peek = [](task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) -> kern_return_t {
128 *local_memory = (void*) remote_address;
129 return KERN_SUCCESS;
130};
131vm_address_t *zones = 0;
Greg Clayton13fbb992013-02-01 00:47:49 +0000132unsigned int num_zones = 0;task_t task = 0;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000133kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones);
134if (KERN_SUCCESS == err)
135{
136 for (unsigned int i=0; i<num_zones; ++i)
137 {
138 const malloc_zone_t *zone = (const malloc_zone_t *)zones[i];
139 if (zone && zone->introspect)
140 zone->introspect->enumerator (task,
141 &baton,
142 MALLOC_PTR_IN_USE_RANGE_TYPE,
143 (vm_address_t)zone,
144 task_peek,
145 [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void
146 {
147 range_callback_t callback = ((callback_baton_t *)baton)->callback;
148 for (unsigned i=0; i<size; ++i)
149 {
150 callback (task, baton, type, ranges[i].address, ranges[i].size);
151 }
152 });
153 }
Greg Clayton13fbb992013-02-01 00:47:49 +0000154}'''
155
156 if options.search_stack:
157 expr += '''
Greg Clayton966c6f62014-01-07 21:55:00 +0000158#ifdef NUM_STACKS
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000159// Call the callback for the thread stack ranges
Greg Clayton13fbb992013-02-01 00:47:49 +0000160for (uint32_t i=0; i<NUM_STACKS; ++i) {
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000161 range_callback(task, &baton, 8, stacks[i].base, stacks[i].size);
Greg Clayton13fbb992013-02-01 00:47:49 +0000162 if (STACK_RED_ZONE_SIZE > 0) {
163 range_callback(task, &baton, 16, stacks[i].base - STACK_RED_ZONE_SIZE, STACK_RED_ZONE_SIZE);
164 }
165}
Greg Clayton966c6f62014-01-07 21:55:00 +0000166#endif'''
Greg Clayton13fbb992013-02-01 00:47:49 +0000167
168 if options.search_segments:
169 expr += '''
Greg Clayton966c6f62014-01-07 21:55:00 +0000170#ifdef NUM_SEGMENTS
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000171// Call the callback for all segments
172for (uint32_t i=0; i<NUM_SEGMENTS; ++i)
Greg Clayton966c6f62014-01-07 21:55:00 +0000173 range_callback(task, &baton, 32, segments[i].base, segments[i].size);
174#endif'''
Greg Clayton13fbb992013-02-01 00:47:49 +0000175
176 if user_return_code:
177 expr += "\n%s" % (user_return_code,)
178
179 return expr
Greg Clayton94073022012-07-07 01:22:45 +0000180
181def get_member_types_for_offset(value_type, offset, member_list):
182 member = value_type.GetFieldAtIndex(0)
183 search_bases = False
184 if member:
185 if member.GetOffsetInBytes() <= offset:
186 for field_idx in range (value_type.GetNumberOfFields()):
187 member = value_type.GetFieldAtIndex(field_idx)
188 member_byte_offset = member.GetOffsetInBytes()
189 member_end_byte_offset = member_byte_offset + member.type.size
190 if member_byte_offset <= offset and offset < member_end_byte_offset:
191 member_list.append(member)
192 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
193 return
194 else:
195 search_bases = True
196 else:
197 search_bases = True
198 if search_bases:
199 for field_idx in range (value_type.GetNumberOfDirectBaseClasses()):
200 member = value_type.GetDirectBaseClassAtIndex(field_idx)
201 member_byte_offset = member.GetOffsetInBytes()
202 member_end_byte_offset = member_byte_offset + member.type.size
203 if member_byte_offset <= offset and offset < member_end_byte_offset:
204 member_list.append(member)
205 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
206 return
207 for field_idx in range (value_type.GetNumberOfVirtualBaseClasses()):
208 member = value_type.GetVirtualBaseClassAtIndex(field_idx)
209 member_byte_offset = member.GetOffsetInBytes()
210 member_end_byte_offset = member_byte_offset + member.type.size
211 if member_byte_offset <= offset and offset < member_end_byte_offset:
212 member_list.append(member)
213 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list)
214 return
Greg Claytone04cfd22012-07-11 22:13:18 +0000215
216def append_regex_callback(option, opt, value, parser):
217 try:
218 ivar_regex = re.compile(value)
219 parser.values.ivar_regex_blacklist.append(ivar_regex)
220 except:
221 print 'error: an exception was thrown when compiling the ivar regular expression for "%s"' % value
Greg Claytonb403a152012-04-21 00:11:26 +0000222
Greg Claytond84bb482012-04-13 16:24:09 +0000223def add_common_options(parser):
224 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
Greg Claytone04cfd22012-07-11 22:13:18 +0000225 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 +0000226 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 +0000227 parser.add_option('-z', '--size', action='store_true', dest='show_size', help='print the allocation size in bytes', default=False)
228 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 +0000229 parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False)
230 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 +0000231 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 +0000232 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 +0000233 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 +0000234 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)
235 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 +0000236 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 +0000237 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 +0000238 parser.add_option('--ignore-stack', action='store_false', dest='search_stack', help="Don't search the stack when enumerating memory", default=True)
239 parser.add_option('--ignore-heap', action='store_false', dest='search_heap', help="Don't search the heap allocations when enumerating memory", default=True)
240 parser.add_option('--ignore-segments', action='store_false', dest='search_segments', help="Don't search readable executable segments enumerating memory", default=True)
241 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 +0000242
Greg Clayton85e62ec2013-01-30 22:57:34 +0000243def type_flags_to_string(type_flags):
244 if type_flags == 0:
245 type_str = 'free'
246 elif type_flags & 2:
247 type_str = 'malloc'
248 elif type_flags & 4:
249 type_str = 'free'
250 elif type_flags & 1:
251 type_str = 'generic'
252 elif type_flags & 8:
253 type_str = 'stack'
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000254 elif type_flags & 16:
Greg Clayton13fbb992013-02-01 00:47:49 +0000255 type_str = 'stack (red zone)'
256 elif type_flags & 32:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000257 type_str = 'segment'
Greg Clayton13fbb992013-02-01 00:47:49 +0000258 elif type_flags & 64:
259 type_str = 'vm_region'
Greg Clayton85e62ec2013-01-30 22:57:34 +0000260 else:
261 type_str = hex(type_flags)
262 return type_str
263
Greg Clayton424a5db2015-05-28 03:27:22 +0000264def find_variable_containing_address(verbose, frame, match_addr):
265 variables = frame.GetVariables(True,True,True,True)
266 matching_var = None
267 for var in variables:
268 var_addr = var.GetLoadAddress()
269 if var_addr != lldb.LLDB_INVALID_ADDRESS:
270 byte_size = var.GetType().GetByteSize()
271 if verbose:
272 print 'frame #%u: [%#x - %#x) %s' % (frame.GetFrameID(), var.load_addr, var.load_addr + byte_size, var.name)
273 if var_addr == match_addr:
274 if verbose:
275 print 'match'
276 return var
277 else:
278 if byte_size > 0 and var_addr <= match_addr and match_addr < (var_addr + byte_size):
279 if verbose:
280 print 'match'
281 return var
282 return None
283
284def find_frame_for_stack_address(process, addr):
285 closest_delta = sys.maxint
286 closest_frame = None
287 #print 'find_frame_for_stack_address(%#x)' % (addr)
288 for thread in process:
289 prev_sp = lldb.LLDB_INVALID_ADDRESS
290 for frame in thread:
291 cfa = frame.GetCFA()
292 #print 'frame #%u: cfa = %#x' % (frame.GetFrameID(), cfa)
293 if addr < cfa:
294 delta = cfa - addr
295 #print '%#x < %#x, delta = %i' % (addr, cfa, delta)
296 if delta < closest_delta:
297 #print 'closest'
298 closest_delta = delta
299 closest_frame = frame
300 # else:
301 # print 'delta >= closest_delta'
302 return closest_frame
303
304def type_flags_to_description(process, type_flags, ptr_addr, ptr_size, offset, match_addr):
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000305 show_offset = False
Greg Clayton85e62ec2013-01-30 22:57:34 +0000306 if type_flags == 0 or type_flags & 4:
307 type_str = 'free(%#x)' % (ptr_addr,)
308 elif type_flags & 2 or type_flags & 1:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000309 type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr)
310 show_offset = True
Greg Clayton85e62ec2013-01-30 22:57:34 +0000311 elif type_flags & 8:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000312 type_str = 'stack'
Greg Clayton424a5db2015-05-28 03:27:22 +0000313 frame = find_frame_for_stack_address(process, match_addr)
314 if frame:
315 type_str += ' in frame #%u of thread #%u: tid %#x' % (frame.GetFrameID(), frame.GetThread().GetIndexID(), frame.GetThread().GetThreadID())
316 variables = frame.GetVariables(True,True,True,True)
317 matching_var = None
318 for var in variables:
319 var_addr = var.GetLoadAddress()
320 if var_addr != lldb.LLDB_INVALID_ADDRESS:
321 #print 'variable "%s" @ %#x (%#x)' % (var.name, var.load_addr, match_addr)
322 if var_addr == match_addr:
323 matching_var = var
324 break
325 else:
326 byte_size = var.GetType().GetByteSize()
327 if byte_size > 0 and var_addr <= match_addr and match_addr < (var_addr + byte_size):
328 matching_var = var
329 break
330 if matching_var:
331 type_str += ' in variable at %#x:\n %s' % (matching_var.GetLoadAddress(), matching_var)
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000332 elif type_flags & 16:
Greg Clayton13fbb992013-02-01 00:47:49 +0000333 type_str = 'stack (red zone)'
334 elif type_flags & 32:
Greg Clayton424a5db2015-05-28 03:27:22 +0000335 sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000336 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 +0000337 elif type_flags & 64:
Greg Clayton424a5db2015-05-28 03:27:22 +0000338 sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
Greg Clayton13fbb992013-02-01 00:47:49 +0000339 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 +0000340 else:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000341 type_str = '%#x' % (ptr_addr,)
342 show_offset = True
343 if show_offset and offset != 0:
344 type_str += ' + %-6u' % (offset,)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000345 return type_str
346
347def dump_stack_history_entry(options, result, stack_history_entry, idx):
Greg Clayton7200ed12012-05-10 23:37:52 +0000348 address = int(stack_history_entry.address)
349 if address:
350 type_flags = int(stack_history_entry.type_flags)
Greg Claytond9fc5352012-05-10 23:17:28 +0000351 symbolicator = lldb.utils.symbolication.Symbolicator()
Greg Clayton85e62ec2013-01-30 22:57:34 +0000352 symbolicator.target = lldb.debugger.GetSelectedTarget()
353 type_str = type_flags_to_string(type_flags)
Greg Claytonc373ca52012-09-01 00:34:35 +0000354 result.AppendMessage('stack[%u]: addr = 0x%x, type=%s, frames:' % (idx, address, type_str))
Greg Claytond9fc5352012-05-10 23:17:28 +0000355 frame_idx = 0
Greg Clayton7200ed12012-05-10 23:37:52 +0000356 idx = 0
357 pc = int(stack_history_entry.frames[idx])
Greg Claytond9fc5352012-05-10 23:17:28 +0000358 while pc != 0:
359 if pc >= 0x1000:
360 frames = symbolicator.symbolicate(pc)
361 if frames:
362 for frame in frames:
Greg Claytonc373ca52012-09-01 00:34:35 +0000363 result.AppendMessage(' [%u] %s' % (frame_idx, frame))
Greg Claytond9fc5352012-05-10 23:17:28 +0000364 frame_idx += 1
365 else:
Greg Claytonc373ca52012-09-01 00:34:35 +0000366 result.AppendMessage(' [%u] 0x%x' % (frame_idx, pc))
Greg Claytond9fc5352012-05-10 23:17:28 +0000367 frame_idx += 1
Greg Clayton7200ed12012-05-10 23:37:52 +0000368 idx = idx + 1
369 pc = int(stack_history_entry.frames[idx])
Greg Claytond9fc5352012-05-10 23:17:28 +0000370 else:
371 pc = 0
Greg Clayton85e62ec2013-01-30 22:57:34 +0000372 if idx >= options.max_frames:
373 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))
374
Greg Claytonc373ca52012-09-01 00:34:35 +0000375 result.AppendMessage('')
Greg Claytond9fc5352012-05-10 23:17:28 +0000376
Greg Clayton85e62ec2013-01-30 22:57:34 +0000377def dump_stack_history_entries(options, result, addr, history):
Greg Claytond9fc5352012-05-10 23:17:28 +0000378 # malloc_stack_entry *get_stack_history_for_address (const void * addr)
Greg Clayton424a5db2015-05-28 03:27:22 +0000379 expr_prefix = '''
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000380typedef int kern_return_t;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000381typedef struct $malloc_stack_entry {
382 uint64_t address;
383 uint64_t argument;
384 uint32_t type_flags;
385 uint32_t num_frames;
386 uint64_t frames[512];
387 kern_return_t err;
388} $malloc_stack_entry;
Greg Clayton424a5db2015-05-28 03:27:22 +0000389'''
390 single_expr = '''
391#define MAX_FRAMES %u
Greg Clayton85e62ec2013-01-30 22:57:34 +0000392typedef unsigned task_t;
393$malloc_stack_entry stack;
394stack.address = 0x%x;
395stack.type_flags = 2;
396stack.num_frames = 0;
397stack.frames[0] = 0;
398uint32_t max_stack_frames = MAX_FRAMES;
399stack.err = (kern_return_t)__mach_stack_logging_get_frames (
400 (task_t)mach_task_self(),
401 stack.address,
402 &stack.frames[0],
403 max_stack_frames,
404 &stack.num_frames);
405if (stack.num_frames < MAX_FRAMES)
406 stack.frames[stack.num_frames] = 0;
407else
408 stack.frames[MAX_FRAMES-1] = 0;
409stack''' % (options.max_frames, addr);
410
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000411 history_expr = '''
412typedef int kern_return_t;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000413typedef unsigned task_t;
414#define MAX_FRAMES %u
415#define MAX_HISTORY %u
416typedef struct mach_stack_logging_record_t {
417 uint32_t type_flags;
418 uint64_t stack_identifier;
419 uint64_t argument;
420 uint64_t address;
421} mach_stack_logging_record_t;
422typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
423typedef struct malloc_stack_entry {
424 uint64_t address;
425 uint64_t argument;
426 uint32_t type_flags;
427 uint32_t num_frames;
428 uint64_t frames[MAX_FRAMES];
429 kern_return_t frames_err;
430} malloc_stack_entry;
431typedef struct $malloc_stack_history {
432 task_t task;
433 unsigned idx;
434 malloc_stack_entry entries[MAX_HISTORY];
435} $malloc_stack_history;
436$malloc_stack_history info = { (task_t)mach_task_self(), 0 };
437uint32_t max_stack_frames = MAX_FRAMES;
438enumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void {
439 $malloc_stack_history *info = ($malloc_stack_history *)baton;
440 if (info->idx < MAX_HISTORY) {
441 malloc_stack_entry *stack_entry = &(info->entries[info->idx]);
442 stack_entry->address = stack_record.address;
443 stack_entry->type_flags = stack_record.type_flags;
444 stack_entry->argument = stack_record.argument;
445 stack_entry->num_frames = 0;
446 stack_entry->frames[0] = 0;
447 stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack (
448 info->task,
449 stack_record.stack_identifier,
450 stack_entry->frames,
451 (uint32_t)MAX_FRAMES,
452 &stack_entry->num_frames);
453 // Terminate the frames with zero if there is room
454 if (stack_entry->num_frames < MAX_FRAMES)
455 stack_entry->frames[stack_entry->num_frames] = 0;
456 }
457 ++info->idx;
458};
459(kern_return_t)__mach_stack_logging_enumerate_records (info.task, (uint64_t)0x%x, callback, &info);
460info''' % (options.max_frames, options.max_history, addr);
461
462 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
463 if history:
464 expr = history_expr
465 else:
466 expr = single_expr
Greg Clayton927fa012013-04-03 07:25:30 +0000467 expr_options = lldb.SBExpressionOptions()
468 expr_options.SetIgnoreBreakpoints(True);
469 expr_options.SetTimeoutInMicroSeconds (5*1000*1000) # 5 second timeout
470 expr_options.SetTryAllThreads (True)
Greg Clayton6e7ab022015-01-14 21:37:19 +0000471 expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
Greg Clayton424a5db2015-05-28 03:27:22 +0000472 expr_options.SetPrefix(expr_prefix)
Greg Clayton927fa012013-04-03 07:25:30 +0000473 expr_sbvalue = frame.EvaluateExpression (expr, expr_options)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000474 if options.verbose:
475 print "expression:"
476 print expr
477 print "expression result:"
478 print expr_sbvalue
479 if expr_sbvalue.error.Success():
480 if history:
481 malloc_stack_history = lldb.value(expr_sbvalue)
482 num_stacks = int(malloc_stack_history.idx)
483 if num_stacks <= options.max_history:
484 i_max = num_stacks
485 else:
486 i_max = options.max_history
487 for i in range(i_max):
488 stack_history_entry = malloc_stack_history.entries[i]
489 dump_stack_history_entry(options, result, stack_history_entry, i)
490 if num_stacks > options.max_history:
491 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))
492 else:
493 stack_history_entry = lldb.value(expr_sbvalue)
494 dump_stack_history_entry(options, result, stack_history_entry, 0)
495
496 else:
497 result.AppendMessage('error: expression failed "%s" => %s' % (expr, expr_sbvalue.error))
498
499
Greg Clayton424a5db2015-05-28 03:27:22 +0000500def display_match_results (process, result, options, arg_str_description, expr, print_no_matches, expr_prefix = None):
Greg Clayton85e62ec2013-01-30 22:57:34 +0000501 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame()
502 if not frame:
503 result.AppendMessage('error: invalid frame')
504 return 0
Greg Clayton927fa012013-04-03 07:25:30 +0000505 expr_options = lldb.SBExpressionOptions()
506 expr_options.SetIgnoreBreakpoints(True);
507 expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues);
508 expr_options.SetTimeoutInMicroSeconds (30*1000*1000) # 30 second timeout
509 expr_options.SetTryAllThreads (False)
Greg Clayton6e7ab022015-01-14 21:37:19 +0000510 expr_options.SetLanguage (lldb.eLanguageTypeObjC_plus_plus)
Greg Clayton4e1042e2015-05-27 22:32:39 +0000511 if expr_prefix:
512 expr_options.SetPrefix (expr_prefix)
Greg Clayton927fa012013-04-03 07:25:30 +0000513 expr_sbvalue = frame.EvaluateExpression (expr, expr_options)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000514 if options.verbose:
515 print "expression:"
516 print expr
517 print "expression result:"
518 print expr_sbvalue
Greg Clayton7fb671b2012-04-11 18:30:53 +0000519 if expr_sbvalue.error.Success():
Greg Clayton966c6f62014-01-07 21:55:00 +0000520 match_value = lldb.value(expr_sbvalue)
521 i = 0
522 match_idx = 0
523 while 1:
524 print_entry = True
525 match_entry = match_value[i]; i += 1
526 if i > options.max_matches:
527 result.AppendMessage('warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' % (options.max_matches))
528 break
529 malloc_addr = match_entry.addr.sbvalue.unsigned
530 if malloc_addr == 0:
531 break
532 malloc_size = int(match_entry.size)
533 offset = int(match_entry.offset)
534
535 if options.offset >= 0 and options.offset != offset:
536 print_entry = False
537 else:
538 match_addr = malloc_addr + offset
539 type_flags = int(match_entry.type)
540 #result.AppendMessage (hex(malloc_addr + offset))
541 if type_flags == 64:
542 search_stack_old = options.search_stack
543 search_segments_old = options.search_segments
544 search_heap_old = options.search_heap
545 search_vm_regions = options.search_vm_regions
546 options.search_stack = True
547 options.search_segments = True
548 options.search_heap = True
549 options.search_vm_regions = False
550 if malloc_info_impl (lldb.debugger, result, options, [hex(malloc_addr + offset)]):
551 print_entry = False
552 options.search_stack = search_stack_old
553 options.search_segments = search_segments_old
554 options.search_heap = search_heap_old
555 options.search_vm_regions = search_vm_regions
Greg Claytone04cfd22012-07-11 22:13:18 +0000556 if print_entry:
Greg Clayton424a5db2015-05-28 03:27:22 +0000557 description = '%#16.16x: %s' % (match_addr, type_flags_to_description(process, type_flags, malloc_addr, malloc_size, offset, match_addr))
Greg Clayton966c6f62014-01-07 21:55:00 +0000558 if options.show_size:
559 description += ' <%5u>' % (malloc_size)
560 if options.show_range:
561 description += ' [%#x - %#x)' % (malloc_addr, malloc_addr + malloc_size)
562 derefed_dynamic_value = None
563 dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
564 if dynamic_value.type.name == 'void *':
565 if options.type == 'pointer' and malloc_size == 4096:
566 error = lldb.SBError()
567 process = expr_sbvalue.GetProcess()
568 target = expr_sbvalue.GetTarget()
569 data = bytearray(process.ReadMemory(malloc_addr, 16, error))
570 if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
571 ptr_size = target.addr_size
572 thread = process.ReadUnsignedFromMemory (malloc_addr + 16 + ptr_size, ptr_size, error)
573 # 4 bytes 0xa1a1a1a1
574 # 12 bytes 'AUTORELEASE!'
575 # ptr bytes autorelease insertion point
576 # ptr bytes pthread_t
577 # ptr bytes next colder page
578 # ptr bytes next hotter page
579 # 4 bytes this page's depth in the list
580 # 4 bytes high-water mark
581 description += ' AUTORELEASE! for pthread_t %#x' % (thread)
582 # else:
583 # description += 'malloc(%u)' % (malloc_size)
584 # else:
585 # description += 'malloc(%u)' % (malloc_size)
586 else:
587 derefed_dynamic_value = dynamic_value.deref
588 if derefed_dynamic_value:
589 derefed_dynamic_type = derefed_dynamic_value.type
590 derefed_dynamic_type_size = derefed_dynamic_type.size
591 derefed_dynamic_type_name = derefed_dynamic_type.name
592 description += ' '
593 description += derefed_dynamic_type_name
594 if offset < derefed_dynamic_type_size:
595 member_list = list();
596 get_member_types_for_offset (derefed_dynamic_type, offset, member_list)
597 if member_list:
598 member_path = ''
599 for member in member_list:
600 member_name = member.name
601 if member_name:
602 if member_path:
603 member_path += '.'
604 member_path += member_name
605 if member_path:
606 if options.ivar_regex_blacklist:
607 for ivar_regex in options.ivar_regex_blacklist:
608 if ivar_regex.match(member_path):
609 print_entry = False
610 description += '.%s' % (member_path)
611 else:
612 description += '%u bytes after %s' % (offset - derefed_dynamic_type_size, derefed_dynamic_type_name)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000613 else:
Greg Clayton966c6f62014-01-07 21:55:00 +0000614 # strip the "*" from the end of the name since we were unable to dereference this
615 description += dynamic_value.type.name[0:-1]
616 if print_entry:
617 match_idx += 1
618 result_output = ''
619 if description:
620 result_output += description
621 if options.print_type and derefed_dynamic_value:
622 result_output += ' %s' % (derefed_dynamic_value)
623 if options.print_object_description and dynamic_value:
624 desc = dynamic_value.GetObjectDescription()
625 if desc:
626 result_output += '\n%s' % (desc)
627 if result_output:
628 result.AppendMessage(result_output)
629 if options.memory:
630 cmd_result = lldb.SBCommandReturnObject()
631 if options.format == None:
632 memory_command = "memory read --force 0x%x 0x%x" % (malloc_addr, malloc_addr + malloc_size)
633 else:
634 memory_command = "memory read --force -f %s 0x%x 0x%x" % (options.format, malloc_addr, malloc_addr + malloc_size)
635 if options.verbose:
636 result.AppendMessage(memory_command)
637 lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
638 result.AppendMessage(cmd_result.GetOutput())
639 if options.stack_history:
640 dump_stack_history_entries(options, result, malloc_addr, 1)
641 elif options.stack:
642 dump_stack_history_entries(options, result, malloc_addr, 0)
643 return i
Greg Clayton7fb671b2012-04-11 18:30:53 +0000644 else:
Greg Clayton7143f002012-09-04 14:09:21 +0000645 result.AppendMessage(str(expr_sbvalue.error))
Greg Claytone04cfd22012-07-11 22:13:18 +0000646 return 0
647
Greg Clayton85e62ec2013-01-30 22:57:34 +0000648def get_ptr_refs_options ():
649 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
650 description='''Searches all allocations on the heap for pointer values on
651darwin user space programs. Any matches that were found will dump the malloc
652blocks that contain the pointers and might be able to print what kind of
653objects the pointers are contained in using dynamic type information in the
654program.'''
655 parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
656 add_common_options(parser)
657 return parser
Greg Clayton7fb671b2012-04-11 18:30:53 +0000658
Greg Clayton424a5db2015-05-28 03:27:22 +0000659def find_variable(debugger, command, result, dict):
660 usage = "usage: %prog [options] <ADDR> [ADDR ...]"
661 description='''Searches for a local variable in all frames that contains a hex ADDR.'''
662 command_args = shlex.split(command)
663 parser = optparse.OptionParser(description=description, prog='find_variable',usage=usage)
664 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
665 try:
666 (options, args) = parser.parse_args(command_args)
667 except:
668 return
669
670 process = debugger.GetSelectedTarget().GetProcess()
671 if not process:
672 result.AppendMessage('error: invalid process')
673 return
674
675 for arg in args:
676 var_addr = int(arg, 16)
677 print >>result, "Finding a variable with address %#x..." % (var_addr)
678 done = False
679 for thread in process:
680 for frame in thread:
681 var = find_variable_containing_address(options.verbose, frame, var_addr)
682 if var:
683 print var
684 done = True
685 break
686 if done:
687 break
688
Greg Clayton77677162012-04-12 18:57:36 +0000689def ptr_refs(debugger, command, result, dict):
Greg Clayton804de012012-04-11 16:27:06 +0000690 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000691 parser = get_ptr_refs_options()
Greg Clayton804de012012-04-11 16:27:06 +0000692 try:
693 (options, args) = parser.parse_args(command_args)
694 except:
695 return
Greg Clayton7fb671b2012-04-11 18:30:53 +0000696
Greg Clayton424a5db2015-05-28 03:27:22 +0000697 process = debugger.GetSelectedTarget().GetProcess()
Greg Clayton85e62ec2013-01-30 22:57:34 +0000698 if not process:
699 result.AppendMessage('error: invalid process')
700 return
701 frame = process.GetSelectedThread().GetSelectedFrame()
702 if not frame:
703 result.AppendMessage('error: invalid frame')
704 return
705
Greg Clayton7fb671b2012-04-11 18:30:53 +0000706 options.type = 'pointer'
Greg Clayton85e62ec2013-01-30 22:57:34 +0000707 if options.format == None:
708 options.format = "A" # 'A' is "address" format
709
Greg Clayton804de012012-04-11 16:27:06 +0000710 if args:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000711 # When we initialize the expression, we must define any types that
712 # we will need when looking at every allocation. We must also define
713 # a type named callback_baton_t and make an instance named "baton"
714 # and initialize it how ever we want to. The address of "baton" will
715 # be passed into our range callback. callback_baton_t must contain
716 # a member named "callback" whose type is "range_callback_t". This
717 # will be used by our zone callbacks to call the range callback for
718 # each malloc range.
Greg Clayton4e1042e2015-05-27 22:32:39 +0000719 expr_prefix = '''
Greg Clayton85e62ec2013-01-30 22:57:34 +0000720struct $malloc_match {
721 void *addr;
722 uintptr_t size;
723 uintptr_t offset;
724 uintptr_t type;
725};
Greg Clayton4e1042e2015-05-27 22:32:39 +0000726'''
727 user_init_code_format = '''
728#define MAX_MATCHES %u
Greg Clayton85e62ec2013-01-30 22:57:34 +0000729typedef struct callback_baton_t {
730 range_callback_t callback;
731 unsigned num_matches;
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000732 $malloc_match matches[MAX_MATCHES];
Greg Clayton85e62ec2013-01-30 22:57:34 +0000733 void *ptr;
734} callback_baton_t;
735range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
736 callback_baton_t *info = (callback_baton_t *)baton;
737 typedef void* T;
738 const unsigned size = sizeof(T);
739 T *array = (T*)ptr_addr;
740 for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) {
741 if (array[idx] == info->ptr) {
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000742 if (info->num_matches < MAX_MATCHES) {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000743 info->matches[info->num_matches].addr = (void*)ptr_addr;
744 info->matches[info->num_matches].size = ptr_size;
745 info->matches[info->num_matches].offset = idx*sizeof(T);
746 info->matches[info->num_matches].type = type;
747 ++info->num_matches;
748 }
749 }
750 }
751};
752callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
753'''
754 # We must also define a snippet of code to be run that returns
755 # the result of the expression we run.
756 # Here we return NULL if our pointer was not found in any malloc blocks,
757 # and we return the address of the matches array so we can then access
758 # the matching results
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000759 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
760 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
Greg Clayton85e62ec2013-01-30 22:57:34 +0000761baton.matches'''
762 # Iterate through all of our pointer expressions and display the results
763 for ptr_expr in args:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000764 user_init_code = user_init_code_format % (options.max_matches, ptr_expr)
Greg Clayton13fbb992013-02-01 00:47:49 +0000765 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000766 arg_str_description = 'malloc block containing pointer %s' % ptr_expr
Greg Clayton424a5db2015-05-28 03:27:22 +0000767 display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
Greg Clayton804de012012-04-11 16:27:06 +0000768 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000769 result.AppendMessage('error: no pointer arguments were given')
770
771def get_cstr_refs_options():
772 usage = "usage: %prog [options] <CSTR> [CSTR ...]"
773 description='''Searches all allocations on the heap for C string values on
774darwin user space programs. Any matches that were found will dump the malloc
775blocks that contain the C strings and might be able to print what kind of
776objects the pointers are contained in using dynamic type information in the
777program.'''
778 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
779 add_common_options(parser)
780 return parser
Greg Clayton804de012012-04-11 16:27:06 +0000781
Greg Clayton77677162012-04-12 18:57:36 +0000782def cstr_refs(debugger, command, result, dict):
Greg Clayton7fb671b2012-04-11 18:30:53 +0000783 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000784 parser = get_cstr_refs_options();
Greg Clayton7fb671b2012-04-11 18:30:53 +0000785 try:
786 (options, args) = parser.parse_args(command_args)
787 except:
788 return
789
Greg Clayton424a5db2015-05-28 03:27:22 +0000790 process = debugger.GetSelectedTarget().GetProcess()
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000791 if not process:
792 result.AppendMessage('error: invalid process')
793 return
794 frame = process.GetSelectedThread().GetSelectedFrame()
Greg Clayton85e62ec2013-01-30 22:57:34 +0000795 if not frame:
796 result.AppendMessage('error: invalid frame')
797 return
798
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000799
Greg Clayton7fb671b2012-04-11 18:30:53 +0000800 options.type = 'cstr'
Greg Clayton85e62ec2013-01-30 22:57:34 +0000801 if options.format == None:
802 options.format = "Y" # 'Y' is "bytes with ASCII" format
Greg Clayton7fb671b2012-04-11 18:30:53 +0000803
804 if args:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000805 # When we initialize the expression, we must define any types that
806 # we will need when looking at every allocation. We must also define
807 # a type named callback_baton_t and make an instance named "baton"
808 # and initialize it how ever we want to. The address of "baton" will
809 # be passed into our range callback. callback_baton_t must contain
810 # a member named "callback" whose type is "range_callback_t". This
811 # will be used by our zone callbacks to call the range callback for
812 # each malloc range.
Greg Clayton4e1042e2015-05-27 22:32:39 +0000813 expr_prefix = '''
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000814struct $malloc_match {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000815 void *addr;
816 uintptr_t size;
817 uintptr_t offset;
818 uintptr_t type;
819};
Greg Clayton4e1042e2015-05-27 22:32:39 +0000820'''
821 user_init_code_format = '''
822#define MAX_MATCHES %u
Greg Clayton85e62ec2013-01-30 22:57:34 +0000823typedef struct callback_baton_t {
824 range_callback_t callback;
825 unsigned num_matches;
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000826 $malloc_match matches[MAX_MATCHES];
Greg Clayton85e62ec2013-01-30 22:57:34 +0000827 const char *cstr;
828 unsigned cstr_len;
829} callback_baton_t;
830range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
831 callback_baton_t *info = (callback_baton_t *)baton;
832 if (info->cstr_len < ptr_size) {
833 const char *begin = (const char *)ptr_addr;
834 const char *end = begin + ptr_size - info->cstr_len;
835 for (const char *s = begin; s < end; ++s) {
836 if ((int)memcmp(s, info->cstr, info->cstr_len) == 0) {
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000837 if (info->num_matches < MAX_MATCHES) {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000838 info->matches[info->num_matches].addr = (void*)ptr_addr;
839 info->matches[info->num_matches].size = ptr_size;
840 info->matches[info->num_matches].offset = s - begin;
841 info->matches[info->num_matches].type = type;
842 ++info->num_matches;
843 }
844 }
845 }
846 }
847};
848const char *cstr = "%s";
849callback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };'''
850 # We must also define a snippet of code to be run that returns
851 # the result of the expression we run.
852 # Here we return NULL if our pointer was not found in any malloc blocks,
853 # and we return the address of the matches array so we can then access
854 # the matching results
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000855 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
856 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
857baton.matches'''
Greg Clayton85e62ec2013-01-30 22:57:34 +0000858 # Iterate through all of our pointer expressions and display the results
859 for cstr in args:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000860 user_init_code = user_init_code_format % (options.max_matches, cstr)
Greg Clayton13fbb992013-02-01 00:47:49 +0000861 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000862 arg_str_description = 'malloc block containing "%s"' % cstr
Greg Clayton424a5db2015-05-28 03:27:22 +0000863 display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
Greg Clayton7fb671b2012-04-11 18:30:53 +0000864 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000865 result.AppendMessage('error: command takes one or more C string arguments')
866
867
868def get_malloc_info_options():
869 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
870 description='''Searches the heap a malloc block that contains the addresses
871specified as one or more address expressions. Any matches that were found will
872dump the malloc blocks that match or contain the specified address. The matching
873blocks might be able to show what kind of objects they are using dynamic type
874information in the program.'''
875 parser = optparse.OptionParser(description=description, prog='malloc_info',usage=usage)
876 add_common_options(parser)
877 return parser
Greg Clayton804de012012-04-11 16:27:06 +0000878
Greg Clayton77677162012-04-12 18:57:36 +0000879def malloc_info(debugger, command, result, dict):
880 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +0000881 parser = get_malloc_info_options()
Greg Clayton77677162012-04-12 18:57:36 +0000882 try:
883 (options, args) = parser.parse_args(command_args)
884 except:
885 return
Greg Clayton13fbb992013-02-01 00:47:49 +0000886 malloc_info_impl (debugger, result, options, args)
887
888def malloc_info_impl (debugger, result, options, args):
889 # We are specifically looking for something on the heap only
890 options.type = 'malloc_info'
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000891
Greg Clayton424a5db2015-05-28 03:27:22 +0000892 process = debugger.GetSelectedTarget().GetProcess()
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000893 if not process:
894 result.AppendMessage('error: invalid process')
895 return
896 frame = process.GetSelectedThread().GetSelectedFrame()
897 if not frame:
898 result.AppendMessage('error: invalid frame')
899 return
Greg Clayton4e1042e2015-05-27 22:32:39 +0000900 expr_prefix = '''
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000901struct $malloc_match {
Greg Clayton85e62ec2013-01-30 22:57:34 +0000902 void *addr;
903 uintptr_t size;
904 uintptr_t offset;
905 uintptr_t type;
906};
Greg Clayton4e1042e2015-05-27 22:32:39 +0000907'''
908
909 user_init_code_format = '''
Greg Clayton85e62ec2013-01-30 22:57:34 +0000910typedef struct callback_baton_t {
911 range_callback_t callback;
912 unsigned num_matches;
913 $malloc_match matches[2]; // Two items so they can be NULL terminated
914 void *ptr;
915} callback_baton_t;
916range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
917 callback_baton_t *info = (callback_baton_t *)baton;
918 if (info->num_matches == 0) {
919 uint8_t *p = (uint8_t *)info->ptr;
920 uint8_t *lo = (uint8_t *)ptr_addr;
921 uint8_t *hi = lo + ptr_size;
922 if (lo <= p && p < hi) {
923 info->matches[info->num_matches].addr = (void*)ptr_addr;
924 info->matches[info->num_matches].size = ptr_size;
925 info->matches[info->num_matches].offset = p - lo;
926 info->matches[info->num_matches].type = type;
927 info->num_matches = 1;
928 }
929 }
930};
931callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000932baton.matches[0].addr = 0;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000933baton.matches[1].addr = 0;'''
Greg Clayton77677162012-04-12 18:57:36 +0000934 if args:
Greg Clayton13fbb992013-02-01 00:47:49 +0000935 total_matches = 0
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000936 for ptr_expr in args:
937 user_init_code = user_init_code_format % (ptr_expr)
Greg Clayton13fbb992013-02-01 00:47:49 +0000938 expr = get_iterate_memory_expr(options, process, user_init_code, 'baton.matches')
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000939 arg_str_description = 'malloc block that contains %s' % ptr_expr
Greg Clayton424a5db2015-05-28 03:27:22 +0000940 total_matches += display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
Greg Clayton13fbb992013-02-01 00:47:49 +0000941 return total_matches
Greg Clayton77677162012-04-12 18:57:36 +0000942 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +0000943 result.AppendMessage('error: command takes one or more pointer expressions')
Greg Clayton13fbb992013-02-01 00:47:49 +0000944 return 0
Greg Clayton77677162012-04-12 18:57:36 +0000945
Greg Clayton85e62ec2013-01-30 22:57:34 +0000946def get_thread_stack_ranges_struct (process):
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000947 '''Create code that defines a structure that represents threads stack bounds
948 for all threads. It returns a static sized array initialized with all of
949 the tid, base, size structs for all the threads.'''
Greg Clayton85e62ec2013-01-30 22:57:34 +0000950 stack_dicts = list()
951 if process:
952 i = 0;
953 for thread in process:
954 min_sp = thread.frame[0].sp
955 max_sp = min_sp
956 for frame in thread.frames:
957 sp = frame.sp
958 if sp < min_sp: min_sp = sp
959 if sp > max_sp: max_sp = sp
960 if min_sp < max_sp:
961 stack_dicts.append ({ 'tid' : thread.GetThreadID(), 'base' : min_sp , 'size' : max_sp-min_sp, 'index' : i })
962 i += 1
963 stack_dicts_len = len(stack_dicts)
964 if stack_dicts_len > 0:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000965 result = '''
966#define NUM_STACKS %u
Greg Clayton13fbb992013-02-01 00:47:49 +0000967#define STACK_RED_ZONE_SIZE %u
Greg Clayton85e62ec2013-01-30 22:57:34 +0000968typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
Greg Clayton13fbb992013-02-01 00:47:49 +0000969thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize())
Greg Clayton85e62ec2013-01-30 22:57:34 +0000970 for stack_dict in stack_dicts:
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000971 result += '''
972stacks[%(index)u].tid = 0x%(tid)x;
Greg Clayton85e62ec2013-01-30 22:57:34 +0000973stacks[%(index)u].base = 0x%(base)x;
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000974stacks[%(index)u].size = 0x%(size)x;''' % stack_dict
Greg Clayton85e62ec2013-01-30 22:57:34 +0000975 return result
976 else:
Greg Clayton966c6f62014-01-07 21:55:00 +0000977 return ''
Greg Claytonae23ed32012-10-08 22:39:38 +0000978
Greg Claytone7fc9cc2013-01-31 06:38:09 +0000979def get_sections_ranges_struct (process):
980 '''Create code that defines a structure that represents all segments that
981 can contain data for all images in "target". It returns a static sized
982 array initialized with all of base, size structs for all the threads.'''
983 target = process.target
984 segment_dicts = list()
985 for (module_idx, module) in enumerate(target.modules):
986 for sect_idx in range(module.GetNumSections()):
987 section = module.GetSectionAtIndex(sect_idx)
988 if not section:
989 break
990 name = section.name
991 if name != '__TEXT' and name != '__LINKEDIT' and name != '__PAGEZERO':
992 base = section.GetLoadAddress(target)
993 size = section.GetByteSize()
994 if base != lldb.LLDB_INVALID_ADDRESS and size > 0:
995 segment_dicts.append ({ 'base' : base, 'size' : size })
996 segment_dicts_len = len(segment_dicts)
997 if segment_dicts_len > 0:
998 result = '''
999#define NUM_SEGMENTS %u
1000typedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t;
1001segment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,)
1002 for (idx, segment_dict) in enumerate(segment_dicts):
1003 segment_dict['index'] = idx
1004 result += '''
1005segments[%(index)u].base = 0x%(base)x;
1006segments[%(index)u].size = 0x%(size)x;''' % segment_dict
1007 return result
Greg Claytonae23ed32012-10-08 22:39:38 +00001008 else:
Greg Clayton966c6f62014-01-07 21:55:00 +00001009 return ''
Greg Claytonae23ed32012-10-08 22:39:38 +00001010
Greg Claytone04cfd22012-07-11 22:13:18 +00001011def section_ptr_refs(debugger, command, result, dict):
1012 command_args = shlex.split(command)
1013 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
1014 description='''Searches section contents for pointer values in darwin user space programs.'''
1015 parser = optparse.OptionParser(description=description, prog='section_ptr_refs',usage=usage)
1016 add_common_options(parser)
1017 parser.add_option('--section', action='append', type='string', dest='section_names', help='section name to search', default=list())
1018 try:
1019 (options, args) = parser.parse_args(command_args)
1020 except:
1021 return
1022
1023 options.type = 'pointer'
Greg Claytonae23ed32012-10-08 22:39:38 +00001024
Greg Claytone04cfd22012-07-11 22:13:18 +00001025 sections = list()
1026 section_modules = list()
1027 if not options.section_names:
Greg Claytonc373ca52012-09-01 00:34:35 +00001028 result.AppendMessage('error: at least one section must be specified with the --section option')
Greg Claytone04cfd22012-07-11 22:13:18 +00001029 return
1030
Greg Clayton424a5db2015-05-28 03:27:22 +00001031 target = debugger.GetSelectedTarget()
Greg Clayton85e62ec2013-01-30 22:57:34 +00001032 for module in target.modules:
Greg Claytone04cfd22012-07-11 22:13:18 +00001033 for section_name in options.section_names:
1034 section = module.section[section_name]
1035 if section:
1036 sections.append (section)
1037 section_modules.append (module)
1038 if sections:
1039 dylid_load_err = load_dylib()
1040 if dylid_load_err:
Greg Claytonc373ca52012-09-01 00:34:35 +00001041 result.AppendMessage(dylid_load_err)
Greg Claytone04cfd22012-07-11 22:13:18 +00001042 return
Greg Clayton85e62ec2013-01-30 22:57:34 +00001043 frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
Greg Claytone04cfd22012-07-11 22:13:18 +00001044 for expr_str in args:
1045 for (idx, section) in enumerate(sections):
1046 expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (section.addr.load_addr, section.size, expr_str)
1047 arg_str_description = 'section %s.%s containing "%s"' % (section_modules[idx].file.fullpath, section.name, expr_str)
Greg Clayton424a5db2015-05-28 03:27:22 +00001048 num_matches = display_match_results (target.GetProcess(), result, options, arg_str_description, expr, False)
Greg Claytone04cfd22012-07-11 22:13:18 +00001049 if num_matches:
1050 if num_matches < options.max_matches:
1051 options.max_matches = options.max_matches - num_matches
1052 else:
1053 options.max_matches = 0
1054 if options.max_matches == 0:
1055 return
1056 else:
Greg Claytonc373ca52012-09-01 00:34:35 +00001057 result.AppendMessage('error: no sections were found that match any of %s' % (', '.join(options.section_names)))
Greg Claytone04cfd22012-07-11 22:13:18 +00001058
Greg Clayton85e62ec2013-01-30 22:57:34 +00001059def get_objc_refs_options():
1060 usage = "usage: %prog [options] <CLASS> [CLASS ...]"
1061 description='''Searches all allocations on the heap for instances of
1062objective C classes, or any classes that inherit from the specified classes
1063in darwin user space programs. Any matches that were found will dump the malloc
1064blocks that contain the C strings and might be able to print what kind of
1065objects the pointers are contained in using dynamic type information in the
1066program.'''
1067 parser = optparse.OptionParser(description=description, prog='objc_refs',usage=usage)
1068 add_common_options(parser)
1069 return parser
1070
Greg Clayton774ebdf2012-08-11 02:26:26 +00001071def objc_refs(debugger, command, result, dict):
1072 command_args = shlex.split(command)
Greg Clayton85e62ec2013-01-30 22:57:34 +00001073 parser = get_objc_refs_options()
Greg Clayton774ebdf2012-08-11 02:26:26 +00001074 try:
1075 (options, args) = parser.parse_args(command_args)
1076 except:
1077 return
1078
Greg Clayton424a5db2015-05-28 03:27:22 +00001079 process = debugger.GetSelectedTarget().GetProcess()
Greg Claytone7fc9cc2013-01-31 06:38:09 +00001080 if not process:
1081 result.AppendMessage('error: invalid process')
1082 return
1083 frame = process.GetSelectedThread().GetSelectedFrame()
Greg Clayton85e62ec2013-01-30 22:57:34 +00001084 if not frame:
1085 result.AppendMessage('error: invalid frame')
1086 return
1087
1088 options.type = 'isa'
1089 if options.format == None:
1090 options.format = "A" # 'A' is "address" format
1091
Greg Clayton927fa012013-04-03 07:25:30 +00001092 expr_options = lldb.SBExpressionOptions()
1093 expr_options.SetIgnoreBreakpoints(True);
1094 expr_options.SetTimeoutInMicroSeconds (3*1000*1000) # 3 second infinite timeout
1095 expr_options.SetTryAllThreads (True)
Greg Clayton6e7ab022015-01-14 21:37:19 +00001096 expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
Greg Clayton927fa012013-04-03 07:25:30 +00001097 num_objc_classes_value = frame.EvaluateExpression("(int)objc_getClassList((void *)0, (int)0)", expr_options)
Greg Clayton85e62ec2013-01-30 22:57:34 +00001098 if not num_objc_classes_value.error.Success():
1099 result.AppendMessage('error: %s' % num_objc_classes_value.error.GetCString())
1100 return
1101
1102 num_objc_classes = num_objc_classes_value.GetValueAsUnsigned()
1103 if num_objc_classes == 0:
1104 result.AppendMessage('error: no objective C classes in program')
1105 return
1106
1107 if args:
1108 # When we initialize the expression, we must define any types that
1109 # we will need when looking at every allocation. We must also define
1110 # a type named callback_baton_t and make an instance named "baton"
1111 # and initialize it how ever we want to. The address of "baton" will
1112 # be passed into our range callback. callback_baton_t must contain
1113 # a member named "callback" whose type is "range_callback_t". This
1114 # will be used by our zone callbacks to call the range callback for
1115 # each malloc range.
Greg Clayton4e1042e2015-05-27 22:32:39 +00001116 expr_prefix = '''
Greg Claytone7fc9cc2013-01-31 06:38:09 +00001117struct $malloc_match {
Greg Clayton85e62ec2013-01-30 22:57:34 +00001118 void *addr;
1119 uintptr_t size;
1120 uintptr_t offset;
1121 uintptr_t type;
1122};
Greg Clayton4e1042e2015-05-27 22:32:39 +00001123'''
1124
1125 user_init_code_format = '''
1126#define MAX_MATCHES %u
Greg Clayton85e62ec2013-01-30 22:57:34 +00001127typedef int (*compare_callback_t)(const void *a, const void *b);
1128typedef struct callback_baton_t {
1129 range_callback_t callback;
1130 compare_callback_t compare_callback;
1131 unsigned num_matches;
Greg Claytone7fc9cc2013-01-31 06:38:09 +00001132 $malloc_match matches[MAX_MATCHES];
Greg Clayton85e62ec2013-01-30 22:57:34 +00001133 void *isa;
1134 Class classes[%u];
1135} callback_baton_t;
1136compare_callback_t compare_callback = [](const void *a, const void *b) -> int {
1137 Class a_ptr = *(Class *)a;
1138 Class b_ptr = *(Class *)b;
1139 if (a_ptr < b_ptr) return -1;
1140 if (a_ptr > b_ptr) return +1;
1141 return 0;
1142};
Sean Callananf09a3db2013-08-13 00:53:33 +00001143typedef Class (*class_getSuperclass_type)(void *isa);
Greg Clayton85e62ec2013-01-30 22:57:34 +00001144range_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 +00001145 class_getSuperclass_type class_getSuperclass_impl = (class_getSuperclass_type)class_getSuperclass;
Greg Clayton85e62ec2013-01-30 22:57:34 +00001146 callback_baton_t *info = (callback_baton_t *)baton;
1147 if (sizeof(Class) <= ptr_size) {
1148 Class *curr_class_ptr = (Class *)ptr_addr;
1149 Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr,
1150 (const void *)info->classes,
1151 sizeof(info->classes)/sizeof(Class),
1152 sizeof(Class),
1153 info->compare_callback);
1154 if (matching_class_ptr) {
1155 bool match = false;
1156 if (info->isa) {
1157 Class isa = *curr_class_ptr;
1158 if (info->isa == isa)
1159 match = true;
1160 else { // if (info->objc.match_superclasses) {
Sean Callananf09a3db2013-08-13 00:53:33 +00001161 Class super = class_getSuperclass_impl(isa);
Greg Clayton85e62ec2013-01-30 22:57:34 +00001162 while (super) {
1163 if (super == info->isa) {
1164 match = true;
1165 break;
1166 }
Sean Callananf09a3db2013-08-13 00:53:33 +00001167 super = class_getSuperclass_impl(super);
Greg Clayton85e62ec2013-01-30 22:57:34 +00001168 }
1169 }
1170 }
1171 else
1172 match = true;
1173 if (match) {
Greg Claytone7fc9cc2013-01-31 06:38:09 +00001174 if (info->num_matches < MAX_MATCHES) {
Greg Clayton85e62ec2013-01-30 22:57:34 +00001175 info->matches[info->num_matches].addr = (void*)ptr_addr;
1176 info->matches[info->num_matches].size = ptr_size;
1177 info->matches[info->num_matches].offset = 0;
1178 info->matches[info->num_matches].type = type;
1179 ++info->num_matches;
1180 }
1181 }
1182 }
1183 }
1184};
1185callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
1186int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class));
1187(void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);'''
1188 # We must also define a snippet of code to be run that returns
1189 # the result of the expression we run.
1190 # Here we return NULL if our pointer was not found in any malloc blocks,
1191 # and we return the address of the matches array so we can then access
1192 # the matching results
Greg Claytone7fc9cc2013-01-31 06:38:09 +00001193 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
1194 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
1195 baton.matches'''
Greg Clayton85e62ec2013-01-30 22:57:34 +00001196 # Iterate through all of our ObjC class name arguments
1197 for class_name in args:
1198 addr_expr_str = "(void *)[%s class]" % class_name
Greg Clayton927fa012013-04-03 07:25:30 +00001199 expr_options = lldb.SBExpressionOptions()
1200 expr_options.SetIgnoreBreakpoints(True);
1201 expr_options.SetTimeoutInMicroSeconds (1*1000*1000) # 1 second timeout
1202 expr_options.SetTryAllThreads (True)
Greg Clayton6e7ab022015-01-14 21:37:19 +00001203 expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
Greg Clayton927fa012013-04-03 07:25:30 +00001204 expr_sbvalue = frame.EvaluateExpression (addr_expr_str, expr_options)
Greg Clayton85e62ec2013-01-30 22:57:34 +00001205 if expr_sbvalue.error.Success():
1206 isa = expr_sbvalue.unsigned
1207 if isa:
1208 options.type = 'isa'
Greg Claytone7fc9cc2013-01-31 06:38:09 +00001209 result.AppendMessage('Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' % (class_name, isa))
1210 user_init_code = user_init_code_format % (options.max_matches, num_objc_classes, isa)
Greg Clayton13fbb992013-02-01 00:47:49 +00001211 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code)
Greg Clayton85e62ec2013-01-30 22:57:34 +00001212 arg_str_description = 'objective C classes with isa 0x%x' % isa
Greg Clayton424a5db2015-05-28 03:27:22 +00001213 display_match_results (process, result, options, arg_str_description, expr, True, expr_prefix)
Greg Clayton774ebdf2012-08-11 02:26:26 +00001214 else:
Greg Clayton85e62ec2013-01-30 22:57:34 +00001215 result.AppendMessage('error: Can\'t find isa for an ObjC class named "%s"' % (class_name))
1216 else:
1217 result.AppendMessage('error: expression error for "%s": %s' % (addr_expr_str, expr_sbvalue.error))
1218 else:
1219 result.AppendMessage('error: command takes one or more C string arguments');
Greg Clayton774ebdf2012-08-11 02:26:26 +00001220
Greg Claytond712ef02012-04-25 18:40:20 +00001221if __name__ == '__main__':
1222 lldb.debugger = lldb.SBDebugger.Create()
1223
Greg Clayton85e62ec2013-01-30 22:57:34 +00001224# Make the options so we can generate the help text for the new LLDB
1225# command line command prior to registering it with LLDB below. This way
1226# if clients in LLDB type "help malloc_info", they will see the exact same
1227# output as typing "malloc_info --help".
1228ptr_refs.__doc__ = get_ptr_refs_options().format_help()
1229cstr_refs.__doc__ = get_cstr_refs_options().format_help()
1230malloc_info.__doc__ = get_malloc_info_options().format_help()
1231objc_refs.__doc__ = get_objc_refs_options().format_help()
Enrico Granatacd4218f2013-02-22 02:21:10 +00001232lldb.debugger.HandleCommand('command script add -f %s.ptr_refs ptr_refs' % __name__)
1233lldb.debugger.HandleCommand('command script add -f %s.cstr_refs cstr_refs' % __name__)
1234lldb.debugger.HandleCommand('command script add -f %s.malloc_info malloc_info' % __name__)
Greg Clayton424a5db2015-05-28 03:27:22 +00001235lldb.debugger.HandleCommand('command script add -f %s.find_variable find_variable' % __name__)
Greg Clayton85e62ec2013-01-30 22:57:34 +00001236# lldb.debugger.HandleCommand('command script add -f %s.heap heap' % package_name)
1237# lldb.debugger.HandleCommand('command script add -f %s.section_ptr_refs section_ptr_refs' % package_name)
1238# lldb.debugger.HandleCommand('command script add -f %s.stack_ptr_refs stack_ptr_refs' % package_name)
Enrico Granatacd4218f2013-02-22 02:21:10 +00001239lldb.debugger.HandleCommand('command script add -f %s.objc_refs objc_refs' % __name__)
Greg Clayton424a5db2015-05-28 03:27:22 +00001240print '"malloc_info", "ptr_refs", "cstr_refs", "find_variable", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help.'
Greg Clayton804de012012-04-11 16:27:06 +00001241
1242
1243
1244