1074075aeSMasahiro Yamada#!/usr/bin/env python3 26ad7cbc0SNathan Huckleberry# SPDX-License-Identifier: GPL-2.0 36ad7cbc0SNathan Huckleberry# 46ad7cbc0SNathan Huckleberry# Copyright (C) Google LLC, 2020 56ad7cbc0SNathan Huckleberry# 66ad7cbc0SNathan Huckleberry# Author: Nathan Huckleberry <nhuck@google.com> 76ad7cbc0SNathan Huckleberry# 86ad7cbc0SNathan Huckleberry"""A helper routine run clang-tidy and the clang static-analyzer on 96ad7cbc0SNathan Huckleberrycompile_commands.json. 106ad7cbc0SNathan Huckleberry""" 116ad7cbc0SNathan Huckleberry 126ad7cbc0SNathan Huckleberryimport argparse 136ad7cbc0SNathan Huckleberryimport json 146ad7cbc0SNathan Huckleberryimport multiprocessing 156ad7cbc0SNathan Huckleberryimport subprocess 166ad7cbc0SNathan Huckleberryimport sys 176ad7cbc0SNathan Huckleberry 186ad7cbc0SNathan Huckleberry 196ad7cbc0SNathan Huckleberrydef parse_arguments(): 206ad7cbc0SNathan Huckleberry """Set up and parses command-line arguments. 216ad7cbc0SNathan Huckleberry Returns: 226ad7cbc0SNathan Huckleberry args: Dict of parsed args 236ad7cbc0SNathan Huckleberry Has keys: [path, type] 246ad7cbc0SNathan Huckleberry """ 256ad7cbc0SNathan Huckleberry usage = """Run clang-tidy or the clang static-analyzer on a 266ad7cbc0SNathan Huckleberry compilation database.""" 276ad7cbc0SNathan Huckleberry parser = argparse.ArgumentParser(description=usage) 286ad7cbc0SNathan Huckleberry 296ad7cbc0SNathan Huckleberry type_help = "Type of analysis to be performed" 306ad7cbc0SNathan Huckleberry parser.add_argument("type", 316ad7cbc0SNathan Huckleberry choices=["clang-tidy", "clang-analyzer"], 326ad7cbc0SNathan Huckleberry help=type_help) 336ad7cbc0SNathan Huckleberry path_help = "Path to the compilation database to parse" 346ad7cbc0SNathan Huckleberry parser.add_argument("path", type=str, help=path_help) 356ad7cbc0SNathan Huckleberry 366ad7cbc0SNathan Huckleberry return parser.parse_args() 376ad7cbc0SNathan Huckleberry 386ad7cbc0SNathan Huckleberry 396ad7cbc0SNathan Huckleberrydef init(l, a): 406ad7cbc0SNathan Huckleberry global lock 416ad7cbc0SNathan Huckleberry global args 426ad7cbc0SNathan Huckleberry lock = l 436ad7cbc0SNathan Huckleberry args = a 446ad7cbc0SNathan Huckleberry 456ad7cbc0SNathan Huckleberry 466ad7cbc0SNathan Huckleberrydef run_analysis(entry): 476ad7cbc0SNathan Huckleberry # Disable all checks, then re-enable the ones we want 4804518e4cSGuru Das Srinagesh checks = [] 4904518e4cSGuru Das Srinagesh checks.append("-checks=-*") 506ad7cbc0SNathan Huckleberry if args.type == "clang-tidy": 5104518e4cSGuru Das Srinagesh checks.append("linuxkernel-*") 526ad7cbc0SNathan Huckleberry else: 5304518e4cSGuru Das Srinagesh checks.append("clang-analyzer-*") 5404518e4cSGuru Das Srinagesh checks.append("-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling") 5504518e4cSGuru Das Srinagesh p = subprocess.run(["clang-tidy", "-p", args.path, ",".join(checks), entry["file"]], 566ad7cbc0SNathan Huckleberry stdout=subprocess.PIPE, 576ad7cbc0SNathan Huckleberry stderr=subprocess.STDOUT, 586ad7cbc0SNathan Huckleberry cwd=entry["directory"]) 596ad7cbc0SNathan Huckleberry with lock: 606ad7cbc0SNathan Huckleberry sys.stderr.buffer.write(p.stdout) 616ad7cbc0SNathan Huckleberry 626ad7cbc0SNathan Huckleberry 636ad7cbc0SNathan Huckleberrydef main(): 64*87c7ee67SMasahiro Yamada try: 656ad7cbc0SNathan Huckleberry args = parse_arguments() 666ad7cbc0SNathan Huckleberry 676ad7cbc0SNathan Huckleberry lock = multiprocessing.Lock() 686ad7cbc0SNathan Huckleberry pool = multiprocessing.Pool(initializer=init, initargs=(lock, args)) 696ad7cbc0SNathan Huckleberry # Read JSON data into the datastore variable 706ad7cbc0SNathan Huckleberry with open(args.path, "r") as f: 716ad7cbc0SNathan Huckleberry datastore = json.load(f) 726ad7cbc0SNathan Huckleberry pool.map(run_analysis, datastore) 73*87c7ee67SMasahiro Yamada except BrokenPipeError: 74*87c7ee67SMasahiro Yamada # Python flushes standard streams on exit; redirect remaining output 75*87c7ee67SMasahiro Yamada # to devnull to avoid another BrokenPipeError at shutdown 76*87c7ee67SMasahiro Yamada devnull = os.open(os.devnull, os.O_WRONLY) 77*87c7ee67SMasahiro Yamada os.dup2(devnull, sys.stdout.fileno()) 78*87c7ee67SMasahiro Yamada sys.exit(1) # Python exits with error code 1 on EPIPE 796ad7cbc0SNathan Huckleberry 806ad7cbc0SNathan Huckleberry 816ad7cbc0SNathan Huckleberryif __name__ == "__main__": 826ad7cbc0SNathan Huckleberry main() 83