203 lines
6.6 KiB
Python
203 lines
6.6 KiB
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.
|
|
|
|
import re
|
|
import sys
|
|
|
|
import android_commands
|
|
import json
|
|
import logging
|
|
import math
|
|
|
|
# Valid values of result type.
|
|
RESULT_TYPES = {'unimportant': 'RESULT ',
|
|
'default': '*RESULT ',
|
|
'informational': '',
|
|
'unimportant-histogram': 'HISTOGRAM ',
|
|
'histogram': '*HISTOGRAM '}
|
|
|
|
|
|
def _EscapePerfResult(s):
|
|
"""Escapes |s| for use in a perf result."""
|
|
return re.sub('[\:|=/#&,]', '_', s)
|
|
|
|
|
|
def _Flatten(values):
|
|
"""Returns a simple list without sub-lists."""
|
|
ret = []
|
|
for entry in values:
|
|
if isinstance(entry, list):
|
|
ret.extend(_Flatten(entry))
|
|
else:
|
|
ret.append(entry)
|
|
return ret
|
|
|
|
|
|
def GeomMeanAndStdDevFromHistogram(histogram_json):
|
|
histogram = json.loads(histogram_json)
|
|
# Handle empty histograms gracefully.
|
|
if not 'buckets' in histogram:
|
|
return 0.0, 0.0
|
|
count = 0
|
|
sum_of_logs = 0
|
|
for bucket in histogram['buckets']:
|
|
if 'high' in bucket:
|
|
bucket['mean'] = (bucket['low'] + bucket['high']) / 2.0
|
|
else:
|
|
bucket['mean'] = bucket['low']
|
|
if bucket['mean'] > 0:
|
|
sum_of_logs += math.log(bucket['mean']) * bucket['count']
|
|
count += bucket['count']
|
|
|
|
if count == 0:
|
|
return 0.0, 0.0
|
|
|
|
sum_of_squares = 0
|
|
geom_mean = math.exp(sum_of_logs / count)
|
|
for bucket in histogram['buckets']:
|
|
if bucket['mean'] > 0:
|
|
sum_of_squares += (bucket['mean'] - geom_mean) ** 2 * bucket['count']
|
|
return geom_mean, math.sqrt(sum_of_squares / count)
|
|
|
|
|
|
def _MeanAndStdDevFromList(values):
|
|
avg = None
|
|
sd = None
|
|
if len(values) > 1:
|
|
try:
|
|
value = '[%s]' % ','.join([str(v) for v in values])
|
|
avg = sum([float(v) for v in values]) / len(values)
|
|
sqdiffs = [(float(v) - avg) ** 2 for v in values]
|
|
variance = sum(sqdiffs) / (len(values) - 1)
|
|
sd = math.sqrt(variance)
|
|
except ValueError:
|
|
value = ", ".join(values)
|
|
else:
|
|
value = values[0]
|
|
return value, avg, sd
|
|
|
|
|
|
def PrintPages(page_list):
|
|
"""Prints list of pages to stdout in the format required by perf tests."""
|
|
print 'Pages: [%s]' % ','.join([_EscapePerfResult(p) for p in page_list])
|
|
|
|
|
|
def PrintPerfResult(measurement, trace, values, units, result_type='default',
|
|
print_to_stdout=True):
|
|
"""Prints numerical data to stdout in the format required by perf tests.
|
|
|
|
The string args may be empty but they must not contain any colons (:) or
|
|
equals signs (=).
|
|
|
|
Args:
|
|
measurement: A description of the quantity being measured, e.g. "vm_peak".
|
|
trace: A description of the particular data point, e.g. "reference".
|
|
values: A list of numeric measured values. An N-dimensional list will be
|
|
flattened and treated as a simple list.
|
|
units: A description of the units of measure, e.g. "bytes".
|
|
result_type: Accepts values of RESULT_TYPES.
|
|
print_to_stdout: If True, prints the output in stdout instead of returning
|
|
the output to caller.
|
|
|
|
Returns:
|
|
String of the formated perf result.
|
|
"""
|
|
assert result_type in RESULT_TYPES, 'result type: %s is invalid' % result_type
|
|
|
|
trace_name = _EscapePerfResult(trace)
|
|
|
|
if result_type in ['unimportant', 'default', 'informational']:
|
|
assert isinstance(values, list)
|
|
assert len(values)
|
|
assert '/' not in measurement
|
|
value, avg, sd = _MeanAndStdDevFromList(_Flatten(values))
|
|
output = '%s%s: %s%s%s %s' % (
|
|
RESULT_TYPES[result_type],
|
|
_EscapePerfResult(measurement),
|
|
trace_name,
|
|
# Do not show equal sign if the trace is empty. Usually it happens when
|
|
# measurement is enough clear to describe the result.
|
|
'= ' if trace_name else '',
|
|
value,
|
|
units)
|
|
else:
|
|
assert(result_type in ['histogram', 'unimportant-histogram'])
|
|
assert isinstance(values, list)
|
|
# The histograms can only be printed individually, there's no computation
|
|
# across different histograms.
|
|
assert len(values) == 1
|
|
value = values[0]
|
|
output = '%s%s: %s= %s' % (
|
|
RESULT_TYPES[result_type],
|
|
_EscapePerfResult(measurement),
|
|
trace_name,
|
|
value)
|
|
avg, sd = GeomMeanAndStdDevFromHistogram(value)
|
|
|
|
if avg:
|
|
output += '\nAvg %s: %f%s' % (measurement, avg, units)
|
|
if sd:
|
|
output += '\nSd %s: %f%s' % (measurement, sd, units)
|
|
if print_to_stdout:
|
|
print output
|
|
sys.stdout.flush()
|
|
return output
|
|
|
|
|
|
class CacheControl(object):
|
|
_DROP_CACHES = '/proc/sys/vm/drop_caches'
|
|
|
|
def __init__(self, adb):
|
|
self._adb = adb
|
|
|
|
def DropRamCaches(self):
|
|
"""Drops the filesystem ram caches for performance testing."""
|
|
self._adb.RunShellCommand('su -c sync')
|
|
self._adb.SetProtectedFileContents(CacheControl._DROP_CACHES, '3')
|
|
|
|
|
|
class PerfControl(object):
|
|
"""Provides methods for setting the performance mode of a device."""
|
|
_SCALING_GOVERNOR_FMT = (
|
|
'/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor')
|
|
|
|
def __init__(self, adb):
|
|
self._adb = adb
|
|
kernel_max = self._adb.GetFileContents('/sys/devices/system/cpu/kernel_max',
|
|
log_result=False)
|
|
assert kernel_max, 'Unable to find /sys/devices/system/cpu/kernel_max'
|
|
self._kernel_max = int(kernel_max[0])
|
|
logging.info('Maximum CPU index: %d' % self._kernel_max)
|
|
self._original_scaling_governor = self._adb.GetFileContents(
|
|
PerfControl._SCALING_GOVERNOR_FMT % 0,
|
|
log_result=False)[0]
|
|
|
|
def SetHighPerfMode(self):
|
|
"""Sets the highest possible performance mode for the device."""
|
|
self._SetScalingGovernorInternal('performance')
|
|
|
|
def SetDefaultPerfMode(self):
|
|
"""Sets the performance mode for the device to its default mode."""
|
|
product_model = self._adb.GetProductModel()
|
|
governor_mode = {
|
|
"GT-I9300" : 'pegasusq',
|
|
"Galaxy Nexus" : 'interactive',
|
|
"Nexus 4" : 'ondemand',
|
|
"Nexus 7" : 'interactive',
|
|
"Nexus 10": 'interactive'
|
|
}.get(product_model, 'ondemand')
|
|
self._SetScalingGovernorInternal(governor_mode)
|
|
|
|
def RestoreOriginalPerfMode(self):
|
|
"""Resets the original performance mode of the device."""
|
|
self._SetScalingGovernorInternal(self._original_scaling_governor)
|
|
|
|
def _SetScalingGovernorInternal(self, value):
|
|
for cpu in range(self._kernel_max + 1):
|
|
scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu
|
|
if self._adb.FileExistsOnDevice(scaling_governor_file):
|
|
logging.info('Writing scaling governor mode \'%s\' -> %s' %
|
|
(value, scaling_governor_file))
|
|
self._adb.SetProtectedFileContents(scaling_governor_file, value)
|