#!/usr/bin/env python # Copyright (c) 2013 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be found # in the LICENSE file. """ Analyze per-tile and viewport bench data, and output visualized results. """ __author__ = 'bensong@google.com (Ben Chen)' import bench_util import boto import math import optparse import os import re import shutil from oauth2_plugin import oauth2_plugin # The default platform to analyze. Used when OPTION_PLATFORM flag is not set. DEFAULT_PLATFORM = 'Nexus10_4-1_Float_Bench_32' # Template for gsutil uri. GOOGLE_STORAGE_URI_SCHEME = 'gs' URI_BUCKET = 'chromium-skia-gm' # Maximum number of rows of tiles to track for viewport covering. MAX_TILE_ROWS = 8 # Constants for optparse. USAGE_STRING = 'USAGE: %s [options]' HOWTO_STRING = """ Note: to read bench data stored in Google Storage, you will need to set up the corresponding Python library. See http://developers.google.com/storage/docs/gspythonlibrary for details. """ HELP_STRING = """ For the given platform and revision number, find corresponding viewport and tile benchmarks for each available picture bench, and output visualization and analysis in HTML. By default it reads from Skia's Google Storage location where bot data are stored, but if --dir is given, will read from local directory instead. """ + HOWTO_STRING OPTION_DIR = '--dir' OPTION_DIR_SHORT = '-d' OPTION_REVISION = '--rev' OPTION_REVISION_SHORT = '-r' OPTION_PLATFORM = '--platform' OPTION_PLATFORM_SHORT = '-p' # Bench representation algorithm flag. OPTION_REPRESENTATION_ALG = '--algorithm' OPTION_REPRESENTATION_ALG_SHORT = '-a' # Bench representation algorithm. See trunk/bench/bench_util.py. REPRESENTATION_ALG = bench_util.ALGORITHM_25TH_PERCENTILE # Constants for bench file matching. GOOGLE_STORAGE_OBJECT_NAME_PREFIX = 'perfdata/Skia_' BENCH_FILE_PREFIX_TEMPLATE = 'bench_r%s_' TILING_FILE_NAME_INDICATOR = '_tile_' VIEWPORT_FILE_NAME_INDICATOR = '_viewport_' # Regular expression for matching format 'x'. DIMENSIONS_RE = '(\d+)x(\d+)' # HTML and JS output templates. HTML_PREFIX = """ ' '

PLATFORM: %s REVISION: %s


' % (platform, rev)) bench_dic = {} # [bench][config] -> [layout, [values]] file_dic = GetFiles(rev, bench_dir, platform) for f in file_dic: for point in bench_util.parse('', file_dic[f].split('\n'), representation_alg): if point.time_type: # Ignores non-walltime time_type. continue bench = point.bench.replace('.skp', '') config = point.config.replace('simple_', '') components = config.split('_') if components[0] == 'viewport': bench_dic.setdefault(bench, {})[config] = [components[1], [point.time]] else: # Stores per-tile benches. bench_dic.setdefault(bench, {})[config] = [ point.tile_layout, point.per_tile_values] benches = bench_dic.keys() benches.sort() for bench in benches: body_codes += '

%s


' % bench heat_plots = '' # For table row of heatmap plots. table_plots = '' # For table row of data table plots. # For bar plot legends and values in URL string. legends = '' values = '' keys = bench_dic[bench].keys() keys.sort() if not keys[-1].startswith('viewport'): # No viewport to analyze; skip. continue else: # Extracts viewport size, which for all viewport configs is the same. viewport = bench_dic[bench][keys[-1]][0] for config in keys: [layout, value_li] = bench_dic[bench][config] if config.startswith('tile_'): # For per-tile data, visualize tiles. tile_size = config.split('_')[1] if (not re.search(DIMENSIONS_RE, layout) or not re.search(DIMENSIONS_RE, tile_size) or not re.search(DIMENSIONS_RE, viewport)): continue # Skip unrecognized formats. [viewport_tile_sum, matrix] = GetTileMatrix( layout, tile_size, value_li, viewport) values += '%s|' % viewport_tile_sum [this_js, row1, row2] = GetTileVisCodes(config + '_' + bench, matrix) heat_plots += row1 table_plots += row2 js_codes += this_js else: # For viewport data, there is only one element in value_li. values += '%s|' % sum(value_li) legends += '%s:%s|' % (config, sum(value_li)) body_codes += (heat_plots + '' + table_plots + '
' + '
' + BAR_CHART_TEMPLATE % (legends[:-1], values[:-1])) return (js_codes, body_codes) def main(): """Parses flags and outputs expected Skia picture bench results.""" parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING) parser.add_option(OPTION_PLATFORM_SHORT, OPTION_PLATFORM, dest='plat', default=DEFAULT_PLATFORM, help='Platform to analyze. Set to DEFAULT_PLATFORM if not given.') parser.add_option(OPTION_REVISION_SHORT, OPTION_REVISION, dest='rev', help='(Mandatory) revision number to analyze.') parser.add_option(OPTION_DIR_SHORT, OPTION_DIR, dest='log_dir', default='', help=('(Optional) local directory where bench log files reside. If left ' 'empty (by default), will try to read from Google Storage.')) parser.add_option(OPTION_REPRESENTATION_ALG_SHORT, OPTION_REPRESENTATION_ALG, dest='alg', default=REPRESENTATION_ALG, help=('Bench representation algorithm. ' 'Default to "%s".' % REPRESENTATION_ALG)) (options, args) = parser.parse_args() if not (options.rev and options.rev.isdigit()): parser.error('Please provide correct mandatory flag %s' % OPTION_REVISION) return rev = int(options.rev) (js_codes, body_codes) = OutputTileAnalysis( rev, options.alg, options.log_dir, options.plat) print HTML_PREFIX + js_codes + body_codes + HTML_SUFFIX if '__main__' == __name__: main()