Index: uspace/app/perf/Makefile
===================================================================
--- uspace/app/perf/Makefile	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
+++ uspace/app/perf/Makefile	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
@@ -0,0 +1,39 @@
+#
+# Copyright (c) 2018 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+
+LIBS = math
+
+BINARY = perf
+
+SOURCES = \
+	perf.c \
+	ipc/ping_pong.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/perf/doc/doxygroups.h
===================================================================
--- uspace/app/perf/doc/doxygroups.h	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
+++ uspace/app/perf/doc/doxygroups.h	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
@@ -0,0 +1,4 @@
+/** @addtogroup perf perf
+ * @brief User space performance measuring tool
+ * @ingroup apps
+ */
Index: uspace/app/perf/ipc/ping_pong.c
===================================================================
--- uspace/app/perf/ipc/ping_pong.c	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
+++ uspace/app/perf/ipc/ping_pong.c	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2009 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ns.h>
+#include <async.h>
+#include <errno.h>
+#include "../perf.h"
+
+#define MIN_DURATION_SECS  10
+#define NUM_SAMPLES 10
+
+static errno_t ping_pong_measure(uint64_t niter, uint64_t *rduration)
+{
+	struct timespec start;
+	uint64_t count;
+
+	getuptime(&start);
+
+	for (count = 0; count < niter; count++) {
+		errno_t retval = ns_ping();
+
+		if (retval != EOK) {
+			printf("Error sending ping message.\n");
+			return EIO;
+		}
+	}
+
+	struct timespec now;
+	getuptime(&now);
+
+	*rduration = ts_sub_diff(&now, &start) / 1000;
+	return EOK;
+}
+
+static void ping_pong_report(uint64_t niter, uint64_t duration)
+{
+	printf("Completed %" PRIu64 " round trips in %" PRIu64 " us",
+	    niter, duration);
+
+	if (duration > 0) {
+		printf(", %" PRIu64 " rt/s.\n", niter * 1000 * 1000 / duration);
+	} else {
+		printf(".\n");
+	}
+}
+
+const char *bench_ping_pong(void)
+{
+	errno_t rc;
+	uint64_t duration;
+	uint64_t dsmp[NUM_SAMPLES];
+
+	printf("Benchmark ns server ping time\n");
+	printf("Warm up and determine work size...\n");
+
+	struct timespec start;
+	getuptime(&start);
+
+	uint64_t niter = 1;
+
+	while (true) {
+		rc = ping_pong_measure(niter, &duration);
+		if (rc != EOK)
+			return "Failed.";
+
+		ping_pong_report(niter, duration);
+
+		if (duration >= MIN_DURATION_SECS * 1000000)
+			break;
+
+		niter *= 2;
+	}
+
+	printf("Measure %d samples...\n", NUM_SAMPLES);
+
+	int i;
+
+	for (i = 0; i < NUM_SAMPLES; i++) {
+		rc = ping_pong_measure(niter, &dsmp[i]);
+		if (rc != EOK)
+			return "Failed.";
+
+		ping_pong_report(niter, dsmp[i]);
+	}
+
+	double sum = 0.0;
+
+	for (i = 0; i < NUM_SAMPLES; i++)
+		sum += (double)niter / ((double)dsmp[i] / 1000000.0l);
+
+	double avg = sum / NUM_SAMPLES;
+
+	double qd = 0.0;
+	double d;
+	for (i = 0; i < NUM_SAMPLES; i++) {
+		d = (double)niter / ((double)dsmp[i] / 1000000.0l) - avg;
+		qd += d * d;
+	}
+
+	double stddev = qd / (NUM_SAMPLES - 1); // XXX sqrt
+
+	printf("Average: %.0f rt/s Std.dev^2: %.0f rt/s Samples: %d\n",
+	    avg, stddev, NUM_SAMPLES);
+
+	return NULL;
+}
Index: uspace/app/perf/ipc/ping_pong.def
===================================================================
--- uspace/app/perf/ipc/ping_pong.def	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
+++ uspace/app/perf/ipc/ping_pong.def	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
@@ -0,0 +1,5 @@
+{
+	"ping_pong",
+	"IPC ping-pong benchmark",
+	&bench_ping_pong
+},
Index: uspace/app/perf/perf.c
===================================================================
--- uspace/app/perf/perf.c	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
+++ uspace/app/perf/perf.c	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup perf
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <str.h>
+#include "perf.h"
+
+benchmark_t benchmarks[] = {
+#include "ipc/ping_pong.def"
+	{ NULL, NULL, NULL }
+};
+
+static bool run_benchmark(benchmark_t *bench)
+{
+	/* Execute the benchmarl */
+	const char *ret = bench->entry();
+
+	if (ret == NULL) {
+		printf("\nBenchmark completed\n");
+		return true;
+	}
+
+	printf("\n%s\n", ret);
+	return false;
+}
+
+static int run_benchmarks(void)
+{
+	benchmark_t *bench;
+	unsigned int i = 0;
+	unsigned int n = 0;
+
+	char *failed_names = NULL;
+
+	printf("\n*** Running all benchmarks ***\n\n");
+
+	for (bench = benchmarks; bench->name != NULL; bench++) {
+		printf("%s (%s)\n", bench->name, bench->desc);
+		if (run_benchmark(bench)) {
+			i++;
+			continue;
+		}
+
+		if (!failed_names) {
+			failed_names = str_dup(bench->name);
+		} else {
+			char *f = NULL;
+			asprintf(&f, "%s, %s", failed_names, bench->name);
+			if (!f) {
+				printf("Out of memory.\n");
+				abort();
+			}
+			free(failed_names);
+			failed_names = f;
+		}
+		n++;
+	}
+
+	printf("\nCompleted, %u benchmarks run, %u succeeded.\n", i + n, i);
+	if (failed_names)
+		printf("Failed benchmarks: %s\n", failed_names);
+
+	return n;
+}
+
+static void list_benchmarks(void)
+{
+	size_t len = 0;
+	benchmark_t *bench;
+	for (bench = benchmarks; bench->name != NULL; bench++) {
+		if (str_length(bench->name) > len)
+			len = str_length(bench->name);
+	}
+
+	unsigned int _len = (unsigned int) len;
+	if ((_len != len) || (((int) _len) < 0)) {
+		printf("Command length overflow\n");
+		return;
+	}
+
+	for (bench = benchmarks; bench->name != NULL; bench++)
+		printf("%-*s %s\n", _len, bench->name, bench->desc);
+
+	printf("%-*s Run all benchmarks\n", _len, "*");
+}
+
+int main(int argc, char *argv[])
+{
+	if (argc < 2) {
+		printf("Usage:\n\n");
+		printf("%s <benchmark>\n\n", argv[0]);
+		list_benchmarks();
+		return 0;
+	}
+
+	if (str_cmp(argv[1], "*") == 0) {
+		return run_benchmarks();
+	}
+
+	benchmark_t *bench;
+	for (bench = benchmarks; bench->name != NULL; bench++) {
+		if (str_cmp(argv[1], bench->name) == 0) {
+			return (run_benchmark(bench) ? 0 : -1);
+		}
+	}
+
+	printf("Unknown benchmark \"%s\"\n", argv[1]);
+	return -2;
+}
+
+/** @}
+ */
Index: uspace/app/perf/perf.h
===================================================================
--- uspace/app/perf/perf.h	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
+++ uspace/app/perf/perf.h	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup perf
+ * @{
+ */
+/** @file
+ */
+
+#ifndef PERF_H_
+#define PERF_H_
+
+#include <stdbool.h>
+
+typedef const char *(*benchmark_entry_t)(void);
+
+typedef struct {
+	const char *name;
+	const char *desc;
+	benchmark_entry_t entry;
+} benchmark_t;
+
+extern const char *bench_ping_pong(void);
+
+extern benchmark_t benchmarks[];
+
+#endif
+
+/** @}
+ */
Index: uspace/app/tester/Makefile
===================================================================
--- uspace/app/tester/Makefile	(revision 64f85f54a27d11f2ce4cf0aef2e2fb35918fe5cd)
+++ uspace/app/tester/Makefile	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
@@ -56,5 +56,4 @@
 	float/float2.c \
 	vfs/vfs1.c \
-	ipc/ping_pong.c \
 	ipc/starve.c \
 	loop/loop1.c \
Index: uspace/app/tester/ipc/ping_pong.c
===================================================================
--- uspace/app/tester/ipc/ping_pong.c	(revision 64f85f54a27d11f2ce4cf0aef2e2fb35918fe5cd)
+++ 	(revision )
@@ -1,135 +1,0 @@
-/*
- * Copyright (c) 2009 Jiri Svoboda
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <ns.h>
-#include <async.h>
-#include <errno.h>
-#include "../tester.h"
-
-#define MIN_DURATION_SECS  10
-#define NUM_SAMPLES 10
-
-static errno_t ping_pong_measure(uint64_t niter, uint64_t *rduration)
-{
-	struct timespec start;
-	uint64_t count;
-
-	getuptime(&start);
-
-	for (count = 0; count < niter; count++) {
-		errno_t retval = ns_ping();
-
-		if (retval != EOK) {
-			TPRINTF("Error sending ping message.\n");
-			return EIO;
-		}
-	}
-
-	struct timespec now;
-	getuptime(&now);
-
-	*rduration = ts_sub_diff(&now, &start) / 1000;
-	return EOK;
-}
-
-static void ping_pong_report(uint64_t niter, uint64_t duration)
-{
-	TPRINTF("Completed %" PRIu64 " round trips in %" PRIu64 " us",
-	    niter, duration);
-
-	if (duration > 0) {
-		TPRINTF(", %" PRIu64 " rt/s.\n", niter * 1000 * 1000 / duration);
-	} else {
-		TPRINTF(".\n");
-	}
-}
-
-const char *test_ping_pong(void)
-{
-	errno_t rc;
-	uint64_t duration;
-	uint64_t dsmp[NUM_SAMPLES];
-
-	TPRINTF("Benchmark ns server ping time\n");
-	TPRINTF("Warm up and determine work size...\n");
-
-	struct timespec start;
-	getuptime(&start);
-
-	uint64_t niter = 1;
-
-	while (true) {
-		rc = ping_pong_measure(niter, &duration);
-		if (rc != EOK)
-			return "Failed.";
-
-		ping_pong_report(niter, duration);
-
-		if (duration >= MIN_DURATION_SECS * 1000000)
-			break;
-
-		niter *= 2;
-	}
-
-	TPRINTF("Measure %d samples...\n", NUM_SAMPLES);
-
-	int i;
-
-	for (i = 0; i < NUM_SAMPLES; i++) {
-		rc = ping_pong_measure(niter, &dsmp[i]);
-		if (rc != EOK)
-			return "Failed.";
-
-		ping_pong_report(niter, dsmp[i]);
-	}
-
-	double sum = 0.0;
-
-	for (i = 0; i < NUM_SAMPLES; i++)
-		sum += (double)niter / ((double)dsmp[i] / 1000000.0l);
-
-	double avg = sum / NUM_SAMPLES;
-
-	double qd = 0.0;
-	double d;
-	for (i = 0; i < NUM_SAMPLES; i++) {
-		d = (double)niter / ((double)dsmp[i] / 1000000.0l) - avg;
-		qd += d * d;
-	}
-
-	double stddev = qd / (NUM_SAMPLES - 1); // XXX sqrt
-
-	TPRINTF("Average: %.0f rt/s Std.dev^2: %.0f rt/s Samples: %d\n",
-	    avg, stddev, NUM_SAMPLES);
-
-	return NULL;
-}
Index: uspace/app/tester/ipc/ping_pong.def
===================================================================
--- uspace/app/tester/ipc/ping_pong.def	(revision 64f85f54a27d11f2ce4cf0aef2e2fb35918fe5cd)
+++ 	(revision )
@@ -1,6 +1,0 @@
-{
-	"ping_pong",
-	"IPC ping-pong benchmark",
-	&test_ping_pong,
-	true
-},
Index: uspace/app/tester/tester.c
===================================================================
--- uspace/app/tester/tester.c	(revision 64f85f54a27d11f2ce4cf0aef2e2fb35918fe5cd)
+++ uspace/app/tester/tester.c	(revision d230358b547cc23fcfa8731fb956099b3ccc73f0)
@@ -66,5 +66,4 @@
 #include "float/float2.def"
 #include "vfs/vfs1.def"
-#include "ipc/ping_pong.def"
 #include "ipc/starve.def"
 #include "loop/loop1.def"
