results_report.py revision df3d7d197f33c19e51ca688bdcc2d750e8ffb484
1#!/usr/bin/python 2 3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7from utils.tabulator import * 8 9from column_chart import ColumnChart 10from results_organizer import ResultOrganizer 11from perf_table import PerfTable 12 13 14class ResultsReport(object): 15 MAX_COLOR_CODE = 255 16 PERF_ROWS = 5 17 18 def __init__(self, experiment): 19 self.experiment = experiment 20 self.benchmark_runs = experiment.benchmark_runs 21 self.labels = experiment.labels 22 self.benchmarks = experiment.benchmarks 23 self.baseline = self.labels[0] 24 25 def _SortByLabel(self, runs): 26 labels = {} 27 for benchmark_run in runs: 28 if benchmark_run.label_name not in labels: 29 labels[benchmark_run.label_name] = [] 30 labels[benchmark_run.label_name].append(benchmark_run) 31 return labels 32 33 def GetFullTables(self, perf=False): 34 columns = [Column(RawResult(), 35 Format()), 36 Column(MinResult(), 37 Format()), 38 Column(MaxResult(), 39 Format()), 40 Column(AmeanResult(), 41 Format()), 42 Column(StdResult(), 43 Format(), "StdDev"), 44 Column(CoeffVarResult(), 45 CoeffVarFormat(), "StdDev/Mean"), 46 Column(GmeanRatioResult(), 47 RatioFormat(), "GmeanSpeedup"), 48 Column(PValueResult(), 49 PValueFormat(), "p-value") 50 ] 51 if not perf: 52 return self._GetTables(self.labels, self.benchmark_runs, columns, 53 "full") 54 return self._GetPerfTables(self.labels, columns, "full") 55 56 def GetSummaryTables(self, perf=False): 57 columns = [Column(AmeanResult(), 58 Format()), 59 Column(StdResult(), 60 Format(), "StdDev"), 61 Column(CoeffVarResult(), 62 CoeffVarFormat(), "StdDev/Mean"), 63 Column(GmeanRatioResult(), 64 RatioFormat(), "GmeanSpeedup"), 65 Column(GmeanRatioResult(), 66 ColorBoxFormat(), " "), 67 Column(PValueResult(), 68 PValueFormat(), "p-value") 69 ] 70 if not perf: 71 return self._GetTables(self.labels, self.benchmark_runs, columns, 72 "summary") 73 return self._GetPerfTables(self.labels, columns, "summary") 74 75 def _ParseColumn(self, columns, iteration): 76 new_column = [] 77 for column in columns: 78 if column.result.__class__.__name__ != "RawResult": 79 #TODO(asharif): tabulator should support full table natively. 80 new_column.append(column) 81 else: 82 for i in range(iteration): 83 cc = Column(LiteralResult(i), Format(), str(i+1)) 84 new_column.append(cc) 85 return new_column 86 87 def _AreAllRunsEmpty(self, runs): 88 for label in runs: 89 for dictionary in label: 90 if dictionary: 91 return False 92 return True 93 94 def _GetTableHeader(self, benchmark): 95 benchmark_info = ("Benchmark: {0}; Iterations: {1}" 96 .format(benchmark.name, benchmark.iterations)) 97 cell = Cell() 98 cell.string_value = benchmark_info 99 cell.header = True 100 return [[cell]] 101 102 def _GetTables(self, labels, benchmark_runs, columns, table_type): 103 tables = [] 104 ro = ResultOrganizer(benchmark_runs, labels, self.benchmarks) 105 result = ro.result 106 label_name = ro.labels 107 for item in result: 108 runs = result[item] 109 for benchmark in self.benchmarks: 110 if benchmark.name == item: 111 break 112 ben_table = self._GetTableHeader(benchmark) 113 114 if self._AreAllRunsEmpty(runs): 115 cell = Cell() 116 cell.string_value = ("This benchmark contains no result." 117 " Is the benchmark name valid?") 118 cell_table = [[cell]] 119 else: 120 tg = TableGenerator(runs, label_name) 121 table = tg.GetTable() 122 parsed_columns = self._ParseColumn(columns, benchmark.iterations) 123 tf = TableFormatter(table, parsed_columns) 124 cell_table = tf.GetCellTable(table_type) 125 tables.append(ben_table) 126 tables.append(cell_table) 127 return tables 128 129 def _GetPerfTables(self, labels, columns, table_type): 130 tables = [] 131 label_names = [label.name for label in labels] 132 p_table = PerfTable(self.experiment, label_names) 133 134 if not p_table.perf_data: 135 return tables 136 137 for benchmark in p_table.perf_data: 138 ben = None 139 for ben in self.benchmarks: 140 if ben.name == benchmark: 141 break 142 143 ben_table = self._GetTableHeader(ben) 144 tables.append(ben_table) 145 benchmark_data = p_table.perf_data[benchmark] 146 row_info = p_table.row_info[benchmark] 147 table = [] 148 for event in benchmark_data: 149 tg = TableGenerator(benchmark_data[event], label_names, 150 sort=TableGenerator.SORT_BY_VALUES_DESC) 151 table = tg.GetTable(max(self.PERF_ROWS, row_info[event])) 152 parsed_columns = self._ParseColumn(columns, ben.iterations) 153 tf = TableFormatter(table, parsed_columns) 154 tf.GenerateCellTable() 155 tf.AddColumnName() 156 tf.AddLabelName() 157 tf.AddHeader(str(event)) 158 table = tf.GetCellTable(table_type, headers=False) 159 tables.append(table) 160 return tables 161 162 def PrintTables(self, tables, out_to): 163 output = "" 164 if not tables: 165 return output 166 for table in tables: 167 if out_to == "HTML": 168 tp = TablePrinter(table, TablePrinter.HTML) 169 elif out_to == "PLAIN": 170 tp = TablePrinter(table, TablePrinter.PLAIN) 171 elif out_to == "CONSOLE": 172 tp = TablePrinter(table, TablePrinter.CONSOLE) 173 elif out_to == "TSV": 174 tp = TablePrinter(table, TablePrinter.TSV) 175 elif out_to == "EMAIL": 176 tp = TablePrinter(table, TablePrinter.EMAIL) 177 else: 178 pass 179 output += tp.Print() 180 return output 181 182 183class TextResultsReport(ResultsReport): 184 TEXT = """ 185=========================================== 186Results report for: '%s' 187=========================================== 188 189------------------------------------------- 190Summary 191------------------------------------------- 192%s 193 194 195Number re-images: %s 196 197------------------------------------------- 198Benchmark Run Status 199------------------------------------------- 200%s 201 202 203------------------------------------------- 204Perf Data 205------------------------------------------- 206%s 207 208 209 210Experiment File 211------------------------------------------- 212%s 213 214 215CPUInfo 216------------------------------------------- 217%s 218=========================================== 219""" 220 221 def __init__(self, experiment, email=False): 222 super(TextResultsReport, self).__init__(experiment) 223 self.email = email 224 225 def GetStatusTable(self): 226 """Generate the status table by the tabulator.""" 227 table = [["", ""]] 228 columns = [Column(LiteralResult(iteration=0), Format(), "Status"), 229 Column(LiteralResult(iteration=1), Format(), "Failing Reason")] 230 231 for benchmark_run in self.benchmark_runs: 232 status = [benchmark_run.name, [benchmark_run.timeline.GetLastEvent(), 233 benchmark_run.failure_reason]] 234 table.append(status) 235 tf = TableFormatter(table, columns) 236 cell_table = tf.GetCellTable("status") 237 return [cell_table] 238 239 def GetReport(self): 240 """Generate the report for email and console.""" 241 status_table = self.GetStatusTable() 242 summary_table = self.GetSummaryTables() 243 full_table = self.GetFullTables() 244 perf_table = self.GetSummaryTables(perf=True) 245 if not perf_table: 246 perf_table = None 247 if not self.email: 248 return self.TEXT % (self.experiment.name, 249 self.PrintTables(summary_table, "CONSOLE"), 250 self.experiment.machine_manager.num_reimages, 251 self.PrintTables(status_table, "CONSOLE"), 252 self.PrintTables(perf_table, "CONSOLE"), 253 self.experiment.experiment_file, 254 self.experiment.machine_manager.GetAllCPUInfo( 255 self.experiment.labels)) 256 257 return self.TEXT % (self.experiment.name, 258 self.PrintTables(summary_table, "EMAIL"), 259 self.experiment.machine_manager.num_reimages, 260 self.PrintTables(status_table, "EMAIL"), 261 self.PrintTables(perf_table, "EMAIL"), 262 self.experiment.experiment_file, 263 self.experiment.machine_manager.GetAllCPUInfo( 264 self.experiment.labels)) 265 266 267class HTMLResultsReport(ResultsReport): 268 269 HTML = """ 270<html> 271 <head> 272 <style type="text/css"> 273 274body { 275 font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif; 276 font-size: 12px; 277} 278 279pre { 280 margin: 10px; 281 color: #039; 282 font-size: 14px; 283} 284 285.chart { 286 display: inline; 287} 288 289.hidden { 290 visibility: hidden; 291} 292 293.results-section { 294 border: 1px solid #b9c9fe; 295 margin: 10px; 296} 297 298.results-section-title { 299 background-color: #b9c9fe; 300 color: #039; 301 padding: 7px; 302 font-size: 14px; 303 width: 200px; 304} 305 306.results-section-content { 307 margin: 10px; 308 padding: 10px; 309 overflow:auto; 310} 311 312#box-table-a { 313 font-size: 12px; 314 width: 480px; 315 text-align: left; 316 border-collapse: collapse; 317} 318 319#box-table-a th { 320 padding: 6px; 321 background: #b9c9fe; 322 border-right: 1px solid #fff; 323 border-bottom: 1px solid #fff; 324 color: #039; 325 text-align: center; 326} 327 328#box-table-a td { 329 padding: 4px; 330 background: #e8edff; 331 border-bottom: 1px solid #fff; 332 border-right: 1px solid #fff; 333 color: #669; 334 border-top: 1px solid transparent; 335} 336 337#box-table-a tr:hover td { 338 background: #d0dafd; 339 color: #339; 340} 341 342 </style> 343 <script type='text/javascript' src='https://www.google.com/jsapi'></script> 344 <script type='text/javascript'> 345 google.load('visualization', '1', {packages:['corechart']}); 346 google.setOnLoadCallback(init); 347 function init() { 348 switchTab('summary', 'html'); 349 %s 350 switchTab('full', 'html'); 351 drawTable(); 352 } 353 function drawTable() { 354 %s 355 } 356 function switchTab(table, tab) { 357 document.getElementById(table + '-html').style.display = 'none'; 358 document.getElementById(table + '-text').style.display = 'none'; 359 document.getElementById(table + '-tsv').style.display = 'none'; 360 document.getElementById(table + '-' + tab).style.display = 'block'; 361 } 362 </script> 363 </head> 364 365 <body> 366 <div class='results-section'> 367 <div class='results-section-title'>Summary Table</div> 368 <div class='results-section-content'> 369 <div id='summary-html'>%s</div> 370 <div id='summary-text'><pre>%s</pre></div> 371 <div id='summary-tsv'><pre>%s</pre></div> 372 </div> 373 %s 374 </div> 375 %s 376 <div class='results-section'> 377 <div class='results-section-title'>Charts</div> 378 <div class='results-section-content'>%s</div> 379 </div> 380 <div class='results-section'> 381 <div class='results-section-title'>Full Table</div> 382 <div class='results-section-content'> 383 <div id='full-html'>%s</div> 384 <div id='full-text'><pre>%s</pre></div> 385 <div id='full-tsv'><pre>%s</pre></div> 386 </div> 387 %s 388 </div> 389 <div class='results-section'> 390 <div class='results-section-title'>Experiment File</div> 391 <div class='results-section-content'> 392 <pre>%s</pre> 393 </div> 394 </div> 395 </body> 396</html> 397""" 398 399 PERF_HTML = """ 400 <div class='results-section'> 401 <div class='results-section-title'>Perf Table</div> 402 <div class='results-section-content'> 403 <div id='perf-html'>%s</div> 404 <div id='perf-text'><pre>%s</pre></div> 405 <div id='perf-tsv'><pre>%s</pre></div> 406 </div> 407 %s 408 </div> 409""" 410 411 def __init__(self, experiment): 412 super(HTMLResultsReport, self).__init__(experiment) 413 414 def _GetTabMenuHTML(self, table): 415 return """ 416<div class='tab-menu'> 417 <a href="javascript:switchTab('%s', 'html')">HTML</a> 418 <a href="javascript:switchTab('%s', 'text')">Text</a> 419 <a href="javascript:switchTab('%s', 'tsv')">TSV</a> 420</div>""" % (table, table, table) 421 422 def GetReport(self): 423 chart_javascript = "" 424 charts = self._GetCharts(self.labels, self.benchmark_runs) 425 for chart in charts: 426 chart_javascript += chart.GetJavascript() 427 chart_divs = "" 428 for chart in charts: 429 chart_divs += chart.GetDiv() 430 431 summary_table = self.GetSummaryTables() 432 full_table = self.GetFullTables() 433 perf_table = self.GetSummaryTables(perf=True) 434 if perf_table: 435 perf_html = self.PERF_HTML % ( 436 self.PrintTables(perf_table, "HTML"), 437 self.PrintTables(perf_table, "PLAIN"), 438 self.PrintTables(perf_table, "TSV"), 439 self._GetTabMenuHTML("perf") 440 ) 441 perf_init = "switchTab('perf', 'html');" 442 else: 443 perf_html = "" 444 perf_init = "" 445 446 return self.HTML % (perf_init, 447 chart_javascript, 448 self.PrintTables(summary_table, "HTML"), 449 self.PrintTables(summary_table, "PLAIN"), 450 self.PrintTables(summary_table, "TSV"), 451 self._GetTabMenuHTML("summary"), 452 perf_html, 453 chart_divs, 454 self.PrintTables(full_table, "HTML"), 455 self.PrintTables(full_table, "PLAIN"), 456 self.PrintTables(full_table, "TSV"), 457 self._GetTabMenuHTML("full"), 458 self.experiment.experiment_file) 459 460 def _GetCharts(self, labels, benchmark_runs): 461 charts = [] 462 ro = ResultOrganizer(benchmark_runs, labels) 463 result = ro.result 464 for item in result: 465 runs = result[item] 466 tg = TableGenerator(runs, ro.labels) 467 table = tg.GetTable() 468 columns = [Column(AmeanResult(), 469 Format()), 470 Column(MinResult(), 471 Format()), 472 Column(MaxResult(), 473 Format()) 474 ] 475 tf = TableFormatter(table, columns) 476 data_table = tf.GetCellTable("full") 477 478 for i in range(2, len(data_table)): 479 cur_row_data = data_table[i] 480 test_key = cur_row_data[0].string_value 481 title = "{0}: {1}".format(item, test_key.replace("/", "")) 482 chart = ColumnChart(title, 300, 200) 483 chart.AddColumn("Label", "string") 484 chart.AddColumn("Average", "number") 485 chart.AddColumn("Min", "number") 486 chart.AddColumn("Max", "number") 487 chart.AddSeries("Min", "line", "black") 488 chart.AddSeries("Max", "line", "black") 489 cur_index = 1 490 for label in ro.labels: 491 chart.AddRow([label, cur_row_data[cur_index].value, 492 cur_row_data[cur_index + 1].value, 493 cur_row_data[cur_index + 2].value]) 494 if isinstance(cur_row_data[cur_index].value, str): 495 chart = None 496 break 497 cur_index += 3 498 if chart: 499 charts.append(chart) 500 return charts 501