From 9b975c48726c0a9ac19c3c9b0e701970b3dab8ea Mon Sep 17 00:00:00 2001
From: Justus Winter <justus@gnupg.org>
Date: Mon, 25 Apr 2016 00:59:01 +0200
Subject: [PATCH hurd 3/3] fstests: new micro benchmark

* fstests/Makefile: Build 'benchmark'.
* fstests/benchmark.c: New file.
---
 fstests/Makefile    |   6 +-
 fstests/benchmark.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 213 insertions(+), 1 deletion(-)
 create mode 100644 fstests/benchmark.c

diff --git a/fstests/Makefile b/fstests/Makefile
index 6374242..8058247 100644
--- a/fstests/Makefile
+++ b/fstests/Makefile
@@ -19,7 +19,9 @@ dir := fstests
 makemode := utilities
 
 SRCS = fstests.c fdtests.c timertest.c opendisk.c
-targets = timertest fstests # opendisk fdtests
+targets = timertest fstests benchmark # opendisk fdtests
+HURDLIBS = shouldbeinlibc
+benchmark-LDLIBS = -lpthread
 
 include ../Makeconf
 
@@ -27,3 +29,5 @@ timertest: timertest.o
 fstests: fstests.o
 opendisk: opendisk.o
 fdtests: fdtests.o
+benchmark: benchmark.o \
+  ../libshouldbeinlibc/libshouldbeinlibc.a
diff --git a/fstests/benchmark.c b/fstests/benchmark.c
new file mode 100644
index 0000000..a4c501e
--- /dev/null
+++ b/fstests/benchmark.c
@@ -0,0 +1,208 @@
+/* Performance testing.
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <argp.h>
+#include <error.h>
+#include <fcntl.h>
+#include <hurd.h>
+#include <inttypes.h>
+#include <mach.h>
+#include <maptime.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <version.h>
+
+static char *target_path = NULL;
+
+size_t iterations = 1l<<18;
+size_t threads = 2;
+
+const char *argp_program_version = STANDARD_HURD_VERSION (benchmark);
+
+static const struct argp_option options[] =
+{
+  {"iterations", 'n', "N", 0, "Repeat tests N times"},
+  {"threads", 't', "N", 0, "Run N threads"},
+  {}
+};
+
+/* Parse a command line option.	 */
+error_t parse_opt (int key, char *arg, struct argp_state *state)
+{
+  char *arg_end;
+  unsigned long *ptr = NULL;
+
+  switch (key)
+    {
+    case 't':
+      ptr = ptr ?: &threads;
+    case 'n':
+      ptr = ptr ?: &iterations;
+
+      *ptr = strtoul (arg, &arg_end, 10);
+      if (*arg == '\0' || *arg_end != '\0')
+	argp_error (state, "invalid integer: %s", arg);
+      break;
+
+    case ARGP_KEY_ARG:
+      target_path = strdup (arg);
+      if (! target_path)
+	argp_error (state, "Error while canonicalizing path");
+      break;
+
+    case ARGP_KEY_NO_ARGS:
+      argp_usage (state);
+      return EINVAL;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
+static struct argp argp =
+  {
+    options,
+    parse_opt,
+    "TARGET\tFile name of a node with an active translator",
+    "A translator providing mtab compatible information about active "
+    "and passive translators below TARGET.",
+  };
+
+
+
+volatile struct mapped_time_value *mtime;
+
+struct worker_cookie
+{
+  pthread_t thread;
+  error_t (*dost) (void *);
+  error_t err;
+  size_t count;
+  struct timeval runtime;
+};
+
+void *
+worker_thread (void *cookie)
+{
+  error_t err = 0;
+  struct worker_cookie *args = cookie;
+  struct timeval start_time;
+  struct timeval end_time;
+  size_t count;
+
+  maptime_read (mtime, &start_time);
+
+  for (count = args->count; count; count--)
+    {
+      err = args->dost (cookie);
+      if (err)
+	break;
+    }
+
+  maptime_read (mtime, &end_time);
+  timersub (&end_time, &start_time, &args->runtime);
+
+  args->count = count;
+  args->err = err;
+  return NULL;
+}
+
+void
+print_stats (const char *id, size_t nt, size_t n, struct timeval *tv)
+{
+  double duration =
+    (tv->tv_sec + (tv->tv_usec / 1000000.)) / (double) nt;
+  fprintf (stderr,
+           "% 10s: %.2fs\t%fns\t%9.3f (1/s)\n",
+           id,
+           duration,
+           1000000000. * duration / n,
+           n / duration);
+}
+
+
+
+static error_t
+do_dir_lookup (void *cookie)
+{
+  file_t f;
+  f = file_name_lookup (target_path, 0, 0);
+  if (! MACH_PORT_VALID (f))
+    return errno;
+  mach_port_deallocate (mach_task_self (), f);
+  return 0;
+}
+
+
+
+int
+main (int argc, char *argv[])
+{
+  error_t err;
+  size_t i;
+  struct worker_cookie *cookies;
+  struct timeval accumulated_time = {0};
+
+  err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+  if (err)
+    error (1, err, "argument parsing");
+
+  fprintf (stderr, "N: %u (1<<%d)\n",
+	   iterations, __builtin_ffs (iterations) - 1);
+
+  err = maptime_map (0, NULL, &mtime);
+  if (err)
+    error (1, err, "maptime_map");
+
+  cookies = calloc (threads, sizeof *cookies);
+  if (cookies == NULL)
+    error (1, errno, "calloc");
+
+  for (i = 0; i < threads; i++)
+    {
+      cookies[i].count = iterations / threads;
+      cookies[i].dost = do_dir_lookup;
+      err = pthread_create (&cookies[i].thread, NULL,
+                            &worker_thread, &cookies[i]);
+      if (err)
+        error (2, err, "pthread_create");
+    }
+
+  for (i = 0; i < threads; i++)
+    {
+      err = pthread_join (cookies[i].thread, NULL);
+      if (err)
+        error (3, err, "pthread_join");
+
+      if (cookies[i].err)
+        error (3, err, "thread %d", i);
+
+      timeradd (&accumulated_time, &cookies[i].runtime, &accumulated_time);
+    }
+
+  print_stats ("dir_lookup", threads, iterations, &accumulated_time);
+
+  return 0;
+}
-- 
2.1.4