]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - examples/python/types.py
Vendor import of lldb trunk r290819:
[FreeBSD/FreeBSD.git] / examples / python / types.py
1 #!/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/cmdtemplate.py
10 #----------------------------------------------------------------------
11
12 import commands
13 import platform
14 import os
15 import re
16 import signal
17 import sys
18
19 try:
20     # Just try for LLDB in case PYTHONPATH is already correctly setup
21     import lldb
22 except ImportError:
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")
29         if xcode_dir:
30             lldb_python_dirs.append(
31                 os.path.realpath(
32                     xcode_dir +
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')
38     success = False
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)
43                 try:
44                     import lldb
45                 except ImportError:
46                     pass
47                 else:
48                     print 'imported lldb from: "%s"' % (lldb_python_dir)
49                     success = True
50                     break
51     if not success:
52         print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"
53         sys.exit(1)
54
55 import commands
56 import optparse
57 import shlex
58 import time
59
60
61 def regex_option_callback(option, opt_str, value, parser):
62     if opt_str == "--std":
63         value = '^std::'
64     regex = re.compile(value)
65     parser.values.skip_type_regexes.append(regex)
66
67
68 def create_types_options(for_lldb_command):
69     if 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
76 searched.
77 '''
78     else:
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.
86 '''
87     parser = optparse.OptionParser(
88         description=description,
89         prog='framestats',
90         usage=usage)
91     if not for_lldb_command:
92         parser.add_option(
93             '-a',
94             '--arch',
95             type='string',
96             dest='arch',
97             help='The architecture to use when creating the debug target.',
98             default=None)
99         parser.add_option(
100             '-p',
101             '--platform',
102             type='string',
103             metavar='platform',
104             dest='platform',
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".')
106     parser.add_option(
107         '-m',
108         '--module',
109         action='append',
110         type='string',
111         metavar='MODULE',
112         dest='modules',
113         help='Specify one or more modules which will be used to verify the types.',
114         default=[])
115     parser.add_option(
116         '-d',
117         '--debug',
118         action='store_true',
119         dest='debug',
120         help='Pause 10 seconds to wait for a debugger to attach.',
121         default=False)
122     parser.add_option(
123         '-t',
124         '--type',
125         action='append',
126         type='string',
127         metavar='TYPENAME',
128         dest='typenames',
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.',
130         default=[])
131     parser.add_option(
132         '-v',
133         '--verbose',
134         action='store_true',
135         dest='verbose',
136         help='Enable verbose logging and information.',
137         default=False)
138     parser.add_option(
139         '-s',
140         '--skip-type-regex',
141         action="callback",
142         callback=regex_option_callback,
143         type='string',
144         metavar='REGEX',
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.',
147         default=[])
148     parser.add_option(
149         '--std',
150         action="callback",
151         callback=regex_option_callback,
152         metavar='REGEX',
153         dest='skip_type_regexes',
154         help="Don't' recurse into types in the std namespace.",
155         default=[])
156     return parser
157
158
159 def verify_type(target, options, type):
160     print 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)
172     if padding > 0:
173         print 'Padding percentage: %2.2f %%' % ((float(padding) / float(byte_size)) * 100.0)
174     print
175
176
177 def verify_type_recursive(
178         target,
179         options,
180         type,
181         member_name,
182         depth,
183         base_offset,
184         padding):
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)
190     else:
191         print '%+4u {%3u} %s%s' % (base_offset, byte_size, '    ' * depth, typename)
192
193     for type_regex in options.skip_type_regexes:
194         match = type_regex.match(typename)
195         if match:
196             return (base_offset + byte_size, padding)
197
198     members = type.members
199     if 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
216             else:
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))
221
222             if member_is_class_or_struct:
223                 (prev_end_offset,
224                  padding) = verify_type_recursive(target,
225                                                   options,
226                                                   member_canonical_type,
227                                                   member_name,
228                                                   depth + 1,
229                                                   member_total_offset,
230                                                   padding)
231             else:
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)
236                 else:
237                     print '%+4u <%3u> %s%s %s;' % (member_total_offset, member_byte_size, '    ' * (depth + 1), member_typename, member_name)
238
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
243     else:
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
249
250     return (prev_end_offset, padding)
251
252
253 def check_padding_command(debugger, command, result, dict):
254     # Use the Shell Lexer to properly parse up command options just like a
255     # shell would
256     command_args = shlex.split(command)
257     parser = create_types_options(True)
258     try:
259         (options, args) = parser.parse_args(command_args)
260     except:
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)
268
269
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)
279         for t in types:
280             print t
281         print ""
282
283
284 def verify_types(target, options):
285
286     if not target:
287         print 'error: invalid target'
288         return
289
290     modules = list()
291     if len(options.modules) == 0:
292         # Append just the main executable if nothing was specified
293         module = target.modules[0]
294         if module:
295             modules.append(module)
296     else:
297         for module_name in options.modules:
298             module = lldb.target.module[module_name]
299             if module:
300                 modules.append(module)
301
302     if modules:
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)
308                     if types.GetSize():
309                         print 'Found %u types matching "%s" in "%s"' % (len(types), typename, module.file)
310                         for type in types:
311                             verify_type(target, options, type)
312                     else:
313                         print 'error: no type matches "%s" in "%s"' % (typename, module.file)
314             else:
315                 types = module.GetTypes(
316                     lldb.eTypeClassClass | lldb.eTypeClassStruct)
317                 print 'Found %u types in "%s"' % (len(types), module.file)
318                 for type in types:
319                     verify_type(target, options, type)
320     else:
321         print 'error: no modules'
322
323 if __name__ == '__main__':
324     debugger = lldb.SBDebugger.Create()
325     parser = create_types_options(False)
326
327     # try:
328     (options, args) = parser.parse_args(sys.argv[1:])
329     # except:
330     #     print "error: option parsing failed"
331     #     sys.exit(1)
332
333     if options.debug:
334         print "Waiting for debugger to attach to process %d" % os.getpid()
335         os.kill(os.getpid(), signal.SIGSTOP)
336
337     for path in args:
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
341         # command
342         error = lldb.SBError()
343         target = debugger.CreateTarget(path,
344                                        options.arch,
345                                        options.platform,
346                                        True,
347                                        error)
348         if error.Fail():
349             print error.GetCString()
350             continue
351         verify_types(target, options)
352
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'