blob: 32c7335501ae99ab0d5deb0418e412512c0cf13d [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
29 if triple not in g_libheap_dylib_dict:
30 if not g_libheap_dylib_dir:
31 g_libheap_dylib_dir = tempfile.mkdtemp()
32 triple_dir = g_libheap_dylib_dir + '/' + triple
33 if not os.path.exists(triple_dir):
34 os.mkdir(triple_dir)
35 libheap_dylib_path = triple_dir + '/libheap.dylib'
36 g_libheap_dylib_dict[triple] = libheap_dylib_path
37 libheap_dylib_path = g_libheap_dylib_dict[triple]
Greg Clayton9098fee2012-04-21 00:11:26 +000038 if not os.path.exists(libheap_dylib_path):
Greg Clayton1dae6f32012-04-25 18:40:20 +000039 heap_code_directory = os.path.dirname(__file__) + '/heap'
40 make_command = '(cd "%s" ; make EXE="%s" ARCH=%s)' % (heap_code_directory, libheap_dylib_path, string.split(triple, '-')[0])
41 #print make_command
42 make_output = commands.getoutput(make_command)
43 #print make_output
Greg Clayton9098fee2012-04-21 00:11:26 +000044 if os.path.exists(libheap_dylib_path):
45 libheap_dylib_spec = lldb.SBFileSpec(libheap_dylib_path)
46 if lldb.target.FindModule(libheap_dylib_spec):
47 return None # success, 'libheap.dylib' already loaded
48 if lldb.process:
49 state = lldb.process.state
50 if state == lldb.eStateStopped:
51 (libheap_dylib_path)
52 error = lldb.SBError()
53 image_idx = lldb.process.LoadImage(libheap_dylib_spec, error)
54 if error.Success():
55 return None
56 else:
57 if error:
58 return 'error: %s' % error
59 else:
60 return 'error: "process load \'%s\'" failed' % libheap_dylib_spec
61 else:
62 return 'error: process is not stopped'
63 else:
64 return 'error: invalid process'
65 else:
66 return 'error: file does not exist "%s"' % libheap_dylib_path
67 else:
68 return 'error: invalid target'
69
70 debugger.HandleCommand('process load "%s"' % libheap_dylib_path)
71
Greg Clayton93e5ba52012-04-13 16:24:09 +000072def add_common_options(parser):
73 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
74 parser.add_option('-o', '--po', action='store_true', dest='print_object_description', help='print the object descriptions for any matches', default=False)
75 parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False)
76 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 +000077 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)
78 #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 Clayton93e5ba52012-04-13 16:24:09 +000079
Greg Clayton96666442012-04-11 18:30:53 +000080def heap_search(options, arg_str):
Greg Clayton9098fee2012-04-21 00:11:26 +000081 dylid_load_err = load_dylib()
82 if dylid_load_err:
83 print dylid_load_err
84 return
Greg Clayton96666442012-04-11 18:30:53 +000085 expr = None
Greg Claytonbff78412012-04-12 18:57:36 +000086 arg_str_description = arg_str
Greg Clayton93e5ba52012-04-13 16:24:09 +000087 default_memory_format = "Y" # 'Y' is "bytes with ASCII" format
88 #memory_chunk_size = 1
Greg Clayton96666442012-04-11 18:30:53 +000089 if options.type == 'pointer':
Greg Clayton1dae6f32012-04-25 18:40:20 +000090 expr = 'find_pointer_in_heap((void *)%s)' % (arg_str)
Greg Claytonbff78412012-04-12 18:57:36 +000091 arg_str_description = 'malloc block containing pointer %s' % arg_str
Greg Clayton93e5ba52012-04-13 16:24:09 +000092 default_memory_format = "A" # 'A' is "address" format
93 #memory_chunk_size = lldb.process.GetAddressByteSize()
Greg Clayton96666442012-04-11 18:30:53 +000094 elif options.type == 'cstr':
95 expr = 'find_cstring_in_heap("%s")' % arg_str
Greg Claytonbff78412012-04-12 18:57:36 +000096 arg_str_description = 'malloc block containing "%s"' % arg_str
97 elif options.type == 'addr':
Greg Claytonf5902cb2012-04-12 21:06:22 +000098 expr = 'find_block_for_address((void *)%s)' % arg_str
Greg Claytonbff78412012-04-12 18:57:36 +000099 arg_str_description = 'malloc block for %s' % arg_str
Greg Clayton96666442012-04-11 18:30:53 +0000100 else:
101 print 'error: invalid type "%s"\nvalid values are "pointer", "cstr"' % options.type
102 return
103
104 expr_sbvalue = lldb.frame.EvaluateExpression (expr)
105 if expr_sbvalue.error.Success():
106 if expr_sbvalue.unsigned:
107 match_value = lldb.value(expr_sbvalue)
108 i = 0
109 while 1:
110 match_entry = match_value[i]; i += 1
111 malloc_addr = match_entry.addr.sbvalue.unsigned
112 if malloc_addr == 0:
113 break
114 malloc_size = int(match_entry.size)
115 offset = int(match_entry.offset)
116 dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
117 # If the type is still 'void *' then we weren't able to figure
118 # out a dynamic type for the malloc_addr
119 type_name = dynamic_value.type.name
Greg Claytonbff78412012-04-12 18:57:36 +0000120 description = '[%u] %s: addr = 0x%x' % (i, arg_str_description, malloc_addr)
121 if offset != 0:
122 description += ' + %u' % (offset)
123 description += ', size = %u' % (malloc_size)
Greg Clayton96666442012-04-11 18:30:53 +0000124 if type_name == 'void *':
125 if options.type == 'pointer' and malloc_size == 4096:
126 error = lldb.SBError()
127 data = bytearray(lldb.process.ReadMemory(malloc_addr, 16, error))
128 if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
Greg Claytonbff78412012-04-12 18:57:36 +0000129 description += ', type = (AUTORELEASE!)'
Greg Clayton93e5ba52012-04-13 16:24:09 +0000130 print description
Greg Claytonbff78412012-04-12 18:57:36 +0000131 else:
132 description += ', type = %s' % (type_name)
133 derefed_dynamic_value = dynamic_value.deref
134 ivar_member = None
135 if derefed_dynamic_value:
136 derefed_dynamic_type = derefed_dynamic_value.type
137 member = derefed_dynamic_type.GetFieldAtIndex(0)
138 search_bases = False
139 if member:
140 if member.GetOffsetInBytes() <= offset:
141 for field_idx in range (derefed_dynamic_type.GetNumberOfFields()):
142 member = derefed_dynamic_type.GetFieldAtIndex(field_idx)
143 member_byte_offset = member.GetOffsetInBytes()
144 if member_byte_offset == offset:
145 ivar_member = member
146 break
147 else:
148 search_bases = True
Greg Clayton96666442012-04-11 18:30:53 +0000149 else:
150 search_bases = True
Greg Clayton96666442012-04-11 18:30:53 +0000151
Greg Claytonbff78412012-04-12 18:57:36 +0000152 if not ivar_member and search_bases:
153 for field_idx in range (derefed_dynamic_type.GetNumberOfDirectBaseClasses()):
154 member = derefed_dynamic_type.GetDirectBaseClassAtIndex(field_idx)
Greg Clayton96666442012-04-11 18:30:53 +0000155 member_byte_offset = member.GetOffsetInBytes()
156 if member_byte_offset == offset:
157 ivar_member = member
158 break
Greg Claytonbff78412012-04-12 18:57:36 +0000159 if not ivar_member:
160 for field_idx in range (derefed_dynamic_type.GetNumberOfVirtualBaseClasses()):
161 member = derefed_dynamic_type.GetVirtualBaseClassAtIndex(field_idx)
162 member_byte_offset = member.GetOffsetInBytes()
163 if member_byte_offset == offset:
164 ivar_member = member
165 break
Greg Clayton96666442012-04-11 18:30:53 +0000166 if ivar_member:
Greg Claytonbff78412012-04-12 18:57:36 +0000167 description +=', ivar = %s' % (ivar_member.name)
168
169 print description
170 if derefed_dynamic_value:
171 print derefed_dynamic_value
172 if options.print_object_description:
173 desc = dynamic_value.GetObjectDescription()
174 if desc:
175 print ' (%s) 0x%x %s\n' % (type_name, malloc_addr, desc)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000176 if options.memory:
177 memory_format = options.format
178 if not memory_format:
179 memory_format = default_memory_format
180 cmd_result = lldb.SBCommandReturnObject()
181 #count = malloc_size / memory_chunk_size
182 memory_command = "memory read -f %s 0x%x 0x%x" % (memory_format, malloc_addr, malloc_addr + malloc_size)
183 lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
184 print cmd_result.GetOutput()
Greg Clayton9098fee2012-04-21 00:11:26 +0000185 if options.stack:
Greg Clayton6f2f0ab2012-04-25 01:49:50 +0000186 symbolicator = lldb.utils.symbolication.Symbolicator()
Greg Clayton9098fee2012-04-21 00:11:26 +0000187 symbolicator.target = lldb.target
188 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)
189 #print expr_str
190 expr = lldb.frame.EvaluateExpression (expr_str);
191 expr_error = expr.GetError()
192 if expr_error.Success():
193 err = expr.unsigned
194 if err:
195 print 'error: __mach_stack_logging_get_frames() returned error %i' % (err)
196 else:
197 count_expr = lldb.frame.EvaluateExpression ("g_stack_frames_count")
198 count = count_expr.unsigned
199 #print 'g_stack_frames_count is %u' % (count)
200 if count > 0:
201 frame_idx = 0
202 frames_expr = lldb.value(lldb.frame.EvaluateExpression ("g_stack_frames"))
203 done = False
204 for stack_frame_idx in range(count):
205 if not done:
206 frame_load_addr = int(frames_expr[stack_frame_idx])
207 if frame_load_addr >= 0x1000:
208 frames = symbolicator.symbolicate(frame_load_addr)
209 if frames:
210 for frame in frames:
211 print '[%3u] %s' % (frame_idx, frame)
212 frame_idx += 1
213 else:
214 print '[%3u] 0x%x' % (frame_idx, frame_load_addr)
215 frame_idx += 1
216 else:
217 done = True
218 else:
219 print 'error: %s' % (expr_error)
Greg Clayton96666442012-04-11 18:30:53 +0000220 else:
221 print '%s %s was not found in any malloc blocks' % (options.type, arg_str)
222 else:
Greg Claytonbff78412012-04-12 18:57:36 +0000223 print expr_sbvalue.error
224 print
Greg Clayton96666442012-04-11 18:30:53 +0000225
Greg Claytonbff78412012-04-12 18:57:36 +0000226def ptr_refs(debugger, command, result, dict):
Greg Claytone93e24f2012-04-11 16:27:06 +0000227 command_args = shlex.split(command)
Greg Claytonbff78412012-04-12 18:57:36 +0000228 usage = "usage: %prog [options] <PTR> [PTR ...]"
Greg Clayton96666442012-04-11 18:30:53 +0000229 description='''Searches the heap for pointer references on darwin user space programs.
230
231 Any matches that were found will dump the malloc blocks that contain the pointers
232 and might be able to print what kind of objects the pointers are contained in using
Greg Claytonbff78412012-04-12 18:57:36 +0000233 dynamic type information in the program.'''
234 parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000235 add_common_options(parser)
Greg Claytone93e24f2012-04-11 16:27:06 +0000236 try:
237 (options, args) = parser.parse_args(command_args)
238 except:
239 return
Greg Clayton96666442012-04-11 18:30:53 +0000240
241 options.type = 'pointer'
Greg Claytone93e24f2012-04-11 16:27:06 +0000242
243 if args:
244
245 for data in args:
Greg Clayton96666442012-04-11 18:30:53 +0000246 heap_search (options, data)
Greg Claytone93e24f2012-04-11 16:27:06 +0000247 else:
Greg Clayton96666442012-04-11 18:30:53 +0000248 print 'error: no pointer arguments were given'
Greg Claytone93e24f2012-04-11 16:27:06 +0000249
Greg Claytonbff78412012-04-12 18:57:36 +0000250def cstr_refs(debugger, command, result, dict):
Greg Clayton96666442012-04-11 18:30:53 +0000251 command_args = shlex.split(command)
Greg Claytonbff78412012-04-12 18:57:36 +0000252 usage = "usage: %prog [options] <CSTR> [CSTR ...]"
Greg Clayton96666442012-04-11 18:30:53 +0000253 description='''Searches the heap for C string references on darwin user space programs.
254
255 Any matches that were found will dump the malloc blocks that contain the C strings
256 and might be able to print what kind of objects the pointers are contained in using
Greg Claytonbff78412012-04-12 18:57:36 +0000257 dynamic type information in the program.'''
258 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000259 add_common_options(parser)
Greg Clayton96666442012-04-11 18:30:53 +0000260 try:
261 (options, args) = parser.parse_args(command_args)
262 except:
263 return
264
265 options.type = 'cstr'
266
267 if args:
268
269 for data in args:
270 heap_search (options, data)
271 else:
272 print 'error: no c string arguments were given to search for'
Greg Claytone93e24f2012-04-11 16:27:06 +0000273
Greg Claytonbff78412012-04-12 18:57:36 +0000274def malloc_info(debugger, command, result, dict):
275 command_args = shlex.split(command)
276 usage = "usage: %prog [options] <ADDR> [ADDR ...]"
277 description='''Searches the heap a malloc block that contains the addresses specified as arguments.
278
279 Any matches that were found will dump the malloc blocks that match or contain
280 the specified address. The matching blocks might be able to show what kind
281 of objects they are using dynamic type information in the program.'''
282 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000283 add_common_options(parser)
Greg Claytonbff78412012-04-12 18:57:36 +0000284 try:
285 (options, args) = parser.parse_args(command_args)
286 except:
287 return
288
289 options.type = 'addr'
290
291 if args:
292
293 for data in args:
294 heap_search (options, data)
295 else:
296 print 'error: no c string arguments were given to search for'
297
Greg Clayton1dae6f32012-04-25 18:40:20 +0000298if __name__ == '__main__':
299 lldb.debugger = lldb.SBDebugger.Create()
300
301# This initializer is being run from LLDB in the embedded command interpreter
302# Add any commands contained in this module to LLDB
303lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.ptr_refs ptr_refs')
304lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.cstr_refs cstr_refs')
305lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.malloc_info malloc_info')
306print '"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 +0000307
308
309
310