1# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import re
6
7import android_commands
8import math
9
10# Valid values of result type.
11RESULT_TYPES = {'unimportant': 'RESULT ',
12                'default': '*RESULT ',
13                'informational': ''}
14
15
16def _EscapePerfResult(s):
17  """Escapes |s| for use in a perf result."""
18  # Colons (:) and equal signs (=) are not allowed, and we chose an arbitrary
19  # limit of 40 chars.
20  return re.sub(':|=', '_', s[:40])
21
22
23def PrintPerfResult(measurement, trace, values, units, result_type='default',
24                    print_to_stdout=True):
25  """Prints numerical data to stdout in the format required by perf tests.
26
27  The string args may be empty but they must not contain any colons (:) or
28  equals signs (=).
29
30  Args:
31    measurement: A description of the quantity being measured, e.g. "vm_peak".
32    trace: A description of the particular data point, e.g. "reference".
33    values: A list of numeric measured values.
34    units: A description of the units of measure, e.g. "bytes".
35    result_type: A  tri-state that accepts values of ['unimportant', 'default',
36        'informational']. 'unimportant' prints RESULT, 'default' prints *RESULT
37        and 'informational' prints nothing.
38    print_to_stdout: If True, prints the output in stdout instead of returning
39        the output to caller.
40
41    Returns:
42      String of the formated perf result.
43  """
44  assert result_type in RESULT_TYPES, 'result type: %s is invalid' % result_type
45
46  assert isinstance(values, list)
47  assert len(values)
48  assert '/' not in measurement
49  avg = None
50  sd = None
51  if len(values) > 1:
52    try:
53      value = '[%s]' % ','.join([str(v) for v in values])
54      avg = sum([float(v) for v in values]) / len(values)
55      sqdiffs = [(float(v) - avg) ** 2 for v in values]
56      variance = sum(sqdiffs) / (len(values) - 1)
57      sd = math.sqrt(variance)
58    except ValueError:
59      value = ", ".join(values)
60  else:
61    value = values[0]
62
63  trace_name = _EscapePerfResult(trace)
64  output = '%s%s: %s%s%s %s' % (
65    RESULT_TYPES[result_type],
66    _EscapePerfResult(measurement),
67    trace_name,
68    # Do not show equal sign if the trace is empty. Usually it happens when
69    # measurement is enough clear to describe the result.
70    '= ' if trace_name else '',
71    value,
72    units)
73  if avg:
74    output += '\nAvg %s: %f%s' % (measurement, avg, units)
75  if sd:
76    output += '\nSd  %s: %f%s' % (measurement, sd, units)
77  if print_to_stdout:
78    print output
79  return output
80
81
82class PerfTestSetup(object):
83  """Provides methods for setting up a device for perf testing."""
84  _DROP_CACHES = '/proc/sys/vm/drop_caches'
85  _SCALING_GOVERNOR = '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor'
86
87  def __init__(self, adb):
88    self._adb = adb
89    num_cpus = self._adb.GetFileContents('/sys/devices/system/cpu/online',
90                                         log_result=False)
91    assert num_cpus, 'Unable to find /sys/devices/system/cpu/online'
92    self._num_cpus = int(num_cpus[0].split('-')[-1])
93    self._original_scaling_governor = None
94
95  def DropRamCaches(self):
96    """Drops the filesystem ram caches for performance testing."""
97    if not self._adb.IsRootEnabled():
98      self._adb.EnableAdbRoot()
99    self._adb.RunShellCommand('sync')
100    self._adb.RunShellCommand('echo 3 > ' + PerfTestSetup._DROP_CACHES)
101
102  def SetUp(self):
103    """Sets up performance tests."""
104    if not self._original_scaling_governor:
105      self._original_scaling_governor = self._adb.GetFileContents(
106          PerfTestSetup._SCALING_GOVERNOR % 0,
107          log_result=False)[0]
108      self._SetScalingGovernorInternal('performance')
109    self.DropRamCaches()
110
111  def TearDown(self):
112    """Tears down performance tests."""
113    if self._original_scaling_governor:
114      self._SetScalingGovernorInternal(self._original_scaling_governor)
115    self._original_scaling_governor = None
116
117  def _SetScalingGovernorInternal(self, value):
118    for cpu in range(self._num_cpus):
119      self._adb.RunShellCommand(
120          ('echo %s > ' + PerfTestSetup._SCALING_GOVERNOR) % (value, cpu))
121