1#!/usr/bin/env python3 2 3# requires https://github.com/bright-tools/ccsm 4 5import os 6import sys 7import csv 8 9folders = [ 10'src', 11'src/ble', 12'src/ble/gatt-service', 13'src/classic', 14] 15 16metrics = {} 17targets = {} 18histogram_data = {} 19 20targets['PATH'] = 1000 21targets['GOTO'] = 0 22targets['CCN'] = 20 23targets['CALLS'] = 12 24targets['PARAM'] = 7 25targets['STMT'] = 100 26targets['LEVEL'] = 6 27targets['RETURN'] = 1 28 29excluded_functions = [ 30 # deprecated functions 31 'src/l2cap.c:l2cap_le_register_service', 32 'src/l2cap.c:l2cap_le_unregister_service', 33 'src/l2cap.c:l2cap_le_accept_connection', 34 'src/l2cap.c:l2cap_le_decline_connection', 35 'src/l2cap.c:l2cap_le_provide_credits', 36 'src/l2cap.c:l2cap_le_create_channel', 37 'src/l2cap.c:l2cap_le_can_send_now', 38 'src/l2cap.c:l2cap_le_request_can_send_now_event', 39 'src/l2cap.c:l2cap_le_send_data', 40 'src/l2cap.c:l2cap_le_disconnect', 41 'src/l2cap.c:l2cap_cbm_can_send_now', 42 'src/l2cap.c:l2cap_cbm_request_can_send_now_even' 43] 44 45def histogram_add_data(key, value): 46 global histogram_data 47 if key not in histogram_data.keys(): 48 histogram_data[key] = [] 49 histogram_data[key].append(value) 50 51 52def metric_sum(name, value): 53 global metrics 54 old = 0 55 if name in metrics: 56 old = metrics[name] 57 metrics[name] = old + value 58 59 60def metric_list(name, item): 61 global metrics 62 value = [] 63 if name in metrics: 64 value = metrics[name] 65 value.append(item) 66 metrics[name] = value 67 68def metric_max(name, max): 69 global metrics 70 if name in metrics: 71 if metrics[name] > max: 72 return 73 metrics[name] = max 74 75def metric_measure(metric_name, function_name, actual): 76 metric_max(metric_name + '_MAX', actual) 77 if metric_name in targets: 78 metric_sum(metric_name + '_SUM', actual) 79 if actual > targets[metric_name]: 80 metric_sum(metric_name + '_DEVIATIONS', 1) 81 # metric_list(metric_name + '_LIST', function_name + '(%u)' % actual) 82 metric_list(metric_name + '_LIST', function_name) 83 84 85def analyze_folders(btstack_root, folders, metrics_file): 86 global excluded_functions 87 88 # File,Name,"'goto' keyword count (raw source)","Return points","Statement count (raw source)(local)", 89 # "Statement count (raw source)(cumulative)","Comment density","McCabe complexity (raw source)", 90 # "Number of paths through the function","No. different functions called","Function Parameters", 91 # "Nesting Level","VOCF","Number of functions which call this function", 92 fields = [ 'file','function','GOTO','RETURN','_','STMT' ,'_','CCN','PATH','CALLS','PARAM','LEVEL','_','_','_'] 93 94 # init deviations 95 for key in fields: 96 metrics[key + '_DEVIATIONS'] = 0 97 98 # for now, just read the file 99 with open(metrics_file) as fd: 100 rd = csv.reader(fd, delimiter="\t") 101 last_function_name = '' 102 for row in rd: 103 file = '' 104 function_metrics = {} 105 for key, value in zip(fields, row): 106 if key == 'file': 107 # get rid of directory traversal on buildbot 108 pos_metrics_folder = value.find('tool/metrics/') 109 if pos_metrics_folder > 0: 110 value = value[pos_metrics_folder+13:] 111 # streamline path 112 file = value.replace('../../','') 113 continue 114 if key == 'function': 115 function_name = value 116 continue 117 if key == '_': 118 continue 119 function_metrics[key] = value 120 histogram_add_data(key,int(value)) 121 122 if file.endswith('.h'): 123 continue 124 qualified_function_name = file+':'+function_name 125 # excluded functions 126 if qualified_function_name in excluded_functions: 127 continue 128 metric_sum('FUNC', 1) 129 for key,value in function_metrics.items(): 130 metric_measure(key, qualified_function_name, int(function_metrics[key])) 131 132def analyze(folders, metrics_file): 133 # print ("\nAnalyzing:") 134 # for path in folders: 135 # print('- %s' % path) 136 # analyze_folder(btstack_root + "/" + path) 137 btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/../..') 138 analyze_folders(btstack_root, folders, metrics_file) 139 140def list_targets(): 141 print ("Targets:") 142 for key,value in sorted(targets.items()): 143 print ('- %-20s: %u' % (key, value)) 144 145def list_metrics(): 146 print ("\nResult:") 147 num_funcs = metrics['FUNC'] 148 for key,value in sorted(metrics.items()): 149 if key.endswith('LIST'): 150 continue 151 if key.endswith('_SUM'): 152 average = 1.0 * value / num_funcs 153 metric = key.replace('_SUM','_AVERAGE') 154 print ('- %-20s: %4.3f' % (metric, average)) 155 else: 156 print ('- %-20s: %5u' % (key, value)) 157 158def list_metrics_table(): 159 row = "%-11s |%11s |%11s |%11s" 160 161 print( row % ('Name', 'Target', 'Deviations', 'Max value')) 162 print("------------|------------|------------|------------") 163 164 ordered_metrics = [ 'PATH', 'GOTO', 'CCN', 'CALLS', 'PARAM', 'STMT', 'LEVEL', 'RETURN', 'FUNC']; 165 for metric_name in ordered_metrics: 166 if metric_name in targets: 167 target = targets[metric_name] 168 deviations = metrics[metric_name + '_DEVIATIONS'] 169 max = metrics[metric_name + '_MAX'] 170 print ( row % ( metric_name, target, deviations, max)) 171 else: 172 max = metrics[metric_name] 173 print ( row % ( metric_name, '', '', max)) 174 175def list_deviations(): 176 global metrics 177 for key,value in sorted(metrics.items()): 178 if not key.endswith('LIST'): 179 continue 180 print ("\n%s" % key) 181 print ('\n'.join(value)) 182 183def main(argv): 184 analyze(folders, "metrics.tsv") 185 list_metrics_table() 186 # list_targets() 187 # list_metrics() 188 # list_deviations() 189 190if __name__ == "__main__": 191 main(sys.argv[1:]) 192