예제 #1
0
def govern_analyzer_runs(args):
    """ Governs multiple runs in CTU mode or runs once in normal mode. """

    ctu_config = get_ctu_config_from_args(args)
    # If we do a CTU collect (1st phase) we remove all previous collection
    # data first.
    if ctu_config.collect:
        shutil.rmtree(ctu_config.dir, ignore_errors=True)

    # If the user asked for a collect (1st) and analyze (2nd) phase, we do an
    # all-in-one run where we deliberately remove collection data before and
    # also after the run. If the user asks only for a single phase data is
    # left so multiple analyze runs can use the same data gathered by a single
    # collection run.
    if ctu_config.collect and ctu_config.analyze:
        # CTU strings are coming from args.ctu_dir and func_map_cmd,
        # so we can leave it empty
        args.ctu_phases = CtuConfig(collect=True, analyze=False,
                                    dir='', func_map_cmd='')
        run_analyzer_parallel(args)
        merge_ctu_func_maps(ctu_config.dir)
        args.ctu_phases = CtuConfig(collect=False, analyze=True,
                                    dir='', func_map_cmd='')
        run_analyzer_parallel(args)
        shutil.rmtree(ctu_config.dir, ignore_errors=True)
    else:
        # Single runs (collect or analyze) are launched from here.
        run_analyzer_parallel(args)
        if ctu_config.collect:
            merge_ctu_func_maps(ctu_config.dir)
예제 #2
0
def get_ctu_config_from_args(args):
    """ CTU configuration is created from the chosen phases and dir. """

    return (
        CtuConfig(collect=args.ctu_phases.collect,
                  analyze=args.ctu_phases.analyze,
                  dir=args.ctu_dir,
                  func_map_cmd=args.func_map_cmd)
        if hasattr(args, 'ctu_phases') and hasattr(args.ctu_phases, 'dir')
        else CtuConfig(collect=False, analyze=False, dir='', func_map_cmd=''))
예제 #3
0
def dispatch_ctu(opts, continuation=run_analyzer):
    """ Execute only one phase of 2 phases of CTU if needed. """

    ctu_config = opts['ctu']
    # Recover namedtuple from json when coming from analyze_cc
    if not hasattr(ctu_config, 'collect'):
        ctu_config = CtuConfig(collect=ctu_config[0],
                               analyze=ctu_config[1],
                               dir=ctu_config[2],
                               func_map_cmd=ctu_config[3])
    opts['ctu'] = ctu_config

    if ctu_config.collect or ctu_config.analyze:
        assert ctu_config.collect != ctu_config.analyze
        if ctu_config.collect:
            return ctu_collect_phase(opts)
        if ctu_config.analyze:
            cwd = opts['directory']
            cmd = [opts['clang'], '--analyze'] + opts['direct_args'] \
                + opts['flags'] + [opts['file']]
            triarch = get_triple_arch(cmd, cwd)
            ctu_options = ['ctu-dir=' + os.path.join(ctu_config.dir),
                           'reanalyze-ctu-visited=true']
            analyzer_options = prefix_with('-analyzer-config', ctu_options)
            direct_options = prefix_with('-Xanalyzer', analyzer_options)
            opts['direct_args'].extend(direct_options)

    return continuation(opts)
예제 #4
0
def get_ctu_config_from_json(ctu_conf_json):
    """ CTU configuration is created from the chosen phases and dir. """

    ctu_config = json.loads(ctu_conf_json)
    # Recover namedtuple from json when coming from analyze-cc or analyze-c++
    return CtuConfig(collect=ctu_config[0],
                     analyze=ctu_config[1],
                     dir=ctu_config[2],
                     func_map_cmd=ctu_config[3])
예제 #5
0
def run_analyzer_with_ctu(args):
    """ Governs multiple runs in CTU mode or runs once in normal mode. """

    ctu_config = get_ctu_config(args)
    if ctu_config.collect:
        shutil.rmtree(ctu_config.dir, ignore_errors=True)
    if ctu_config.collect and ctu_config.analyze:
        # CTU strings are coming from args.ctu_dir and func_map_cmd,
        # so we can leave it empty
        args.ctu_phases = CtuConfig(collect=True, analyze=False,
                                    dir='', func_map_cmd='')
        run_analyzer_parallel(args)
        merge_ctu_func_maps(ctu_config.dir)
        args.ctu_phases = CtuConfig(collect=False, analyze=True,
                                    dir='', func_map_cmd='')
        run_analyzer_parallel(args)
        shutil.rmtree(ctu_config.dir, ignore_errors=True)
    else:
        run_analyzer_parallel(args)
        if ctu_config.collect:
            merge_ctu_func_maps(ctu_config.dir)
예제 #6
0
def create_analyze_parser(from_build_command):
    """ Creates a parser for command-line arguments to 'analyze'. """

    parser = create_default_parser()

    if from_build_command:
        parser_add_prefer_wrapper(parser)
        parser_add_compilers(parser)

        parser.add_argument(
            '--intercept-first',
            action='store_true',
            help="""Run the build commands first, intercept compiler
            calls and then run the static analyzer afterwards.
            Generally speaking it has better coverage on build commands.
            With '--override-compiler' it use compiler wrapper, but does
            not run the analyzer till the build is finished.""")
    else:
        parser_add_cdb(parser)

    parser.add_argument(
        '--status-bugs',
        action='store_true',
        help="""The exit status of '%(prog)s' is the same as the executed
        build command. This option ignores the build exit status and sets to
        be non zero if it found potential bugs or zero otherwise.""")
    parser.add_argument(
        '--exclude',
        metavar='<directory>',
        dest='excludes',
        action='append',
        default=[],
        help="""Do not run static analyzer against files found in this
        directory. (You can specify this option multiple times.)
        Could be useful when project contains 3rd party libraries.""")

    output = parser.add_argument_group('output control options')
    output.add_argument(
        '--output',
        '-o',
        metavar='<path>',
        default=tempfile.gettempdir(),
        help="""Specifies the output directory for analyzer reports.
        Subdirectory will be created if default directory is targeted.""")
    output.add_argument(
        '--keep-empty',
        action='store_true',
        help="""Don't remove the build results directory even if no issues
        were reported.""")
    output.add_argument(
        '--html-title',
        metavar='<title>',
        help="""Specify the title used on generated HTML pages.
        If not specified, a default title will be used.""")
    format_group = output.add_mutually_exclusive_group()
    format_group.add_argument(
        '--plist',
        '-plist',
        dest='output_format',
        const='plist',
        default='html',
        action='store_const',
        help="""Cause the results as a set of .plist files.""")
    format_group.add_argument(
        '--plist-html',
        '-plist-html',
        dest='output_format',
        const='plist-html',
        default='html',
        action='store_const',
        help="""Cause the results as a set of .html and .plist files.""")
    # TODO: implement '-view '
    format_group.add_argument(
        '--plist-multi-file',
        '-plist-multi-file',
        dest='output_format',
        const='plist-multi-file',
        default='html',
        action='store_const',
        help="""Cause the results as a set of .plist files with extra
        information on related files.""")
    advanced = parser.add_argument_group('advanced options')
    advanced.add_argument(
        '--use-analyzer',
        metavar='<path>',
        dest='clang',
        default='clang',
        help="""'%(prog)s' uses the 'clang' executable relative to itself for
        static analysis. One can override this behavior with this option by
        using the 'clang' packaged with Xcode (on OS X) or from the PATH.""")
    advanced.add_argument(
        '--no-failure-reports',
        '-no-failure-reports',
        dest='output_failures',
        action='store_false',
        help="""Do not create a 'failures' subdirectory that includes analyzer
        crash reports and preprocessed source files.""")
    parser.add_argument(
        '--analyze-headers',
        action='store_true',
        help="""Also analyze functions in #included files. By default, such
        functions are skipped unless they are called by functions within the
        main source file.""")
    advanced.add_argument(
        '--stats',
        '-stats',
        action='store_true',
        help="""Generates visitation statistics for the project.""")
    advanced.add_argument(
        '--internal-stats',
        action='store_true',
        help="""Generate internal analyzer statistics.""")
    advanced.add_argument(
        '--maxloop',
        '-maxloop',
        metavar='<loop count>',
        type=int,
        help="""Specifiy the number of times a block can be visited before
        giving up. Increase for more comprehensive coverage at a cost of
        speed.""")
    advanced.add_argument(
        '--store',
        '-store',
        metavar='<model>',
        dest='store_model',
        choices=['region', 'basic'],
        help="""Specify the store model used by the analyzer. 'region'
        specifies a field- sensitive store model. 'basic' which is far less
        precise but can more quickly analyze code. 'basic' was the default
        store model for checker-0.221 and earlier.""")
    advanced.add_argument(
        '--constraints',
        '-constraints',
        metavar='<model>',
        dest='constraints_model',
        choices=['range', 'basic'],
        help="""Specify the constraint engine used by the analyzer. Specifying
        'basic' uses a simpler, less powerful constraint model used by
        checker-0.160 and earlier.""")
    advanced.add_argument(
        '--analyzer-config',
        '-analyzer-config',
        metavar='<options>',
        help="""Provide options to pass through to the analyzer's
        -analyzer-config flag. Several options are separated with comma:
        'key1=val1,key2=val2'

        Available options:
            stable-report-filename=true or false (default)

        Switch the page naming to:
        report-<filename>-<function/method name>-<id>.html
        instead of report-XXXXXX.html""")
    advanced.add_argument(
        '--force-analyze-debug-code',
        dest='force_debug',
        action='store_true',
        help="""Tells analyzer to enable assertions in code even if they were
        disabled during compilation, enabling more precise results.""")

    plugins = parser.add_argument_group('checker options')
    plugins.add_argument(
        '--load-plugin',
        '-load-plugin',
        metavar='<plugin library>',
        dest='plugins',
        action='append',
        help="""Loading external checkers using the clang plugin interface.""")
    plugins.add_argument(
        '--enable-checker',
        '-enable-checker',
        metavar='<checker name>',
        action=AppendCommaSeparated,
        help="""Enable specific checker.""")
    plugins.add_argument(
        '--disable-checker',
        '-disable-checker',
        metavar='<checker name>',
        action=AppendCommaSeparated,
        help="""Disable specific checker.""")
    plugins.add_argument(
        '--help-checkers',
        action='store_true',
        help="""A default group of checkers is run unless explicitly disabled.
        Exactly which checkers constitute the default group is a function of
        the operating system in use. These can be printed with this flag.""")
    plugins.add_argument(
        '--help-checkers-verbose',
        action='store_true',
        help="""Print all available checkers and mark the enabled ones.""")

    if from_build_command:
        parser.add_argument(
            dest='build', nargs=argparse.REMAINDER, help="""Command to run.""")
    else:
        ctu = parser.add_argument_group('cross translation unit analysis')
        ctu_mutex_group = ctu.add_mutually_exclusive_group()
        ctu_mutex_group.add_argument(
            '--ctu',
            action='store_const',
            const=CtuConfig(collect=True, analyze=True,
                            dir='', func_map_cmd=''),
            dest='ctu_phases',
            help="""Perform cross translation unit (ctu) analysis (both collect
            and analyze phases) using default <ctu-dir> for temporary output.
            At the end of the analysis, the temporary directory is removed.""")
        ctu.add_argument(
            '--ctu-dir',
            metavar='<ctu-dir>',
            default='ctu-dir',
            help="""Defines the temporary directory used between ctu
            phases.""")
        ctu_mutex_group.add_argument(
            '--ctu-collect-only',
            action='store_const',
            const=CtuConfig(collect=True, analyze=False,
                            dir='', func_map_cmd=''),
            dest='ctu_phases',
            help="""Perform only the collect phase of ctu.
            Keep <ctu-dir> for further use.""")
        ctu_mutex_group.add_argument(
            '--ctu-analyze-only',
            action='store_const',
            const=CtuConfig(collect=False, analyze=True,
                            dir='', func_map_cmd=''),
            dest='ctu_phases',
            help="""Perform only the analyze phase of ctu. <ctu-dir> should be
            present and will not be removed after analysis.""")
        ctu.add_argument(
            '--use-func-map-cmd',
            metavar='<path>',
            dest='func_map_cmd',
            default='clang-func-mapping',
            help="""'%(prog)s' uses the 'clang-func-mapping' executable
            relative to itself for generating function maps for static
            analysis. One can override this behavior with this option by using
            the 'clang-func-mapping' packaged with Xcode (on OS X) or from the
            PATH.""")
    return parser