1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
#!/usr/bin/env python3
"""Utility script for submitting benchmark results to an LNT server."""
import datetime
import errno
import json
import optparse
import subprocess
import sys
import urllib
import urllib2
# Test status codes.
PASS = 0
FAIL = 1
XFAIL = 2
###
def capture_with_result(args, include_stderr=False):
"""capture_with_result(command) -> (output, exit code)
Run the given command (or argv list) in a shell and return the standard
output and exit code.
"""
stderr = subprocess.PIPE
if include_stderr:
stderr = subprocess.STDOUT
try:
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=stderr)
except OSError as e:
if e.errno == errno.ENOENT:
sys.exit('no such file or directory: %r when running %s.' % (
args[0], ' '.join(args)))
raise
out, _ = p.communicate()
return out, p.wait()
def capture(args, include_stderr=False):
"""capture(command) - Run the given command (or argv list) in a shell and
return the standard output.
"""
return capture_with_result(args, include_stderr)[0]
def timestamp():
return datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
###
def submit_results_to_server(results_data, submit_url):
# Submit the URL encoded data.
data = urllib.urlencode({'input_data': results_data,
'commit': '1'})
response = urllib2.urlopen(urllib2.Request(submit_url, data))
result_data = response.read()
# The result is expected to be a JSON object.
try:
return json.loads(result_data)
except ValueError:
import traceback
print("Unable to load result, not a valid JSON object.")
print()
print("Traceback:")
traceback.print_exc()
print()
print("Result:")
print(result_data)
return
###
def main():
parser = optparse.OptionParser("""%prog [options] <results>""")
parser.add_option("", "--submit", dest="submit_url", metavar="URL",
help="Submit results to the given URL",
action="store", type=str, default=None)
parser.add_option("", "--output", dest="output", metavar="PATH",
help="Write raw report data to PATH",
action="store", type=str, default=None)
parser.add_option("", "--machine-name", dest="machine_name",
metavar="NAME",
help="Set the machine name to embed in the report",
action="store", type=str, default=None)
parser.add_option("", "--run-order", dest="run_order", metavar="REVISION",
help="Set the run order to embed in the report",
action="store", type=int, default=None)
opts, args = parser.parse_args()
# At least one of --submit or --output is required.
if len(args) != 1:
parser.error("incorrect number of arguments")
if opts.submit_url is None and opts.output is None:
parser.error("no action given (provide --submit or --output)")
if opts.machine_name is None:
parser.error("--machine-name is required")
if opts.run_order is None:
parser.error("--run-order is required")
# Load the results data.
results_path, = args
with open(results_path) as f:
data = json.load(f)
# Compute some data not present in the 'lit' report.
machine_name = opts.machine_name
run_order = str(opts.run_order)
# Estimate the end time as being now, and the start time as being that
# minus the elapsed testing time.
utcnow = datetime.datetime.utcnow()
start_time = utcnow - datetime.timedelta(seconds=data['elapsed'])
end_time = utcnow
# Create the LNT report format.
lnt_results = {}
lnt_results['Machine'] = {
'Name': machine_name,
'Info': {
'hardware': capture(["uname", "-m"], include_stderr=True).strip(),
'name': capture(["uname", "-n"], include_stderr=True).strip(),
'os': capture(["uname", "-sr"], include_stderr=True).strip(),
'uname': capture(["uname", "-a"], include_stderr=True).strip(),
}
}
# FIXME: Record source versions for LLVM, Swift, etc.?
lnt_results['Run'] = {
'Start Time': start_time.strftime('%Y-%m-%d %H:%M:%S'),
'End Time': end_time.strftime('%Y-%m-%d %H:%M:%S'),
'Info': {
'__report_version__': '1',
'tag': 'nts',
'inferred_run_order': run_order,
'run_order': run_order,
'sw_vers': capture(['sw_vers'], include_stderr=True).strip(),
}
}
lnt_results['Tests'] = lnt_tests = []
for test in data['tests']:
# Ignore tests which have unexpected status.
code = test['code']
if code not in ('PASS', 'XPASS', 'FAIL', 'XFAIL'):
sys.stderr.write("ignoring test %r with result code %r" % (
test['name'], code))
continue
# Extract the test name, which is encoded as 'suite :: name'.
test_name = 'nts.%s' % (test['name'].split('::', 1)[1][1:],)
# Convert this test to the 'nts' schema.
compile_success = test['metrics'].get('compile_success', 1)
compile_time = test['metrics']['compile_time']
exec_success = test['metrics'].get('exec_success', 1)
exec_time = test['metrics']['exec_time']
# FIXME: Ensure the test success flags matches the result code.
# FIXME: The XFAIL handling here isn't going to be right.
if not compile_success:
lnt_tests.append({'Name': '%s.compile.status' % (test_name,),
'Info': {},
'Data': [FAIL]})
if not exec_success:
lnt_tests.append({'Name': '%s.exec.status' % (test_name,),
'Info': {},
'Data': [FAIL]})
lnt_tests.append({'Name': '%s.compile' % (test_name,),
'Info': {},
'Data': [compile_time]})
lnt_tests.append({'Name': '%s.exec' % (test_name,),
'Info': {},
'Data': [exec_time]})
# Create the report data.
lnt_result_data = json.dumps(lnt_results, indent=2) + '\n'
# Write the results, if requested.
if opts.output:
sys.stderr.write('%s: generating report: %r\n' % (
timestamp(), opts.output))
with open(opts.output, 'w') as f:
f.write(lnt_result_data)
# Submit the results to an LNT server, if requested.
if opts.submit_url:
submit_results_to_server(lnt_result_data, opts.submit_url)
if __name__ == '__main__':
main()
|