blob: 9f68497dd6b93c66cae0a3518436cda0ed4fc039 [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):
87 if stack_history_entry.addr != 0:
88 symbolicator = lldb.utils.symbolication.Symbolicator()
89 symbolicator.target = lldb.target
90 global type_flag_strings
91 print 'stack_history_entry[%u]: addr = 0x%x, type=%s, arg=%u, frames=' % (idx, stack_history_entry.address, type_flag_strings[stack_history_entry.type_flags], stack_history_entry.argument)
92 frame_idx = 0
93 pc = int(stack_history_entry.frames[frame_idx])
94 while pc != 0:
95 if pc >= 0x1000:
96 frames = symbolicator.symbolicate(pc)
97 if frames:
98 for frame in frames:
99 print '[%3u] %s' % (frame_idx, frame)
100 frame_idx += 1
101 else:
102 print '[%3u] 0x%x' % (frame_idx, pc)
103 frame_idx += 1
104 frame_idx = frame_idx + 1
105 pc = int(stack_history_entry.frames[frame_idx])
106 else:
107 pc = 0
108
109def dump_stack_history_entries(addr):
110 # malloc_stack_entry *get_stack_history_for_address (const void * addr)
111
112 expr = 'get_stack_history_for_address((void *)%s)' % addr
113 expr_sbvalue = lldb.frame.EvaluateExpression (expr)
114 if expr_sbvalue.error.Success():
115 if expr_sbvalue.unsigned:
116 expr_value = lldb.value(expr_sbvalue)
117 idx = 0;
118 stack_history_entry = expr_value[idx]
119 while stack_history_entry.address:
120 dump_stack_history_entry(stack_history_entry, idx)
121 idx = idx + 1
122 stack_history_entry = expr_value[idx]
123
Greg Clayton93e5ba52012-04-13 16:24:09 +0000124
Greg Clayton96666442012-04-11 18:30:53 +0000125def heap_search(options, arg_str):
Greg Clayton9098fee2012-04-21 00:11:26 +0000126 dylid_load_err = load_dylib()
127 if dylid_load_err:
128 print dylid_load_err
129 return
Greg Clayton96666442012-04-11 18:30:53 +0000130 expr = None
Greg Claytonbff78412012-04-12 18:57:36 +0000131 arg_str_description = arg_str
Greg Clayton93e5ba52012-04-13 16:24:09 +0000132 default_memory_format = "Y" # 'Y' is "bytes with ASCII" format
133 #memory_chunk_size = 1
Greg Clayton96666442012-04-11 18:30:53 +0000134 if options.type == 'pointer':
Greg Clayton1dae6f32012-04-25 18:40:20 +0000135 expr = 'find_pointer_in_heap((void *)%s)' % (arg_str)
Greg Claytonbff78412012-04-12 18:57:36 +0000136 arg_str_description = 'malloc block containing pointer %s' % arg_str
Greg Clayton93e5ba52012-04-13 16:24:09 +0000137 default_memory_format = "A" # 'A' is "address" format
138 #memory_chunk_size = lldb.process.GetAddressByteSize()
Greg Clayton96666442012-04-11 18:30:53 +0000139 elif options.type == 'cstr':
140 expr = 'find_cstring_in_heap("%s")' % arg_str
Greg Claytonbff78412012-04-12 18:57:36 +0000141 arg_str_description = 'malloc block containing "%s"' % arg_str
142 elif options.type == 'addr':
Greg Claytonf5902cb2012-04-12 21:06:22 +0000143 expr = 'find_block_for_address((void *)%s)' % arg_str
Greg Claytonbff78412012-04-12 18:57:36 +0000144 arg_str_description = 'malloc block for %s' % arg_str
Greg Clayton96666442012-04-11 18:30:53 +0000145 else:
146 print 'error: invalid type "%s"\nvalid values are "pointer", "cstr"' % options.type
147 return
148
149 expr_sbvalue = lldb.frame.EvaluateExpression (expr)
150 if expr_sbvalue.error.Success():
151 if expr_sbvalue.unsigned:
152 match_value = lldb.value(expr_sbvalue)
153 i = 0
154 while 1:
155 match_entry = match_value[i]; i += 1
156 malloc_addr = match_entry.addr.sbvalue.unsigned
157 if malloc_addr == 0:
158 break
159 malloc_size = int(match_entry.size)
160 offset = int(match_entry.offset)
161 dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
162 # If the type is still 'void *' then we weren't able to figure
163 # out a dynamic type for the malloc_addr
164 type_name = dynamic_value.type.name
Greg Claytonbff78412012-04-12 18:57:36 +0000165 description = '[%u] %s: addr = 0x%x' % (i, arg_str_description, malloc_addr)
166 if offset != 0:
167 description += ' + %u' % (offset)
168 description += ', size = %u' % (malloc_size)
Greg Clayton96666442012-04-11 18:30:53 +0000169 if type_name == 'void *':
170 if options.type == 'pointer' and malloc_size == 4096:
171 error = lldb.SBError()
172 data = bytearray(lldb.process.ReadMemory(malloc_addr, 16, error))
173 if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
Greg Claytonbff78412012-04-12 18:57:36 +0000174 description += ', type = (AUTORELEASE!)'
Greg Clayton93e5ba52012-04-13 16:24:09 +0000175 print description
Greg Claytonbff78412012-04-12 18:57:36 +0000176 else:
177 description += ', type = %s' % (type_name)
178 derefed_dynamic_value = dynamic_value.deref
179 ivar_member = None
180 if derefed_dynamic_value:
181 derefed_dynamic_type = derefed_dynamic_value.type
182 member = derefed_dynamic_type.GetFieldAtIndex(0)
183 search_bases = False
184 if member:
185 if member.GetOffsetInBytes() <= offset:
186 for field_idx in range (derefed_dynamic_type.GetNumberOfFields()):
187 member = derefed_dynamic_type.GetFieldAtIndex(field_idx)
188 member_byte_offset = member.GetOffsetInBytes()
189 if member_byte_offset == offset:
190 ivar_member = member
191 break
192 else:
193 search_bases = True
Greg Clayton96666442012-04-11 18:30:53 +0000194 else:
195 search_bases = True
Greg Clayton96666442012-04-11 18:30:53 +0000196
Greg Claytonbff78412012-04-12 18:57:36 +0000197 if not ivar_member and search_bases:
198 for field_idx in range (derefed_dynamic_type.GetNumberOfDirectBaseClasses()):
199 member = derefed_dynamic_type.GetDirectBaseClassAtIndex(field_idx)
Greg Clayton96666442012-04-11 18:30:53 +0000200 member_byte_offset = member.GetOffsetInBytes()
201 if member_byte_offset == offset:
202 ivar_member = member
203 break
Greg Claytonbff78412012-04-12 18:57:36 +0000204 if not ivar_member:
205 for field_idx in range (derefed_dynamic_type.GetNumberOfVirtualBaseClasses()):
206 member = derefed_dynamic_type.GetVirtualBaseClassAtIndex(field_idx)
207 member_byte_offset = member.GetOffsetInBytes()
208 if member_byte_offset == offset:
209 ivar_member = member
210 break
Greg Clayton96666442012-04-11 18:30:53 +0000211 if ivar_member:
Greg Claytonbff78412012-04-12 18:57:36 +0000212 description +=', ivar = %s' % (ivar_member.name)
213
214 print description
215 if derefed_dynamic_value:
216 print derefed_dynamic_value
217 if options.print_object_description:
218 desc = dynamic_value.GetObjectDescription()
219 if desc:
220 print ' (%s) 0x%x %s\n' % (type_name, malloc_addr, desc)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000221 if options.memory:
222 memory_format = options.format
223 if not memory_format:
224 memory_format = default_memory_format
225 cmd_result = lldb.SBCommandReturnObject()
226 #count = malloc_size / memory_chunk_size
227 memory_command = "memory read -f %s 0x%x 0x%x" % (memory_format, malloc_addr, malloc_addr + malloc_size)
228 lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
229 print cmd_result.GetOutput()
Greg Clayton7a245762012-05-10 23:17:28 +0000230 if options.stack_history:
231 dump_stack_history_entries(malloc_addr)
232 elif options.stack:
Greg Clayton6f2f0ab2012-04-25 01:49:50 +0000233 symbolicator = lldb.utils.symbolication.Symbolicator()
Greg Clayton9098fee2012-04-21 00:11:26 +0000234 symbolicator.target = lldb.target
235 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)
236 #print expr_str
237 expr = lldb.frame.EvaluateExpression (expr_str);
238 expr_error = expr.GetError()
239 if expr_error.Success():
240 err = expr.unsigned
241 if err:
242 print 'error: __mach_stack_logging_get_frames() returned error %i' % (err)
243 else:
244 count_expr = lldb.frame.EvaluateExpression ("g_stack_frames_count")
245 count = count_expr.unsigned
246 #print 'g_stack_frames_count is %u' % (count)
247 if count > 0:
248 frame_idx = 0
249 frames_expr = lldb.value(lldb.frame.EvaluateExpression ("g_stack_frames"))
250 done = False
251 for stack_frame_idx in range(count):
252 if not done:
253 frame_load_addr = int(frames_expr[stack_frame_idx])
254 if frame_load_addr >= 0x1000:
255 frames = symbolicator.symbolicate(frame_load_addr)
256 if frames:
257 for frame in frames:
258 print '[%3u] %s' % (frame_idx, frame)
259 frame_idx += 1
260 else:
261 print '[%3u] 0x%x' % (frame_idx, frame_load_addr)
262 frame_idx += 1
263 else:
264 done = True
265 else:
266 print 'error: %s' % (expr_error)
Greg Clayton96666442012-04-11 18:30:53 +0000267 else:
268 print '%s %s was not found in any malloc blocks' % (options.type, arg_str)
269 else:
Greg Claytonbff78412012-04-12 18:57:36 +0000270 print expr_sbvalue.error
271 print
Greg Clayton96666442012-04-11 18:30:53 +0000272
Greg Claytonbff78412012-04-12 18:57:36 +0000273def ptr_refs(debugger, command, result, dict):
Greg Claytone93e24f2012-04-11 16:27:06 +0000274 command_args = shlex.split(command)
Greg Claytonbff78412012-04-12 18:57:36 +0000275 usage = "usage: %prog [options] <PTR> [PTR ...]"
Greg Clayton96666442012-04-11 18:30:53 +0000276 description='''Searches the heap for pointer references on darwin user space programs.
277
278 Any matches that were found will dump the malloc blocks that contain the pointers
279 and might be able to print what kind of objects the pointers are contained in using
Greg Claytonbff78412012-04-12 18:57:36 +0000280 dynamic type information in the program.'''
281 parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000282 add_common_options(parser)
Greg Claytone93e24f2012-04-11 16:27:06 +0000283 try:
284 (options, args) = parser.parse_args(command_args)
285 except:
286 return
Greg Clayton96666442012-04-11 18:30:53 +0000287
288 options.type = 'pointer'
Greg Claytone93e24f2012-04-11 16:27:06 +0000289
290 if args:
291
292 for data in args:
Greg Clayton96666442012-04-11 18:30:53 +0000293 heap_search (options, data)
Greg Claytone93e24f2012-04-11 16:27:06 +0000294 else:
Greg Clayton96666442012-04-11 18:30:53 +0000295 print 'error: no pointer arguments were given'
Greg Claytone93e24f2012-04-11 16:27:06 +0000296
Greg Claytonbff78412012-04-12 18:57:36 +0000297def cstr_refs(debugger, command, result, dict):
Greg Clayton96666442012-04-11 18:30:53 +0000298 command_args = shlex.split(command)
Greg Claytonbff78412012-04-12 18:57:36 +0000299 usage = "usage: %prog [options] <CSTR> [CSTR ...]"
Greg Clayton96666442012-04-11 18:30:53 +0000300 description='''Searches the heap for C string references on darwin user space programs.
301
302 Any matches that were found will dump the malloc blocks that contain the C strings
303 and might be able to print what kind of objects the pointers are contained in using
Greg Claytonbff78412012-04-12 18:57:36 +0000304 dynamic type information in the program.'''
305 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000306 add_common_options(parser)
Greg Clayton96666442012-04-11 18:30:53 +0000307 try:
308 (options, args) = parser.parse_args(command_args)
309 except:
310 return
311
312 options.type = 'cstr'
313
314 if args:
315
316 for data in args:
317 heap_search (options, data)
318 else:
319 print 'error: no c string arguments were given to search for'
Greg Claytone93e24f2012-04-11 16:27:06 +0000320
Greg Claytonbff78412012-04-12 18:57:36 +0000321def malloc_info(debugger, command, result, dict):
322 command_args = shlex.split(command)
323 usage = "usage: %prog [options] <ADDR> [ADDR ...]"
324 description='''Searches the heap a malloc block that contains the addresses specified as arguments.
325
326 Any matches that were found will dump the malloc blocks that match or contain
327 the specified address. The matching blocks might be able to show what kind
328 of objects they are using dynamic type information in the program.'''
329 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000330 add_common_options(parser)
Greg Claytonbff78412012-04-12 18:57:36 +0000331 try:
332 (options, args) = parser.parse_args(command_args)
333 except:
334 return
335
336 options.type = 'addr'
337
338 if args:
339
340 for data in args:
341 heap_search (options, data)
342 else:
343 print 'error: no c string arguments were given to search for'
344
Greg Clayton1dae6f32012-04-25 18:40:20 +0000345if __name__ == '__main__':
346 lldb.debugger = lldb.SBDebugger.Create()
347
348# This initializer is being run from LLDB in the embedded command interpreter
349# Add any commands contained in this module to LLDB
350lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.ptr_refs ptr_refs')
351lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.cstr_refs cstr_refs')
352lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.malloc_info malloc_info')
353print '"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 +0000354
355
356
357