1*62c56f98SSadaf Ebrahimi#!/usr/bin/env python3 2*62c56f98SSadaf Ebrahimi"""Describe the test coverage of PSA functions in terms of return statuses. 3*62c56f98SSadaf Ebrahimi 4*62c56f98SSadaf Ebrahimi1. Build Mbed TLS with -DRECORD_PSA_STATUS_COVERAGE_LOG 5*62c56f98SSadaf Ebrahimi2. Run psa_collect_statuses.py 6*62c56f98SSadaf Ebrahimi 7*62c56f98SSadaf EbrahimiThe output is a series of line of the form "psa_foo PSA_ERROR_XXX". Each 8*62c56f98SSadaf Ebrahimifunction/status combination appears only once. 9*62c56f98SSadaf Ebrahimi 10*62c56f98SSadaf EbrahimiThis script must be run from the top of an Mbed TLS source tree. 11*62c56f98SSadaf EbrahimiThe build command is "make -DRECORD_PSA_STATUS_COVERAGE_LOG", which is 12*62c56f98SSadaf Ebrahimionly supported with make (as opposed to CMake or other build methods). 13*62c56f98SSadaf Ebrahimi""" 14*62c56f98SSadaf Ebrahimi 15*62c56f98SSadaf Ebrahimi# Copyright The Mbed TLS Contributors 16*62c56f98SSadaf Ebrahimi# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 17*62c56f98SSadaf Ebrahimi 18*62c56f98SSadaf Ebrahimiimport argparse 19*62c56f98SSadaf Ebrahimiimport os 20*62c56f98SSadaf Ebrahimiimport subprocess 21*62c56f98SSadaf Ebrahimiimport sys 22*62c56f98SSadaf Ebrahimi 23*62c56f98SSadaf EbrahimiDEFAULT_STATUS_LOG_FILE = 'tests/statuses.log' 24*62c56f98SSadaf EbrahimiDEFAULT_PSA_CONSTANT_NAMES = 'programs/psa/psa_constant_names' 25*62c56f98SSadaf Ebrahimi 26*62c56f98SSadaf Ebrahimiclass Statuses: 27*62c56f98SSadaf Ebrahimi """Information about observed return statues of API functions.""" 28*62c56f98SSadaf Ebrahimi 29*62c56f98SSadaf Ebrahimi def __init__(self): 30*62c56f98SSadaf Ebrahimi self.functions = {} 31*62c56f98SSadaf Ebrahimi self.codes = set() 32*62c56f98SSadaf Ebrahimi self.status_names = {} 33*62c56f98SSadaf Ebrahimi 34*62c56f98SSadaf Ebrahimi def collect_log(self, log_file_name): 35*62c56f98SSadaf Ebrahimi """Read logs from RECORD_PSA_STATUS_COVERAGE_LOG. 36*62c56f98SSadaf Ebrahimi 37*62c56f98SSadaf Ebrahimi Read logs produced by running Mbed TLS test suites built with 38*62c56f98SSadaf Ebrahimi -DRECORD_PSA_STATUS_COVERAGE_LOG. 39*62c56f98SSadaf Ebrahimi """ 40*62c56f98SSadaf Ebrahimi with open(log_file_name) as log: 41*62c56f98SSadaf Ebrahimi for line in log: 42*62c56f98SSadaf Ebrahimi value, function, tail = line.split(':', 2) 43*62c56f98SSadaf Ebrahimi if function not in self.functions: 44*62c56f98SSadaf Ebrahimi self.functions[function] = {} 45*62c56f98SSadaf Ebrahimi fdata = self.functions[function] 46*62c56f98SSadaf Ebrahimi if value not in self.functions[function]: 47*62c56f98SSadaf Ebrahimi fdata[value] = [] 48*62c56f98SSadaf Ebrahimi fdata[value].append(tail) 49*62c56f98SSadaf Ebrahimi self.codes.add(int(value)) 50*62c56f98SSadaf Ebrahimi 51*62c56f98SSadaf Ebrahimi def get_constant_names(self, psa_constant_names): 52*62c56f98SSadaf Ebrahimi """Run psa_constant_names to obtain names for observed numerical values.""" 53*62c56f98SSadaf Ebrahimi values = [str(value) for value in self.codes] 54*62c56f98SSadaf Ebrahimi cmd = [psa_constant_names, 'status'] + values 55*62c56f98SSadaf Ebrahimi output = subprocess.check_output(cmd).decode('ascii') 56*62c56f98SSadaf Ebrahimi for value, name in zip(values, output.rstrip().split('\n')): 57*62c56f98SSadaf Ebrahimi self.status_names[value] = name 58*62c56f98SSadaf Ebrahimi 59*62c56f98SSadaf Ebrahimi def report(self): 60*62c56f98SSadaf Ebrahimi """Report observed return values for each function. 61*62c56f98SSadaf Ebrahimi 62*62c56f98SSadaf Ebrahimi The report is a series of line of the form "psa_foo PSA_ERROR_XXX". 63*62c56f98SSadaf Ebrahimi """ 64*62c56f98SSadaf Ebrahimi for function in sorted(self.functions.keys()): 65*62c56f98SSadaf Ebrahimi fdata = self.functions[function] 66*62c56f98SSadaf Ebrahimi names = [self.status_names[value] for value in fdata.keys()] 67*62c56f98SSadaf Ebrahimi for name in sorted(names): 68*62c56f98SSadaf Ebrahimi sys.stdout.write('{} {}\n'.format(function, name)) 69*62c56f98SSadaf Ebrahimi 70*62c56f98SSadaf Ebrahimidef collect_status_logs(options): 71*62c56f98SSadaf Ebrahimi """Build and run unit tests and report observed function return statuses. 72*62c56f98SSadaf Ebrahimi 73*62c56f98SSadaf Ebrahimi Build Mbed TLS with -DRECORD_PSA_STATUS_COVERAGE_LOG, run the 74*62c56f98SSadaf Ebrahimi test suites and display information about observed return statuses. 75*62c56f98SSadaf Ebrahimi """ 76*62c56f98SSadaf Ebrahimi rebuilt = False 77*62c56f98SSadaf Ebrahimi if not options.use_existing_log and os.path.exists(options.log_file): 78*62c56f98SSadaf Ebrahimi os.remove(options.log_file) 79*62c56f98SSadaf Ebrahimi if not os.path.exists(options.log_file): 80*62c56f98SSadaf Ebrahimi if options.clean_before: 81*62c56f98SSadaf Ebrahimi subprocess.check_call(['make', 'clean'], 82*62c56f98SSadaf Ebrahimi cwd='tests', 83*62c56f98SSadaf Ebrahimi stdout=sys.stderr) 84*62c56f98SSadaf Ebrahimi with open(os.devnull, 'w') as devnull: 85*62c56f98SSadaf Ebrahimi make_q_ret = subprocess.call(['make', '-q', 'lib', 'tests'], 86*62c56f98SSadaf Ebrahimi stdout=devnull, stderr=devnull) 87*62c56f98SSadaf Ebrahimi if make_q_ret != 0: 88*62c56f98SSadaf Ebrahimi subprocess.check_call(['make', 'RECORD_PSA_STATUS_COVERAGE_LOG=1'], 89*62c56f98SSadaf Ebrahimi stdout=sys.stderr) 90*62c56f98SSadaf Ebrahimi rebuilt = True 91*62c56f98SSadaf Ebrahimi subprocess.check_call(['make', 'test'], 92*62c56f98SSadaf Ebrahimi stdout=sys.stderr) 93*62c56f98SSadaf Ebrahimi data = Statuses() 94*62c56f98SSadaf Ebrahimi data.collect_log(options.log_file) 95*62c56f98SSadaf Ebrahimi data.get_constant_names(options.psa_constant_names) 96*62c56f98SSadaf Ebrahimi if rebuilt and options.clean_after: 97*62c56f98SSadaf Ebrahimi subprocess.check_call(['make', 'clean'], 98*62c56f98SSadaf Ebrahimi cwd='tests', 99*62c56f98SSadaf Ebrahimi stdout=sys.stderr) 100*62c56f98SSadaf Ebrahimi return data 101*62c56f98SSadaf Ebrahimi 102*62c56f98SSadaf Ebrahimidef main(): 103*62c56f98SSadaf Ebrahimi parser = argparse.ArgumentParser(description=globals()['__doc__']) 104*62c56f98SSadaf Ebrahimi parser.add_argument('--clean-after', 105*62c56f98SSadaf Ebrahimi action='store_true', 106*62c56f98SSadaf Ebrahimi help='Run "make clean" after rebuilding') 107*62c56f98SSadaf Ebrahimi parser.add_argument('--clean-before', 108*62c56f98SSadaf Ebrahimi action='store_true', 109*62c56f98SSadaf Ebrahimi help='Run "make clean" before regenerating the log file)') 110*62c56f98SSadaf Ebrahimi parser.add_argument('--log-file', metavar='FILE', 111*62c56f98SSadaf Ebrahimi default=DEFAULT_STATUS_LOG_FILE, 112*62c56f98SSadaf Ebrahimi help='Log file location (default: {})'.format( 113*62c56f98SSadaf Ebrahimi DEFAULT_STATUS_LOG_FILE 114*62c56f98SSadaf Ebrahimi )) 115*62c56f98SSadaf Ebrahimi parser.add_argument('--psa-constant-names', metavar='PROGRAM', 116*62c56f98SSadaf Ebrahimi default=DEFAULT_PSA_CONSTANT_NAMES, 117*62c56f98SSadaf Ebrahimi help='Path to psa_constant_names (default: {})'.format( 118*62c56f98SSadaf Ebrahimi DEFAULT_PSA_CONSTANT_NAMES 119*62c56f98SSadaf Ebrahimi )) 120*62c56f98SSadaf Ebrahimi parser.add_argument('--use-existing-log', '-e', 121*62c56f98SSadaf Ebrahimi action='store_true', 122*62c56f98SSadaf Ebrahimi help='Don\'t regenerate the log file if it exists') 123*62c56f98SSadaf Ebrahimi options = parser.parse_args() 124*62c56f98SSadaf Ebrahimi data = collect_status_logs(options) 125*62c56f98SSadaf Ebrahimi data.report() 126*62c56f98SSadaf Ebrahimi 127*62c56f98SSadaf Ebrahimiif __name__ == '__main__': 128*62c56f98SSadaf Ebrahimi main() 129