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)
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=''))
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)
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])
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)
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