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

Reply via email to