blob: 812bf604ce8f02a1baa2755c3c11d0160fc8ce7f [file] [log] [blame]
Greg Claytone93e24f2012-04-11 16:27:06 +00001#!/usr/bin/python
2
3#----------------------------------------------------------------------
4# Be sure to add the python path that points to the LLDB shared library.
5#
6# # To use this in the embedded python interpreter using "lldb" just
7# import it with the full path using the "command script import"
8# command
9# (lldb) command script import /path/to/heap.py
10#
11# For the shells csh, tcsh:
12# ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./heap.py )
13#
14# For the shells sh, bash:
15# PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./heap.py
16#----------------------------------------------------------------------
17
18import lldb
19import commands
20import optparse
Greg Clayton96666442012-04-11 18:30:53 +000021import os
Greg Claytone93e24f2012-04-11 16:27:06 +000022import shlex
Greg Clayton6f2f0ab2012-04-25 01:49:50 +000023import lldb.utils.symbolication
Greg Claytone93e24f2012-04-11 16:27:06 +000024
Greg Clayton9098fee2012-04-21 00:11:26 +000025def load_dylib():
26 if lldb.target:
27 python_module_directory = os.path.dirname(__file__)
Greg Clayton6f2f0ab2012-04-25 01:49:50 +000028 heap_code_directory = python_module_directory + '/heap'
29 libheap_dylib_path = heap_code_directory + '/heap/libheap.dylib'
Greg Clayton9098fee2012-04-21 00:11:26 +000030 if not os.path.exists(libheap_dylib_path):
Greg Clayton6f2f0ab2012-04-25 01:49:50 +000031 make_command = '(cd "%s" ; make)' % heap_code_directory
Greg Clayton9098fee2012-04-21 00:11:26 +000032 print make_command
33 print commands.getoutput(make_command)
34 if os.path.exists(libheap_dylib_path):
35 libheap_dylib_spec = lldb.SBFileSpec(libheap_dylib_path)
36 if lldb.target.FindModule(libheap_dylib_spec):
37 return None # success, 'libheap.dylib' already loaded
38 if lldb.process:
39 state = lldb.process.state
40 if state == lldb.eStateStopped:
41 (libheap_dylib_path)
42 error = lldb.SBError()
43 image_idx = lldb.process.LoadImage(libheap_dylib_spec, error)
44 if error.Success():
45 return None
46 else:
47 if error:
48 return 'error: %s' % error
49 else:
50 return 'error: "process load \'%s\'" failed' % libheap_dylib_spec
51 else:
52 return 'error: process is not stopped'
53 else:
54 return 'error: invalid process'
55 else:
56 return 'error: file does not exist "%s"' % libheap_dylib_path
57 else:
58 return 'error: invalid target'
59
60 debugger.HandleCommand('process load "%s"' % libheap_dylib_path)
61
Greg Clayton93e5ba52012-04-13 16:24:09 +000062def add_common_options(parser):
63 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
64 parser.add_option('-o', '--po', action='store_true', dest='print_object_description', help='print the object descriptions for any matches', default=False)
65 parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False)
66 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 +000067 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)
68 #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 +000069
Greg Clayton96666442012-04-11 18:30:53 +000070def heap_search(options, arg_str):
Greg Clayton9098fee2012-04-21 00:11:26 +000071 dylid_load_err = load_dylib()
72 if dylid_load_err:
73 print dylid_load_err
74 return
Greg Clayton96666442012-04-11 18:30:53 +000075 expr = None
Greg Claytonbff78412012-04-12 18:57:36 +000076 arg_str_description = arg_str
Greg Clayton93e5ba52012-04-13 16:24:09 +000077 default_memory_format = "Y" # 'Y' is "bytes with ASCII" format
78 #memory_chunk_size = 1
Greg Clayton96666442012-04-11 18:30:53 +000079 if options.type == 'pointer':
Greg Claytonf5902cb2012-04-12 21:06:22 +000080 expr = 'find_pointer_in_heap((void *)%s)' % arg_str
Greg Claytonbff78412012-04-12 18:57:36 +000081 arg_str_description = 'malloc block containing pointer %s' % arg_str
Greg Clayton93e5ba52012-04-13 16:24:09 +000082 default_memory_format = "A" # 'A' is "address" format
83 #memory_chunk_size = lldb.process.GetAddressByteSize()
Greg Clayton96666442012-04-11 18:30:53 +000084 elif options.type == 'cstr':
85 expr = 'find_cstring_in_heap("%s")' % arg_str
Greg Claytonbff78412012-04-12 18:57:36 +000086 arg_str_description = 'malloc block containing "%s"' % arg_str
87 elif options.type == 'addr':
Greg Claytonf5902cb2012-04-12 21:06:22 +000088 expr = 'find_block_for_address((void *)%s)' % arg_str
Greg Claytonbff78412012-04-12 18:57:36 +000089 arg_str_description = 'malloc block for %s' % arg_str
Greg Clayton96666442012-04-11 18:30:53 +000090 else:
91 print 'error: invalid type "%s"\nvalid values are "pointer", "cstr"' % options.type
92 return
93
94 expr_sbvalue = lldb.frame.EvaluateExpression (expr)
95 if expr_sbvalue.error.Success():
96 if expr_sbvalue.unsigned:
97 match_value = lldb.value(expr_sbvalue)
98 i = 0
99 while 1:
100 match_entry = match_value[i]; i += 1
101 malloc_addr = match_entry.addr.sbvalue.unsigned
102 if malloc_addr == 0:
103 break
104 malloc_size = int(match_entry.size)
105 offset = int(match_entry.offset)
106 dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
107 # If the type is still 'void *' then we weren't able to figure
108 # out a dynamic type for the malloc_addr
109 type_name = dynamic_value.type.name
Greg Claytonbff78412012-04-12 18:57:36 +0000110 description = '[%u] %s: addr = 0x%x' % (i, arg_str_description, malloc_addr)
111 if offset != 0:
112 description += ' + %u' % (offset)
113 description += ', size = %u' % (malloc_size)
Greg Clayton96666442012-04-11 18:30:53 +0000114 if type_name == 'void *':
115 if options.type == 'pointer' and malloc_size == 4096:
116 error = lldb.SBError()
117 data = bytearray(lldb.process.ReadMemory(malloc_addr, 16, error))
118 if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
Greg Claytonbff78412012-04-12 18:57:36 +0000119 description += ', type = (AUTORELEASE!)'
Greg Clayton93e5ba52012-04-13 16:24:09 +0000120 print description
Greg Claytonbff78412012-04-12 18:57:36 +0000121 else:
122 description += ', type = %s' % (type_name)
123 derefed_dynamic_value = dynamic_value.deref
124 ivar_member = None
125 if derefed_dynamic_value:
126 derefed_dynamic_type = derefed_dynamic_value.type
127 member = derefed_dynamic_type.GetFieldAtIndex(0)
128 search_bases = False
129 if member:
130 if member.GetOffsetInBytes() <= offset:
131 for field_idx in range (derefed_dynamic_type.GetNumberOfFields()):
132 member = derefed_dynamic_type.GetFieldAtIndex(field_idx)
133 member_byte_offset = member.GetOffsetInBytes()
134 if member_byte_offset == offset:
135 ivar_member = member
136 break
137 else:
138 search_bases = True
Greg Clayton96666442012-04-11 18:30:53 +0000139 else:
140 search_bases = True
Greg Clayton96666442012-04-11 18:30:53 +0000141
Greg Claytonbff78412012-04-12 18:57:36 +0000142 if not ivar_member and search_bases:
143 for field_idx in range (derefed_dynamic_type.GetNumberOfDirectBaseClasses()):
144 member = derefed_dynamic_type.GetDirectBaseClassAtIndex(field_idx)
Greg Clayton96666442012-04-11 18:30:53 +0000145 member_byte_offset = member.GetOffsetInBytes()
146 if member_byte_offset == offset:
147 ivar_member = member
148 break
Greg Claytonbff78412012-04-12 18:57:36 +0000149 if not ivar_member:
150 for field_idx in range (derefed_dynamic_type.GetNumberOfVirtualBaseClasses()):
151 member = derefed_dynamic_type.GetVirtualBaseClassAtIndex(field_idx)
152 member_byte_offset = member.GetOffsetInBytes()
153 if member_byte_offset == offset:
154 ivar_member = member
155 break
Greg Clayton96666442012-04-11 18:30:53 +0000156 if ivar_member:
Greg Claytonbff78412012-04-12 18:57:36 +0000157 description +=', ivar = %s' % (ivar_member.name)
158
159 print description
160 if derefed_dynamic_value:
161 print derefed_dynamic_value
162 if options.print_object_description:
163 desc = dynamic_value.GetObjectDescription()
164 if desc:
165 print ' (%s) 0x%x %s\n' % (type_name, malloc_addr, desc)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000166 if options.memory:
167 memory_format = options.format
168 if not memory_format:
169 memory_format = default_memory_format
170 cmd_result = lldb.SBCommandReturnObject()
171 #count = malloc_size / memory_chunk_size
172 memory_command = "memory read -f %s 0x%x 0x%x" % (memory_format, malloc_addr, malloc_addr + malloc_size)
173 lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
174 print cmd_result.GetOutput()
Greg Clayton9098fee2012-04-21 00:11:26 +0000175 if options.stack:
Greg Clayton6f2f0ab2012-04-25 01:49:50 +0000176 symbolicator = lldb.utils.symbolication.Symbolicator()
Greg Clayton9098fee2012-04-21 00:11:26 +0000177 symbolicator.target = lldb.target
178 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)
179 #print expr_str
180 expr = lldb.frame.EvaluateExpression (expr_str);
181 expr_error = expr.GetError()
182 if expr_error.Success():
183 err = expr.unsigned
184 if err:
185 print 'error: __mach_stack_logging_get_frames() returned error %i' % (err)
186 else:
187 count_expr = lldb.frame.EvaluateExpression ("g_stack_frames_count")
188 count = count_expr.unsigned
189 #print 'g_stack_frames_count is %u' % (count)
190 if count > 0:
191 frame_idx = 0
192 frames_expr = lldb.value(lldb.frame.EvaluateExpression ("g_stack_frames"))
193 done = False
194 for stack_frame_idx in range(count):
195 if not done:
196 frame_load_addr = int(frames_expr[stack_frame_idx])
197 if frame_load_addr >= 0x1000:
198 frames = symbolicator.symbolicate(frame_load_addr)
199 if frames:
200 for frame in frames:
201 print '[%3u] %s' % (frame_idx, frame)
202 frame_idx += 1
203 else:
204 print '[%3u] 0x%x' % (frame_idx, frame_load_addr)
205 frame_idx += 1
206 else:
207 done = True
208 else:
209 print 'error: %s' % (expr_error)
Greg Clayton96666442012-04-11 18:30:53 +0000210 else:
211 print '%s %s was not found in any malloc blocks' % (options.type, arg_str)
212 else:
Greg Claytonbff78412012-04-12 18:57:36 +0000213 print expr_sbvalue.error
214 print
Greg Clayton96666442012-04-11 18:30:53 +0000215
Greg Claytonbff78412012-04-12 18:57:36 +0000216def ptr_refs(debugger, command, result, dict):
Greg Claytone93e24f2012-04-11 16:27:06 +0000217 command_args = shlex.split(command)
Greg Claytonbff78412012-04-12 18:57:36 +0000218 usage = "usage: %prog [options] <PTR> [PTR ...]"
Greg Clayton96666442012-04-11 18:30:53 +0000219 description='''Searches the heap for pointer references on darwin user space programs.
220
221 Any matches that were found will dump the malloc blocks that contain the pointers
222 and might be able to print what kind of objects the pointers are contained in using
Greg Claytonbff78412012-04-12 18:57:36 +0000223 dynamic type information in the program.'''
224 parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000225 add_common_options(parser)
Greg Claytone93e24f2012-04-11 16:27:06 +0000226 try:
227 (options, args) = parser.parse_args(command_args)
228 except:
229 return
Greg Clayton96666442012-04-11 18:30:53 +0000230
231 options.type = 'pointer'
Greg Claytone93e24f2012-04-11 16:27:06 +0000232
233 if args:
234
235 for data in args:
Greg Clayton96666442012-04-11 18:30:53 +0000236 heap_search (options, data)
Greg Claytone93e24f2012-04-11 16:27:06 +0000237 else:
Greg Clayton96666442012-04-11 18:30:53 +0000238 print 'error: no pointer arguments were given'
Greg Claytone93e24f2012-04-11 16:27:06 +0000239
Greg Claytonbff78412012-04-12 18:57:36 +0000240def cstr_refs(debugger, command, result, dict):
Greg Clayton96666442012-04-11 18:30:53 +0000241 command_args = shlex.split(command)
Greg Claytonbff78412012-04-12 18:57:36 +0000242 usage = "usage: %prog [options] <CSTR> [CSTR ...]"
Greg Clayton96666442012-04-11 18:30:53 +0000243 description='''Searches the heap for C string references on darwin user space programs.
244
245 Any matches that were found will dump the malloc blocks that contain the C strings
246 and might be able to print what kind of objects the pointers are contained in using
Greg Claytonbff78412012-04-12 18:57:36 +0000247 dynamic type information in the program.'''
248 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000249 add_common_options(parser)
Greg Clayton96666442012-04-11 18:30:53 +0000250 try:
251 (options, args) = parser.parse_args(command_args)
252 except:
253 return
254
255 options.type = 'cstr'
256
257 if args:
258
259 for data in args:
260 heap_search (options, data)
261 else:
262 print 'error: no c string arguments were given to search for'
Greg Claytone93e24f2012-04-11 16:27:06 +0000263
Greg Claytonbff78412012-04-12 18:57:36 +0000264def malloc_info(debugger, command, result, dict):
265 command_args = shlex.split(command)
266 usage = "usage: %prog [options] <ADDR> [ADDR ...]"
267 description='''Searches the heap a malloc block that contains the addresses specified as arguments.
268
269 Any matches that were found will dump the malloc blocks that match or contain
270 the specified address. The matching blocks might be able to show what kind
271 of objects they are using dynamic type information in the program.'''
272 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
Greg Clayton93e5ba52012-04-13 16:24:09 +0000273 add_common_options(parser)
Greg Claytonbff78412012-04-12 18:57:36 +0000274 try:
275 (options, args) = parser.parse_args(command_args)
276 except:
277 return
278
279 options.type = 'addr'
280
281 if args:
282
283 for data in args:
284 heap_search (options, data)
285 else:
286 print 'error: no c string arguments were given to search for'
287
Greg Claytone93e24f2012-04-11 16:27:06 +0000288def __lldb_init_module (debugger, dict):
289 # This initializer is being run from LLDB in the embedded command interpreter
290 # Add any commands contained in this module to LLDB
Greg Claytonbff78412012-04-12 18:57:36 +0000291 debugger.HandleCommand('command script add -f heap.ptr_refs ptr_refs')
292 debugger.HandleCommand('command script add -f heap.cstr_refs cstr_refs')
293 debugger.HandleCommand('command script add -f heap.malloc_info malloc_info')
294 print '"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 +0000295
296
297
298