1515bc8c1Sserge-sans-paille#!/usr/bin/env python
2d8c3d4b1SGreg Clayton
3d8c3d4b1SGreg Clayton#----------------------------------------------------------------------
4d8c3d4b1SGreg Clayton# Be sure to add the python path that points to the LLDB shared library.
5d8c3d4b1SGreg Clayton#
6d8c3d4b1SGreg Clayton# # To use this in the embedded python interpreter using "lldb" just
7d8c3d4b1SGreg Clayton# import it with the full path using the "command script import"
8d8c3d4b1SGreg Clayton# command
9d8c3d4b1SGreg Clayton#   (lldb) command script import /path/to/cmdtemplate.py
10d8c3d4b1SGreg Clayton#----------------------------------------------------------------------
11d8c3d4b1SGreg Clayton
12525cd59fSSerge Gueltonfrom __future__ import print_function
13525cd59fSSerge Guelton
14d8c3d4b1SGreg Claytonimport platform
15d8c3d4b1SGreg Claytonimport os
1657d1c486SGreg Claytonimport re
17e110ad8fSGreg Claytonimport signal
18d8c3d4b1SGreg Claytonimport sys
191a12dd70SSerge Gueltonimport subprocess
201a12dd70SSerge Guelton
21d8c3d4b1SGreg Claytontry:
22d8c3d4b1SGreg Clayton    # Just try for LLDB in case PYTHONPATH is already correctly setup
23d8c3d4b1SGreg Clayton    import lldb
24d8c3d4b1SGreg Claytonexcept ImportError:
25d8c3d4b1SGreg Clayton    lldb_python_dirs = list()
26d8c3d4b1SGreg Clayton    # lldb is not in the PYTHONPATH, try some defaults for the current platform
27d8c3d4b1SGreg Clayton    platform_system = platform.system()
28d8c3d4b1SGreg Clayton    if platform_system == 'Darwin':
29d8c3d4b1SGreg Clayton        # On Darwin, try the currently selected Xcode directory
30185de8eeSDavide Italiano        xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True)
31d8c3d4b1SGreg Clayton        if xcode_dir:
32b9c1b51eSKate Stone            lldb_python_dirs.append(
33b9c1b51eSKate Stone                os.path.realpath(
34b9c1b51eSKate Stone                    xcode_dir +
35b9c1b51eSKate Stone                    '/../SharedFrameworks/LLDB.framework/Resources/Python'))
36b9c1b51eSKate Stone            lldb_python_dirs.append(
37b9c1b51eSKate Stone                xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
38b9c1b51eSKate Stone        lldb_python_dirs.append(
39b9c1b51eSKate Stone            '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
40d8c3d4b1SGreg Clayton    success = False
41d8c3d4b1SGreg Clayton    for lldb_python_dir in lldb_python_dirs:
42d8c3d4b1SGreg Clayton        if os.path.exists(lldb_python_dir):
43d8c3d4b1SGreg Clayton            if not (sys.path.__contains__(lldb_python_dir)):
44d8c3d4b1SGreg Clayton                sys.path.append(lldb_python_dir)
45d8c3d4b1SGreg Clayton                try:
46d8c3d4b1SGreg Clayton                    import lldb
47d8c3d4b1SGreg Clayton                except ImportError:
48d8c3d4b1SGreg Clayton                    pass
49d8c3d4b1SGreg Clayton                else:
50525cd59fSSerge Guelton                    print('imported lldb from: "%s"' % (lldb_python_dir))
51d8c3d4b1SGreg Clayton                    success = True
52d8c3d4b1SGreg Clayton                    break
53d8c3d4b1SGreg Clayton    if not success:
54525cd59fSSerge Guelton        print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly")
55d8c3d4b1SGreg Clayton        sys.exit(1)
56d8c3d4b1SGreg Clayton
57d8c3d4b1SGreg Claytonimport optparse
58d8c3d4b1SGreg Claytonimport shlex
59d8c3d4b1SGreg Claytonimport time
60d8c3d4b1SGreg Clayton
61b9c1b51eSKate Stone
6257d1c486SGreg Claytondef regex_option_callback(option, opt_str, value, parser):
6357d1c486SGreg Clayton    if opt_str == "--std":
6457d1c486SGreg Clayton        value = '^std::'
6557d1c486SGreg Clayton    regex = re.compile(value)
6657d1c486SGreg Clayton    parser.values.skip_type_regexes.append(regex)
6757d1c486SGreg Clayton
68b9c1b51eSKate Stone
6957d1c486SGreg Claytondef create_types_options(for_lldb_command):
7057d1c486SGreg Clayton    if for_lldb_command:
71d8c3d4b1SGreg Clayton        usage = "usage: %prog [options]"
7257d1c486SGreg Clayton        description = '''This command will help check for padding in between
7357d1c486SGreg Claytonbase classes and members in structs and classes. It will summarize the types
7457d1c486SGreg Claytonand how much padding was found. If no types are specified with the --types TYPENAME
7557d1c486SGreg Claytonoption, all structure and class types will be verified. If no modules are
7657d1c486SGreg Claytonspecified with the --module option, only the target's main executable will be
7757d1c486SGreg Claytonsearched.
7857d1c486SGreg Clayton'''
7957d1c486SGreg Clayton    else:
8057d1c486SGreg Clayton        usage = "usage: %prog [options] EXEPATH [EXEPATH ...]"
8157d1c486SGreg Clayton        description = '''This command will help check for padding in between
8257d1c486SGreg Claytonbase classes and members in structures and classes. It will summarize the types
8357d1c486SGreg Claytonand how much padding was found. One or more paths to executable files must be
8457d1c486SGreg Claytonspecified and targets will be created with these modules. If no types are
8557d1c486SGreg Claytonspecified with the --types TYPENAME option, all structure and class types will
8657d1c486SGreg Claytonbe verified in all specified modules.
87d8c3d4b1SGreg Clayton'''
88b9c1b51eSKate Stone    parser = optparse.OptionParser(
89b9c1b51eSKate Stone        description=description,
90b9c1b51eSKate Stone        prog='framestats',
91b9c1b51eSKate Stone        usage=usage)
9257d1c486SGreg Clayton    if not for_lldb_command:
93b9c1b51eSKate Stone        parser.add_option(
94b9c1b51eSKate Stone            '-a',
95b9c1b51eSKate Stone            '--arch',
96b9c1b51eSKate Stone            type='string',
97b9c1b51eSKate Stone            dest='arch',
98b9c1b51eSKate Stone            help='The architecture to use when creating the debug target.',
99b9c1b51eSKate Stone            default=None)
100b9c1b51eSKate Stone        parser.add_option(
101b9c1b51eSKate Stone            '-p',
102b9c1b51eSKate Stone            '--platform',
103b9c1b51eSKate Stone            type='string',
104b9c1b51eSKate Stone            metavar='platform',
105b9c1b51eSKate Stone            dest='platform',
106b9c1b51eSKate Stone            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".')
107b9c1b51eSKate Stone    parser.add_option(
108b9c1b51eSKate Stone        '-m',
109b9c1b51eSKate Stone        '--module',
110b9c1b51eSKate Stone        action='append',
111b9c1b51eSKate Stone        type='string',
112b9c1b51eSKate Stone        metavar='MODULE',
113b9c1b51eSKate Stone        dest='modules',
114b9c1b51eSKate Stone        help='Specify one or more modules which will be used to verify the types.',
115b9c1b51eSKate Stone        default=[])
116b9c1b51eSKate Stone    parser.add_option(
117b9c1b51eSKate Stone        '-d',
118b9c1b51eSKate Stone        '--debug',
119b9c1b51eSKate Stone        action='store_true',
120b9c1b51eSKate Stone        dest='debug',
121b9c1b51eSKate Stone        help='Pause 10 seconds to wait for a debugger to attach.',
122b9c1b51eSKate Stone        default=False)
123b9c1b51eSKate Stone    parser.add_option(
124b9c1b51eSKate Stone        '-t',
125b9c1b51eSKate Stone        '--type',
126b9c1b51eSKate Stone        action='append',
127b9c1b51eSKate Stone        type='string',
128b9c1b51eSKate Stone        metavar='TYPENAME',
129b9c1b51eSKate Stone        dest='typenames',
130b9c1b51eSKate Stone        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.',
131b9c1b51eSKate Stone        default=[])
132b9c1b51eSKate Stone    parser.add_option(
133b9c1b51eSKate Stone        '-v',
134b9c1b51eSKate Stone        '--verbose',
135b9c1b51eSKate Stone        action='store_true',
136b9c1b51eSKate Stone        dest='verbose',
137b9c1b51eSKate Stone        help='Enable verbose logging and information.',
138b9c1b51eSKate Stone        default=False)
139b9c1b51eSKate Stone    parser.add_option(
140b9c1b51eSKate Stone        '-s',
141b9c1b51eSKate Stone        '--skip-type-regex',
142b9c1b51eSKate Stone        action="callback",
143b9c1b51eSKate Stone        callback=regex_option_callback,
144b9c1b51eSKate Stone        type='string',
145b9c1b51eSKate Stone        metavar='REGEX',
146b9c1b51eSKate Stone        dest='skip_type_regexes',
147b9c1b51eSKate Stone        help='Regular expressions that, if they match the current member typename, will cause the type to no be recursively displayed.',
148b9c1b51eSKate Stone        default=[])
149b9c1b51eSKate Stone    parser.add_option(
150b9c1b51eSKate Stone        '--std',
151b9c1b51eSKate Stone        action="callback",
152b9c1b51eSKate Stone        callback=regex_option_callback,
153b9c1b51eSKate Stone        metavar='REGEX',
154b9c1b51eSKate Stone        dest='skip_type_regexes',
155b9c1b51eSKate Stone        help="Don't' recurse into types in the std namespace.",
156b9c1b51eSKate Stone        default=[])
157d8c3d4b1SGreg Clayton    return parser
158d8c3d4b1SGreg Clayton
159b9c1b51eSKate Stone
16057d1c486SGreg Claytondef verify_type(target, options, type):
161525cd59fSSerge Guelton    print(type)
162d8c3d4b1SGreg Clayton    typename = type.GetName()
163d8c3d4b1SGreg Clayton    # print 'type: %s' % (typename)
164b9c1b51eSKate Stone    (end_offset, padding) = verify_type_recursive(
165b9c1b51eSKate Stone        target, options, type, None, 0, 0, 0)
166d8c3d4b1SGreg Clayton    byte_size = type.GetByteSize()
167d8c3d4b1SGreg Clayton    # if end_offset < byte_size:
168d8c3d4b1SGreg Clayton    #     last_member_padding = byte_size - end_offset
169d8c3d4b1SGreg Clayton    #     print '%+4u <%u> padding' % (end_offset, last_member_padding)
170d8c3d4b1SGreg Clayton    #     padding += last_member_padding
171525cd59fSSerge Guelton    print('Total byte size: %u' % (byte_size))
172525cd59fSSerge Guelton    print('Total pad bytes: %u' % (padding))
173d8c3d4b1SGreg Clayton    if padding > 0:
174525cd59fSSerge Guelton        print('Padding percentage: %2.2f %%' % ((float(padding) / float(byte_size)) * 100.0))
175525cd59fSSerge Guelton    print()
176d8c3d4b1SGreg Clayton
177b9c1b51eSKate Stone
178b9c1b51eSKate Stonedef verify_type_recursive(
179b9c1b51eSKate Stone        target,
180b9c1b51eSKate Stone        options,
181b9c1b51eSKate Stone        type,
182b9c1b51eSKate Stone        member_name,
183b9c1b51eSKate Stone        depth,
184b9c1b51eSKate Stone        base_offset,
185b9c1b51eSKate Stone        padding):
186d8c3d4b1SGreg Clayton    prev_end_offset = base_offset
187d8c3d4b1SGreg Clayton    typename = type.GetName()
188d8c3d4b1SGreg Clayton    byte_size = type.GetByteSize()
189d8c3d4b1SGreg Clayton    if member_name and member_name != typename:
190525cd59fSSerge Guelton        print('%+4u <%3u> %s%s %s;' % (base_offset, byte_size, '    ' * depth, typename, member_name))
191d8c3d4b1SGreg Clayton    else:
192525cd59fSSerge Guelton        print('%+4u {%3u} %s%s' % (base_offset, byte_size, '    ' * depth, typename))
19357d1c486SGreg Clayton
19457d1c486SGreg Clayton    for type_regex in options.skip_type_regexes:
19557d1c486SGreg Clayton        match = type_regex.match(typename)
19657d1c486SGreg Clayton        if match:
19757d1c486SGreg Clayton            return (base_offset + byte_size, padding)
198d8c3d4b1SGreg Clayton
199d8c3d4b1SGreg Clayton    members = type.members
200d8c3d4b1SGreg Clayton    if members:
201d8c3d4b1SGreg Clayton        for member_idx, member in enumerate(members):
202d8c3d4b1SGreg Clayton            member_type = member.GetType()
203d8c3d4b1SGreg Clayton            member_canonical_type = member_type.GetCanonicalType()
204d8c3d4b1SGreg Clayton            member_type_class = member_canonical_type.GetTypeClass()
205d8c3d4b1SGreg Clayton            member_name = member.GetName()
206d8c3d4b1SGreg Clayton            member_offset = member.GetOffsetInBytes()
207d8c3d4b1SGreg Clayton            member_total_offset = member_offset + base_offset
208d8c3d4b1SGreg Clayton            member_byte_size = member_type.GetByteSize()
20957d1c486SGreg Clayton            member_is_class_or_struct = False
210d8c3d4b1SGreg Clayton            if member_type_class == lldb.eTypeClassStruct or member_type_class == lldb.eTypeClassClass:
21157d1c486SGreg Clayton                member_is_class_or_struct = True
212b9c1b51eSKate Stone            if member_idx == 0 and member_offset == target.GetAddressByteSize(
213b9c1b51eSKate Stone            ) and type.IsPolymorphicClass():
214d8c3d4b1SGreg Clayton                ptr_size = target.GetAddressByteSize()
215525cd59fSSerge Guelton                print('%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, '    ' * (depth + 1)))
216d8c3d4b1SGreg Clayton                prev_end_offset = ptr_size
217d8c3d4b1SGreg Clayton            else:
218d8c3d4b1SGreg Clayton                if prev_end_offset < member_total_offset:
219d8c3d4b1SGreg Clayton                    member_padding = member_total_offset - prev_end_offset
220d8c3d4b1SGreg Clayton                    padding = padding + member_padding
221525cd59fSSerge Guelton                    print('%+4u <%3u> %s<PADDING>' % (prev_end_offset, member_padding, '    ' * (depth + 1)))
222d8c3d4b1SGreg Clayton
22357d1c486SGreg Clayton            if member_is_class_or_struct:
224b9c1b51eSKate Stone                (prev_end_offset,
225b9c1b51eSKate Stone                 padding) = verify_type_recursive(target,
226b9c1b51eSKate Stone                                                  options,
227b9c1b51eSKate Stone                                                  member_canonical_type,
228b9c1b51eSKate Stone                                                  member_name,
229b9c1b51eSKate Stone                                                  depth + 1,
230b9c1b51eSKate Stone                                                  member_total_offset,
231b9c1b51eSKate Stone                                                  padding)
232d8c3d4b1SGreg Clayton            else:
233d8c3d4b1SGreg Clayton                prev_end_offset = member_total_offset + member_byte_size
23457d1c486SGreg Clayton                member_typename = member_type.GetName()
235d8c3d4b1SGreg Clayton                if member.IsBitfield():
236525cd59fSSerge Guelton                    print('%+4u <%3u> %s%s:%u %s;' % (member_total_offset, member_byte_size, '    ' * (depth + 1), member_typename, member.GetBitfieldSizeInBits(), member_name))
237d8c3d4b1SGreg Clayton                else:
238525cd59fSSerge Guelton                    print('%+4u <%3u> %s%s %s;' % (member_total_offset, member_byte_size, '    ' * (depth + 1), member_typename, member_name))
239d8c3d4b1SGreg Clayton
240d8c3d4b1SGreg Clayton        if prev_end_offset < byte_size:
241d8c3d4b1SGreg Clayton            last_member_padding = byte_size - prev_end_offset
242525cd59fSSerge Guelton            print('%+4u <%3u> %s<PADDING>' % (prev_end_offset, last_member_padding, '    ' * (depth + 1)))
243d8c3d4b1SGreg Clayton            padding += last_member_padding
244d8c3d4b1SGreg Clayton    else:
245d8c3d4b1SGreg Clayton        if type.IsPolymorphicClass():
246d8c3d4b1SGreg Clayton            ptr_size = target.GetAddressByteSize()
247525cd59fSSerge Guelton            print('%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, '    ' * (depth + 1)))
248d8c3d4b1SGreg Clayton            prev_end_offset = ptr_size
24957d1c486SGreg Clayton        prev_end_offset = base_offset + byte_size
250d8c3d4b1SGreg Clayton
251d8c3d4b1SGreg Clayton    return (prev_end_offset, padding)
252d8c3d4b1SGreg Clayton
253b9c1b51eSKate Stone
25457d1c486SGreg Claytondef check_padding_command(debugger, command, result, dict):
255d8c3d4b1SGreg Clayton    # Use the Shell Lexer to properly parse up command options just like a
256d8c3d4b1SGreg Clayton    # shell would
257d8c3d4b1SGreg Clayton    command_args = shlex.split(command)
25857d1c486SGreg Clayton    parser = create_types_options(True)
259d8c3d4b1SGreg Clayton    try:
260d8c3d4b1SGreg Clayton        (options, args) = parser.parse_args(command_args)
261d8c3d4b1SGreg Clayton    except:
262d8c3d4b1SGreg Clayton        # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit
263d8c3d4b1SGreg Clayton        # (courtesy of OptParse dealing with argument errors by throwing SystemExit)
264d8c3d4b1SGreg Clayton        result.SetStatus(lldb.eReturnStatusFailed)
265b9c1b51eSKate Stone        # returning a string is the same as returning an error whose
266b9c1b51eSKate Stone        # description is the string
267b9c1b51eSKate Stone        return "option parsing failed"
268811051e3SGreg Clayton    verify_types(debugger.GetSelectedTarget(), options)
269d8c3d4b1SGreg Clayton
270b9c1b51eSKate Stone
27109effdacSGreg Clayton@lldb.command("parse_all_struct_class_types")
27209effdacSGreg Claytondef parse_all_struct_class_types(debugger, command, result, dict):
27309effdacSGreg Clayton    command_args = shlex.split(command)
27409effdacSGreg Clayton    for f in command_args:
27509effdacSGreg Clayton        error = lldb.SBError()
27609effdacSGreg Clayton        target = debugger.CreateTarget(f, None, None, False, error)
27709effdacSGreg Clayton        module = target.GetModuleAtIndex(0)
278525cd59fSSerge Guelton        print("Parsing all types in '%s'" % (module))
27909effdacSGreg Clayton        types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct)
28009effdacSGreg Clayton        for t in types:
281525cd59fSSerge Guelton            print(t)
282525cd59fSSerge Guelton        print("")
28309effdacSGreg Clayton
284d8c3d4b1SGreg Clayton
28557d1c486SGreg Claytondef verify_types(target, options):
28657d1c486SGreg Clayton
28757d1c486SGreg Clayton    if not target:
288525cd59fSSerge Guelton        print('error: invalid target')
28957d1c486SGreg Clayton        return
290d8c3d4b1SGreg Clayton
291d8c3d4b1SGreg Clayton    modules = list()
292d8c3d4b1SGreg Clayton    if len(options.modules) == 0:
293d8c3d4b1SGreg Clayton        # Append just the main executable if nothing was specified
294d8c3d4b1SGreg Clayton        module = target.modules[0]
295d8c3d4b1SGreg Clayton        if module:
296d8c3d4b1SGreg Clayton            modules.append(module)
297d8c3d4b1SGreg Clayton    else:
298d8c3d4b1SGreg Clayton        for module_name in options.modules:
299d8c3d4b1SGreg Clayton            module = lldb.target.module[module_name]
300d8c3d4b1SGreg Clayton            if module:
301d8c3d4b1SGreg Clayton                modules.append(module)
302d8c3d4b1SGreg Clayton
303d8c3d4b1SGreg Clayton    if modules:
304d8c3d4b1SGreg Clayton        for module in modules:
305525cd59fSSerge Guelton            print('module: %s' % (module.file))
306d8c3d4b1SGreg Clayton            if options.typenames:
307d8c3d4b1SGreg Clayton                for typename in options.typenames:
308d8c3d4b1SGreg Clayton                    types = module.FindTypes(typename)
309d8c3d4b1SGreg Clayton                    if types.GetSize():
310525cd59fSSerge Guelton                        print('Found %u types matching "%s" in "%s"' % (len(types), typename, module.file))
311d8c3d4b1SGreg Clayton                        for type in types:
31257d1c486SGreg Clayton                            verify_type(target, options, type)
313d8c3d4b1SGreg Clayton                    else:
314525cd59fSSerge Guelton                        print('error: no type matches "%s" in "%s"' % (typename, module.file))
315d8c3d4b1SGreg Clayton            else:
316b9c1b51eSKate Stone                types = module.GetTypes(
317b9c1b51eSKate Stone                    lldb.eTypeClassClass | lldb.eTypeClassStruct)
318525cd59fSSerge Guelton                print('Found %u types in "%s"' % (len(types), module.file))
319d8c3d4b1SGreg Clayton                for type in types:
32057d1c486SGreg Clayton                    verify_type(target, options, type)
321d8c3d4b1SGreg Clayton    else:
322525cd59fSSerge Guelton        print('error: no modules')
323d8c3d4b1SGreg Clayton
324d8c3d4b1SGreg Claytonif __name__ == '__main__':
325d8c3d4b1SGreg Clayton    debugger = lldb.SBDebugger.Create()
32657d1c486SGreg Clayton    parser = create_types_options(False)
32757d1c486SGreg Clayton
32857d1c486SGreg Clayton    # try:
32957d1c486SGreg Clayton    (options, args) = parser.parse_args(sys.argv[1:])
33057d1c486SGreg Clayton    # except:
33157d1c486SGreg Clayton    #     print "error: option parsing failed"
33257d1c486SGreg Clayton    #     sys.exit(1)
33357d1c486SGreg Clayton
334e110ad8fSGreg Clayton    if options.debug:
335525cd59fSSerge Guelton        print("Waiting for debugger to attach to process %d" % os.getpid())
336e110ad8fSGreg Clayton        os.kill(os.getpid(), signal.SIGSTOP)
337e110ad8fSGreg Clayton
33857d1c486SGreg Clayton    for path in args:
33957d1c486SGreg Clayton        # in a command - the lldb.* convenience variables are not to be used
34057d1c486SGreg Clayton        # and their values (if any) are undefined
341b9c1b51eSKate Stone        # this is the best practice to access those objects from within a
342b9c1b51eSKate Stone        # command
34357d1c486SGreg Clayton        error = lldb.SBError()
34457d1c486SGreg Clayton        target = debugger.CreateTarget(path,
34557d1c486SGreg Clayton                                       options.arch,
34657d1c486SGreg Clayton                                       options.platform,
34757d1c486SGreg Clayton                                       True,
34857d1c486SGreg Clayton                                       error)
34957d1c486SGreg Clayton        if error.Fail():
350525cd59fSSerge Guelton            print(error.GetCString())
35157d1c486SGreg Clayton            continue
35257d1c486SGreg Clayton        verify_types(target, options)
35357d1c486SGreg Clayton
354*1441ffe6SDave Leedef __lldb_init_module(debugger, internal_dict):
355*1441ffe6SDave Lee    debugger.HandleCommand(
356b9c1b51eSKate Stone        'command script add -f types.check_padding_command check_padding')
357525cd59fSSerge Guelton    print('"check_padding" command installed, use the "--help" option for detailed help')
358