Index: uspace/app/perf/ipc/ns_ping.c
===================================================================
--- uspace/app/perf/ipc/ns_ping.c	(revision 8ee106b96f359e4491128a4081824a5a3f07983b)
+++ uspace/app/perf/ipc/ns_ping.c	(revision 3bd747587973aa824a1405a1ff9753641ff0fedc)
@@ -27,108 +27,29 @@
  */
 
-#include <math.h>
 #include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
 #include <ns.h>
 #include <async.h>
 #include <errno.h>
+#include <str_error.h>
 #include "../perf.h"
 
-#define MIN_DURATION_SECS  10
-#define NUM_SAMPLES 10
+bool bench_ns_ping(stopwatch_t *stopwatch, uint64_t niter,
+    char *error, size_t error_size)
+{
+	stopwatch_start(stopwatch);
 
-static errno_t ping_pong_measure(uint64_t niter, uint64_t *rduration)
-{
-	struct timespec start;
-	uint64_t count;
+	for (uint64_t count = 0; count < niter; count++) {
+		errno_t rc = ns_ping();
 
-	getuptime(&start);
-
-	for (count = 0; count < niter; count++) {
-		errno_t retval = ns_ping();
-
-		if (retval != EOK) {
-			printf("Error sending ping message.\n");
-			return EIO;
+		if (rc != EOK) {
+			snprintf(error, error_size,
+			    "failed sending ping message: %s (%d)",
+			    str_error(rc), rc);
+			return false;
 		}
 	}
 
-	struct timespec now;
-	getuptime(&now);
+	stopwatch_stop(stopwatch);
 
-	*rduration = ts_sub_diff(&now, &start) / 1000;
-	return EOK;
+	return true;
 }
-
-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_ns_ping(void)
-{
-	errno_t rc;
-	uint64_t duration;
-	uint64_t dsmp[NUM_SAMPLES];
-
-	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/ns_ping.def
===================================================================
--- uspace/app/perf/ipc/ns_ping.def	(revision 8ee106b96f359e4491128a4081824a5a3f07983b)
+++ uspace/app/perf/ipc/ns_ping.def	(revision 3bd747587973aa824a1405a1ff9753641ff0fedc)
@@ -2,4 +2,5 @@
 	"ns_ping",
 	"Name service IPC ping-pong benchmark",
-	&bench_ns_ping
+	&bench_ns_ping,
+	NULL, NULL
 },
Index: uspace/app/perf/ipc/ping_pong.c
===================================================================
--- uspace/app/perf/ipc/ping_pong.c	(revision 8ee106b96f359e4491128a4081824a5a3f07983b)
+++ uspace/app/perf/ipc/ping_pong.c	(revision 3bd747587973aa824a1405a1ff9753641ff0fedc)
@@ -27,123 +27,50 @@
  */
 
-#include <math.h>
 #include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
 #include <ipc_test.h>
 #include <async.h>
 #include <errno.h>
+#include <str_error.h>
 #include "../perf.h"
 
-#define MIN_DURATION_SECS  10
-#define NUM_SAMPLES 10
+static ipc_test_t *test = NULL;
 
-static errno_t ping_pong_measure(ipc_test_t *test, uint64_t niter,
-    uint64_t *rduration)
+bool bench_ping_pong_setup(char *error, size_t error_size)
 {
-	struct timespec start;
-	uint64_t count;
+	errno_t rc = ipc_test_create(&test);
+	if (rc != EOK) {
+		snprintf(error, error_size,
+		    "failed contacting IPC test server: %s (%d)",
+		    str_error(rc), rc);
+		return false;
+	}
 
-	getuptime(&start);
+	return true;
+}
 
-	for (count = 0; count < niter; count++) {
-		errno_t retval = ipc_test_ping(test);
+bool bench_ping_pong_teardown(char *error, size_t error_size)
+{
+	ipc_test_destroy(test);
+	return true;
+}
 
-		if (retval != EOK) {
-			printf("Error sending ping message.\n");
-			return EIO;
+bool bench_ping_pong(stopwatch_t *stopwatch, uint64_t niter,
+    char *error, size_t error_size)
+{
+	stopwatch_start(stopwatch);
+
+	for (uint64_t count = 0; count < niter; count++) {
+		errno_t rc = ipc_test_ping(test);
+
+		if (rc != EOK) {
+			snprintf(error, error_size,
+			    "failed sending ping message: %s (%d)",
+			    str_error(rc), rc);
+			return false;
 		}
 	}
 
-	struct timespec now;
-	getuptime(&now);
+	stopwatch_stop(stopwatch);
 
-	*rduration = ts_sub_diff(&now, &start) / 1000;
-	return EOK;
+	return true;
 }
-
-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];
-	ipc_test_t *test;
-	const char *msg;
-
-	rc = ipc_test_create(&test);
-	if (rc != EOK)
-		return "Failed contacting IPC test server.";
-
-	printf("Warm up and determine work size...\n");
-
-	struct timespec start;
-	getuptime(&start);
-
-	uint64_t niter = 1;
-
-	while (true) {
-		rc = ping_pong_measure(test, niter, &duration);
-		if (rc != EOK) {
-			msg = "Failed.";
-			goto error;
-		}
-
-		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(test, niter, &dsmp[i]);
-		if (rc != EOK) {
-			msg = "Failed.";
-			goto error;
-		}
-
-		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);
-
-	ipc_test_destroy(test);
-	return NULL;
-error:
-	ipc_test_destroy(test);
-	return msg;
-}
Index: uspace/app/perf/ipc/ping_pong.def
===================================================================
--- uspace/app/perf/ipc/ping_pong.def	(revision 8ee106b96f359e4491128a4081824a5a3f07983b)
+++ uspace/app/perf/ipc/ping_pong.def	(revision 3bd747587973aa824a1405a1ff9753641ff0fedc)
@@ -2,4 +2,5 @@
 	"ping_pong",
 	"IPC ping-pong benchmark",
-	&bench_ping_pong
+	&bench_ping_pong,
+	&bench_ping_pong_setup, &bench_ping_pong_teardown
 },
Index: uspace/app/perf/malloc/malloc1.c
===================================================================
--- uspace/app/perf/malloc/malloc1.c	(revision 8ee106b96f359e4491128a4081824a5a3f07983b)
+++ uspace/app/perf/malloc/malloc1.c	(revision 3bd747587973aa824a1405a1ff9753641ff0fedc)
@@ -30,109 +30,22 @@
 #include <stdio.h>
 #include <stdlib.h>
-#include <time.h>
-#include <errno.h>
 #include "../perf.h"
 
-#define MIN_DURATION_SECS  10
-#define NUM_SAMPLES 10
-
-static errno_t malloc1_measure(uint64_t niter, uint64_t *rduration)
+bool bench_malloc1(stopwatch_t *stopwatch, uint64_t size,
+    char *error, size_t error_size)
 {
-	struct timespec start;
-	uint64_t count;
-	void *p;
-
-	getuptime(&start);
-
-	for (count = 0; count < niter; count++) {
-		p = malloc(1);
-		if (p == NULL)
-			return ENOMEM;
+	stopwatch_start(stopwatch);
+	for (uint64_t i = 0; i < size; i++) {
+		void *p = malloc(1);
+		if (p == NULL) {
+			snprintf(error, error_size,
+			    "failed to allocate 1B in run %" PRIu64 " (out of %" PRIu64 ")",
+			    i, size);
+			return false;
+		}
 		free(p);
 	}
+	stopwatch_stop(stopwatch);
 
-	struct timespec now;
-	getuptime(&now);
-
-	*rduration = ts_sub_diff(&now, &start) / 1000;
-	return EOK;
+	return true;
 }
-
-static void malloc1_report(uint64_t niter, uint64_t duration)
-{
-	printf("Completed %" PRIu64 " allocations and deallocations in %" PRIu64 " us",
-	    niter, duration);
-
-	if (duration > 0) {
-		printf(", %" PRIu64 " cycles/s.\n", niter * 1000 * 1000 / duration);
-	} else {
-		printf(".\n");
-	}
-}
-
-const char *bench_malloc1(void)
-{
-	errno_t rc;
-	uint64_t duration;
-	uint64_t dsmp[NUM_SAMPLES];
-	const char *msg;
-
-	printf("Warm up and determine work size...\n");
-
-	struct timespec start;
-	getuptime(&start);
-
-	uint64_t niter = 1;
-
-	while (true) {
-		rc = malloc1_measure(niter, &duration);
-		if (rc != EOK) {
-			msg = "Failed.";
-			goto error;
-		}
-
-		malloc1_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 = malloc1_measure(niter, &dsmp[i]);
-		if (rc != EOK) {
-			msg = "Failed.";
-			goto error;
-		}
-
-		malloc1_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 cycles/s Std.dev^2: %.0f cycles/s Samples: %d\n",
-	    avg, stddev, NUM_SAMPLES);
-
-	return NULL;
-error:
-	return msg;
-}
Index: uspace/app/perf/malloc/malloc1.def
===================================================================
--- uspace/app/perf/malloc/malloc1.def	(revision 8ee106b96f359e4491128a4081824a5a3f07983b)
+++ uspace/app/perf/malloc/malloc1.def	(revision 3bd747587973aa824a1405a1ff9753641ff0fedc)
@@ -2,4 +2,5 @@
 	"malloc1",
 	"User-space memory allocator benchmark, repeatedly allocate one block",
-	&bench_malloc1
+	&bench_malloc1,
+	NULL, NULL
 },
Index: uspace/app/perf/malloc/malloc2.c
===================================================================
--- uspace/app/perf/malloc/malloc2.c	(revision 8ee106b96f359e4491128a4081824a5a3f07983b)
+++ uspace/app/perf/malloc/malloc2.c	(revision 3bd747587973aa824a1405a1ff9753641ff0fedc)
@@ -27,120 +27,42 @@
  */
 
-#include <math.h>
+#include <stdlib.h>
 #include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <errno.h>
 #include "../perf.h"
 
-#define MIN_DURATION_SECS  10
-#define NUM_SAMPLES 10
+bool bench_malloc2(stopwatch_t *stopwatch, uint64_t niter,
+    char *error, size_t error_size)
+{
+	stopwatch_start(stopwatch);
 
-static errno_t malloc2_measure(uint64_t niter, uint64_t *rduration)
-{
-	struct timespec start;
-	uint64_t count;
-	void **p;
-
-	getuptime(&start);
-
-	p = malloc(niter * sizeof(void *));
-	if (p == NULL)
-		return ENOMEM;
-
-	for (count = 0; count < niter; count++) {
-		p[count] = malloc(1);
-		if (p[count] == NULL)
-			return ENOMEM;
+	void **p = malloc(niter * sizeof(void *));
+	if (p == NULL) {
+		snprintf(error, error_size,
+		    "failed to allocate backend array (%" PRIu64 "B)",
+		    niter * sizeof(void *));
+		return false;
 	}
 
-	for (count = 0; count < niter; count++)
+	for (uint64_t count = 0; count < niter; count++) {
+		p[count] = malloc(1);
+		if (p[count] == NULL) {
+			snprintf(error, error_size,
+			    "failed to allocate 1B in run %" PRIu64 " (out of %" PRIu64 ")",
+			    count, niter);
+			for (uint64_t j = 0; j < count; j++) {
+				free(p[j]);
+			}
+			free(p);
+			return false;
+		}
+	}
+
+	for (uint64_t count = 0; count < niter; count++)
 		free(p[count]);
 
 	free(p);
 
-	struct timespec now;
-	getuptime(&now);
+	stopwatch_stop(stopwatch);
 
-	*rduration = ts_sub_diff(&now, &start) / 1000;
-	return EOK;
+	return true;
 }
-
-static void malloc2_report(uint64_t niter, uint64_t duration)
-{
-	printf("Completed %" PRIu64 " allocations and deallocations in %" PRIu64 " us",
-	    niter, duration);
-
-	if (duration > 0) {
-		printf(", %" PRIu64 " cycles/s.\n", niter * 1000 * 1000 / duration);
-	} else {
-		printf(".\n");
-	}
-}
-
-const char *bench_malloc2(void)
-{
-	errno_t rc;
-	uint64_t duration;
-	uint64_t dsmp[NUM_SAMPLES];
-	const char *msg;
-
-	printf("Warm up and determine work size...\n");
-
-	struct timespec start;
-	getuptime(&start);
-
-	uint64_t niter = 1;
-
-	while (true) {
-		rc = malloc2_measure(niter, &duration);
-		if (rc != EOK) {
-			msg = "Failed.";
-			goto error;
-		}
-
-		malloc2_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 = malloc2_measure(niter, &dsmp[i]);
-		if (rc != EOK) {
-			msg = "Failed.";
-			goto error;
-		}
-
-		malloc2_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 cycles/s Std.dev^2: %.0f cycles/s Samples: %d\n",
-	    avg, stddev, NUM_SAMPLES);
-
-	return NULL;
-error:
-	return msg;
-}
Index: uspace/app/perf/malloc/malloc2.def
===================================================================
--- uspace/app/perf/malloc/malloc2.def	(revision 8ee106b96f359e4491128a4081824a5a3f07983b)
+++ uspace/app/perf/malloc/malloc2.def	(revision 3bd747587973aa824a1405a1ff9753641ff0fedc)
@@ -2,4 +2,5 @@
 	"malloc2",
 	"User-space memory allocator benchmark, allocate many small blocks",
-	&bench_malloc2
+	&bench_malloc2,
+	NULL, NULL
 },
Index: uspace/app/perf/perf.c
===================================================================
--- uspace/app/perf/perf.c	(revision 8ee106b96f359e4491128a4081824a5a3f07983b)
+++ uspace/app/perf/perf.c	(revision 3bd747587973aa824a1405a1ff9753641ff0fedc)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2018 Jiri Svoboda
+ * Copyright (c) 2018 Vojtech Horky
  * All rights reserved.
  *
@@ -38,5 +39,12 @@
 #include <stdlib.h>
 #include <str.h>
+#include <time.h>
+#include <errno.h>
+#include <perf.h>
 #include "perf.h"
+
+#define MIN_DURATION_SECS 10
+#define NUM_SAMPLES 10
+#define MAX_ERROR_STR_LENGTH 1024
 
 benchmark_t benchmarks[] = {
@@ -45,19 +53,125 @@
 #include "malloc/malloc1.def"
 #include "malloc/malloc2.def"
-	{ NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL }
 };
 
+static void short_report(stopwatch_t *stopwatch, int run_index,
+    benchmark_t *bench, size_t workload_size)
+{
+	usec_t duration_usec = NSEC2USEC(stopwatch_get_nanos(stopwatch));
+
+	printf("Completed %zu operations in %llu us",
+	    workload_size, duration_usec);
+	if (duration_usec > 0) {
+		double cycles = workload_size * 1000 * 1000 / duration_usec;
+		printf(", %.0f cycles/s.\n", cycles);
+	} else {
+		printf(".\n");
+	}
+}
+
+static void summary_stats(stopwatch_t *stopwatch, size_t stopwatch_count,
+    benchmark_t *bench, size_t workload_size)
+{
+	double sum = 0.0;
+	double sum_square = 0.0;
+
+	for (size_t i = 0; i < stopwatch_count; i++) {
+		double nanos = stopwatch_get_nanos(&stopwatch[i]);
+		double thruput = (double) workload_size / (nanos / 1000000000.0l);
+		sum += thruput;
+		sum_square += thruput * thruput;
+	}
+
+	double avg = sum / stopwatch_count;
+
+	double sd_numer = sum_square + stopwatch_count * avg * avg - 2 * sum * avg;
+	double sd_square = sd_numer / ((double) stopwatch_count - 1);
+
+	printf("Average: %.0f cycles/s Std.dev^2: %.0f cycles/s Samples: %zu\n",
+	    avg, sd_square, stopwatch_count);
+}
+
 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;
+	printf("Warm up and determine workload size...\n");
+
+	char *error_msg = malloc(MAX_ERROR_STR_LENGTH + 1);
+	if (error_msg == NULL) {
+		printf("Out of memory!\n");
+		return false;
+	}
+	str_cpy(error_msg, MAX_ERROR_STR_LENGTH, "");
+
+	bool ret = true;
+
+	if (bench->setup != NULL) {
+		ret = bench->setup(error_msg, MAX_ERROR_STR_LENGTH);
+		if (!ret) {
+			goto leave_error;
+		}
+	}
+
+	size_t workload_size = 1;
+
+	while (true) {
+		stopwatch_t stopwatch = STOPWATCH_INITIALIZE_STATIC;
+
+		bool ok = bench->entry(&stopwatch, workload_size,
+		    error_msg, MAX_ERROR_STR_LENGTH);
+		if (!ok) {
+			goto leave_error;
+		}
+		short_report(&stopwatch, -1, bench, workload_size);
+
+		nsec_t duration = stopwatch_get_nanos(&stopwatch);
+		if (duration > SEC2NSEC(MIN_DURATION_SECS)) {
+			break;
+		}
+		workload_size *= 2;
+	}
+
+	printf("Workload size set to %zu, measuring %d samples.\n", workload_size, NUM_SAMPLES);
+
+	stopwatch_t *stopwatch = calloc(NUM_SAMPLES, sizeof(stopwatch_t));
+	if (stopwatch == NULL) {
+		snprintf(error_msg, MAX_ERROR_STR_LENGTH, "failed allocating memory");
+		goto leave_error;
+	}
+	for (int i = 0; i < NUM_SAMPLES; i++) {
+		stopwatch_init(&stopwatch[i]);
+
+		bool ok = bench->entry(&stopwatch[i], workload_size,
+		    error_msg, MAX_ERROR_STR_LENGTH);
+		if (!ok) {
+			free(stopwatch);
+			goto leave_error;
+		}
+		short_report(&stopwatch[i], i, bench, workload_size);
+	}
+
+	summary_stats(stopwatch, NUM_SAMPLES, bench, workload_size);
+	printf("\nBenchmark completed\n");
+
+	free(stopwatch);
+
+	goto leave;
+
+leave_error:
+	printf("Error: %s\n", error_msg);
+	ret = false;
+
+leave:
+	if (bench->teardown != NULL) {
+		bool ok = bench->teardown(error_msg, MAX_ERROR_STR_LENGTH);
+		if (!ok) {
+			printf("Error: %s\n", error_msg);
+			ret = false;
+		}
+	}
+
+	free(error_msg);
+
+	return ret;
 }
 
Index: uspace/app/perf/perf.h
===================================================================
--- uspace/app/perf/perf.h	(revision 8ee106b96f359e4491128a4081824a5a3f07983b)
+++ uspace/app/perf/perf.h	(revision 3bd747587973aa824a1405a1ff9753641ff0fedc)
@@ -37,6 +37,9 @@
 
 #include <stdbool.h>
+#include <perf.h>
 
-typedef const char *(*benchmark_entry_t)(void);
+typedef bool (*benchmark_entry_t)(stopwatch_t *, uint64_t,
+    char *, size_t);
+typedef bool (*benchmark_helper_t)(char *, size_t);
 
 typedef struct {
@@ -44,10 +47,14 @@
 	const char *desc;
 	benchmark_entry_t entry;
+	benchmark_helper_t setup;
+	benchmark_helper_t teardown;
 } benchmark_t;
 
-extern const char *bench_malloc1(void);
-extern const char *bench_malloc2(void);
-extern const char *bench_ns_ping(void);
-extern const char *bench_ping_pong(void);
+extern bool bench_malloc1(stopwatch_t *, uint64_t, char *, size_t);
+extern bool bench_malloc2(stopwatch_t *, uint64_t, char *, size_t);
+extern bool bench_ns_ping(stopwatch_t *, uint64_t, char *, size_t);
+extern bool bench_ping_pong(stopwatch_t *, uint64_t, char *, size_t);
+extern bool bench_ping_pong_setup(char *, size_t);
+extern bool bench_ping_pong_teardown(char *, size_t);
 
 extern benchmark_t benchmarks[];
