blob: c54224cf203e3466d4e798d522ee954fa9bf5fde [file] [log] [blame]
# Copyright (c) 2014 The Chromium OS 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 os
from autotest_lib.client.bin import test
from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib import error
import numpy
import perf_lbr_verification
import perf_verification
import stats_utils
INTEL_LBR_UARCHS = (
# 'Broadwell', # Waiting on kernel support.
'Haswell',
'IvyBridge',
'SandyBridge')
class hardware_PerfCounterVerification(test.test):
"""Verify perf counters count what we think they count.
For cycles and instructions, we expect a strong correlation between
the number of iterations of a "noploop" program and the number of
cycles and instructions. That is, each loop iteration should retire
a constant number of additional instructions, and should take a
nearly constant number of additional cycles.
"""
version = 1
preserve_srcdir = True
def initialize(self, perf_cmd='stat', events=('cycles', 'instructions')):
self.job.require_gcc()
self.perf_cmd = perf_cmd
self.events = events
def setup(self):
os.chdir(self.srcdir)
utils.make('clean')
utils.make()
def warmup(self):
if self.perf_cmd == 'record -b':
uarch = utils.get_intel_cpu_uarch()
if uarch not in INTEL_LBR_UARCHS:
raise error.TestNAError('Unsupported microarchitecture.')
def run_once(self, **kwargs):
noploop = os.path.join(self.srcdir, 'noploop')
if self.perf_cmd == 'stat':
self.facts = perf_verification.GatherPerfStats(
noploop, ','.join(self.events))
elif self.perf_cmd == 'record -b':
branch = perf_lbr_verification.ReadBranchAddressesFile(
os.path.join(self.srcdir, 'noploop_branch.txt'))
self.facts = perf_lbr_verification.GatherPerfBranchSamples(
noploop, branch, ','.join(self.events),
10000)
else:
raise error.TestError('Unrecognized perf_cmd')
def postprocess_iteration(self):
if self.perf_cmd == 'stat':
dt = numpy.dtype([('loops', numpy.int)] +
[(e, numpy.int) for e in self.events])
elif self.perf_cmd == 'record -b':
dt = numpy.dtype([('loops', numpy.int),
('branch_count', numpy.int)])
arr = stats_utils.FactsToNumpyArray(self.facts, dt)
results = {}
for y_var in dt.names:
if y_var == 'loops': continue
(slope, intercept), r2 = stats_utils.LinearRegression(
arr['loops'], arr[y_var])
prefix = y_var + '_'
results[prefix+'slope'] = slope
results[prefix+'intercept'] = intercept
results[prefix+'r_squared'] = r2
self.write_perf_keyval(results)
cpu_arch = utils.get_cpu_arch()
if cpu_arch == 'arm':
# ARM is observed to have a somewhat weaker correlation in cycles.
cycles_r_squared_expectation = 0.996
else:
cycles_r_squared_expectation = 0.999
if ('cycles' in self.events and
results['cycles_r_squared'] < cycles_r_squared_expectation):
raise error.TestFail('Poor correlation for cycles ~ loops')
if ('instructions' in self.events and
results['instructions_r_squared'] < 0.999999):
raise error.TestFail('Poor correlation for instructions ~ loops')
if (self.perf_cmd == 'record -b' and
results['branch_count_r_squared'] < 0.9999999):
raise error.TestFail('Poor correlation for branch_count ~ loops')