blob: fc62ea85b559b16189ebd0fefe72ec08ba832f68 [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 Claytone93e24f2012-04-11 16:27:06 +000016import shlex
Greg Clayton1dae6f32012-04-25 18:40:20 +000017import string
18import tempfile
Greg Clayton6f2f0ab2012-04-25 01:49:50 +000019import lldb.utils.symbolication
Greg Claytone93e24f2012-04-11 16:27:06 +000020
Greg Clayton1dae6f32012-04-25 18:40:20 +000021g_libheap_dylib_dir = None
22g_libheap_dylib_dict = dict()
23
Greg Clayton9098fee2012-04-21 00:11:26 +000024def load_dylib():
25 if lldb.target:
Greg Clayton1dae6f32012-04-25 18:40:20 +000026 global g_libheap_dylib_dir
27 global g_libheap_dylib_dict
28 triple = lldb.target.triple
Greg Clayton3e339792012-04-25 21:23:07 +000029 if triple in g_libheap_dylib_dict:
30 libheap_dylib_path = g_libheap_dylib_dict[triple]
31 else:
Greg Clayton1dae6f32012-04-25 18:40:20 +000032 if not g_libheap_dylib_dir:
Greg Clayton3e339792012-04-25 21:23:07 +000033 g_libheap_dylib_dir = tempfile.gettempdir() + '/lldb-dylibs'
34 triple_dir = g_libheap_dylib_dir + '/' + triple + '/' + __name__
Greg Clayton1dae6f32012-04-25 18:40:20 +000035 if not os.path.exists(triple_dir):
Greg Clayton3e339792012-04-25 21:23:07 +000036 os.makedirs(triple_dir)
Greg Clayton1dae6f32012-04-25 18:40:20 +000037 libheap_dylib_path = triple_dir + '/libheap.dylib'
38 g_libheap_dylib_dict[triple] = libheap_dylib_path
Greg Clayton3e339792012-04-25 21:23:07 +000039 heap_code_directory = os.path.dirname(__file__) + '/heap'
40 heap_source_file = heap_code_directory + '/heap_find.cpp'
41 # Check if the dylib doesn't exist, or if "heap_find.cpp" is newer than the dylib
42 if not os.path.exists(libheap_dylib_path) or os.stat(heap_source_file).st_mtime > os.stat(libheap_dylib_path).st_mtime:
43 # Remake the dylib
Greg Clayton1dae6f32012-04-25 18:40:20 +000044 make_command = '(cd "%s" ; make EXE="%s" ARCH=%s)' % (heap_code_directory, libheap_dylib_path, string.split(triple, '-')[0])
Greg Clayton3e339792012-04-25 21:23:07 +000045 # print make_command
Greg Clayton1dae6f32012-04-25 18:40:20 +000046 make_output = commands.getoutput(make_command)
Greg Clayton3e339792012-04-25 21:23:07 +000047 # print make_output
Greg Clayton9098fee2012-04-21 00:11:26 +000048 if os.path.exists(libheap_dylib_path):
49 libheap_dylib_spec = lldb.SBFileSpec(libheap_dylib_path)
50 if lldb.target.FindModule(libheap_dylib_spec):
51 return None # success, 'libheap.dylib' already loaded
52 if lldb.process:
53 state = lldb.process.state
54 if state == lldb.eStateStopped:
55 (libheap_dylib_path)
56 error = lldb.SBError()
57 image_idx = lldb.process.LoadImage(libheap_dylib_spec, error)
58 if error.Success():
59 return None
60 else:
61 if error:
62 return 'error: %s' % error
63 else:
64 return 'error: "process load \'%s\'" failed' % libheap_dylib_spec
65 else:
66 return 'error: process is not stopped'
67 else:
68 return 'error: invalid process'
69 else:
70 return 'error: file does not exist "%s"' % libheap_dylib_path
71 else:
72 return 'error: invalid target'
73
74 debugger.HandleCommand('process load "%s"' % libheap_dylib_path)
75
Greg Clayton93e5ba52012-04-13 16:24:09 +000076def add_common_options(parser):
77 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
78 parser.add_option('-o', '--po', action='store_true', dest='print_object_description', help='print the object descriptions for any matches', default=False)
79 parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False)
80 parser.add_option('-f', '--format', type='string', dest='format', help='the format to use when dumping memory if --memory is specified', default=None)
Greg Clayton9098fee2012-04-21 00:11:26 +000081 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 +000082 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)
83
84type_flag_strings = [ 'free', 'generic', 'alloc', 'dealloc' ];
85
86def dump_stack_history_entry(stack_history_entry, idx):
Greg Clayton6f446f32012-05-10 23:37:52 +000087 address = int(stack_history_entry.address)
88 if address:
89 type_flags = int(stack_history_entry.type_flags)
90 argument = int(stack_history_entry.argument)
Greg Clayton7a245762012-05-10 23:17:28 +000091 symbolicator = lldb.utils.symbolication.Symbolicator()
92 symbolicator.target = lldb.target
93 global type_flag_strings
Greg Clayton6f446f32012-05-10 23:37:52 +000094 print 'stack_history_entry[%u]: addr = 0x%x, type=%s, arg=%u, frames=' % (idx, address, type_flag_strings[type_flags], argument)
Greg Clayton7a245762012-05-10 23:17:28 +000095 frame_idx = 0
Greg Clayton6f446f32012-05-10 23:37:52 +000096 idx = 0
97 pc = int(stack_history_entry.frames[idx])
Greg Clayton7a245762012-05-10 23:17:28 +000098 while pc != 0:
99 if pc >= 0x1000:
100 frames = symbolicator.symbolicate(pc)
101 if frames:
102 for frame in frames:
103 print '[%3u] %s' % (frame_idx, frame)
104 frame_idx += 1
105 else:
106 print '[%3u] 0x%x' % (frame_idx, pc)
107 frame_idx += 1
Greg Clayton6f446f32012-05-10 23:37:52 +0000108 idx = idx + 1
109 pc = int(stack_history_entry.frames[idx])
Greg Clayton7a245762012-05-10 23:17:28 +0000110 else:
111 pc = 0
112
113def dump_stack_history_entries(addr):
114 # malloc_stack_entry *get_stack_history_for_address (const void * addr)
115
Greg Clayton6f446f32012-05-10 23:37:52 +0000116 expr = 'get_stack_history_for_address((void *)0x%x)' % (addr)
117 print 'expr = "%s"' % (expr)
Greg Clayton7a245762012-05-10 23:17:28 +0000118 expr_sbvalue = lldb.frame.EvaluateExpression (expr)
119 if expr_sbvalue.error.Success():
120 if expr_sbvalue.unsigned:
121 expr_value = lldb.value(expr_sbvalue)
Greg Clayton6f446f32012-05-10 23:37:52 +0000122 #print 'expr_value = ', expr_value
Greg Clayton7a245762012-05-10 23:17:28 +0000123 idx = 0;
124 stack_history_entry = expr_value[idx]
Greg Clayton6f446f32012-05-10 23:37:52 +0000125 while int(stack_history_entry.address) != 0:
Greg Clayton7a245762012-05-10 23:17:28 +0000126 dump_stack_history_entry(stack_history_entry, idx)
127 idx = idx + 1
128 stack_history_entry = expr_value[idx]
129
Greg Clayton93e5ba52012-04-13 16:24:09 +0000130
Greg Clayton96666442012-04-11 18:30:53 +0000131def heap_search(options, arg_str):
Greg Clayton9098fee2012-04-21 00:11:26 +0000132 dylid_load_err = load_dylib()
133 if dylid_load_err:
134 print dylid_load_err
135 return
Greg Clayton96666442012-04-11 18:30:53 +0000136 expr = None
Greg Claytonbff78412012-04-12 18:57:36 +0000137 arg_str_description = arg_str
Greg Clayton93e5ba52012-04-13 16:24:09 +0000138 default_memory_format = "Y" # 'Y' is "bytes with ASCII" format
139 #memory_chunk_size = 1
Greg Clayton96666442012-04-11 18:30:53 +0000140 if options.type == 'pointer':
Greg Clayton1dae6f32012-04-25 18:40:20 +0000141 expr = 'find_pointer_in_heap((void *)%s)' % (arg_str)
Greg Claytonbff78412012-04-12 18:57:36 +0000142 arg_str_description = 'malloc block containing pointer %s' % arg_str
Greg Clayton93e5ba52012-04-13 16:24:09 +0000143 default_memory_format = "A" # 'A' is "address" format
144 #memory_chunk_size = lldb.process.GetAddressByteSize()
Greg Clayton96666442012-04-11 18:30:53 +0000145 elif options.type == 'cstr':
146 expr = 'find_cstring_in_heap("%s")' % arg_str
Greg Claytonbff78412012-04-12 18:57:36 +0000147 arg_str_description = 'malloc block containing "%s"' % arg_str
148 elif options.type == 'addr':
Greg Claytonf5902cb2012-04-12 21:06:22 +0000149 expr = 'find_block_for_address((void *)%s)' % arg_str
Greg Claytonbff78412012-04-12 18:57:36 +0000150 arg_str_description = 'malloc block for %s' % arg_str
Greg Clayton96666442012-04-11 18:30:53 +0000151 else:
152 print 'error: invalid type "%s"\nvalid values are "pointer", "cstr"' % options.type
153 return
154
155 expr_sbvalue = lldb.frame.EvaluateExpression (expr)
156 if expr_sbvalue.error.Success():
157 if expr_sbvalue.unsigned:
158 match_value = lldb.value(expr_sbvalue)
159 i = 0
160 while 1:
161 match_entry = match_value[i]; i += 1
162 malloc_addr = match_entry.addr.sbvalue.unsigned
163 if malloc_addr == 0:
164 break
165 malloc_size = int(match_entry.size)
166 offset = int(match_entry.offset)
167 dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
168 # If the type is still 'void *' then we weren't able to figure
169 # out a dynamic type for the malloc_addr
170 type_name = dynamic_value.type.name
Greg Claytonbff78412012-04-12 18:57:36 +0000171 description = '[%u] %s: addr = 0x%x' % (i, arg_str_description, malloc_addr)
172 if offset != 0:
173 description += ' + %u' % (offset)
174 description += ', size = %u' % (malloc_size)
Greg Clayton96666442012-04-11 18:30:53 +0000175 if type_name == 'void *':
176 if options.type == 'pointer' and malloc_size == 4096:
177 error = lldb.SBError()
178 data = bytearray(lldb.process.ReadMemory(malloc_addr, 16, error))
179 if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
Greg Claytonbff78412012-04-12 18:57:36 +0000180 description += ', type = (AUTORELEASE!)'
Greg Clayton93e5ba52012-04-13 16:24:09 +0000181 print description
Greg Claytonbff78412012-04-12 18:57:36 +0000182 else:
183 description += ', type = %s' % (type_name)
184 derefed_dynamic_value = dynamic_value.deref
185 ivar_member = None
186 if derefed_dynamic_value:
187 derefed_dynamic_type = derefed_dynamic_value.type
188 member = derefed_dynamic_type.GetFieldAtIndex(0)
189 search_bases = False
190 if member:
191 if member.GetOffsetInBytes() <= offset:
192 for field_idx in range (derefed_dynamic_type.GetNumberOfFields()):
193 member = derefed_dynamic_type.GetFieldAtIndex(field_idx)
194 member_byte_offset = member.GetOffsetInBytes()
195 if member_byte_offset == offset:
196 ivar_member = member
197 break
198 else:
199 search_bases = True
Greg Clayton96666442012-04-11 18:30:53 +0000200 else:
201 search_bases = True
Greg Clayton96666442012-04-11 18:30:53 +0000202
Greg Claytonbff78412012-04-12 18:57:36 +0000203 if not ivar_member and search_bases:
204 for field_idx in range (derefed_dynamic_type.GetNumberOfDirectBaseClasses()):
205 member = derefed_dynamic_type.GetDirectBaseClassAtIndex(field_idx)
Greg Clayton96666442012-04-11 18:30:53 +0000206 member_byte_offset = member.GetOffsetInBytes()
207 if member_byte_offset == offset:
208 ivar_member = member
209 break
Greg Claytonbff78412012-04-12 18:57:36 +0000210 if not ivar_member:
211 for field_idx in range (derefed_dynamic_type.GetNumberOfVirtualBaseClasses()):
212 member = derefed_dynamic_type.GetVirtualBaseClassAtIndex(field_idx)
213 member_byte_offset = member.GetOffsetInBytes()
214 if member_byte_offset == offset:
215 ivar_member = member
216 break
Greg Clayton96666442012-04-11 18:30:53 +0000217 if ivar_member:
Greg Claytonbff78412012-04-12 18:57:36 +0000218 description +=', ivar = %s' % (ivar_member.name)
219
220 print description
221 if derefed_dynamic_value:
222 print derefed_dynamic_value
223 if options.print_object_description:
224 desc = dynamic_value.GetObjectDescription()
225 if desc:
226 print ' (%s) 0x%x %s\n' % (type_name, malloc_addr, desc)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000227 if options.memory:
228 memory_format = options.format
229 if not memory_format:
230 memory_format = default_memory_format
231 cmd_result = lldb.SBCommandReturnObject()
232 #count = malloc_size / memory_chunk_size
233 memory_command = "memory read -f %s 0x%x 0x%x" % (memory_format, malloc_addr, malloc_addr + malloc_size)
234 lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
235 print cmd_result.GetOutput()
Greg Clayton7a245762012-05-10 23:17:28 +0000236 if options.stack_history:
237 dump_stack_history_entries(malloc_addr)
238 elif options.stack:
Greg Clayton6f2f0ab2012-04-25 01:49:50 +0000239 symbolicator = lldb.utils.symbolication.Symbolicator()
Greg Clayton9098fee2012-04-21 00:11:26 +0000240 symbolicator.target = lldb.target
241 expr_str = "g_stack_frames_count = sizeof(g_stack_frames)/sizeof(uint64_t); (int)__mach_stack_logging_get_frames((unsigned)mach_task_self(), 0x%xull, g_stack_frames, g_stack_frames_count, &g_stack_frames_count)" % (malloc_addr)
242 #print expr_str
243 expr = lldb.frame.EvaluateExpression (expr_str);
244 expr_error = expr.GetError()
245 if expr_error.Success():
246 err = expr.unsigned
247 if err:
248 print 'error: __mach_stack_logging_get_frames() returned error %i' % (err)
249 else:
250 count_expr = lldb.frame.EvaluateExpression ("g_stack_frames_count")
251 count = count_expr.unsigned
252 #print 'g_stack_frames_count is %u' % (count)
253 if count > 0:
254 frame_idx = 0
255 frames_expr = lldb.value(lldb.frame.EvaluateExpression ("g_stack_frames"))
256 done = False
257 for stack_frame_idx in range(count):
258 if not done:
259 frame_load_addr = int(frames_expr[stack_frame_idx])
260 if frame_load_addr >= 0x1000:
261 frames = symbolicator.symbolicate(frame_load_addr)
262 if frames:
263 for frame in frames:
264 print '[%3u] %s' % (frame_idx, frame)
265 frame_idx += 1
266 else:
267 print '[%3u] 0x%x' % (frame_idx, frame_load_addr)
268 frame_idx += 1
269 else:
270 done = True
271 else:
272 print 'error: %s' % (expr_error)
Greg Clayton96666442012-04-11 18:30:53 +0000273 else:
274 print '%s %s was not found in any malloc blocks' % (options.type, arg_str)
275 else:
Greg Claytonbff78412012-04-12 18:57:36 +0000276 print expr_sbvalue.error
277 print
Greg Clayton96666442012-04-11 18:30:53 +0000278
Greg Claytonbff78412012-04-12 18:57:36 +0000279def ptr_refs(debugger, command, result, dict):
Greg Claytone93e24f2012-04-11 16:27:06 +0000280 command_args = shlex.split(command)
Greg Claytonbff78412012-04-12 18:57:36 +0000281 usage = "usage: %prog [options] <PTR> [PTR ...]"
Greg Clayton96666442012-04-11 18:30:53 +0000282 description='''Searches the heap for pointer references on darwin user space programs.
283
284 Any matches that were found will dump the malloc blocks that contain the pointers
285 and might be able to print what kind of objects the pointers are contained in using
Greg Claytonbff78412012-04-12 18:57:36 +0000286 dynamic type information in the program.'''
287 parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000288 add_common_options(parser)
Greg Claytone93e24f2012-04-11 16:27:06 +0000289 try:
290 (options, args) = parser.parse_args(command_args)
291 except:
292 return
Greg Clayton96666442012-04-11 18:30:53 +0000293
294 options.type = 'pointer'
Greg Claytone93e24f2012-04-11 16:27:06 +0000295
296 if args:
297
298 for data in args:
Greg Clayton96666442012-04-11 18:30:53 +0000299 heap_search (options, data)
Greg Claytone93e24f2012-04-11 16:27:06 +0000300 else:
Greg Clayton96666442012-04-11 18:30:53 +0000301 print 'error: no pointer arguments were given'
Greg Claytone93e24f2012-04-11 16:27:06 +0000302
Greg Claytonbff78412012-04-12 18:57:36 +0000303def cstr_refs(debugger, command, result, dict):
Greg Clayton96666442012-04-11 18:30:53 +0000304 command_args = shlex.split(command)
Greg Claytonbff78412012-04-12 18:57:36 +0000305 usage = "usage: %prog [options] <CSTR> [CSTR ...]"
Greg Clayton96666442012-04-11 18:30:53 +0000306 description='''Searches the heap for C string references on darwin user space programs.
307
308 Any matches that were found will dump the malloc blocks that contain the C strings
309 and might be able to print what kind of objects the pointers are contained in using
Greg Claytonbff78412012-04-12 18:57:36 +0000310 dynamic type information in the program.'''
311 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000312 add_common_options(parser)
Greg Clayton96666442012-04-11 18:30:53 +0000313 try:
314 (options, args) = parser.parse_args(command_args)
315 except:
316 return
317
318 options.type = 'cstr'
319
320 if args:
321
322 for data in args:
323 heap_search (options, data)
324 else:
325 print 'error: no c string arguments were given to search for'
Greg Claytone93e24f2012-04-11 16:27:06 +0000326
Greg Claytonbff78412012-04-12 18:57:36 +0000327def malloc_info(debugger, command, result, dict):
328 command_args = shlex.split(command)
329 usage = "usage: %prog [options] <ADDR> [ADDR ...]"
330 description='''Searches the heap a malloc block that contains the addresses specified as arguments.
331
332 Any matches that were found will dump the malloc blocks that match or contain
333 the specified address. The matching blocks might be able to show what kind
334 of objects they are using dynamic type information in the program.'''
335 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000336 add_common_options(parser)
Greg Claytonbff78412012-04-12 18:57:36 +0000337 try:
338 (options, args) = parser.parse_args(command_args)
339 except:
340 return
341
342 options.type = 'addr'
343
344 if args:
345
346 for data in args:
347 heap_search (options, data)
348 else:
349 print 'error: no c string arguments were given to search for'
350
Greg Clayton1dae6f32012-04-25 18:40:20 +0000351if __name__ == '__main__':
352 lldb.debugger = lldb.SBDebugger.Create()
353
354# This initializer is being run from LLDB in the embedded command interpreter
355# Add any commands contained in this module to LLDB
356lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.ptr_refs ptr_refs')
357lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.cstr_refs cstr_refs')
358lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.malloc_info malloc_info')
359print '"ptr_refs", "cstr_refs", and "malloc_info" commands have been installed, use the "--help" options on these commands for detailed help.'
Greg Claytone93e24f2012-04-11 16:27:06 +0000360
361
362
363