Added autotest that calculates costs of TLB misses
BUG=None
TEST=Run on peppy board
Change-Id: I253b1656b29136a67b8cb124f76fc41b18b1cc0e
Reviewed-on: https://chromium-review.googlesource.com/297177
Commit-Ready: Nemanja Vasić <nvasic@google.com>
Tested-by: Nemanja Vasić <nvasic@google.com>
Reviewed-by: David Sharp <dhsharp@chromium.org>
diff --git a/client/site_tests/hardware_TLBMissCost/control b/client/site_tests/hardware_TLBMissCost/control
new file mode 100644
index 0000000..67aab8f
--- /dev/null
+++ b/client/site_tests/hardware_TLBMissCost/control
@@ -0,0 +1,24 @@
+# Copyright 2015 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.
+
+AUTHOR = "Nemanja Vasic <nvasic@google.com>"
+NAME = "hardware_TLBMissCost"
+TIME = "SHORT"
+TEST_CATEGORY = "Functional"
+TEST_CLASS = "hardware"
+TEST_TYPE = "client"
+ATTRIBUTES = "suite:experimental"
+SUITE = "experimental"
+
+DOC = """
+Calculate cost of a TLB miss
+
+Arguments:
+ events: Events to pass to perf stat -e
+ program: Benchmark binary
+"""
+
+job.run_test('hardware_TLBMissCost', tag='TLBMissCost',
+ events=('cycles', 'iTLB-misses'),
+ program='iTLB_benchmark')
diff --git a/client/site_tests/hardware_TLBMissCost/hardware_TLBMissCost.py b/client/site_tests/hardware_TLBMissCost/hardware_TLBMissCost.py
new file mode 100644
index 0000000..8558dad
--- /dev/null
+++ b/client/site_tests/hardware_TLBMissCost/hardware_TLBMissCost.py
@@ -0,0 +1,63 @@
+# Copyright 2015 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
+import perf_measurement
+import numpy
+
+from autotest_lib.client.bin import test
+from autotest_lib.client.bin import utils
+from autotest_lib.client.common_lib import error
+
+# This event counts cycles when the page miss handler is servicing page walks
+# caused by ITLB misses. Raw event codes for x86 microarchitectures can be
+# found at Intel Open Source technology center website:
+# https://download.01.org/perfmon
+RAW_PAGE_WALK_EVENT_CODES = {
+ 'Broadwell': 'r1085',
+ 'Haswell': 'r1085',
+ 'IvyBridge': 'r0485',
+ 'SandyBridge': 'r0485',
+}
+
+class hardware_TLBMissCost(test.test):
+ """Calculates cost of one iTLB miss in
+ terms of cycles spent on page walking.
+ """
+
+ version = 1
+ preserve_srcdir = True
+
+ def initialize(self, events=('cycles', 'iTLB-misses')):
+ self.job.require_gcc()
+ self.events = events
+
+ def setup(self):
+ os.chdir(self.srcdir)
+ utils.make('clean')
+ utils.make()
+
+ def warmup(self):
+ uarch = utils.get_intel_cpu_uarch()
+ if uarch not in RAW_PAGE_WALK_EVENT_CODES:
+ raise error.TestNAError('Unsupported microarchitecture.')
+ self.pw_event = RAW_PAGE_WALK_EVENT_CODES.get(uarch)
+
+ def run_once(self, program):
+ program = os.path.join(self.srcdir, program)
+ self.events = self.events + (self.pw_event,)
+ self.facts = perf_measurement.GatherPerfStats(program,
+ ','.join(self.events))
+
+ def postprocess_iteration(self):
+ results = {}
+ if ('iTLB-misses' in self.events):
+ pw_cycles_per_miss = [x[self.pw_event] * 1.0 / x['iTLB-misses']
+ for x in self.facts]
+ results['pw-cycles-per-miss'] = numpy.average(pw_cycles_per_miss)
+ if ('cycles' in self.events):
+ pw_cycle_percent = [x[self.pw_event] * 100.0 / x['cycles']
+ for x in self.facts]
+ results['pw-cycle-percent'] = numpy.average(pw_cycle_percent)
+ self.write_perf_keyval(results)
diff --git a/client/site_tests/hardware_TLBMissCost/perf_measurement.py b/client/site_tests/hardware_TLBMissCost/perf_measurement.py
new file mode 100755
index 0000000..6511d42
--- /dev/null
+++ b/client/site_tests/hardware_TLBMissCost/perf_measurement.py
@@ -0,0 +1,47 @@
+#!/usr/bin/python2.7
+# Copyright 2015 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 subprocess
+import numpy
+
+class Error(Exception):
+ """Module error class."""
+
+def GatherPerfStats(program, events):
+ """Run perf stat with the given events and given program.
+
+ @param program: path to benchmark binary. It should take one argument
+ (number of loop iterations) and produce no output.
+ @param events: value to pass to '-e' arg of perf stat.
+ @returns: List of dicts.
+ """
+ facts = []
+ for i in xrange(0, 10):
+ loops = (i + 1) * 10
+ out = subprocess.check_output(
+ ('perf', 'stat', '-x', ',',
+ '-e', events,
+ program, '%d' % loops),
+ stderr=subprocess.STDOUT)
+ unsupported_events = []
+ f = {}
+ for line in out.splitlines():
+ fields = line.split(',')
+ count, unit, event = None, None, None
+ if len(fields) == 2:
+ count, event = fields
+ elif len(fields) == 3:
+ count, unit, event = fields
+ else:
+ raise Error('Unable to parse perf stat output')
+ if count == '<not supported>':
+ unsupported_events.append(event)
+ else:
+ f[event] = int(count)
+ if unsupported_events:
+ raise Error('These events are not supported: %s'
+ % unsupported_events)
+ facts.append(f)
+ return facts
diff --git a/client/site_tests/hardware_TLBMissCost/src/Makefile b/client/site_tests/hardware_TLBMissCost/src/Makefile
new file mode 100644
index 0000000..e1a54b4
--- /dev/null
+++ b/client/site_tests/hardware_TLBMissCost/src/Makefile
@@ -0,0 +1,15 @@
+CFLAGS=-O0 -g
+
+BINS=iTLB_benchmark
+OBJS=iTLB_benchmark.o iTLB_benchmark_function.o
+OUTPUTS=$(BINS) $(OBJS) iTLB_benchmark_function.c
+
+all: $(OUTPUTS)
+
+iTLB_benchmark: iTLB_benchmark.o iTLB_benchmark_function.o
+
+iTLB_benchmark_function.c: generateBenchmarkFunction.sh
+ ./generateBenchmarkFunction.sh 4096 1024 > iTLB_benchmark_function.c
+
+clean:
+ rm -rf $(OUTPUTS)
diff --git a/client/site_tests/hardware_TLBMissCost/src/generateBenchmarkFunction.sh b/client/site_tests/hardware_TLBMissCost/src/generateBenchmarkFunction.sh
new file mode 100755
index 0000000..eba11fc
--- /dev/null
+++ b/client/site_tests/hardware_TLBMissCost/src/generateBenchmarkFunction.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# This script generates an assembly function that jumps over blocks of size
+# determined by its first argument. Number of such blocks is determined
+# by the second argument.
+
+PAGE_SIZE=$(( $1 ))
+TLB_ENTRY_CNT=$(( $2 ))
+
+function instruction_block {
+ for (( c=0; c < PAGE_SIZE ; c++ )) ; do
+ echo ' "nop\n\t"'
+ done
+}
+
+echo 'void iTLB_bechmark_function() {'
+echo ' __asm__ ('
+
+for (( i=0; i < TLB_ENTRY_CNT; i++ )) ; do
+ echo ' "1:jmp 1f\n\t"'
+ instruction_block
+done
+echo ' "1:nop\n\t"'
+echo ' );'
+echo '}'
diff --git a/client/site_tests/hardware_TLBMissCost/src/iTLB_benchmark.c b/client/site_tests/hardware_TLBMissCost/src/iTLB_benchmark.c
new file mode 100644
index 0000000..7809f44
--- /dev/null
+++ b/client/site_tests/hardware_TLBMissCost/src/iTLB_benchmark.c
@@ -0,0 +1,18 @@
+#include <stdlib.h>
+#include "iTLB_benchmark_function.h"
+
+int main(int argc, char *argv[]) {
+ unsigned long loops = 1000;
+ if (argc > 1) {
+ loops = strtoul(argv[1], NULL, 10);
+ if (loops < 1) {
+ loops = 1;
+ }
+ }
+
+ while (--loops) {
+ iTLB_bechmark_function();
+ }
+
+ return 0;
+}
diff --git a/client/site_tests/hardware_TLBMissCost/src/iTLB_benchmark_function.h b/client/site_tests/hardware_TLBMissCost/src/iTLB_benchmark_function.h
new file mode 100644
index 0000000..dd31701
--- /dev/null
+++ b/client/site_tests/hardware_TLBMissCost/src/iTLB_benchmark_function.h
@@ -0,0 +1 @@
+void iTLB_bechmark_function();