11cb0ef41Sopenharmony_ci#!/usr/bin/env python
21cb0ef41Sopenharmony_ci# Copyright 2017 the V8 project authors. All rights reserved.
31cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
41cb0ef41Sopenharmony_ci# found in the LICENSE file.
51cb0ef41Sopenharmony_ci'''
61cb0ef41Sopenharmony_cipython %prog
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ciCompare perf trybot JSON files and output the results into a pleasing HTML page.
91cb0ef41Sopenharmony_ciExamples:
101cb0ef41Sopenharmony_ci  %prog -t "ia32 results" Result,../result.json Master,/path-to/master.json -o results.html
111cb0ef41Sopenharmony_ci  %prog -t "x64 results" ../result.json master.json -o results.html
121cb0ef41Sopenharmony_ci'''
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ci# for py2/py3 compatibility
151cb0ef41Sopenharmony_cifrom __future__ import print_function
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_cifrom collections import OrderedDict
181cb0ef41Sopenharmony_ciimport json
191cb0ef41Sopenharmony_ciimport math
201cb0ef41Sopenharmony_cifrom argparse import ArgumentParser
211cb0ef41Sopenharmony_ciimport os
221cb0ef41Sopenharmony_ciimport shutil
231cb0ef41Sopenharmony_ciimport sys
241cb0ef41Sopenharmony_ciimport tempfile
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ciPERCENT_CONSIDERED_SIGNIFICANT = 0.5
271cb0ef41Sopenharmony_ciPROBABILITY_CONSIDERED_SIGNIFICANT = 0.02
281cb0ef41Sopenharmony_ciPROBABILITY_CONSIDERED_MEANINGLESS = 0.05
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_ciclass Statistics:
311cb0ef41Sopenharmony_ci  @staticmethod
321cb0ef41Sopenharmony_ci  def Mean(values):
331cb0ef41Sopenharmony_ci    return float(sum(values)) / len(values)
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci  @staticmethod
361cb0ef41Sopenharmony_ci  def Variance(values, average):
371cb0ef41Sopenharmony_ci    return map(lambda x: (x - average) ** 2, values)
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci  @staticmethod
401cb0ef41Sopenharmony_ci  def StandardDeviation(values, average):
411cb0ef41Sopenharmony_ci    return math.sqrt(Statistics.Mean(Statistics.Variance(values, average)))
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ci  @staticmethod
441cb0ef41Sopenharmony_ci  def ComputeZ(baseline_avg, baseline_sigma, mean, n):
451cb0ef41Sopenharmony_ci    if baseline_sigma == 0:
461cb0ef41Sopenharmony_ci      return 1000.0;
471cb0ef41Sopenharmony_ci    return abs((mean - baseline_avg) / (baseline_sigma / math.sqrt(n)))
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci  # Values from http://www.fourmilab.ch/rpkp/experiments/analysis/zCalc.html
501cb0ef41Sopenharmony_ci  @staticmethod
511cb0ef41Sopenharmony_ci  def ComputeProbability(z):
521cb0ef41Sopenharmony_ci    if z > 2.575829: # p 0.005: two sided < 0.01
531cb0ef41Sopenharmony_ci      return 0
541cb0ef41Sopenharmony_ci    if z > 2.326348: # p 0.010
551cb0ef41Sopenharmony_ci      return 0.01
561cb0ef41Sopenharmony_ci    if z > 2.170091: # p 0.015
571cb0ef41Sopenharmony_ci      return 0.02
581cb0ef41Sopenharmony_ci    if z > 2.053749: # p 0.020
591cb0ef41Sopenharmony_ci      return 0.03
601cb0ef41Sopenharmony_ci    if z > 1.959964: # p 0.025: two sided < 0.05
611cb0ef41Sopenharmony_ci      return 0.04
621cb0ef41Sopenharmony_ci    if z > 1.880793: # p 0.030
631cb0ef41Sopenharmony_ci      return 0.05
641cb0ef41Sopenharmony_ci    if z > 1.811910: # p 0.035
651cb0ef41Sopenharmony_ci      return 0.06
661cb0ef41Sopenharmony_ci    if z > 1.750686: # p 0.040
671cb0ef41Sopenharmony_ci      return 0.07
681cb0ef41Sopenharmony_ci    if z > 1.695397: # p 0.045
691cb0ef41Sopenharmony_ci      return 0.08
701cb0ef41Sopenharmony_ci    if z > 1.644853: # p 0.050: two sided < 0.10
711cb0ef41Sopenharmony_ci      return 0.09
721cb0ef41Sopenharmony_ci    if z > 1.281551: # p 0.100: two sided < 0.20
731cb0ef41Sopenharmony_ci      return 0.10
741cb0ef41Sopenharmony_ci    return 0.20 # two sided p >= 0.20
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_ciclass ResultsDiff:
781cb0ef41Sopenharmony_ci  def __init__(self, significant, notable, percentage_string):
791cb0ef41Sopenharmony_ci    self.significant_ = significant
801cb0ef41Sopenharmony_ci    self.notable_ = notable
811cb0ef41Sopenharmony_ci    self.percentage_string_ = percentage_string
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci  def percentage_string(self):
841cb0ef41Sopenharmony_ci    return self.percentage_string_;
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci  def isSignificant(self):
871cb0ef41Sopenharmony_ci    return self.significant_
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci  def isNotablyPositive(self):
901cb0ef41Sopenharmony_ci    return self.notable_ > 0
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci  def isNotablyNegative(self):
931cb0ef41Sopenharmony_ci    return self.notable_ < 0
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ciclass BenchmarkResult:
971cb0ef41Sopenharmony_ci  def __init__(self, units, count, result, sigma):
981cb0ef41Sopenharmony_ci    self.units_ = units
991cb0ef41Sopenharmony_ci    self.count_ = float(count)
1001cb0ef41Sopenharmony_ci    self.result_ = float(result)
1011cb0ef41Sopenharmony_ci    self.sigma_ = float(sigma)
1021cb0ef41Sopenharmony_ci
1031cb0ef41Sopenharmony_ci  def Compare(self, other):
1041cb0ef41Sopenharmony_ci    if self.units_ != other.units_:
1051cb0ef41Sopenharmony_ci      print ("Incompatible units: %s and %s" % (self.units_, other.units_))
1061cb0ef41Sopenharmony_ci      sys.exit(1)
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_ci    significant = False
1091cb0ef41Sopenharmony_ci    notable = 0
1101cb0ef41Sopenharmony_ci    percentage_string = ""
1111cb0ef41Sopenharmony_ci    # compute notability and significance.
1121cb0ef41Sopenharmony_ci    if self.units_ == "score":
1131cb0ef41Sopenharmony_ci      compare_num = 100*self.result_/other.result_ - 100
1141cb0ef41Sopenharmony_ci    else:
1151cb0ef41Sopenharmony_ci      compare_num = 100*other.result_/self.result_ - 100
1161cb0ef41Sopenharmony_ci    if abs(compare_num) > 0.1:
1171cb0ef41Sopenharmony_ci      percentage_string = "%3.1f" % (compare_num)
1181cb0ef41Sopenharmony_ci      z = Statistics.ComputeZ(other.result_, other.sigma_,
1191cb0ef41Sopenharmony_ci                              self.result_, self.count_)
1201cb0ef41Sopenharmony_ci      p = Statistics.ComputeProbability(z)
1211cb0ef41Sopenharmony_ci      if p < PROBABILITY_CONSIDERED_SIGNIFICANT:
1221cb0ef41Sopenharmony_ci        significant = True
1231cb0ef41Sopenharmony_ci      if compare_num >= PERCENT_CONSIDERED_SIGNIFICANT:
1241cb0ef41Sopenharmony_ci        notable = 1
1251cb0ef41Sopenharmony_ci      elif compare_num <= -PERCENT_CONSIDERED_SIGNIFICANT:
1261cb0ef41Sopenharmony_ci        notable = -1
1271cb0ef41Sopenharmony_ci    return ResultsDiff(significant, notable, percentage_string)
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci  def result(self):
1301cb0ef41Sopenharmony_ci    return self.result_
1311cb0ef41Sopenharmony_ci
1321cb0ef41Sopenharmony_ci  def sigma(self):
1331cb0ef41Sopenharmony_ci    return self.sigma_
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci
1361cb0ef41Sopenharmony_ciclass Benchmark:
1371cb0ef41Sopenharmony_ci  def __init__(self, name):
1381cb0ef41Sopenharmony_ci    self.name_ = name
1391cb0ef41Sopenharmony_ci    self.runs_ = {}
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_ci  def name(self):
1421cb0ef41Sopenharmony_ci    return self.name_
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_ci  def getResult(self, run_name):
1451cb0ef41Sopenharmony_ci    return self.runs_.get(run_name)
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci  def appendResult(self, run_name, trace):
1481cb0ef41Sopenharmony_ci    values = map(float, trace['results'])
1491cb0ef41Sopenharmony_ci    count = len(values)
1501cb0ef41Sopenharmony_ci    mean = Statistics.Mean(values)
1511cb0ef41Sopenharmony_ci    stddev = float(trace.get('stddev') or
1521cb0ef41Sopenharmony_ci                   Statistics.StandardDeviation(values, mean))
1531cb0ef41Sopenharmony_ci    units = trace["units"]
1541cb0ef41Sopenharmony_ci    # print run_name, units, count, mean, stddev
1551cb0ef41Sopenharmony_ci    self.runs_[run_name] = BenchmarkResult(units, count, mean, stddev)
1561cb0ef41Sopenharmony_ci
1571cb0ef41Sopenharmony_ci
1581cb0ef41Sopenharmony_ciclass BenchmarkSuite:
1591cb0ef41Sopenharmony_ci  def __init__(self, name):
1601cb0ef41Sopenharmony_ci    self.name_ = name
1611cb0ef41Sopenharmony_ci    self.benchmarks_ = {}
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci  def SortedTestKeys(self):
1641cb0ef41Sopenharmony_ci    keys = self.benchmarks_.keys()
1651cb0ef41Sopenharmony_ci    keys.sort()
1661cb0ef41Sopenharmony_ci    t = "Total"
1671cb0ef41Sopenharmony_ci    if t in keys:
1681cb0ef41Sopenharmony_ci      keys.remove(t)
1691cb0ef41Sopenharmony_ci      keys.append(t)
1701cb0ef41Sopenharmony_ci    return keys
1711cb0ef41Sopenharmony_ci
1721cb0ef41Sopenharmony_ci  def name(self):
1731cb0ef41Sopenharmony_ci    return self.name_
1741cb0ef41Sopenharmony_ci
1751cb0ef41Sopenharmony_ci  def getBenchmark(self, benchmark_name):
1761cb0ef41Sopenharmony_ci    benchmark_object = self.benchmarks_.get(benchmark_name)
1771cb0ef41Sopenharmony_ci    if benchmark_object == None:
1781cb0ef41Sopenharmony_ci      benchmark_object = Benchmark(benchmark_name)
1791cb0ef41Sopenharmony_ci      self.benchmarks_[benchmark_name] = benchmark_object
1801cb0ef41Sopenharmony_ci    return benchmark_object
1811cb0ef41Sopenharmony_ci
1821cb0ef41Sopenharmony_ci
1831cb0ef41Sopenharmony_ciclass ResultTableRenderer:
1841cb0ef41Sopenharmony_ci  def __init__(self, output_file):
1851cb0ef41Sopenharmony_ci    self.benchmarks_ = []
1861cb0ef41Sopenharmony_ci    self.print_output_ = []
1871cb0ef41Sopenharmony_ci    self.output_file_ = output_file
1881cb0ef41Sopenharmony_ci
1891cb0ef41Sopenharmony_ci  def Print(self, str_data):
1901cb0ef41Sopenharmony_ci    self.print_output_.append(str_data)
1911cb0ef41Sopenharmony_ci
1921cb0ef41Sopenharmony_ci  def FlushOutput(self):
1931cb0ef41Sopenharmony_ci    string_data = "\n".join(self.print_output_)
1941cb0ef41Sopenharmony_ci    print_output = []
1951cb0ef41Sopenharmony_ci    if self.output_file_:
1961cb0ef41Sopenharmony_ci      # create a file
1971cb0ef41Sopenharmony_ci      with open(self.output_file_, "w") as text_file:
1981cb0ef41Sopenharmony_ci        text_file.write(string_data)
1991cb0ef41Sopenharmony_ci    else:
2001cb0ef41Sopenharmony_ci      print(string_data)
2011cb0ef41Sopenharmony_ci
2021cb0ef41Sopenharmony_ci  def bold(self, data):
2031cb0ef41Sopenharmony_ci    return "<b>%s</b>" % data
2041cb0ef41Sopenharmony_ci
2051cb0ef41Sopenharmony_ci  def red(self, data):
2061cb0ef41Sopenharmony_ci    return "<font color=\"red\">%s</font>" % data
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci
2091cb0ef41Sopenharmony_ci  def green(self, data):
2101cb0ef41Sopenharmony_ci    return "<font color=\"green\">%s</font>" % data
2111cb0ef41Sopenharmony_ci
2121cb0ef41Sopenharmony_ci  def PrintHeader(self):
2131cb0ef41Sopenharmony_ci    data = """<html>
2141cb0ef41Sopenharmony_ci<head>
2151cb0ef41Sopenharmony_ci<title>Output</title>
2161cb0ef41Sopenharmony_ci<style type="text/css">
2171cb0ef41Sopenharmony_ci/*
2181cb0ef41Sopenharmony_ciStyle inspired by Andy Ferra's gist at https://gist.github.com/andyferra/2554919
2191cb0ef41Sopenharmony_ci*/
2201cb0ef41Sopenharmony_cibody {
2211cb0ef41Sopenharmony_ci  font-family: Helvetica, arial, sans-serif;
2221cb0ef41Sopenharmony_ci  font-size: 14px;
2231cb0ef41Sopenharmony_ci  line-height: 1.6;
2241cb0ef41Sopenharmony_ci  padding-top: 10px;
2251cb0ef41Sopenharmony_ci  padding-bottom: 10px;
2261cb0ef41Sopenharmony_ci  background-color: white;
2271cb0ef41Sopenharmony_ci  padding: 30px;
2281cb0ef41Sopenharmony_ci}
2291cb0ef41Sopenharmony_cih1, h2, h3, h4, h5, h6 {
2301cb0ef41Sopenharmony_ci  margin: 20px 0 10px;
2311cb0ef41Sopenharmony_ci  padding: 0;
2321cb0ef41Sopenharmony_ci  font-weight: bold;
2331cb0ef41Sopenharmony_ci  -webkit-font-smoothing: antialiased;
2341cb0ef41Sopenharmony_ci  cursor: text;
2351cb0ef41Sopenharmony_ci  position: relative;
2361cb0ef41Sopenharmony_ci}
2371cb0ef41Sopenharmony_cih1 {
2381cb0ef41Sopenharmony_ci  font-size: 28px;
2391cb0ef41Sopenharmony_ci  color: black;
2401cb0ef41Sopenharmony_ci}
2411cb0ef41Sopenharmony_ci
2421cb0ef41Sopenharmony_cih2 {
2431cb0ef41Sopenharmony_ci  font-size: 24px;
2441cb0ef41Sopenharmony_ci  border-bottom: 1px solid #cccccc;
2451cb0ef41Sopenharmony_ci  color: black;
2461cb0ef41Sopenharmony_ci}
2471cb0ef41Sopenharmony_ci
2481cb0ef41Sopenharmony_cih3 {
2491cb0ef41Sopenharmony_ci  font-size: 18px;
2501cb0ef41Sopenharmony_ci}
2511cb0ef41Sopenharmony_ci
2521cb0ef41Sopenharmony_cih4 {
2531cb0ef41Sopenharmony_ci  font-size: 16px;
2541cb0ef41Sopenharmony_ci}
2551cb0ef41Sopenharmony_ci
2561cb0ef41Sopenharmony_cih5 {
2571cb0ef41Sopenharmony_ci  font-size: 14px;
2581cb0ef41Sopenharmony_ci}
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_cih6 {
2611cb0ef41Sopenharmony_ci  color: #777777;
2621cb0ef41Sopenharmony_ci  font-size: 14px;
2631cb0ef41Sopenharmony_ci}
2641cb0ef41Sopenharmony_ci
2651cb0ef41Sopenharmony_cip, blockquote, ul, ol, dl, li, table, pre {
2661cb0ef41Sopenharmony_ci  margin: 15px 0;
2671cb0ef41Sopenharmony_ci}
2681cb0ef41Sopenharmony_ci
2691cb0ef41Sopenharmony_cili p.first {
2701cb0ef41Sopenharmony_ci  display: inline-block;
2711cb0ef41Sopenharmony_ci}
2721cb0ef41Sopenharmony_ci
2731cb0ef41Sopenharmony_ciul, ol {
2741cb0ef41Sopenharmony_ci  padding-left: 30px;
2751cb0ef41Sopenharmony_ci}
2761cb0ef41Sopenharmony_ci
2771cb0ef41Sopenharmony_ciul :first-child, ol :first-child {
2781cb0ef41Sopenharmony_ci  margin-top: 0;
2791cb0ef41Sopenharmony_ci}
2801cb0ef41Sopenharmony_ci
2811cb0ef41Sopenharmony_ciul :last-child, ol :last-child {
2821cb0ef41Sopenharmony_ci  margin-bottom: 0;
2831cb0ef41Sopenharmony_ci}
2841cb0ef41Sopenharmony_ci
2851cb0ef41Sopenharmony_citable {
2861cb0ef41Sopenharmony_ci  padding: 0;
2871cb0ef41Sopenharmony_ci}
2881cb0ef41Sopenharmony_ci
2891cb0ef41Sopenharmony_citable tr {
2901cb0ef41Sopenharmony_ci  border-top: 1px solid #cccccc;
2911cb0ef41Sopenharmony_ci  background-color: white;
2921cb0ef41Sopenharmony_ci  margin: 0;
2931cb0ef41Sopenharmony_ci  padding: 0;
2941cb0ef41Sopenharmony_ci}
2951cb0ef41Sopenharmony_ci
2961cb0ef41Sopenharmony_citable tr:nth-child(2n) {
2971cb0ef41Sopenharmony_ci  background-color: #f8f8f8;
2981cb0ef41Sopenharmony_ci}
2991cb0ef41Sopenharmony_ci
3001cb0ef41Sopenharmony_citable tr th {
3011cb0ef41Sopenharmony_ci  font-weight: bold;
3021cb0ef41Sopenharmony_ci  border: 1px solid #cccccc;
3031cb0ef41Sopenharmony_ci  text-align: left;
3041cb0ef41Sopenharmony_ci  margin: 0;
3051cb0ef41Sopenharmony_ci  padding: 6px 13px;
3061cb0ef41Sopenharmony_ci}
3071cb0ef41Sopenharmony_citable tr td {
3081cb0ef41Sopenharmony_ci  border: 1px solid #cccccc;
3091cb0ef41Sopenharmony_ci  text-align: right;
3101cb0ef41Sopenharmony_ci  margin: 0;
3111cb0ef41Sopenharmony_ci  padding: 6px 13px;
3121cb0ef41Sopenharmony_ci}
3131cb0ef41Sopenharmony_citable tr td.name-column {
3141cb0ef41Sopenharmony_ci  text-align: left;
3151cb0ef41Sopenharmony_ci}
3161cb0ef41Sopenharmony_citable tr th :first-child, table tr td :first-child {
3171cb0ef41Sopenharmony_ci  margin-top: 0;
3181cb0ef41Sopenharmony_ci}
3191cb0ef41Sopenharmony_citable tr th :last-child, table tr td :last-child {
3201cb0ef41Sopenharmony_ci  margin-bottom: 0;
3211cb0ef41Sopenharmony_ci}
3221cb0ef41Sopenharmony_ci</style>
3231cb0ef41Sopenharmony_ci</head>
3241cb0ef41Sopenharmony_ci<body>
3251cb0ef41Sopenharmony_ci"""
3261cb0ef41Sopenharmony_ci    self.Print(data)
3271cb0ef41Sopenharmony_ci
3281cb0ef41Sopenharmony_ci  def StartSuite(self, suite_name, run_names):
3291cb0ef41Sopenharmony_ci    self.Print("<h2>")
3301cb0ef41Sopenharmony_ci    self.Print("<a name=\"%s\">%s</a> <a href=\"#top\">(top)</a>" %
3311cb0ef41Sopenharmony_ci               (suite_name, suite_name))
3321cb0ef41Sopenharmony_ci    self.Print("</h2>");
3331cb0ef41Sopenharmony_ci    self.Print("<table class=\"benchmark\">")
3341cb0ef41Sopenharmony_ci    self.Print("<thead>")
3351cb0ef41Sopenharmony_ci    self.Print("  <th>Test</th>")
3361cb0ef41Sopenharmony_ci    main_run = None
3371cb0ef41Sopenharmony_ci    for run_name in run_names:
3381cb0ef41Sopenharmony_ci      self.Print("  <th>%s</th>" % run_name)
3391cb0ef41Sopenharmony_ci      if main_run == None:
3401cb0ef41Sopenharmony_ci        main_run = run_name
3411cb0ef41Sopenharmony_ci      else:
3421cb0ef41Sopenharmony_ci        self.Print("  <th>%</th>")
3431cb0ef41Sopenharmony_ci    self.Print("</thead>")
3441cb0ef41Sopenharmony_ci    self.Print("<tbody>")
3451cb0ef41Sopenharmony_ci
3461cb0ef41Sopenharmony_ci
3471cb0ef41Sopenharmony_ci  def FinishSuite(self):
3481cb0ef41Sopenharmony_ci    self.Print("</tbody>")
3491cb0ef41Sopenharmony_ci    self.Print("</table>")
3501cb0ef41Sopenharmony_ci
3511cb0ef41Sopenharmony_ci
3521cb0ef41Sopenharmony_ci  def StartBenchmark(self, benchmark_name):
3531cb0ef41Sopenharmony_ci    self.Print("  <tr>")
3541cb0ef41Sopenharmony_ci    self.Print("    <td class=\"name-column\">%s</td>" % benchmark_name)
3551cb0ef41Sopenharmony_ci
3561cb0ef41Sopenharmony_ci  def FinishBenchmark(self):
3571cb0ef41Sopenharmony_ci    self.Print("  </tr>")
3581cb0ef41Sopenharmony_ci
3591cb0ef41Sopenharmony_ci
3601cb0ef41Sopenharmony_ci  def PrintResult(self, run):
3611cb0ef41Sopenharmony_ci    if run == None:
3621cb0ef41Sopenharmony_ci      self.PrintEmptyCell()
3631cb0ef41Sopenharmony_ci      return
3641cb0ef41Sopenharmony_ci    self.Print("    <td>%3.1f</td>" % run.result())
3651cb0ef41Sopenharmony_ci
3661cb0ef41Sopenharmony_ci
3671cb0ef41Sopenharmony_ci  def PrintComparison(self, run, main_run):
3681cb0ef41Sopenharmony_ci    if run == None or main_run == None:
3691cb0ef41Sopenharmony_ci      self.PrintEmptyCell()
3701cb0ef41Sopenharmony_ci      return
3711cb0ef41Sopenharmony_ci    diff = run.Compare(main_run)
3721cb0ef41Sopenharmony_ci    res = diff.percentage_string()
3731cb0ef41Sopenharmony_ci    if diff.isSignificant():
3741cb0ef41Sopenharmony_ci      res = self.bold(res)
3751cb0ef41Sopenharmony_ci    if diff.isNotablyPositive():
3761cb0ef41Sopenharmony_ci      res = self.green(res)
3771cb0ef41Sopenharmony_ci    elif diff.isNotablyNegative():
3781cb0ef41Sopenharmony_ci      res = self.red(res)
3791cb0ef41Sopenharmony_ci    self.Print("    <td>%s</td>" % res)
3801cb0ef41Sopenharmony_ci
3811cb0ef41Sopenharmony_ci
3821cb0ef41Sopenharmony_ci  def PrintEmptyCell(self):
3831cb0ef41Sopenharmony_ci    self.Print("    <td></td>")
3841cb0ef41Sopenharmony_ci
3851cb0ef41Sopenharmony_ci
3861cb0ef41Sopenharmony_ci  def StartTOC(self, title):
3871cb0ef41Sopenharmony_ci    self.Print("<h1>%s</h1>" % title)
3881cb0ef41Sopenharmony_ci    self.Print("<ul>")
3891cb0ef41Sopenharmony_ci
3901cb0ef41Sopenharmony_ci  def FinishTOC(self):
3911cb0ef41Sopenharmony_ci    self.Print("</ul>")
3921cb0ef41Sopenharmony_ci
3931cb0ef41Sopenharmony_ci  def PrintBenchmarkLink(self, benchmark):
3941cb0ef41Sopenharmony_ci    self.Print("<li><a href=\"#" + benchmark + "\">" + benchmark + "</a></li>")
3951cb0ef41Sopenharmony_ci
3961cb0ef41Sopenharmony_ci  def PrintFooter(self):
3971cb0ef41Sopenharmony_ci    data = """</body>
3981cb0ef41Sopenharmony_ci</html>
3991cb0ef41Sopenharmony_ci"""
4001cb0ef41Sopenharmony_ci    self.Print(data)
4011cb0ef41Sopenharmony_ci
4021cb0ef41Sopenharmony_ci
4031cb0ef41Sopenharmony_cidef Render(args):
4041cb0ef41Sopenharmony_ci  benchmark_suites = {}
4051cb0ef41Sopenharmony_ci  run_names = OrderedDict()
4061cb0ef41Sopenharmony_ci
4071cb0ef41Sopenharmony_ci  for json_file_list in args.json_file_list:
4081cb0ef41Sopenharmony_ci    run_name = json_file_list[0]
4091cb0ef41Sopenharmony_ci    if run_name.endswith(".json"):
4101cb0ef41Sopenharmony_ci      # The first item in the list is also a file name
4111cb0ef41Sopenharmony_ci      run_name = os.path.splitext(run_name)[0]
4121cb0ef41Sopenharmony_ci      filenames = json_file_list
4131cb0ef41Sopenharmony_ci    else:
4141cb0ef41Sopenharmony_ci      filenames = json_file_list[1:]
4151cb0ef41Sopenharmony_ci
4161cb0ef41Sopenharmony_ci    for filename in filenames:
4171cb0ef41Sopenharmony_ci      print ("Processing result set \"%s\", file: %s" % (run_name, filename))
4181cb0ef41Sopenharmony_ci      with open(filename) as json_data:
4191cb0ef41Sopenharmony_ci        data = json.load(json_data)
4201cb0ef41Sopenharmony_ci
4211cb0ef41Sopenharmony_ci      run_names[run_name] = 0
4221cb0ef41Sopenharmony_ci
4231cb0ef41Sopenharmony_ci      for error in data["errors"]:
4241cb0ef41Sopenharmony_ci        print("Error:", error)
4251cb0ef41Sopenharmony_ci
4261cb0ef41Sopenharmony_ci      for trace in data["traces"]:
4271cb0ef41Sopenharmony_ci        suite_name = trace["graphs"][0]
4281cb0ef41Sopenharmony_ci        benchmark_name = "/".join(trace["graphs"][1:])
4291cb0ef41Sopenharmony_ci
4301cb0ef41Sopenharmony_ci        benchmark_suite_object = benchmark_suites.get(suite_name)
4311cb0ef41Sopenharmony_ci        if benchmark_suite_object == None:
4321cb0ef41Sopenharmony_ci          benchmark_suite_object = BenchmarkSuite(suite_name)
4331cb0ef41Sopenharmony_ci          benchmark_suites[suite_name] = benchmark_suite_object
4341cb0ef41Sopenharmony_ci
4351cb0ef41Sopenharmony_ci        benchmark_object = benchmark_suite_object.getBenchmark(benchmark_name)
4361cb0ef41Sopenharmony_ci        benchmark_object.appendResult(run_name, trace);
4371cb0ef41Sopenharmony_ci
4381cb0ef41Sopenharmony_ci
4391cb0ef41Sopenharmony_ci  renderer = ResultTableRenderer(args.output)
4401cb0ef41Sopenharmony_ci  renderer.PrintHeader()
4411cb0ef41Sopenharmony_ci
4421cb0ef41Sopenharmony_ci  title = args.title or "Benchmark results"
4431cb0ef41Sopenharmony_ci  renderer.StartTOC(title)
4441cb0ef41Sopenharmony_ci  for suite_name, benchmark_suite_object in sorted(benchmark_suites.iteritems()):
4451cb0ef41Sopenharmony_ci    renderer.PrintBenchmarkLink(suite_name)
4461cb0ef41Sopenharmony_ci  renderer.FinishTOC()
4471cb0ef41Sopenharmony_ci
4481cb0ef41Sopenharmony_ci  for suite_name, benchmark_suite_object in sorted(benchmark_suites.iteritems()):
4491cb0ef41Sopenharmony_ci    renderer.StartSuite(suite_name, run_names)
4501cb0ef41Sopenharmony_ci    for benchmark_name in benchmark_suite_object.SortedTestKeys():
4511cb0ef41Sopenharmony_ci      benchmark_object = benchmark_suite_object.getBenchmark(benchmark_name)
4521cb0ef41Sopenharmony_ci      # print suite_name, benchmark_object.name()
4531cb0ef41Sopenharmony_ci
4541cb0ef41Sopenharmony_ci      renderer.StartBenchmark(benchmark_name)
4551cb0ef41Sopenharmony_ci      main_run = None
4561cb0ef41Sopenharmony_ci      main_result = None
4571cb0ef41Sopenharmony_ci      for run_name in run_names:
4581cb0ef41Sopenharmony_ci        result = benchmark_object.getResult(run_name)
4591cb0ef41Sopenharmony_ci        renderer.PrintResult(result)
4601cb0ef41Sopenharmony_ci        if main_run == None:
4611cb0ef41Sopenharmony_ci          main_run = run_name
4621cb0ef41Sopenharmony_ci          main_result = result
4631cb0ef41Sopenharmony_ci        else:
4641cb0ef41Sopenharmony_ci          renderer.PrintComparison(result, main_result)
4651cb0ef41Sopenharmony_ci      renderer.FinishBenchmark()
4661cb0ef41Sopenharmony_ci    renderer.FinishSuite()
4671cb0ef41Sopenharmony_ci
4681cb0ef41Sopenharmony_ci  renderer.PrintFooter()
4691cb0ef41Sopenharmony_ci  renderer.FlushOutput()
4701cb0ef41Sopenharmony_ci
4711cb0ef41Sopenharmony_cidef CommaSeparatedList(arg):
4721cb0ef41Sopenharmony_ci  return [x for x in arg.split(',')]
4731cb0ef41Sopenharmony_ci
4741cb0ef41Sopenharmony_ciif __name__ == '__main__':
4751cb0ef41Sopenharmony_ci  parser = ArgumentParser(description="Compare perf trybot JSON files and " +
4761cb0ef41Sopenharmony_ci                          "output the results into a pleasing HTML page.")
4771cb0ef41Sopenharmony_ci  parser.add_argument("-t", "--title", dest="title",
4781cb0ef41Sopenharmony_ci                      help="Optional title of the web page")
4791cb0ef41Sopenharmony_ci  parser.add_argument("-o", "--output", dest="output",
4801cb0ef41Sopenharmony_ci                      help="Write html output to this file rather than stdout")
4811cb0ef41Sopenharmony_ci  parser.add_argument("json_file_list", nargs="+", type=CommaSeparatedList,
4821cb0ef41Sopenharmony_ci                      help="[column name,]./path-to/result.json - a comma-separated" +
4831cb0ef41Sopenharmony_ci                      " list of optional column name and paths to json files")
4841cb0ef41Sopenharmony_ci
4851cb0ef41Sopenharmony_ci  args = parser.parse_args()
4861cb0ef41Sopenharmony_ci  Render(args)
487