brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.4 KiB · e5b4766 Raw
413 lines · python
1#!/usr/bin/env python2 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" just7# import it with the full path using the "command script import"8# command9#   (lldb) command script import /path/to/cmdtemplate.py10# ----------------------------------------------------------------------11 12import platform13import os14import re15import signal16import sys17import subprocess18 19try:20    # Just try for LLDB in case PYTHONPATH is already correctly setup21    import lldb22except ImportError:23    lldb_python_dirs = list()24    # lldb is not in the PYTHONPATH, try some defaults for the current platform25    platform_system = platform.system()26    if platform_system == "Darwin":27        # On Darwin, try the currently selected Xcode directory28        xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True)29        if xcode_dir:30            lldb_python_dirs.append(31                os.path.realpath(32                    xcode_dir + "/../SharedFrameworks/LLDB.framework/Resources/Python"33                )34            )35            lldb_python_dirs.append(36                xcode_dir + "/Library/PrivateFrameworks/LLDB.framework/Resources/Python"37            )38        lldb_python_dirs.append(39            "/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python"40        )41    success = False42    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)46                try:47                    import lldb48                except ImportError:49                    pass50                else:51                    print('imported lldb from: "%s"' % (lldb_python_dir))52                    success = True53                    break54    if not success:55        print(56            "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"57        )58        sys.exit(1)59 60import optparse61import shlex62import time63 64 65def regex_option_callback(option, opt_str, value, parser):66    if opt_str == "--std":67        value = "^std::"68    regex = re.compile(value)69    parser.values.skip_type_regexes.append(regex)70 71 72def create_types_options(for_lldb_command):73    if for_lldb_command:74        usage = "usage: %prog [options]"75        description = """This command will help check for padding in between76base classes and members in structs and classes. It will summarize the types77and how much padding was found. If no types are specified with the --types TYPENAME78option, all structure and class types will be verified. If no modules are79specified with the --module option, only the target's main executable will be80searched.81"""82    else:83        usage = "usage: %prog [options] EXEPATH [EXEPATH ...]"84        description = """This command will help check for padding in between85base classes and members in structures and classes. It will summarize the types86and how much padding was found. One or more paths to executable files must be87specified and targets will be created with these modules. If no types are88specified with the --types TYPENAME option, all structure and class types will89be verified in all specified modules.90"""91    parser = optparse.OptionParser(92        description=description, prog="framestats", usage=usage93    )94    if not for_lldb_command:95        parser.add_option(96            "-a",97            "--arch",98            type="string",99            dest="arch",100            help="The architecture to use when creating the debug target.",101            default=None,102        )103        parser.add_option(104            "-p",105            "--platform",106            type="string",107            metavar="platform",108            dest="platform",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".',110        )111    parser.add_option(112        "-m",113        "--module",114        action="append",115        type="string",116        metavar="MODULE",117        dest="modules",118        help="Specify one or more modules which will be used to verify the types.",119        default=[],120    )121    parser.add_option(122        "-d",123        "--debug",124        action="store_true",125        dest="debug",126        help="Pause 10 seconds to wait for a debugger to attach.",127        default=False,128    )129    parser.add_option(130        "-t",131        "--type",132        action="append",133        type="string",134        metavar="TYPENAME",135        dest="typenames",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.",137        default=[],138    )139    parser.add_option(140        "-v",141        "--verbose",142        action="store_true",143        dest="verbose",144        help="Enable verbose logging and information.",145        default=False,146    )147    parser.add_option(148        "-s",149        "--skip-type-regex",150        action="callback",151        callback=regex_option_callback,152        type="string",153        metavar="REGEX",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.",156        default=[],157    )158    parser.add_option(159        "--std",160        action="callback",161        callback=regex_option_callback,162        metavar="REGEX",163        dest="skip_type_regexes",164        help="Don't' recurse into types in the std namespace.",165        default=[],166    )167    return parser168 169 170def verify_type(target, options, type):171    print(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_offset178    #     print '%+4u <%u> padding' % (end_offset, last_member_padding)179    #     padding += last_member_padding180    print("Total byte size: %u" % (byte_size))181    print("Total pad bytes: %u" % (padding))182    if padding > 0:183        print(184            "Padding percentage: %2.2f %%"185            % ((float(padding) / float(byte_size)) * 100.0)186        )187    print()188 189 190def verify_type_recursive(191    target, options, type, member_name, depth, base_offset, padding192):193    prev_end_offset = base_offset194    typename = type.GetName()195    byte_size = type.GetByteSize()196    if member_name and member_name != typename:197        print(198            "%+4u <%3u> %s%s %s;"199            % (base_offset, byte_size, "    " * depth, typename, member_name)200        )201    else:202        print("%+4u {%3u} %s%s" % (base_offset, byte_size, "    " * depth, typename))203 204    for type_regex in options.skip_type_regexes:205        match = type_regex.match(typename)206        if match:207            return (base_offset + byte_size, padding)208 209    members = type.members210    if 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_offset218            member_byte_size = member_type.GetByteSize()219            member_is_class_or_struct = False220            if (221                member_type_class == lldb.eTypeClassStruct222                or member_type_class == lldb.eTypeClassClass223            ):224                member_is_class_or_struct = True225            if (226                member_idx == 0227                and member_offset == target.GetAddressByteSize()228                and type.IsPolymorphicClass()229            ):230                ptr_size = target.GetAddressByteSize()231                print(232                    "%+4u <%3u> %s__vtbl_ptr_type * _vptr;"233                    % (prev_end_offset, ptr_size, "    " * (depth + 1))234                )235                prev_end_offset = ptr_size236            else:237                if prev_end_offset < member_total_offset:238                    member_padding = member_total_offset - prev_end_offset239                    padding = padding + member_padding240                    print(241                        "%+4u <%3u> %s<PADDING>"242                        % (prev_end_offset, member_padding, "    " * (depth + 1))243                    )244 245            if member_is_class_or_struct:246                (prev_end_offset, padding) = verify_type_recursive(247                    target,248                    options,249                    member_canonical_type,250                    member_name,251                    depth + 1,252                    member_total_offset,253                    padding,254                )255            else:256                prev_end_offset = member_total_offset + member_byte_size257                member_typename = member_type.GetName()258                if member.IsBitfield():259                    print(260                        "%+4u <%3u> %s%s:%u %s;"261                        % (262                            member_total_offset,263                            member_byte_size,264                            "    " * (depth + 1),265                            member_typename,266                            member.GetBitfieldSizeInBits(),267                            member_name,268                        )269                    )270                else:271                    print(272                        "%+4u <%3u> %s%s %s;"273                        % (274                            member_total_offset,275                            member_byte_size,276                            "    " * (depth + 1),277                            member_typename,278                            member_name,279                        )280                    )281 282        if prev_end_offset < byte_size:283            last_member_padding = byte_size - prev_end_offset284            print(285                "%+4u <%3u> %s<PADDING>"286                % (prev_end_offset, last_member_padding, "    " * (depth + 1))287            )288            padding += last_member_padding289    else:290        if type.IsPolymorphicClass():291            ptr_size = target.GetAddressByteSize()292            print(293                "%+4u <%3u> %s__vtbl_ptr_type * _vptr;"294                % (prev_end_offset, ptr_size, "    " * (depth + 1))295            )296            prev_end_offset = ptr_size297        prev_end_offset = base_offset + byte_size298 299    return (prev_end_offset, padding)300 301 302def check_padding_command(debugger, command, result, dict):303    # Use the Shell Lexer to properly parse up command options just like a304    # shell would305    command_args = shlex.split(command)306    parser = create_types_options(True)307    try:308        (options, args) = parser.parse_args(command_args)309    except:310        # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit311        # (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 whose314        # description is the string315        return "option parsing failed"316    verify_types(debugger.GetSelectedTarget(), options)317 318 319@lldb.command("parse_all_struct_class_types")320def 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)328        for t in types:329            print(t)330        print("")331 332 333def verify_types(target, options):334    if not target:335        print("error: invalid target")336        return337 338    modules = list()339    if len(options.modules) == 0:340        # Append just the main executable if nothing was specified341        module = target.modules[0]342        if module:343            modules.append(module)344    else:345        for module_name in options.modules:346            module = lldb.target.module[module_name]347            if module:348                modules.append(module)349 350    if modules: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)356                    if types.GetSize():357                        print(358                            'Found %u types matching "%s" in "%s"'359                            % (len(types), typename, module.file)360                        )361                        for type in types:362                            verify_type(target, options, type)363                    else:364                        print(365                            'error: no type matches "%s" in "%s"'366                            % (typename, module.file)367                        )368            else:369                types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct)370                print('Found %u types in "%s"' % (len(types), module.file))371                for type in types:372                    verify_type(target, options, type)373    else:374        print("error: no modules")375 376 377if __name__ == "__main__":378    debugger = lldb.SBDebugger.Create()379    parser = create_types_options(False)380 381    # try:382    (options, args) = parser.parse_args(sys.argv[1:])383    # except:384    #     print "error: option parsing failed"385    #     sys.exit(1)386 387    if options.debug:388        print("Waiting for debugger to attach to process %d" % os.getpid())389        os.kill(os.getpid(), signal.SIGSTOP)390 391    for path in args:392        # in a command - the lldb.* convenience variables are not to be used393        # and their values (if any) are undefined394        # this is the best practice to access those objects from within a395        # command396        error = lldb.SBError()397        target = debugger.CreateTarget(398            path, options.arch, options.platform, True, error399        )400        if error.Fail():401            print(error.GetCString())402            continue403        verify_types(target, options)404 405 406def __lldb_init_module(debugger, internal_dict):407    debugger.HandleCommand(408        "command script add -o -f types.check_padding_command check_padding"409    )410    print(411        '"check_padding" command installed, use the "--help" option for detailed help'412    )413