<commit_before>#!/usr/bin/env python
# Copyright (c) 2012 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.

""" Upload benchmark performance data results. """

from build_step import BuildStep
from utils import sync_bucket_subdir
from utils import gclient_utils
from utils import upload_to_bucket

import builder_name_schema

import copy
import json
import os
import os.path
import re
import sys
from datetime import datetime


# TODO(kelvinly,borenet): eventually share this code across repos
# Modified from Skia repo code, roughly line 108 down of
# bench/check_regressions.py
def ReadExpectations(filename):
  """Reads expectations data from file.

  It returns a dictionary containing tuples of the lower, upper, and expected
  bounds, using the testname and configuration concatenated together as the
  key."""
  expectations = {}
  unstripped_keys = []
  for expectation in open(filename).readlines():
    elements = expectation.strip().split(',')
    if not elements[0] or elements[0].startswith('#'):
      continue
    if len(elements) != 5:
      raise Exception('Invalid expectation line format: {}'.format(
                      expectation))
    # [<Bench_BmpConfig_TimeType>,<Platform-Alg>]
    bench_entry = ','.join(elements[0:1])
    if bench_entry in unstripped_keys:
      raise Exception('Dup entries for bench expectation {}'.format(
                      bench_entry))
    unstripped_keys.append(bench_entry)   # Using this in case multiple lines
                                          # share everything except for the
                                          # algorithm and/or platform
    entry = elements[0]
    # [<Bench_BmpConfig_TimeType>] -> (LB, UB, EXPECTED)
    expectations[entry] = (float(elements[-2]),
                           float(elements[-1]),
                           float(elements[-3]))
  return expectations


def _ParseConfig(config, tileSet=None, idx=0):
  """Converts a config string into a dictionary of configuration values."""
  config_info = config.split('_')
  params = {}
  token_name = None
  for token in config_info:
    if token_name:
      if token_name == 'grid':
        params['bbh'] = 'grid_{}'.format(token)
      else:
        params[token_name] = token
      token_name = None
    elif token in ['scalar', 'viewport', 'grid']:
      token_name = token
      if token == 'scalar':
        token_name = 'scale'
    elif token in ['8888', 'gpu', 'msaa4', '<KEY>',
                   '<KEY>']:
      params['config'] = token
    elif token in ['rtree', 'quadtree', 'grid']:
      params['bbh'] = token
    elif token in ['simple', 'record', 'tile']:
      params['mode'] = token
      if tileSet and token == 'tile':
        params['mode'] = '_'.join(
            'tile',
            idx % tileSet['tx'],
            idx // tileSet['ty'])
    else:
      if 'badParams' not in params:
        params['badParams'] = token
      else:
        params['badParams'] += '_' + token
  return params



class UploadBenchResults(BuildStep):

  def __init__(self, attempts=5, **kwargs):
    super(UploadBenchResults, self).__init__(attempts=attempts, **kwargs)

  def _GetPerfDataDir(self):
    return self._perf_data_dir

  def _RunLogUpload(self, dest_gsbase):
    # Upload the normal bench logs
    print "Uploading normal data files"
    subdir_path = '/'.join(('perfdata', self._builder_name))
    if self._is_try:
      # Trybots need to isolate their results by build number.
      subdir_path = '/'.join((subdir_path, str(self._build_number)))
    sync_bucket_subdir.SyncBucketSubdir(
        directory=self._GetPerfDataDir(),
        dest_gsbase=dest_gsbase,
        subdir=subdir_path,
        do_upload=True,
        exclude_json=True,
        do_download=False)

  def _GetExpectationsPath(self):
    """Gets path to expectations."""
    return os.path.join(
        'expectations',
        'bench',
        'bench_expectations_%s.txt' % builder_name_schema.GetWaterfallBot(
            self._builder_name))


  def _GetTrybotDict(self):
    result = {}
    result['isTrybot'] = builder_name_schema.IsTrybot(self._builder_name)
    return result


  def _UploadJSONResults(self, dest_gsbase, gs_subdir, full_json_path):
    now = datetime.utcnow()
    gs_json_path = '/'.join((str(now.year).zfill(4), str(now.month).zfill(2),
                            str(now.day).zfill(2), str(now.hour).zfill(2)))
    gs_dir = '/'.join((gs_subdir, gs_json_path, self._builder_name))
    if self._is_try:
      gs_dir = '/'.join(('trybot', gs_dir, self.build_number))
    upload_to_bucket.upload_to_bucket(
        full_json_path,
        '/'.join((dest_gsbase, gs_dir)))


  def _RunSKPBenchJSONUpload(self, dest_gsbase):
    """Upload SPK bench JSON data, after converting the JSON format."""
    print "Uploading SPK bench JSON data"

    RE_FILE_SEARCH = re.compile(
        '^bench_{}_data_skp_(.*)_([0-9]*)\.json$'.format(self._got_revision))
    file_list = os.listdir(self._GetPerfDataDir())
    expectations = {}
    pictures_timestamp = ''

    try:
      expectations = ReadExpectations(self._GetExpectationsPath())
    except IOError:
      print "Unable to open expectations file"

    json_file_list = []
    for file_name in file_list:
      if RE_FILE_SEARCH.search(file_name):
        json_file_list.append(os.path.join(
            self._GetPerfDataDir(), file_name))

        if not pictures_timestamp:
          pictures_timestamp = file_name.split('_')[-1].split('.json')[0]<commit_msg>fix typo.<commit_after>#!/usr/bin/env python
# Copyright (c) 2012 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.

""" Upload benchmark performance data results. """

from build_step import BuildStep
from utils import sync_bucket_subdir
from utils import gclient_utils
from utils import upload_to_bucket

import builder_name_schema

import copy
import json
import os
import os.path
import re
import sys
from datetime import datetime


# TODO(kelvinly,borenet): eventually share this code across repos
# Modified from Skia repo code, roughly line 108 down of
# bench/check_regressions.py
def ReadExpectations(filename):
  """Reads expectations data from file.

  It returns a dictionary containing tuples of the lower, upper, and expected
  bounds, using the testname and configuration concatenated together as the
  key."""
  expectations = {}
  unstripped_keys = []
  for expectation in open(filename).readlines():
    elements = expectation.strip().split(',')
    if not elements[0] or elements[0].startswith('#'):
      continue
    if len(elements) != 5:
      raise Exception('Invalid expectation line format: {}'.format(
                      expectation))
    # [<Bench_BmpConfig_TimeType>,<Platform-Alg>]
    bench_entry = ','.join(elements[0:1])
    if bench_entry in unstripped_keys:
      raise Exception('Dup entries for bench expectation {}'.format(
                      bench_entry))
    unstripped_keys.append(bench_entry)   # Using this in case multiple lines
                                          # share everything except for the
                                          # algorithm and/or platform
    entry = elements[0]
    # [<Bench_BmpConfig_TimeType>] -> (LB, UB, EXPECTED)
    expectations[entry] = (float(elements[-2]),
                           float(elements[-1]),
                           float(elements[-3]))
  return expectations


def _ParseConfig(config, tileSet=None, idx=0):
  """Converts a config string into a dictionary of configuration values."""
  config_info = config.split('_')
  params = {}
  token_name = None
  for token in config_info:
    if token_name:
      if token_name == 'grid':
        params['bbh'] = 'grid_{}'.format(token)
      else:
        params[token_name] = token
      token_name = None
    elif token in ['scalar', 'viewport', 'grid']:
      token_name = token
      if token == 'scalar':
        token_name = 'scale'
    elif token in ['8888', 'gpu', 'msaa4', '<KEY>',
                   '<KEY>']:
      params['config'] = token
    elif token in ['rtree', 'quadtree', 'grid']:
      params['bbh'] = token
    elif token in ['simple', 'record', 'tile']:
      params['mode'] = token
      if tileSet and token == 'tile':
        params['mode'] = '_'.join(
            'tile',
            idx % tileSet['tx'],
            idx // tileSet['ty'])
    else:
      if 'badParams' not in params:
        params['badParams'] = token
      else:
        params['badParams'] += '_' + token
  return params



class UploadBenchResults(BuildStep):

  def __init__(self, attempts=5, **kwargs):
    super(UploadBenchResults, self).__init__(attempts=attempts, **kwargs)

  def _GetPerfDataDir(self):
    return self._perf_data_dir

  def _RunLogUpload(self, dest_gsbase):
    # Upload the normal bench logs
    print "Uploading normal data files"
    subdir_path = '/'.join(('perfdata', self._builder_name))
    if self._is_try:
      # Trybots need to isolate their results by build number.
      subdir_path = '/'.join((subdir_path, str(self._build_number)))
    sync_bucket_subdir.SyncBucketSubdir(
        directory=self._GetPerfDataDir(),
        dest_gsbase=dest_gsbase,
        subdir=subdir_path,
        do_upload=True,
        exclude_json=True,
        do_download=False)

  def _GetExpectationsPath(self):
    """Gets path to expectations."""
    return os.path.join(
        'expectations',
        'bench',
        'bench_expectations_%s.txt' % builder_name_schema.GetWaterfallBot(
            self._builder_name))


  def _GetTrybotDict(self):
    result = {}
    result['isTrybot'] = builder_name_schema.IsTrybot(self._builder_name)
    return result


  def _UploadJSONResults(self, dest_gsbase, gs_subdir, full_json_path):
    now = datetime.utcnow()
    gs_json_path = '/'.join((str(now.year).zfill(4), str(now.month).zfill(2),
                            str(now.day).zfill(2), str(now.hour).zfill(2)))
    gs_dir = '/'.join((gs_subdir, gs_json_path, self._builder_name))
    if self._is_try:
      gs_dir = '/'.join(('trybot', gs_dir, self._build_number))
    upload_to_bucket.upload_to_bucket(
        full_json_path,
        '/'.join((dest_gsbase, gs_dir)))


  def _RunSKPBenchJSONUpload(self, dest_gsbase):
    """Upload SPK bench JSON data, after converting the JSON format."""
    print "Uploading SPK bench JSON data"

    RE_FILE_SEARCH = re.compile(
        '^bench_{}_data_skp_(.*)_([0-9]*)\.json$'.format(self._got_revision))
    file_list = os.listdir(self._GetPerfDataDir())
    expectations = {}
    pictures_timestamp = ''

    try:
      expectations = ReadExpectations(self._GetExpectationsPath())
    except IOError:
      print "Unable to open expectations file"

    json_file_list = []
    for file_name in file_list:
      if RE_FILE_SEARCH.search(file_name):
        json_file_list.append(os.path.join(
            self._GetPerfDataDir(), file_name))

        if not pictures_timestamp:
          pictures_timestamp = file_name.split('_')[-1].split('.json')[0]
{'fraction_non_alphanumeric': 0.1176629213483146, 'fraction_numerical': 0.005573033707865168, 'mean_word_length': 2.9384070796460176, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6338434', 'n_tokens_mistral': 3408, 'n_tokens_neox': 3321, 'n_words': 860}