1*58b9f456SAndroid Build Coastguard Worker"""util.py - General utilities for running, loading, and processing benchmarks 2*58b9f456SAndroid Build Coastguard Worker""" 3*58b9f456SAndroid Build Coastguard Workerimport json 4*58b9f456SAndroid Build Coastguard Workerimport os 5*58b9f456SAndroid Build Coastguard Workerimport tempfile 6*58b9f456SAndroid Build Coastguard Workerimport subprocess 7*58b9f456SAndroid Build Coastguard Workerimport sys 8*58b9f456SAndroid Build Coastguard Worker 9*58b9f456SAndroid Build Coastguard Worker# Input file type enumeration 10*58b9f456SAndroid Build Coastguard WorkerIT_Invalid = 0 11*58b9f456SAndroid Build Coastguard WorkerIT_JSON = 1 12*58b9f456SAndroid Build Coastguard WorkerIT_Executable = 2 13*58b9f456SAndroid Build Coastguard Worker 14*58b9f456SAndroid Build Coastguard Worker_num_magic_bytes = 2 if sys.platform.startswith('win') else 4 15*58b9f456SAndroid Build Coastguard Worker 16*58b9f456SAndroid Build Coastguard Worker 17*58b9f456SAndroid Build Coastguard Workerdef is_executable_file(filename): 18*58b9f456SAndroid Build Coastguard Worker """ 19*58b9f456SAndroid Build Coastguard Worker Return 'True' if 'filename' names a valid file which is likely 20*58b9f456SAndroid Build Coastguard Worker an executable. A file is considered an executable if it starts with the 21*58b9f456SAndroid Build Coastguard Worker magic bytes for a EXE, Mach O, or ELF file. 22*58b9f456SAndroid Build Coastguard Worker """ 23*58b9f456SAndroid Build Coastguard Worker if not os.path.isfile(filename): 24*58b9f456SAndroid Build Coastguard Worker return False 25*58b9f456SAndroid Build Coastguard Worker with open(filename, mode='rb') as f: 26*58b9f456SAndroid Build Coastguard Worker magic_bytes = f.read(_num_magic_bytes) 27*58b9f456SAndroid Build Coastguard Worker if sys.platform == 'darwin': 28*58b9f456SAndroid Build Coastguard Worker return magic_bytes in [ 29*58b9f456SAndroid Build Coastguard Worker b'\xfe\xed\xfa\xce', # MH_MAGIC 30*58b9f456SAndroid Build Coastguard Worker b'\xce\xfa\xed\xfe', # MH_CIGAM 31*58b9f456SAndroid Build Coastguard Worker b'\xfe\xed\xfa\xcf', # MH_MAGIC_64 32*58b9f456SAndroid Build Coastguard Worker b'\xcf\xfa\xed\xfe', # MH_CIGAM_64 33*58b9f456SAndroid Build Coastguard Worker b'\xca\xfe\xba\xbe', # FAT_MAGIC 34*58b9f456SAndroid Build Coastguard Worker b'\xbe\xba\xfe\xca' # FAT_CIGAM 35*58b9f456SAndroid Build Coastguard Worker ] 36*58b9f456SAndroid Build Coastguard Worker elif sys.platform.startswith('win'): 37*58b9f456SAndroid Build Coastguard Worker return magic_bytes == b'MZ' 38*58b9f456SAndroid Build Coastguard Worker else: 39*58b9f456SAndroid Build Coastguard Worker return magic_bytes == b'\x7FELF' 40*58b9f456SAndroid Build Coastguard Worker 41*58b9f456SAndroid Build Coastguard Worker 42*58b9f456SAndroid Build Coastguard Workerdef is_json_file(filename): 43*58b9f456SAndroid Build Coastguard Worker """ 44*58b9f456SAndroid Build Coastguard Worker Returns 'True' if 'filename' names a valid JSON output file. 45*58b9f456SAndroid Build Coastguard Worker 'False' otherwise. 46*58b9f456SAndroid Build Coastguard Worker """ 47*58b9f456SAndroid Build Coastguard Worker try: 48*58b9f456SAndroid Build Coastguard Worker with open(filename, 'r') as f: 49*58b9f456SAndroid Build Coastguard Worker json.load(f) 50*58b9f456SAndroid Build Coastguard Worker return True 51*58b9f456SAndroid Build Coastguard Worker except BaseException: 52*58b9f456SAndroid Build Coastguard Worker pass 53*58b9f456SAndroid Build Coastguard Worker return False 54*58b9f456SAndroid Build Coastguard Worker 55*58b9f456SAndroid Build Coastguard Worker 56*58b9f456SAndroid Build Coastguard Workerdef classify_input_file(filename): 57*58b9f456SAndroid Build Coastguard Worker """ 58*58b9f456SAndroid Build Coastguard Worker Return a tuple (type, msg) where 'type' specifies the classified type 59*58b9f456SAndroid Build Coastguard Worker of 'filename'. If 'type' is 'IT_Invalid' then 'msg' is a human readable 60*58b9f456SAndroid Build Coastguard Worker string represeting the error. 61*58b9f456SAndroid Build Coastguard Worker """ 62*58b9f456SAndroid Build Coastguard Worker ftype = IT_Invalid 63*58b9f456SAndroid Build Coastguard Worker err_msg = None 64*58b9f456SAndroid Build Coastguard Worker if not os.path.exists(filename): 65*58b9f456SAndroid Build Coastguard Worker err_msg = "'%s' does not exist" % filename 66*58b9f456SAndroid Build Coastguard Worker elif not os.path.isfile(filename): 67*58b9f456SAndroid Build Coastguard Worker err_msg = "'%s' does not name a file" % filename 68*58b9f456SAndroid Build Coastguard Worker elif is_executable_file(filename): 69*58b9f456SAndroid Build Coastguard Worker ftype = IT_Executable 70*58b9f456SAndroid Build Coastguard Worker elif is_json_file(filename): 71*58b9f456SAndroid Build Coastguard Worker ftype = IT_JSON 72*58b9f456SAndroid Build Coastguard Worker else: 73*58b9f456SAndroid Build Coastguard Worker err_msg = "'%s' does not name a valid benchmark executable or JSON file" % filename 74*58b9f456SAndroid Build Coastguard Worker return ftype, err_msg 75*58b9f456SAndroid Build Coastguard Worker 76*58b9f456SAndroid Build Coastguard Worker 77*58b9f456SAndroid Build Coastguard Workerdef check_input_file(filename): 78*58b9f456SAndroid Build Coastguard Worker """ 79*58b9f456SAndroid Build Coastguard Worker Classify the file named by 'filename' and return the classification. 80*58b9f456SAndroid Build Coastguard Worker If the file is classified as 'IT_Invalid' print an error message and exit 81*58b9f456SAndroid Build Coastguard Worker the program. 82*58b9f456SAndroid Build Coastguard Worker """ 83*58b9f456SAndroid Build Coastguard Worker ftype, msg = classify_input_file(filename) 84*58b9f456SAndroid Build Coastguard Worker if ftype == IT_Invalid: 85*58b9f456SAndroid Build Coastguard Worker print("Invalid input file: %s" % msg) 86*58b9f456SAndroid Build Coastguard Worker sys.exit(1) 87*58b9f456SAndroid Build Coastguard Worker return ftype 88*58b9f456SAndroid Build Coastguard Worker 89*58b9f456SAndroid Build Coastguard Worker 90*58b9f456SAndroid Build Coastguard Workerdef find_benchmark_flag(prefix, benchmark_flags): 91*58b9f456SAndroid Build Coastguard Worker """ 92*58b9f456SAndroid Build Coastguard Worker Search the specified list of flags for a flag matching `<prefix><arg>` and 93*58b9f456SAndroid Build Coastguard Worker if it is found return the arg it specifies. If specified more than once the 94*58b9f456SAndroid Build Coastguard Worker last value is returned. If the flag is not found None is returned. 95*58b9f456SAndroid Build Coastguard Worker """ 96*58b9f456SAndroid Build Coastguard Worker assert prefix.startswith('--') and prefix.endswith('=') 97*58b9f456SAndroid Build Coastguard Worker result = None 98*58b9f456SAndroid Build Coastguard Worker for f in benchmark_flags: 99*58b9f456SAndroid Build Coastguard Worker if f.startswith(prefix): 100*58b9f456SAndroid Build Coastguard Worker result = f[len(prefix):] 101*58b9f456SAndroid Build Coastguard Worker return result 102*58b9f456SAndroid Build Coastguard Worker 103*58b9f456SAndroid Build Coastguard Worker 104*58b9f456SAndroid Build Coastguard Workerdef remove_benchmark_flags(prefix, benchmark_flags): 105*58b9f456SAndroid Build Coastguard Worker """ 106*58b9f456SAndroid Build Coastguard Worker Return a new list containing the specified benchmark_flags except those 107*58b9f456SAndroid Build Coastguard Worker with the specified prefix. 108*58b9f456SAndroid Build Coastguard Worker """ 109*58b9f456SAndroid Build Coastguard Worker assert prefix.startswith('--') and prefix.endswith('=') 110*58b9f456SAndroid Build Coastguard Worker return [f for f in benchmark_flags if not f.startswith(prefix)] 111*58b9f456SAndroid Build Coastguard Worker 112*58b9f456SAndroid Build Coastguard Worker 113*58b9f456SAndroid Build Coastguard Workerdef load_benchmark_results(fname): 114*58b9f456SAndroid Build Coastguard Worker """ 115*58b9f456SAndroid Build Coastguard Worker Read benchmark output from a file and return the JSON object. 116*58b9f456SAndroid Build Coastguard Worker REQUIRES: 'fname' names a file containing JSON benchmark output. 117*58b9f456SAndroid Build Coastguard Worker """ 118*58b9f456SAndroid Build Coastguard Worker with open(fname, 'r') as f: 119*58b9f456SAndroid Build Coastguard Worker return json.load(f) 120*58b9f456SAndroid Build Coastguard Worker 121*58b9f456SAndroid Build Coastguard Worker 122*58b9f456SAndroid Build Coastguard Workerdef run_benchmark(exe_name, benchmark_flags): 123*58b9f456SAndroid Build Coastguard Worker """ 124*58b9f456SAndroid Build Coastguard Worker Run a benchmark specified by 'exe_name' with the specified 125*58b9f456SAndroid Build Coastguard Worker 'benchmark_flags'. The benchmark is run directly as a subprocess to preserve 126*58b9f456SAndroid Build Coastguard Worker real time console output. 127*58b9f456SAndroid Build Coastguard Worker RETURNS: A JSON object representing the benchmark output 128*58b9f456SAndroid Build Coastguard Worker """ 129*58b9f456SAndroid Build Coastguard Worker output_name = find_benchmark_flag('--benchmark_out=', 130*58b9f456SAndroid Build Coastguard Worker benchmark_flags) 131*58b9f456SAndroid Build Coastguard Worker is_temp_output = False 132*58b9f456SAndroid Build Coastguard Worker if output_name is None: 133*58b9f456SAndroid Build Coastguard Worker is_temp_output = True 134*58b9f456SAndroid Build Coastguard Worker thandle, output_name = tempfile.mkstemp() 135*58b9f456SAndroid Build Coastguard Worker os.close(thandle) 136*58b9f456SAndroid Build Coastguard Worker benchmark_flags = list(benchmark_flags) + \ 137*58b9f456SAndroid Build Coastguard Worker ['--benchmark_out=%s' % output_name] 138*58b9f456SAndroid Build Coastguard Worker 139*58b9f456SAndroid Build Coastguard Worker cmd = [exe_name] + benchmark_flags 140*58b9f456SAndroid Build Coastguard Worker print("RUNNING: %s" % ' '.join(cmd)) 141*58b9f456SAndroid Build Coastguard Worker exitCode = subprocess.call(cmd) 142*58b9f456SAndroid Build Coastguard Worker if exitCode != 0: 143*58b9f456SAndroid Build Coastguard Worker print('TEST FAILED...') 144*58b9f456SAndroid Build Coastguard Worker sys.exit(exitCode) 145*58b9f456SAndroid Build Coastguard Worker json_res = load_benchmark_results(output_name) 146*58b9f456SAndroid Build Coastguard Worker if is_temp_output: 147*58b9f456SAndroid Build Coastguard Worker os.unlink(output_name) 148*58b9f456SAndroid Build Coastguard Worker return json_res 149*58b9f456SAndroid Build Coastguard Worker 150*58b9f456SAndroid Build Coastguard Worker 151*58b9f456SAndroid Build Coastguard Workerdef run_or_load_benchmark(filename, benchmark_flags): 152*58b9f456SAndroid Build Coastguard Worker """ 153*58b9f456SAndroid Build Coastguard Worker Get the results for a specified benchmark. If 'filename' specifies 154*58b9f456SAndroid Build Coastguard Worker an executable benchmark then the results are generated by running the 155*58b9f456SAndroid Build Coastguard Worker benchmark. Otherwise 'filename' must name a valid JSON output file, 156*58b9f456SAndroid Build Coastguard Worker which is loaded and the result returned. 157*58b9f456SAndroid Build Coastguard Worker """ 158*58b9f456SAndroid Build Coastguard Worker ftype = check_input_file(filename) 159*58b9f456SAndroid Build Coastguard Worker if ftype == IT_JSON: 160*58b9f456SAndroid Build Coastguard Worker return load_benchmark_results(filename) 161*58b9f456SAndroid Build Coastguard Worker elif ftype == IT_Executable: 162*58b9f456SAndroid Build Coastguard Worker return run_benchmark(filename, benchmark_flags) 163*58b9f456SAndroid Build Coastguard Worker else: 164*58b9f456SAndroid Build Coastguard Worker assert False # This branch is unreachable 165