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
= subprocess
.check_output("xcode-select --print-path", shell
=True)
30 lldb_python_dirs
.append(
32 xcode_dir
+ "/../SharedFrameworks/LLDB.framework/Resources/Python"
35 lldb_python_dirs
.append(
36 xcode_dir
+ "/Library/PrivateFrameworks/LLDB.framework/Resources/Python"
38 lldb_python_dirs
.append(
39 "/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python"
42 for lldb_python_dir
in lldb_python_dirs
:
43 if os
.path
.exists(lldb_python_dir
):
44 if not (sys
.path
.__contains
__(lldb_python_dir
)):
45 sys
.path
.append(lldb_python_dir
)
51 print('imported lldb from: "%s"' % (lldb_python_dir
))
56 "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"
65 def regex_option_callback(option
, opt_str
, value
, parser
):
66 if opt_str
== "--std":
68 regex
= re
.compile(value
)
69 parser
.values
.skip_type_regexes
.append(regex
)
72 def create_types_options(for_lldb_command
):
74 usage
= "usage: %prog [options]"
75 description
= """This command will help check for padding in between
76 base classes and members in structs and classes. It will summarize the types
77 and how much padding was found. If no types are specified with the --types TYPENAME
78 option, all structure and class types will be verified. If no modules are
79 specified with the --module option, only the target's main executable will be
83 usage
= "usage: %prog [options] EXEPATH [EXEPATH ...]"
84 description
= """This command will help check for padding in between
85 base classes and members in structures and classes. It will summarize the types
86 and how much padding was found. One or more paths to executable files must be
87 specified and targets will be created with these modules. If no types are
88 specified with the --types TYPENAME option, all structure and class types will
89 be verified in all specified modules.
91 parser
= optparse
.OptionParser(
92 description
=description
, prog
="framestats", usage
=usage
94 if not for_lldb_command
:
100 help="The architecture to use when creating the debug target.",
109 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".',
118 help="Specify one or more modules which will be used to verify the types.",
126 help="Pause 10 seconds to wait for a debugger to attach.",
136 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.",
144 help="Enable verbose logging and information.",
151 callback
=regex_option_callback
,
154 dest
="skip_type_regexes",
155 help="Regular expressions that, if they match the current member typename, will cause the type to no be recursively displayed.",
161 callback
=regex_option_callback
,
163 dest
="skip_type_regexes",
164 help="Don't' recurse into types in the std namespace.",
170 def verify_type(target
, options
, type):
172 typename
= type.GetName()
173 # print 'type: %s' % (typename)
174 (end_offset
, padding
) = verify_type_recursive(target
, options
, type, None, 0, 0, 0)
175 byte_size
= type.GetByteSize()
176 # if end_offset < byte_size:
177 # last_member_padding = byte_size - end_offset
178 # print '%+4u <%u> padding' % (end_offset, last_member_padding)
179 # padding += last_member_padding
180 print("Total byte size: %u" % (byte_size
))
181 print("Total pad bytes: %u" % (padding
))
184 "Padding percentage: %2.2f %%"
185 % ((float(padding
) / float(byte_size
)) * 100.0)
190 def verify_type_recursive(
191 target
, options
, type, member_name
, depth
, base_offset
, padding
193 prev_end_offset
= base_offset
194 typename
= type.GetName()
195 byte_size
= type.GetByteSize()
196 if member_name
and member_name
!= typename
:
198 "%+4u <%3u> %s%s %s;"
199 % (base_offset
, byte_size
, " " * depth
, typename
, member_name
)
202 print("%+4u {%3u} %s%s" % (base_offset
, byte_size
, " " * depth
, typename
))
204 for type_regex
in options
.skip_type_regexes
:
205 match
= type_regex
.match(typename
)
207 return (base_offset
+ byte_size
, padding
)
209 members
= type.members
211 for member_idx
, member
in enumerate(members
):
212 member_type
= member
.GetType()
213 member_canonical_type
= member_type
.GetCanonicalType()
214 member_type_class
= member_canonical_type
.GetTypeClass()
215 member_name
= member
.GetName()
216 member_offset
= member
.GetOffsetInBytes()
217 member_total_offset
= member_offset
+ base_offset
218 member_byte_size
= member_type
.GetByteSize()
219 member_is_class_or_struct
= False
221 member_type_class
== lldb
.eTypeClassStruct
222 or member_type_class
== lldb
.eTypeClassClass
224 member_is_class_or_struct
= True
227 and member_offset
== target
.GetAddressByteSize()
228 and type.IsPolymorphicClass()
230 ptr_size
= target
.GetAddressByteSize()
232 "%+4u <%3u> %s__vtbl_ptr_type * _vptr;"
233 % (prev_end_offset
, ptr_size
, " " * (depth
+ 1))
235 prev_end_offset
= ptr_size
237 if prev_end_offset
< member_total_offset
:
238 member_padding
= member_total_offset
- prev_end_offset
239 padding
= padding
+ member_padding
241 "%+4u <%3u> %s<PADDING>"
242 % (prev_end_offset
, member_padding
, " " * (depth
+ 1))
245 if member_is_class_or_struct
:
246 (prev_end_offset
, padding
) = verify_type_recursive(
249 member_canonical_type
,
256 prev_end_offset
= member_total_offset
+ member_byte_size
257 member_typename
= member_type
.GetName()
258 if member
.IsBitfield():
260 "%+4u <%3u> %s%s:%u %s;"
266 member
.GetBitfieldSizeInBits(),
272 "%+4u <%3u> %s%s %s;"
282 if prev_end_offset
< byte_size
:
283 last_member_padding
= byte_size
- prev_end_offset
285 "%+4u <%3u> %s<PADDING>"
286 % (prev_end_offset
, last_member_padding
, " " * (depth
+ 1))
288 padding
+= last_member_padding
290 if type.IsPolymorphicClass():
291 ptr_size
= target
.GetAddressByteSize()
293 "%+4u <%3u> %s__vtbl_ptr_type * _vptr;"
294 % (prev_end_offset
, ptr_size
, " " * (depth
+ 1))
296 prev_end_offset
= ptr_size
297 prev_end_offset
= base_offset
+ byte_size
299 return (prev_end_offset
, padding
)
302 def check_padding_command(debugger
, command
, result
, dict):
303 # Use the Shell Lexer to properly parse up command options just like a
305 command_args
= shlex
.split(command
)
306 parser
= create_types_options(True)
308 (options
, args
) = parser
.parse_args(command_args
)
310 # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit
311 # (courtesy of OptParse dealing with argument errors by throwing SystemExit)
312 result
.SetStatus(lldb
.eReturnStatusFailed
)
313 # returning a string is the same as returning an error whose
314 # description is the string
315 return "option parsing failed"
316 verify_types(debugger
.GetSelectedTarget(), options
)
319 @lldb.command("parse_all_struct_class_types")
320 def parse_all_struct_class_types(debugger
, command
, result
, dict):
321 command_args
= shlex
.split(command
)
322 for f
in command_args
:
323 error
= lldb
.SBError()
324 target
= debugger
.CreateTarget(f
, None, None, False, error
)
325 module
= target
.GetModuleAtIndex(0)
326 print("Parsing all types in '%s'" % (module
))
327 types
= module
.GetTypes(lldb
.eTypeClassClass | lldb
.eTypeClassStruct
)
333 def verify_types(target
, options
):
335 print("error: invalid target")
339 if len(options
.modules
) == 0:
340 # Append just the main executable if nothing was specified
341 module
= target
.modules
[0]
343 modules
.append(module
)
345 for module_name
in options
.modules
:
346 module
= lldb
.target
.module
[module_name
]
348 modules
.append(module
)
351 for module
in modules
:
352 print("module: %s" % (module
.file))
353 if options
.typenames
:
354 for typename
in options
.typenames
:
355 types
= module
.FindTypes(typename
)
358 'Found %u types matching "%s" in "%s"'
359 % (len(types
), typename
, module
.file)
362 verify_type(target
, options
, type)
365 'error: no type matches "%s" in "%s"'
366 % (typename
, module
.file)
369 types
= module
.GetTypes(lldb
.eTypeClassClass | lldb
.eTypeClassStruct
)
370 print('Found %u types in "%s"' % (len(types
), module
.file))
372 verify_type(target
, options
, type)
374 print("error: no modules")
377 if __name__
== "__main__":
378 debugger
= lldb
.SBDebugger
.Create()
379 parser
= create_types_options(False)
382 (options
, args
) = parser
.parse_args(sys
.argv
[1:])
384 # print "error: option parsing failed"
388 print("Waiting for debugger to attach to process %d" % os
.getpid())
389 os
.kill(os
.getpid(), signal
.SIGSTOP
)
392 # in a command - the lldb.* convenience variables are not to be used
393 # and their values (if any) are undefined
394 # this is the best practice to access those objects from within a
396 error
= lldb
.SBError()
397 target
= debugger
.CreateTarget(
398 path
, options
.arch
, options
.platform
, True, error
401 print(error
.GetCString())
403 verify_types(target
, options
)
406 def __lldb_init_module(debugger
, internal_dict
):
407 debugger
.HandleCommand(
408 "command script add -o -f types.check_padding_command check_padding"
411 '"check_padding" command installed, use the "--help" option for detailed help'