This is a generalized tracing benchmark, to which various tracers can be added in tracers.py or site_tracers.py. It performs 100,000 calls to getuid() with the specified tracer. Currently supports "off" (no tracing), and "ftrace" tracers. The size of the tracer's ring buffer is meant to be large enough that it will not overflow. This measures best-case overhead of the tracer.
Signed-off-by: David Sharp <[email protected]> --- client/tests/tracing_microbenchmark/base_tracer.py | 30 +++++++++ client/tests/tracing_microbenchmark/control | 28 +++++++++ client/tests/tracing_microbenchmark/src/Makefile | 8 +++ .../tracing_microbenchmark/src/getuid_microbench.c | 63 ++++++++++++++++++++ client/tests/tracing_microbenchmark/tracers.py | 60 +++++++++++++++++++ .../tracing_microbenchmark.py | 50 ++++++++++++++++ 6 files changed, 239 insertions(+), 0 deletions(-) create mode 100755 client/tests/tracing_microbenchmark/__init__.py create mode 100755 client/tests/tracing_microbenchmark/base_tracer.py create mode 100644 client/tests/tracing_microbenchmark/control create mode 100644 client/tests/tracing_microbenchmark/src/Makefile create mode 100644 client/tests/tracing_microbenchmark/src/getuid_microbench.c create mode 100755 client/tests/tracing_microbenchmark/tracers.py create mode 100755 client/tests/tracing_microbenchmark/tracing_microbenchmark.py diff --git a/client/tests/tracing_microbenchmark/__init__.py b/client/tests/tracing_microbenchmark/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/client/tests/tracing_microbenchmark/base_tracer.py b/client/tests/tracing_microbenchmark/base_tracer.py new file mode 100755 index 0000000..e36eca4 --- /dev/null +++ b/client/tests/tracing_microbenchmark/base_tracer.py @@ -0,0 +1,30 @@ +import os +from autotest_lib.client.bin import utils + + +class Tracer(object): + """ + Common interface for tracing. + """ + + tracing_dir = None + + def trace_config(self, path, value): + """ + Write value to a tracing config file under self.tracing_dir. + """ + path = os.path.join(self.tracing_dir, path) + utils.open_write_close(path, value) + + def warmup(self, buffer_size_kb): + pass + def cleanup(self): + pass + def start_tracing(self): + pass + def stop_tracing(self): + pass + def gather_stats(self, results): + pass + def reset_tracing(self): + pass diff --git a/client/tests/tracing_microbenchmark/control b/client/tests/tracing_microbenchmark/control new file mode 100644 index 0000000..ec7017b --- /dev/null +++ b/client/tests/tracing_microbenchmark/control @@ -0,0 +1,28 @@ +AUTHOR = "David Sharp <[email protected]>" +NAME = "Tracing microbenchmark" +TIME = "SHORT" +TEST_CATEGORY = "Benchmark" +TEST_CLASS = "Kernel" +TEST_TYPE = "client" + +DOC = """ +A simple benchmark of kernel tracers such as ftrace. Enables tracepoints in +sys_getuid and makes 100,000 calls to getuid with tracing on and off to measure +the overhead of enabling tracing. The intent for this benchmark is to not +overflow the ring buffer, so the buffer is generously sized. + + +tracer: tracepoint enabled +------ +off: n/a +ftrace: syscalls:sys_enter_getuid + +Args: + tracer: see table above. + buffer_size_kb: Set the tracing ring buffer to this size (per-cpu). + calls: Set the number of calls to make to getuid. +""" + + +job.run_test('tracing_microbenchmark', tracer='off', tag='off', iterations=10) +job.run_test('tracing_microbenchmark', tracer='ftrace', tag='ftrace', iterations=10) diff --git a/client/tests/tracing_microbenchmark/src/Makefile b/client/tests/tracing_microbenchmark/src/Makefile new file mode 100644 index 0000000..ac2af8a --- /dev/null +++ b/client/tests/tracing_microbenchmark/src/Makefile @@ -0,0 +1,8 @@ +CC = $(CROSS_COMPILE)gcc +LDLIBS = -lrt + +getuid_microbench: getuid_microbench.o + +.PHONY: clean +clean: + rm *.o getuid_microbench diff --git a/client/tests/tracing_microbenchmark/src/getuid_microbench.c b/client/tests/tracing_microbenchmark/src/getuid_microbench.c new file mode 100644 index 0000000..fd540cb --- /dev/null +++ b/client/tests/tracing_microbenchmark/src/getuid_microbench.c @@ -0,0 +1,63 @@ +#define _GNU_SOURCE +#include <sys/syscall.h> +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> + +void ts_subtract(struct timespec *result, + const struct timespec *time1, const struct timespec *time2) { + *result = *time1; + result->tv_sec -= time2->tv_sec ; + if (result->tv_nsec < time2->tv_nsec) { + /* borrow a second */ + result->tv_nsec += 1000000000L; + result->tv_sec--; + } + result->tv_nsec -= time2->tv_nsec; +} + +void usage(const char *cmd) { + fprintf(stderr, "usage: %s <iterations>\n", cmd); +} + +int main (int argc, char *argv[]) { + struct timespec start_time, end_time, elapsed_time; + uid_t uid; + long iterations, i; + double per_call; + + if (argc != 2) { + usage(argv[0]); + return 1; + } + + iterations = atol(argv[1]); + if (iterations < 0) { + usage(argv[0]); + return 1; + } + + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_time)) { + perror("clock_gettime"); + return errno; + } + + for (i = iterations; i; i--) + uid = syscall(SYS_getuid); + + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_time)) { + perror("clock_gettime"); + return errno; + } + + ts_subtract(&elapsed_time, &end_time, &start_time); + per_call = (elapsed_time.tv_sec * 1000000000.0L + elapsed_time.tv_nsec) / + (double)iterations; + printf("%ld calls in %ld.%09ld s (%lf ns/call)\n", iterations, + elapsed_time.tv_sec, elapsed_time.tv_nsec, per_call); + + return 0; +} diff --git a/client/tests/tracing_microbenchmark/tracers.py b/client/tests/tracing_microbenchmark/tracers.py new file mode 100755 index 0000000..bdd5194 --- /dev/null +++ b/client/tests/tracing_microbenchmark/tracers.py @@ -0,0 +1,60 @@ +import os +from autotest_lib.client.bin import utils + +import base_tracer +try: + from site_tracers import * +except ImportError: + pass + + +off = base_tracer.Tracer + + +class ftrace(base_tracer.Tracer): + + mountpoint = '/sys/kernel/debug' + tracing_dir = os.path.join(mountpoint, 'tracing') + + def warmup(self, buffer_size_kb): + if not os.path.exists(self.tracing_dir): + utils.system('mount -t debugfs debugfs %s' % self.mountpoint) + + # ensure clean state: + self.trace_config('tracing_enabled', '0') + self.trace_config('current_tracer', 'nop') + self.trace_config('events/enable', '0') + self.trace_config('trace', '') + # set ring buffer size: + self.trace_config('buffer_size_kb', str(buffer_size_kb)) + # enable tracepoints: + self.trace_config('events/syscalls/sys_enter_getuid/enable', '1') + + def cleanup(self): + # reset ring buffer size: + self.trace_config('buffer_size_kb', '1408') + # disable tracepoints: + self.trace_config('events/enable', '0') + + def start_tracing(self): + self.trace_config('tracing_enabled', '1') + + def stop_tracing(self): + self.trace_config('tracing_enabled', '0') + + def reset_tracing(self): + self.trace_config('trace', '') + + def gather_stats(self, results): + per_cpu = os.path.join(self.tracing_dir, 'per_cpu') + for cpu in os.listdir(per_cpu): + cpu_stats = os.path.join(per_cpu, cpu, 'stats') + for line in utils.read_file(cpu_stats).splitlines(): + key, val = line.split(': ') + key = key.replace(' ', '_') + val = int(val) + cpu_key = '%s_%s' % (cpu, key) + total_key = 'total_' + key + results[cpu_key] = val + results[total_key] = (results.get(total_key, 0) + + results[cpu_key]) diff --git a/client/tests/tracing_microbenchmark/tracing_microbenchmark.py b/client/tests/tracing_microbenchmark/tracing_microbenchmark.py new file mode 100755 index 0000000..2d7af6d --- /dev/null +++ b/client/tests/tracing_microbenchmark/tracing_microbenchmark.py @@ -0,0 +1,50 @@ +import os +import re +from autotest_lib.client.bin import test +from autotest_lib.client.bin import utils + +import tracers +import base_tracer + +class tracing_microbenchmark(test.test): + version = 1 + preserve_srcdir = True + + def setup(self): + os.chdir(self.srcdir) + utils.system('make CROSS_COMPILE=""') + + def initialize(self, tracer='ftrace', calls=100000, **kwargs): + self.job.require_gcc() + tracer_class = getattr(tracers, tracer) + if not issubclass(tracer_class, base_tracer.Tracer): + raise TypeError + self.tracer = tracer_class() + + getuid_microbench = os.path.join(self.srcdir, 'getuid_microbench') + self.cmd = '%s %d' % (getuid_microbench, calls) + + def warmup(self, buffer_size_kb=8000, **kwargs): + self.tracer.warmup(buffer_size_kb) + + def cleanup(self): + self.tracer.cleanup() + + def run_once(self, **kwargs): + self.results = {} + + self.tracer.start_tracing() + self.cmd_result = utils.run(self.cmd) + self.tracer.stop_tracing() + + self.tracer.gather_stats(self.results) + self.tracer.reset_tracing() + + def postprocess_iteration(self): + result_re = re.compile(r'(?P<calls>\d+) calls ' + r'in (?P<time>\d+\.\d+) s ' + '\((?P<ns_per_call>\d+\.\d+) ns/call\)') + match = result_re.match(self.cmd_result.stdout) + self.results.update(match.groupdict()) + + self.write_perf_keyval(self.results) -- 1.7.1 _______________________________________________ Autotest mailing list [email protected] http://test.kernel.org/cgi-bin/mailman/listinfo/autotest
