#!/usr/bin/env python
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (C) 2017, ARM Limited, Google, and contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
# Analysis of framestats deltas between 2+ runs
# This analysis outputs potential regressions when comparing
# a baseline folder output of microbenchmarks like UiBench with
# a test folder with similar stats. The base and test folders
# can contain the output of one or more runs of run_uibench.py or
# similar script.
# For example:
# python framestats_analysis.py --baseline_dir=/usr/local/google/home/andresoportus/b/master2/external/lisa/results/UiBench_walleye_em_baseline_b/ --results_dir=/usr/local/google/home/andresoportus/b/master2/external/lisa/results/UiBench_walleye_em_first/ --threshold_ave 0.322
# No handlers could be found for logger "EnergyModel"
# Potential regression in:
# avg-frame-time-50 avg-frame-time-90 avg-frame-time-95 avg-frame-time-99
# UiBenchJankTests#testTrivialAnimation
# all base [ 5. 5. 5. 5. 5. 5. 5.] [ 5. 5. 5. 5. 5. 5. 5.] [ 5. 5. 5. 5. 5. 5. 5.] [ 5. 5.2 5.1 5.2 5.9 5.2 5.2]
# all test [ 5. 5. 5. 5. 5. 5.] [ 5. 5. 5. 5. 5. 5.] [ 5. 5. 5. 5. 5. 5.] [ 5.4 6. 5.3 5.8 6. 5.1]
# ave base [ 5.] [ 5.] [ 5.] [ 5.25714286]
# ave test [ 5.] [ 5.] [ 5.] [ 5.6]
# avg delta [ 0.] [ 0.] [ 0.] [ 0.34285714]
import os
import sys
import argparse
import numpy as np
import pandas as pd
from collections import OrderedDict
from android.workloads.uibench import UiBench
averages = ['avg-frame-time-50', 'avg-frame-time-90',
'avg-frame-time-95', 'avg-frame-time-99']
stats_file = 'framestats.txt'
"""
Parses a directory to find stats
:param stats_dir: path to stats dir
:type stats_dir: str
:param stats_file: name of stats file
:type stats_file: str
"""
def parse_stats_dir(stats_dir, stats_file):
df = {}
run_name = os.path.basename(os.path.normpath(stats_dir))
for root, dirs, files in os.walk(stats_dir):
if stats_file in files:
test_name = os.path.basename(os.path.normpath(root))
if test_name not in df:
df[test_name] = pd.DataFrame()
df[test_name] = df[test_name].append(UiBench.get_results(root))
return df
"""
Prints a header line
:param df: pandas dataframe to extract columns names from
:type df: pd.DataFrame
"""
def print_header(df):
sys.stdout.write('{:16}'.format(''))
for c in df.columns:
sys.stdout.write(' {:32}'.format(str(c)))
sys.stdout.write('\n')
"""
Prints a pandas DataFrame as a row
:param df: pandas dataframe to extract values from
:type df: pd.DataFrame
"""
def print_as_row(name, df):
sys.stdout.write('{:16}'.format(name))
for c in df.columns:
sys.stdout.write(' {:32}'.format(str(df[c].values)))
sys.stdout.write('\n')
def main():
header_printed = False
pd.set_option('precision', 2)
base = parse_stats_dir(args.baseline_dir, stats_file)
test = parse_stats_dir(args.results_dir, stats_file)
for name in base:
try:
# DataFrame created from the Series output of mean() needs to
# be transposed to make averages be columns again
ave_base = pd.DataFrame(base[name][averages].mean()).T
ave_test = pd.DataFrame(test[name][averages].mean()).T
if args.verbose or \
((ave_test - ave_base) > args.threshold_ave).iloc[0].any():
if not header_printed:
if not args.verbose:
print "Potential regression in:"
print_header(base[name][averages])
header_printed = True
print name
print_as_row('all base', base[name][averages])
print_as_row('all test', test[name][averages])
print_as_row('ave base', ave_base)
print_as_row('ave test', ave_test)
print_as_row('avg delta', ave_test - ave_base)
except KeyError:
sys.stdout.write('\n')
if not header_printed:
print "No regression found"
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Framestats analysis")
parser.add_argument("--results_dir", "-d", type=str,
default=os.path.join(os.environ["LISA_HOME"],
"results/UiBench_default"),
help="The test directory to read from. (default \
LISA_HOME/restuls/UiBench_deafult)")
parser.add_argument("--baseline_dir", "-b", type=str, required=True,
help="The directory that provides baseline test to \
compare against")
parser.add_argument("--threshold_ave", "-t", type=float, default=0.99,
help="Amount to filter noise in average values")
parser.add_argument("--verbose", "-v", action='store_true',
help="Verbose output")
args = parser.parse_args()
main()