普通文本  |  122行  |  4.79 KB

# 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. For TLB misses, we expect a strong correlation
    between number of misses and number of iterations of a matching benchmark
    Each loop iteration should retire a constant number of additional
    instructions, and should take a nearly constant number of additional
    cycles or misses.
    """

    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.')
        unsupported_boards = ['gizmo']
        board = utils.get_board()
        if board in unsupported_boards:
            raise error.TestNAError('Unsupported board')

    def run_once(self, program, multiplier, **kwargs):
        program = os.path.join(self.srcdir, program)
        if self.perf_cmd == 'stat':
            self.facts = perf_verification.GatherPerfStats(
                    program, ','.join(self.events), multiplier)
        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(
                    program, 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 = {}
        is_tlb_benchmark = ('iTLB-misses' in dt.names or
                            'dTLB-misses' in dt.names)
        for y_var in dt.names:
            if y_var == 'loops': continue
            if y_var == 'cycles' and is_tlb_benchmark: 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
            if y_var in ('dTLB-misses', 'iTLB-misses'):
                misses_per_milion_cycles = [x[y_var] * 1.0e6 / x['cycles']
                                            for x in self.facts]
                rvar = prefix+'misses_per_milion_cycles'
                results[rvar] = numpy.max(misses_per_milion_cycles)

        # Output the standard Autotest way:
        self.write_perf_keyval(results)
        # ... And the CrOS-specific way:
        for k, v in results.iteritems():
          self.output_perf_value(k, v)

        if ('cycles' in self.events and not is_tlb_benchmark and
            results['cycles_r_squared'] < 0.996):
            raise error.TestFail('Poor correlation for cycles ~ loops')
        if ('instructions' in self.events and
            results['instructions_r_squared'] < 0.999):
            raise error.TestFail('Poor correlation for instructions ~ loops')
        if ('iTLB-misses' in self.events and
            results['iTLB-misses_r_squared'] < 0.999):
            raise error.TestFail('Poor correlation for iTLB-misses ~ loops')
        if ('dTLB-misses' in self.events and
            results['dTLB-misses_r_squared'] < 0.999):
            raise error.TestFail('Poor correlation for dTLB-misses ~ loops')
        if (self.perf_cmd == 'record -b' and
            results['branch_count_r_squared'] < 0.9999999):
            raise error.TestFail('Poor correlation for branch_count ~ loops')