michael@0: # Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: # Use of this source code is governed by a BSD-style license that can be michael@0: # found in the LICENSE file. michael@0: michael@0: import re michael@0: michael@0: import android_commands michael@0: import math michael@0: michael@0: # Valid values of result type. michael@0: RESULT_TYPES = {'unimportant': 'RESULT ', michael@0: 'default': '*RESULT ', michael@0: 'informational': ''} michael@0: michael@0: michael@0: def _EscapePerfResult(s): michael@0: """Escapes |s| for use in a perf result.""" michael@0: # Colons (:) and equal signs (=) are not allowed, and we chose an arbitrary michael@0: # limit of 40 chars. michael@0: return re.sub(':|=', '_', s[:40]) michael@0: michael@0: michael@0: def PrintPerfResult(measurement, trace, values, units, result_type='default', michael@0: print_to_stdout=True): michael@0: """Prints numerical data to stdout in the format required by perf tests. michael@0: michael@0: The string args may be empty but they must not contain any colons (:) or michael@0: equals signs (=). michael@0: michael@0: Args: michael@0: measurement: A description of the quantity being measured, e.g. "vm_peak". michael@0: trace: A description of the particular data point, e.g. "reference". michael@0: values: A list of numeric measured values. michael@0: units: A description of the units of measure, e.g. "bytes". michael@0: result_type: A tri-state that accepts values of ['unimportant', 'default', michael@0: 'informational']. 'unimportant' prints RESULT, 'default' prints *RESULT michael@0: and 'informational' prints nothing. michael@0: print_to_stdout: If True, prints the output in stdout instead of returning michael@0: the output to caller. michael@0: michael@0: Returns: michael@0: String of the formated perf result. michael@0: """ michael@0: assert result_type in RESULT_TYPES, 'result type: %s is invalid' % result_type michael@0: michael@0: assert isinstance(values, list) michael@0: assert len(values) michael@0: assert '/' not in measurement michael@0: avg = None michael@0: sd = None michael@0: if len(values) > 1: michael@0: try: michael@0: value = '[%s]' % ','.join([str(v) for v in values]) michael@0: avg = sum([float(v) for v in values]) / len(values) michael@0: sqdiffs = [(float(v) - avg) ** 2 for v in values] michael@0: variance = sum(sqdiffs) / (len(values) - 1) michael@0: sd = math.sqrt(variance) michael@0: except ValueError: michael@0: value = ", ".join(values) michael@0: else: michael@0: value = values[0] michael@0: michael@0: trace_name = _EscapePerfResult(trace) michael@0: output = '%s%s: %s%s%s %s' % ( michael@0: RESULT_TYPES[result_type], michael@0: _EscapePerfResult(measurement), michael@0: trace_name, michael@0: # Do not show equal sign if the trace is empty. Usually it happens when michael@0: # measurement is enough clear to describe the result. michael@0: '= ' if trace_name else '', michael@0: value, michael@0: units) michael@0: if avg: michael@0: output += '\nAvg %s: %f%s' % (measurement, avg, units) michael@0: if sd: michael@0: output += '\nSd %s: %f%s' % (measurement, sd, units) michael@0: if print_to_stdout: michael@0: print output michael@0: return output michael@0: michael@0: michael@0: class PerfTestSetup(object): michael@0: """Provides methods for setting up a device for perf testing.""" michael@0: _DROP_CACHES = '/proc/sys/vm/drop_caches' michael@0: _SCALING_GOVERNOR = '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor' michael@0: michael@0: def __init__(self, adb): michael@0: self._adb = adb michael@0: num_cpus = self._adb.GetFileContents('/sys/devices/system/cpu/online', michael@0: log_result=False) michael@0: assert num_cpus, 'Unable to find /sys/devices/system/cpu/online' michael@0: self._num_cpus = int(num_cpus[0].split('-')[-1]) michael@0: self._original_scaling_governor = None michael@0: michael@0: def DropRamCaches(self): michael@0: """Drops the filesystem ram caches for performance testing.""" michael@0: if not self._adb.IsRootEnabled(): michael@0: self._adb.EnableAdbRoot() michael@0: self._adb.RunShellCommand('sync') michael@0: self._adb.RunShellCommand('echo 3 > ' + PerfTestSetup._DROP_CACHES) michael@0: michael@0: def SetUp(self): michael@0: """Sets up performance tests.""" michael@0: if not self._original_scaling_governor: michael@0: self._original_scaling_governor = self._adb.GetFileContents( michael@0: PerfTestSetup._SCALING_GOVERNOR % 0, michael@0: log_result=False)[0] michael@0: self._SetScalingGovernorInternal('performance') michael@0: self.DropRamCaches() michael@0: michael@0: def TearDown(self): michael@0: """Tears down performance tests.""" michael@0: if self._original_scaling_governor: michael@0: self._SetScalingGovernorInternal(self._original_scaling_governor) michael@0: self._original_scaling_governor = None michael@0: michael@0: def _SetScalingGovernorInternal(self, value): michael@0: for cpu in range(self._num_cpus): michael@0: self._adb.RunShellCommand( michael@0: ('echo %s > ' + PerfTestSetup._SCALING_GOVERNOR) % (value, cpu))