blob: bdfc96693225556d3e59a56e98f6da61aca70041 [file] [log] [blame]
#!/usr/bin/env python3
import argparse
import csv
import sys
def parse_table(rows, table_title):
"""
Parse a CSV table out of an iterator over rows.
Return a tuple containing (extracted headers, extracted rows).
"""
in_table = False
rows_iter = iter(rows)
extracted = []
headers = None
while True:
try:
row = next(rows_iter)
except StopIteration:
break
if not in_table and row == [table_title]:
in_table = True
next_row = next(rows_iter)
assert next_row == [], f'There should be an empty row after the title of the table, found {next_row}'
headers = next(rows_iter) # Extract the headers
continue
elif in_table and row == []: # An empty row marks the end of the table
in_table = False
break
elif in_table:
extracted.append(row)
assert len(extracted) != 0, f'Could not extract rows from the table, this is suspicious. Table title was {table_title}'
assert headers is not None, f'Could not extract headers from the table, this is suspicious. Table title was {table_title}'
return (headers, extracted)
def main(argv):
parser = argparse.ArgumentParser(
prog='parse-spec-results',
description='Parse SPEC result files (in CSV format) and extract the selected result table, in the selected format.')
parser.add_argument('filename', type=argparse.FileType('r'), nargs='+',
help='One of more CSV files to extract the results from. The results parsed from each file are concatenated '
'together, creating a single CSV table.')
parser.add_argument('--table', type=str, choices=['full', 'selected'], default='full',
help='The name of the table to extract from SPEC results. `full` means extracting the Full Results Table '
'and `selected` means extracting the Selected Results Table. Default is `full`.')
parser.add_argument('--output-format', type=str, choices=['csv', 'lnt'], default='csv',
help='The desired output format for the data. `csv` is CSV format and `lnt` is a format compatible with '
'`lnt importreport` (see https://llvm.org/docs/lnt/importing_data.html#importing-data-in-a-text-file).')
parser.add_argument('--extract', type=str,
help='A comma-separated list of headers to extract from the table. If provided, only the data associated to '
'those headers will be present in the resulting data. Invalid header names are diagnosed. Please make '
'sure to use appropriate quoting for header names that contain spaces. This option only makes sense '
'when the output format is CSV.')
parser.add_argument('--keep-not-run', action='store_true',
help='Keep entries whose \'Base Status\' is marked as \'NR\', aka \'Not Run\'. By default, such entries are discarded.')
args = parser.parse_args(argv)
if args.table == 'full':
table_title = 'Full Results Table'
elif args.table == 'selected':
table_title = 'Selected Results Table'
# Parse the headers and the rows in each file, aggregating all the results
headers = None
rows = []
for file in args.filename:
reader = csv.reader(file)
(parsed_headers, parsed_rows) = parse_table(reader, table_title)
assert headers is None or headers == parsed_headers, f'Found files with different headers: {headers} and {parsed_headers}'
headers = parsed_headers
rows.extend(parsed_rows)
# Remove rows that were not run unless we were asked to keep them
if not args.keep_not_run:
not_run = headers.index('Base Status')
rows = [row for row in rows if row[not_run] != 'NR']
if args.extract is not None:
if args.output_format != 'csv':
raise RuntimeError('Passing --extract requires the output format to be csv')
for h in args.extract.split(','):
if h not in headers:
raise RuntimeError(f'Header name {h} was not present in the parsed headers {headers}')
extracted_fields = [headers.index(h) for h in args.extract.split(',')]
headers = [headers[i] for i in extracted_fields]
rows = [[row[i] for i in extracted_fields] for row in rows]
# Print the results in the right format
if args.output_format == 'csv':
writer = csv.writer(sys.stdout)
writer.writerow(headers)
for row in rows:
writer.writerow(row)
elif args.output_format == 'lnt':
benchmark = headers.index('Benchmark')
time = headers.index('Est. Base Run Time')
for row in rows:
print(f'{row[benchmark].replace(".", "_")}.execution_time {row[time]}')
if __name__ == '__main__':
main(sys.argv[1:])