3 #----------------------------------------------------------------------
4 # Be sure to add the python path that points to the LLDB shared library.
6 # # To use this in the embedded python interpreter using "lldb" just
7 # import it with the full path using the "command script import"
9 # (lldb) command script import /path/to/cmdtemplate.py
10 #----------------------------------------------------------------------
20 # Just try for LLDB in case PYTHONPATH is already correctly setup
23 lldb_python_dirs = list()
24 # lldb is not in the PYTHONPATH, try some defaults for the current platform
25 platform_system = platform.system()
26 if platform_system == 'Darwin':
27 # On Darwin, try the currently selected Xcode directory
28 xcode_dir = commands.getoutput("xcode-select --print-path")
30 lldb_python_dirs.append(
33 '/../SharedFrameworks/LLDB.framework/Resources/Python'))
34 lldb_python_dirs.append(
35 xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
36 lldb_python_dirs.append(
37 '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
39 for lldb_python_dir in lldb_python_dirs:
40 if os.path.exists(lldb_python_dir):
41 if not (sys.path.__contains__(lldb_python_dir)):
42 sys.path.append(lldb_python_dir)
48 print 'imported lldb from: "%s"' % (lldb_python_dir)
52 print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"
61 def regex_option_callback(option, opt_str, value, parser):
62 if opt_str == "--std":
64 regex = re.compile(value)
65 parser.values.skip_type_regexes.append(regex)
68 def create_types_options(for_lldb_command):
70 usage = "usage: %prog [options]"
71 description = '''This command will help check for padding in between
72 base classes and members in structs and classes. It will summarize the types
73 and how much padding was found. If no types are specified with the --types TYPENAME
74 option, all structure and class types will be verified. If no modules are
75 specified with the --module option, only the target's main executable will be
79 usage = "usage: %prog [options] EXEPATH [EXEPATH ...]"
80 description = '''This command will help check for padding in between
81 base classes and members in structures and classes. It will summarize the types
82 and how much padding was found. One or more paths to executable files must be
83 specified and targets will be created with these modules. If no types are
84 specified with the --types TYPENAME option, all structure and class types will
85 be verified in all specified modules.
87 parser = optparse.OptionParser(
88 description=description,
91 if not for_lldb_command:
97 help='The architecture to use when creating the debug target.',
105 help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".')
113 help='Specify one or more modules which will be used to verify the types.',
120 help='Pause 10 seconds to wait for a debugger to attach.',
129 help='Specify one or more type names which should be verified. If no type names are specified, all class and struct types will be verified.',
136 help='Enable verbose logging and information.',
142 callback=regex_option_callback,
145 dest='skip_type_regexes',
146 help='Regular expressions that, if they match the current member typename, will cause the type to no be recursively displayed.',
151 callback=regex_option_callback,
153 dest='skip_type_regexes',
154 help="Don't' recurse into types in the std namespace.",
159 def verify_type(target, options, type):
161 typename = type.GetName()
162 # print 'type: %s' % (typename)
163 (end_offset, padding) = verify_type_recursive(
164 target, options, type, None, 0, 0, 0)
165 byte_size = type.GetByteSize()
166 # if end_offset < byte_size:
167 # last_member_padding = byte_size - end_offset
168 # print '%+4u <%u> padding' % (end_offset, last_member_padding)
169 # padding += last_member_padding
170 print 'Total byte size: %u' % (byte_size)
171 print 'Total pad bytes: %u' % (padding)
173 print 'Padding percentage: %2.2f %%' % ((float(padding) / float(byte_size)) * 100.0)
177 def verify_type_recursive(
185 prev_end_offset = base_offset
186 typename = type.GetName()
187 byte_size = type.GetByteSize()
188 if member_name and member_name != typename:
189 print '%+4u <%3u> %s%s %s;' % (base_offset, byte_size, ' ' * depth, typename, member_name)
191 print '%+4u {%3u} %s%s' % (base_offset, byte_size, ' ' * depth, typename)
193 for type_regex in options.skip_type_regexes:
194 match = type_regex.match(typename)
196 return (base_offset + byte_size, padding)
198 members = type.members
200 for member_idx, member in enumerate(members):
201 member_type = member.GetType()
202 member_canonical_type = member_type.GetCanonicalType()
203 member_type_class = member_canonical_type.GetTypeClass()
204 member_name = member.GetName()
205 member_offset = member.GetOffsetInBytes()
206 member_total_offset = member_offset + base_offset
207 member_byte_size = member_type.GetByteSize()
208 member_is_class_or_struct = False
209 if member_type_class == lldb.eTypeClassStruct or member_type_class == lldb.eTypeClassClass:
210 member_is_class_or_struct = True
211 if member_idx == 0 and member_offset == target.GetAddressByteSize(
212 ) and type.IsPolymorphicClass():
213 ptr_size = target.GetAddressByteSize()
214 print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1))
215 prev_end_offset = ptr_size
217 if prev_end_offset < member_total_offset:
218 member_padding = member_total_offset - prev_end_offset
219 padding = padding + member_padding
220 print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, member_padding, ' ' * (depth + 1))
222 if member_is_class_or_struct:
224 padding) = verify_type_recursive(target,
226 member_canonical_type,
232 prev_end_offset = member_total_offset + member_byte_size
233 member_typename = member_type.GetName()
234 if member.IsBitfield():
235 print '%+4u <%3u> %s%s:%u %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member.GetBitfieldSizeInBits(), member_name)
237 print '%+4u <%3u> %s%s %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member_name)
239 if prev_end_offset < byte_size:
240 last_member_padding = byte_size - prev_end_offset
241 print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, last_member_padding, ' ' * (depth + 1))
242 padding += last_member_padding
244 if type.IsPolymorphicClass():
245 ptr_size = target.GetAddressByteSize()
246 print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1))
247 prev_end_offset = ptr_size
248 prev_end_offset = base_offset + byte_size
250 return (prev_end_offset, padding)
253 def check_padding_command(debugger, command, result, dict):
254 # Use the Shell Lexer to properly parse up command options just like a
256 command_args = shlex.split(command)
257 parser = create_types_options(True)
259 (options, args) = parser.parse_args(command_args)
261 # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit
262 # (courtesy of OptParse dealing with argument errors by throwing SystemExit)
263 result.SetStatus(lldb.eReturnStatusFailed)
264 # returning a string is the same as returning an error whose
265 # description is the string
266 return "option parsing failed"
267 verify_types(debugger.GetSelectedTarget(), options)
270 @lldb.command("parse_all_struct_class_types")
271 def parse_all_struct_class_types(debugger, command, result, dict):
272 command_args = shlex.split(command)
273 for f in command_args:
274 error = lldb.SBError()
275 target = debugger.CreateTarget(f, None, None, False, error)
276 module = target.GetModuleAtIndex(0)
277 print "Parsing all types in '%s'" % (module)
278 types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct)
284 def verify_types(target, options):
287 print 'error: invalid target'
291 if len(options.modules) == 0:
292 # Append just the main executable if nothing was specified
293 module = target.modules[0]
295 modules.append(module)
297 for module_name in options.modules:
298 module = lldb.target.module[module_name]
300 modules.append(module)
303 for module in modules:
304 print 'module: %s' % (module.file)
305 if options.typenames:
306 for typename in options.typenames:
307 types = module.FindTypes(typename)
309 print 'Found %u types matching "%s" in "%s"' % (len(types), typename, module.file)
311 verify_type(target, options, type)
313 print 'error: no type matches "%s" in "%s"' % (typename, module.file)
315 types = module.GetTypes(
316 lldb.eTypeClassClass | lldb.eTypeClassStruct)
317 print 'Found %u types in "%s"' % (len(types), module.file)
319 verify_type(target, options, type)
321 print 'error: no modules'
323 if __name__ == '__main__':
324 debugger = lldb.SBDebugger.Create()
325 parser = create_types_options(False)
328 (options, args) = parser.parse_args(sys.argv[1:])
330 # print "error: option parsing failed"
334 print "Waiting for debugger to attach to process %d" % os.getpid()
335 os.kill(os.getpid(), signal.SIGSTOP)
338 # in a command - the lldb.* convenience variables are not to be used
339 # and their values (if any) are undefined
340 # this is the best practice to access those objects from within a
342 error = lldb.SBError()
343 target = debugger.CreateTarget(path,
349 print error.GetCString()
351 verify_types(target, options)
353 elif getattr(lldb, 'debugger', None):
354 lldb.debugger.HandleCommand(
355 'command script add -f types.check_padding_command check_padding')
356 print '"check_padding" command installed, use the "--help" option for detailed help'