114 lines · plain
1#!/usr/bin/env python32 3import argparse4import csv5import sys6 7def parse_table(rows, table_title):8 """9 Parse a CSV table out of an iterator over rows.10 11 Return a tuple containing (extracted headers, extracted rows).12 """13 in_table = False14 rows_iter = iter(rows)15 extracted = []16 headers = None17 while True:18 try:19 row = next(rows_iter)20 except StopIteration:21 break22 23 if not in_table and row == [table_title]:24 in_table = True25 next_row = next(rows_iter)26 assert next_row == [], f'There should be an empty row after the title of the table, found {next_row}'27 headers = next(rows_iter) # Extract the headers28 continue29 30 elif in_table and row == []: # An empty row marks the end of the table31 in_table = False32 break33 34 elif in_table:35 extracted.append(row)36 37 assert len(extracted) != 0, f'Could not extract rows from the table, this is suspicious. Table title was {table_title}'38 assert headers is not None, f'Could not extract headers from the table, this is suspicious. Table title was {table_title}'39 40 return (headers, extracted)41 42def main(argv):43 parser = argparse.ArgumentParser(44 prog='parse-spec-results',45 description='Parse SPEC result files (in CSV format) and extract the selected result table, in the selected format.')46 parser.add_argument('filename', type=argparse.FileType('r'), nargs='+',47 help='One of more CSV files to extract the results from. The results parsed from each file are concatenated '48 'together.')49 parser.add_argument('--table', type=str, choices=['full', 'selected'], default='full',50 help='The name of the table to extract from SPEC results. `full` means extracting the Full Results Table '51 'and `selected` means extracting the Selected Results Table. Default is `full`.')52 parser.add_argument('--output-format', type=str, choices=['csv', 'lnt'], default='csv',53 help='The desired output format for the data. `csv` is CSV format and `lnt` is a format compatible with '54 '`lnt importreport` (see https://llvm.org/docs/lnt/importing_data.html#importing-data-in-a-text-file).')55 parser.add_argument('--extract', type=str,56 help='A comma-separated list of headers to extract from the table. If provided, only the data associated to '57 'those headers will be present in the resulting data. Invalid header names are diagnosed. Please make '58 'sure to use appropriate quoting for header names that contain spaces. This option only makes sense '59 'when the output format is CSV.')60 parser.add_argument('--keep-not-run', action='store_true',61 help='Keep entries whose "Base Status" is marked as "NR" (aka "Not Run"). By default, such entries are discarded.')62 parser.add_argument('--keep-failed', action='store_true',63 help='Keep entries whose "Base Status" is marked as "CE" (aka "Compilation Error") or "RE" (aka "Runtime Error"). '64 'By default, such entries are discarded.')65 args = parser.parse_args(argv)66 67 if args.table == 'full':68 table_title = 'Full Results Table'69 elif args.table == 'selected':70 table_title = 'Selected Results Table'71 72 # Parse the headers and the rows in each file, aggregating all the results73 headers = None74 rows = []75 for file in args.filename:76 reader = csv.reader(file)77 (parsed_headers, parsed_rows) = parse_table(reader, table_title)78 assert headers is None or headers == parsed_headers, f'Found files with different headers: {headers} and {parsed_headers}'79 headers = parsed_headers80 rows.extend(parsed_rows)81 82 # Remove rows that were not run (or failed) unless we were asked to keep them83 status = headers.index('Base Status')84 if not args.keep_not_run:85 rows = [row for row in rows if row[status] != 'NR']86 if not args.keep_failed:87 rows = [row for row in rows if row[status] not in ('CE', 'RE')]88 89 if args.extract is not None:90 if args.output_format != 'csv':91 raise RuntimeError('Passing --extract requires the output format to be csv')92 for h in args.extract.split(','):93 if h not in headers:94 raise RuntimeError(f'Header name {h} was not present in the parsed headers {headers}')95 96 extracted_fields = [headers.index(h) for h in args.extract.split(',')]97 headers = [headers[i] for i in extracted_fields]98 rows = [[row[i] for i in extracted_fields] for row in rows]99 100 # Print the results in the right format101 if args.output_format == 'csv':102 writer = csv.writer(sys.stdout)103 writer.writerow(headers)104 for row in rows:105 writer.writerow(row)106 elif args.output_format == 'lnt':107 benchmark = headers.index('Benchmark')108 time = headers.index('Est. Base Run Time')109 for row in rows:110 print(f'{row[benchmark].replace(".", "_")}.execution_time {row[time]}')111 112if __name__ == '__main__':113 main(sys.argv[1:])114