Index: .gitignore
===================================================================
--- .gitignore	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ .gitignore	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -90,5 +90,4 @@
 uspace/app/bithenge/bithenge
 uspace/app/blkdump/blkdump
-uspace/app/bnchmark/bnchmark
 uspace/app/contacts/contacts
 uspace/app/corecfg/corecfg
@@ -107,4 +106,5 @@
 uspace/app/getterm/getterm
 uspace/app/gunzip/gunzip
+uspace/app/hbench/hbench
 uspace/app/inet/inet
 uspace/app/init/init
@@ -131,5 +131,4 @@
 uspace/app/nterm/nterm
 uspace/app/pci/pci
-uspace/app/perf/perf
 uspace/app/ping/ping
 uspace/app/ping6/ping6
@@ -173,5 +172,4 @@
 uspace/dist/app/bithenge
 uspace/dist/app/blkdump
-uspace/dist/app/bnchmark
 uspace/dist/app/corecfg
 uspace/dist/app/cpptest
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ boot/Makefile.common	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -181,5 +181,4 @@
 	bithenge \
 	blkdump \
-	bnchmark \
 	contacts \
 	corecfg \
@@ -192,4 +191,5 @@
 	fdisk \
 	gunzip \
+	hbench \
 	inet \
 	kill \
@@ -205,5 +205,4 @@
 	mkmfs \
 	nic \
-	perf \
 	sbi \
 	sportdmp \
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ uspace/Makefile	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -38,5 +38,4 @@
 	app/bithenge \
 	app/blkdump \
-	app/bnchmark \
 	app/contacts \
 	app/corecfg \
@@ -51,4 +50,5 @@
 	app/getterm \
 	app/gunzip \
+	app/hbench \
 	app/init \
 	app/inet \
@@ -68,5 +68,4 @@
 	app/nterm \
 	app/pci \
-	app/perf \
 	app/redir \
 	app/sbi \
Index: pace/app/bnchmark/Makefile
===================================================================
--- uspace/app/bnchmark/Makefile	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,35 +1,0 @@
-#
-# Copyright (c) 2011 Martin Sucha
-# 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 = ../..
-BINARY = bnchmark
-
-SOURCES = \
-	bnchmark.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/app/bnchmark/bnchmark.c
===================================================================
--- uspace/app/bnchmark/bnchmark.c	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,213 +1,0 @@
-/*
- * Copyright (c) 2011 Martin Sucha
- * 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 bnchmark
- * @{
- */
-
-/**
- * @file	bnchmark.c
- * This program measures time for various actions and writes the results
- * to a file.
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <str_error.h>
-#include <mem.h>
-#include <loc.h>
-#include <byteorder.h>
-#include <inttypes.h>
-#include <errno.h>
-#include <time.h>
-#include <dirent.h>
-#include <str.h>
-
-#define NAME	"bnchmark"
-#define BUFSIZE 8096
-#define MBYTE (1024*1024)
-
-typedef errno_t (*measure_func_t)(void *);
-
-static void syntax_print(void);
-
-static errno_t measure(measure_func_t fn, void *data, msec_t *result)
-{
-	struct timespec start_time;
-	getuptime(&start_time);
-
-	errno_t rc = fn(data);
-	if (rc != EOK) {
-		fprintf(stderr, "measured function failed\n");
-		return rc;
-	}
-
-	struct timespec final_time;
-	getuptime(&final_time);
-
-	/* Calculate time difference in milliseconds */
-	*result = NSEC2USEC(ts_sub_diff(&final_time, &start_time));
-	return EOK;
-}
-
-static errno_t sequential_read_file(void *data)
-{
-	char *path = (char *) data;
-	char *buf = malloc(BUFSIZE);
-
-	if (buf == NULL)
-		return ENOMEM;
-
-	FILE *file = fopen(path, "r");
-	if (file == NULL) {
-		fprintf(stderr, "Failed opening file: %s\n", path);
-		free(buf);
-		return EIO;
-	}
-
-	while (!feof(file)) {
-		fread(buf, 1, BUFSIZE, file);
-		if (ferror(file)) {
-			fprintf(stderr, "Failed reading file\n");
-			fclose(file);
-			free(buf);
-			return EIO;
-		}
-	}
-
-	fclose(file);
-	free(buf);
-	return EOK;
-}
-
-static errno_t sequential_read_dir(void *data)
-{
-	char *path = (char *) data;
-
-	DIR *dir = opendir(path);
-	if (dir == NULL) {
-		fprintf(stderr, "Failed opening directory: %s\n", path);
-		return EIO;
-	}
-
-	struct dirent *dp;
-
-	while ((dp = readdir(dir))) {
-		/* Do nothing */
-	}
-
-	closedir(dir);
-	return EOK;
-}
-
-int main(int argc, char **argv)
-{
-	errno_t rc;
-	msec_t milliseconds_taken = 0;
-	char *path = NULL;
-	measure_func_t fn = NULL;
-	int iteration;
-	int iterations;
-	char *log_str = NULL;
-	char *test_type = NULL;
-	char *endptr;
-
-	if (argc < 5) {
-		fprintf(stderr, NAME ": Error, argument missing.\n");
-		syntax_print();
-		return 1;
-	}
-
-	if (argc > 5) {
-		fprintf(stderr, NAME ": Error, too many arguments.\n");
-		syntax_print();
-		return 1;
-	}
-
-	// Skip program name
-	--argc;
-	++argv;
-
-	iterations = strtol(*argv, &endptr, 10);
-	if (*endptr != '\0') {
-		printf(NAME ": Error, invalid argument (iterations).\n");
-		syntax_print();
-		return 1;
-	}
-
-	--argc;
-	++argv;
-	test_type = *argv;
-
-	--argc;
-	++argv;
-	log_str = *argv;
-
-	--argc;
-	++argv;
-	path = *argv;
-
-	if (str_cmp(test_type, "sequential-file-read") == 0) {
-		fn = sequential_read_file;
-	} else if (str_cmp(test_type, "sequential-dir-read") == 0) {
-		fn = sequential_read_dir;
-	} else {
-		fprintf(stderr, "Error, unknown test type\n");
-		syntax_print();
-		return 1;
-	}
-
-	for (iteration = 0; iteration < iterations; iteration++) {
-		rc = measure(fn, path, &milliseconds_taken);
-		if (rc != EOK) {
-			fprintf(stderr, "Error: %s\n", str_error(rc));
-			return 1;
-		}
-
-		printf("%s;%s;%s;%lld;ms\n", test_type, path, log_str, milliseconds_taken);
-	}
-
-	return 0;
-}
-
-static void syntax_print(void)
-{
-	fprintf(stderr, "syntax: " NAME " <iterations> <test type> <log-str> <path>\n");
-	fprintf(stderr, "  <iterations>    number of times to run a given test\n");
-	fprintf(stderr, "  <test-type>     one of:\n");
-	fprintf(stderr, "                    sequential-file-read\n");
-	fprintf(stderr, "                    sequential-dir-read\n");
-	fprintf(stderr, "  <log-str>       a string to attach to results\n");
-	fprintf(stderr, "  <path>          file/directory to use for testing\n");
-}
-
-/**
- * @}
- */
Index: pace/app/bnchmark/doc/doxygroups.h
===================================================================
--- uspace/app/bnchmark/doc/doxygroups.h	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,4 +1,0 @@
-/** @addtogroup bnchmark bnchmark
- * @brief Benchmarking application
- * @ingroup apps
- */
Index: uspace/app/hbench/Makefile
===================================================================
--- uspace/app/hbench/Makefile	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/Makefile	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,49 @@
+#
+# 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 = hbench
+
+SOURCES = \
+	benchlist.c \
+	csv.c \
+	env.c \
+	main.c \
+	utils.c \
+	fs/dirread.c \
+	fs/fileread.c \
+	ipc/ns_ping.c \
+	ipc/ping_pong.c \
+	malloc/malloc1.c \
+	malloc/malloc2.c \
+	synch/fibril_mutex.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/hbench/benchlist.c
===================================================================
--- uspace/app/hbench/benchlist.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/benchlist.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * Copyright (c) 2018 Vojtech Horky
+ * 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 hbench
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdlib.h>
+#include "hbench.h"
+
+benchmark_t *benchmarks[] = {
+	&benchmark_dir_read,
+	&benchmark_fibril_mutex,
+	&benchmark_file_read,
+	&benchmark_malloc1,
+	&benchmark_malloc2,
+	&benchmark_ns_ping,
+	&benchmark_ping_pong
+};
+
+size_t benchmark_count = sizeof(benchmarks) / sizeof(benchmarks[0]);
+
+/** @}
+ */
Index: uspace/app/hbench/csv.c
===================================================================
--- uspace/app/hbench/csv.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/csv.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2019 Vojtech Horky
+ * 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 hbench
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "hbench.h"
+
+static FILE *csv_output = NULL;
+
+/** Open CSV benchmark report.
+ *
+ * @param filename Filename where to store the CSV.
+ * @return Whether it was possible to open the file.
+ */
+errno_t csv_report_open(const char *filename)
+{
+	csv_output = fopen(filename, "w");
+	if (csv_output == NULL) {
+		return errno;
+	}
+
+	fprintf(csv_output, "benchmark,run,size,duration_nanos\n");
+
+	return EOK;
+}
+
+/** Add one entry to the report.
+ *
+ * When csv_report_open() was not called or failed, the function does
+ * nothing.
+ *
+ * @param run Performance data of the entry.
+ * @param run_index Run index, use negative values for warm-up.
+ * @param bench Benchmark information.
+ * @param workload_size Workload size.
+ */
+void csv_report_add_entry(bench_run_t *run, int run_index,
+    benchmark_t *bench, uint64_t workload_size)
+{
+	if (csv_output == NULL) {
+		return;
+	}
+
+	fprintf(csv_output, "%s,%d,%" PRIu64 ",%lld\n",
+	    bench->name, run_index, workload_size,
+	    (long long) stopwatch_get_nanos(&run->stopwatch));
+}
+
+/** Close CSV report.
+ *
+ * When csv_report_open() was not called or failed, the function does
+ * nothing.
+ */
+void csv_report_close(void)
+{
+	if (csv_output != NULL) {
+		fclose(csv_output);
+	}
+}
+
+/** @}
+ */
Index: uspace/app/hbench/doc/doxygroups.h
===================================================================
--- uspace/app/hbench/doc/doxygroups.h	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/doc/doxygroups.h	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,44 @@
+/** @addtogroup hbench hbench
+ * @brief User space benchmarks
+ * @ingroup apps
+ *
+ * @details
+ *
+ * To add a new benchmark, you need to implement the actual benchmarking
+ * code and register it.
+ *
+ * Registration is done by adding
+ * <code>extern benchmark_t bench_YOUR_NAME</code> reference to benchlist.h
+ * and by adding it to the array in benchlist.c.
+ *
+ * The actual benchmark should reside in a separate file (see malloc/malloc1.c
+ * for example) and has to (at least) declare one function (the actual
+ * benchmark) and fill-in the benchmark_t structure.
+ *
+ * Fill-in the name of the benchmark, its description and a reference to the
+ * benchmark function to the benchmark_t.
+ *
+ * The benchmarking function has to accept trhee arguments:
+ *  @li bench_env_t: benchmark environment configuration
+ *  @li bench_run_t: call bench_run_start and bench_run_stop around the
+ *      actual benchmarking code
+ *  @li uint64_t: size of the workload - typically number of inner loops in
+ *      your benchmark (used to self-calibrate benchmark size)
+ *
+ * Typically, the structure of the function is following:
+ * @code{c}
+ * static bool runnerconst bench_env_t const *envbench_run_t *run, uint64_t size)
+ * {
+ * 	bench_run_start(run);
+ * 	for (uint64_t i = 0; i < size; i++) {
+ * 		// measured action
+ * 		if (something_fails) {
+ * 		    return bench_run_fail(run, "oops: %s (%d)", str_error(rc), rc);
+ * 		}
+ * 	}
+ * 	bench_run_stop(run);
+ *
+ * 	return true;
+ * }
+ * @endcode
+ */
Index: uspace/app/hbench/env.c
===================================================================
--- uspace/app/hbench/env.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/env.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2019 Vojtech Horky
+ * 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 hbench
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <str.h>
+#include "hbench.h"
+
+typedef struct {
+	ht_link_t link;
+
+	char *key;
+	char *value;
+} param_t;
+
+static size_t param_hash(const ht_link_t *item)
+{
+	param_t *param = hash_table_get_inst(item, param_t, link);
+	return str_size(param->key);
+}
+
+static size_t param_key_hash(void *key)
+{
+	char *key_str = key;
+	return str_size(key_str);
+}
+
+static bool param_key_equal(void *key, const ht_link_t *item)
+{
+	param_t *param = hash_table_get_inst(item, param_t, link);
+	char *key_str = key;
+
+	return str_cmp(param->key, key_str) == 0;
+}
+
+static bool param_equal(const ht_link_t *link_a, const ht_link_t *link_b)
+{
+	param_t *a = hash_table_get_inst(link_a, param_t, link);
+	param_t *b = hash_table_get_inst(link_b, param_t, link);
+
+	return str_cmp(a->key, b->key) == 0;
+}
+
+static void param_remove(ht_link_t *item)
+{
+	param_t *param = hash_table_get_inst(item, param_t, link);
+	free(param->key);
+	free(param->value);
+}
+
+static hash_table_ops_t param_hash_table_ops = {
+	.hash = param_hash,
+	.key_hash = param_key_hash,
+	.key_equal = param_key_equal,
+	.equal = param_equal,
+	.remove_callback = param_remove
+};
+
+extern errno_t bench_env_init(bench_env_t *env)
+{
+	bool ok = hash_table_create(&env->parameters, 0, 0, &param_hash_table_ops);
+	if (!ok) {
+		return ENOMEM;
+	}
+
+	env->run_count = DEFAULT_RUN_COUNT;
+	env->minimal_run_duration_nanos = MSEC2NSEC(DEFAULT_MIN_RUN_DURATION_SEC);
+
+	return EOK;
+}
+
+extern void bench_env_cleanup(bench_env_t *env)
+{
+	hash_table_destroy(&env->parameters);
+}
+
+errno_t bench_env_param_set(bench_env_t *env, const char *key, const char *value)
+{
+	param_t *param = malloc(sizeof(param_t));
+	if (param == NULL) {
+		return ENOMEM;
+	}
+
+	param->key = str_dup(key);
+	param->value = str_dup(value);
+
+	if ((param->key == NULL) || (param->value == NULL)) {
+		free(param->key);
+		free(param->value);
+		free(param);
+
+		return ENOMEM;
+	}
+
+	hash_table_insert(&env->parameters, &param->link);
+
+	return EOK;
+}
+
+const char *bench_env_param_get(bench_env_t *env, const char *key, const char *default_value)
+{
+	ht_link_t *item = hash_table_find(&env->parameters, (char *) key);
+
+	if (item == NULL) {
+		return default_value;
+	}
+
+	param_t *param = hash_table_get_inst(item, param_t, link);
+	return param->value;
+}
+
+/** @}
+ */
Index: uspace/app/hbench/fs/dirread.c
===================================================================
--- uspace/app/hbench/fs/dirread.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/fs/dirread.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2011 Martin Sucha
+ * Copyright (c) 2019 Vojtech Horky
+ * 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 hbench
+ * @{
+ */
+
+#include <dirent.h>
+#include <str_error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "../hbench.h"
+
+/** Execute directory listing benchmark.
+ *
+ * Note that while this benchmark tries to measure speed of direct
+ * read, it rather measures speed of FS cache as it is highly probable
+ * that the corresponding blocks would be cached after first run.
+ */
+static bool runner(bench_env_t *env, bench_run_t *run, uint64_t size)
+{
+	const char *path = bench_env_param_get(env, "dirname", "/");
+
+	bench_run_start(run);
+	for (uint64_t i = 0; i < size; i++) {
+		DIR *dir = opendir(path);
+		if (dir == NULL) {
+			return bench_run_fail(run, "failed to open %s for reading: %s",
+			    path, str_error(errno));
+		}
+
+		struct dirent *dp;
+		while ((dp = readdir(dir))) {
+			/* Do nothing */
+		}
+
+		closedir(dir);
+	}
+	bench_run_stop(run);
+
+	return true;
+}
+
+benchmark_t benchmark_dir_read = {
+	.name = "dir_read",
+	.desc = "Read contents of a directory (use 'dirname' param to alter the default).",
+	.entry = &runner,
+	.setup = NULL,
+	.teardown = NULL
+};
+
+/**
+ * @}
+ */
Index: uspace/app/hbench/fs/fileread.c
===================================================================
--- uspace/app/hbench/fs/fileread.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/fs/fileread.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2011 Martin Sucha
+ * Copyright (c) 2019 Vojtech Horky
+ * 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 hbench
+ * @{
+ */
+
+#include <str_error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "../hbench.h"
+
+#define BUFFER_SIZE 4096
+
+/** Execute file reading benchmark.
+ *
+ * Note that while this benchmark tries to measure speed of file reading,
+ * it rather measures speed of FS cache as it is highly probable that the
+ * corresponding blocks would be cached after first run.
+ */
+static bool runner(bench_env_t *env, bench_run_t *run, uint64_t size)
+{
+	const char *path = bench_env_param_get(env, "filename", "/data/web/helenos.png");
+
+	char *buf = malloc(BUFFER_SIZE);
+	if (buf == NULL) {
+		return bench_run_fail(run, "failed to allocate %dB buffer", BUFFER_SIZE);
+	}
+
+	bool ret = true;
+
+	FILE *file = fopen(path, "r");
+	if (file == NULL) {
+		bench_run_fail(run, "failed to open %s for reading: %s",
+		    path, str_error(errno));
+		ret = false;
+		goto leave_free_buf;
+	}
+
+	bench_run_start(run);
+	for (uint64_t i = 0; i < size; i++) {
+		int rc = fseek(file, 0, SEEK_SET);
+		if (rc != 0) {
+			bench_run_fail(run, "failed to rewind %s: %s",
+			    path, str_error(errno));
+			ret = false;
+			goto leave_close;
+		}
+		while (!feof(file)) {
+			fread(buf, 1, BUFFER_SIZE, file);
+			if (ferror(file)) {
+				bench_run_fail(run, "failed to read from %s: %s",
+				    path, str_error(errno));
+				ret = false;
+				goto leave_close;
+			}
+		}
+	}
+	bench_run_stop(run);
+
+leave_close:
+	fclose(file);
+
+leave_free_buf:
+	free(buf);
+
+	return ret;
+}
+
+benchmark_t benchmark_file_read = {
+	.name = "file_read",
+	.desc = "Sequentially read contents of a file (use 'filename' param to alter the default).",
+	.entry = &runner,
+	.setup = NULL,
+	.teardown = NULL
+};
+
+/**
+ * @}
+ */
Index: uspace/app/hbench/hbench.h
===================================================================
--- uspace/app/hbench/hbench.h	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/hbench.h	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * Copyright (c) 2019 Vojtech Horky
+ * 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 hbench
+ * @{
+ */
+/** @file
+ */
+
+#ifndef HBENCH_H_
+#define HBENCH_H_
+
+#include <adt/hash_table.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <perf.h>
+
+#define DEFAULT_RUN_COUNT 10
+#define DEFAULT_MIN_RUN_DURATION_SEC 10
+
+/** Single run information.
+ *
+ * Used to store both performance information (now, only wall-clock
+ * time) as well as information about error.
+ *
+ * Use proper access functions when modifying data inside this structure.
+ *
+ * Eventually, we could collection of hardware counters etc. without
+ * modifying signatures of any existing benchmark.
+ */
+typedef struct {
+	stopwatch_t stopwatch;
+	char *error_message;
+	size_t error_message_buffer_size;
+} bench_run_t;
+
+/** Benchmark environment configuration.
+ *
+ * Benchmarking code (runners) should use access functions to read
+ * data from this structure (now only bench_env_param_get).
+ *
+ * Harness can access it directly.
+ */
+typedef struct {
+	hash_table_t parameters;
+	size_t run_count;
+	nsec_t minimal_run_duration_nanos;
+} bench_env_t;
+
+/** Actual benchmark runner.
+ *
+ * The first argument describes the environment, second is used to store
+ * information about the run (performance data or error message) and
+ * third describes workload size (number of iterations).
+ */
+typedef bool (*benchmark_entry_t)(bench_env_t *, bench_run_t *, uint64_t);
+
+/** Setup and teardown callback type.
+ *
+ * Unlike in benchmark_entry_t, we do not need to pass in number of
+ * iterations to execute (note that we use bench_run_t only to simplify
+ * creation of error messages).
+ */
+typedef bool (*benchmark_helper_t)(bench_env_t *, bench_run_t *);
+
+typedef struct {
+	const char *name;
+	const char *desc;
+	benchmark_entry_t entry;
+	benchmark_helper_t setup;
+	benchmark_helper_t teardown;
+} benchmark_t;
+
+extern void bench_run_init(bench_run_t *, char *, size_t);
+extern bool bench_run_fail(bench_run_t *, const char *, ...);
+
+/*
+ * We keep the following two functions inline to ensure that we start
+ * measurement as close to the surrounding code as possible. Note that
+ * this inlining is done at least on the level of individual wrappers
+ * (this one and the one provided by stopwatch_t) to pass the control
+ * as fast as possible to the actual timer used.
+ */
+
+static inline void bench_run_start(bench_run_t *run)
+{
+	stopwatch_start(&run->stopwatch);
+}
+
+static inline void bench_run_stop(bench_run_t *run)
+{
+	stopwatch_stop(&run->stopwatch);
+}
+
+extern errno_t csv_report_open(const char *);
+extern void csv_report_add_entry(bench_run_t *, int, benchmark_t *, uint64_t);
+extern void csv_report_close(void);
+
+extern errno_t bench_env_init(bench_env_t *);
+extern errno_t bench_env_param_set(bench_env_t *, const char *, const char *);
+extern const char *bench_env_param_get(bench_env_t *, const char *, const char *);
+extern void bench_env_cleanup(bench_env_t *);
+
+extern benchmark_t *benchmarks[];
+extern size_t benchmark_count;
+
+/* Put your benchmark descriptors here (and also to benchlist.c). */
+extern benchmark_t benchmark_dir_read;
+extern benchmark_t benchmark_fibril_mutex;
+extern benchmark_t benchmark_file_read;
+extern benchmark_t benchmark_malloc1;
+extern benchmark_t benchmark_malloc2;
+extern benchmark_t benchmark_ns_ping;
+extern benchmark_t benchmark_ping_pong;
+
+#endif
+
+/** @}
+ */
Index: uspace/app/hbench/ipc/ns_ping.c
===================================================================
--- uspace/app/hbench/ipc/ns_ping.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/ipc/ns_ping.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,67 @@
+/*
+ * 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 hbench
+ * @{
+ */
+
+#include <stdio.h>
+#include <ns.h>
+#include <async.h>
+#include <errno.h>
+#include <str_error.h>
+#include "../hbench.h"
+
+static bool runner(bench_env_t *env, bench_run_t *run, uint64_t niter)
+{
+	bench_run_start(run);
+
+	for (uint64_t count = 0; count < niter; count++) {
+		errno_t rc = ns_ping();
+
+		if (rc != EOK) {
+			return bench_run_fail(run, "failed sending ping message: %s (%d)",
+			    str_error(rc), rc);
+		}
+	}
+
+	bench_run_stop(run);
+
+	return true;
+}
+
+benchmark_t benchmark_ns_ping = {
+	.name = "ns_ping",
+	.desc = "Name service IPC ping-pong benchmark",
+	.entry = &runner,
+	.setup = NULL,
+	.teardown = NULL
+};
+
+/** @}
+ */
Index: uspace/app/hbench/ipc/ping_pong.c
===================================================================
--- uspace/app/hbench/ipc/ping_pong.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/ipc/ping_pong.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+/** @addtogroup hbench
+ * @{
+ */
+
+#include <stdio.h>
+#include <ipc_test.h>
+#include <async.h>
+#include <errno.h>
+#include <str_error.h>
+#include "../hbench.h"
+
+static ipc_test_t *test = NULL;
+
+static bool setup(bench_env_t *env, bench_run_t *run)
+{
+	errno_t rc = ipc_test_create(&test);
+	if (rc != EOK) {
+		return bench_run_fail(run,
+		    "failed contacting IPC test server (have you run /srv/test/ipc-test?): %s (%d)",
+		    str_error(rc), rc);
+	}
+
+	return true;
+}
+
+static bool teardown(bench_env_t *env, bench_run_t *run)
+{
+	ipc_test_destroy(test);
+	return true;
+}
+
+static bool runner(bench_env_t *env, bench_run_t *run, uint64_t niter)
+{
+	bench_run_start(run);
+
+	for (uint64_t count = 0; count < niter; count++) {
+		errno_t rc = ipc_test_ping(test);
+
+		if (rc != EOK) {
+			return bench_run_fail(run, "failed sending ping message: %s (%d)",
+			    str_error(rc), rc);
+		}
+	}
+
+	bench_run_stop(run);
+
+	return true;
+}
+
+benchmark_t benchmark_ping_pong = {
+	.name = "ping_pong",
+	.desc = "IPC ping-pong benchmark",
+	.entry = &runner,
+	.setup = &setup,
+	.teardown = &teardown
+};
+
+/** @}
+ */
Index: uspace/app/hbench/main.c
===================================================================
--- uspace/app/hbench/main.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/main.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * Copyright (c) 2018 Vojtech Horky
+ * 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 hbench
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <assert.h>
+#include <getopt.h>
+#include <math.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <str.h>
+#include <time.h>
+#include <errno.h>
+#include <str_error.h>
+#include <perf.h>
+#include <types/casting.h>
+#include "hbench.h"
+
+#define MAX_ERROR_STR_LENGTH 1024
+
+static void short_report(bench_run_t *info, int run_index,
+    benchmark_t *bench, uint64_t workload_size)
+{
+	csv_report_add_entry(info, run_index, bench, workload_size);
+
+	usec_t duration_usec = NSEC2USEC(stopwatch_get_nanos(&info->stopwatch));
+
+	printf("Completed %" PRIu64 " operations in %llu us",
+	    workload_size, duration_usec);
+	if (duration_usec > 0) {
+		double nanos = stopwatch_get_nanos(&info->stopwatch);
+		double thruput = (double) workload_size / (nanos / 1000000000.0l);
+		printf(", %.0f ops/s.\n", thruput);
+	} else {
+		printf(".\n");
+	}
+}
+
+/** Estimate square root value.
+ *
+ * @param value The value to compute square root of.
+ * @param precision Required precision (e.g. 0.00001).
+ *
+ * @details
+ *
+ * This is a temporary solution until we have proper sqrt() implementation
+ * in libmath.
+ *
+ * The algorithm uses Babylonian method [1].
+ *
+ * [1] https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
+ */
+static double estimate_square_root(double value, double precision)
+{
+	double estimate = 1.;
+	double prev_estimate = estimate + 10 * precision;
+
+	while (fabs(estimate - prev_estimate) > precision) {
+		prev_estimate = estimate;
+		estimate = (prev_estimate + value / prev_estimate) / 2.;
+	}
+
+	return estimate;
+}
+
+/** Compute available statistics from given stopwatches.
+ *
+ * We compute normal mean for average duration of the workload and geometric
+ * mean for average thruput. Note that geometric mean is necessary to compute
+ * average throughput correctly - consider the following example:
+ *  - we run always 60 operations,
+ *  - first run executes in 30 s (i.e. 2 ops/s)
+ *  - and second one in 10 s (6 ops/s).
+ * Then, naively, average throughput would be (2+6)/2 = 4 [ops/s]. However, we
+ * actually executed 60 + 60 ops in 30 + 10 seconds. So the actual average
+ * throughput is 3 ops/s (which is exactly what geometric mean means).
+ *
+ */
+static void compute_stats(bench_run_t *runs, size_t run_count,
+    uint64_t workload_size, double precision, double *out_duration_avg,
+    double *out_duration_sigma, double *out_thruput_avg)
+{
+	double inv_thruput_sum = 0.0;
+	double nanos_sum = 0.0;
+	double nanos_sum2 = 0.0;
+
+	for (size_t i = 0; i < run_count; i++) {
+		double nanos = stopwatch_get_nanos(&runs[i].stopwatch);
+		double thruput = (double) workload_size / nanos;
+
+		inv_thruput_sum += 1.0 / thruput;
+		nanos_sum += nanos;
+		nanos_sum2 += nanos * nanos;
+	}
+	*out_duration_avg = nanos_sum / run_count;
+	double sigma2 = (nanos_sum2 - nanos_sum * (*out_duration_avg)) /
+	    ((double) run_count - 1);
+	// FIXME: implement sqrt properly
+	if (run_count > 1) {
+		*out_duration_sigma = estimate_square_root(sigma2, precision);
+	} else {
+		*out_duration_sigma = NAN;
+	}
+	*out_thruput_avg = 1.0 / (inv_thruput_sum / run_count);
+}
+
+static void summary_stats(bench_run_t *runs, size_t run_count,
+    benchmark_t *bench, uint64_t workload_size)
+{
+	double duration_avg, duration_sigma, thruput_avg;
+	compute_stats(runs, run_count, workload_size, 0.001,
+	    &duration_avg, &duration_sigma, &thruput_avg);
+
+	printf("Average: %" PRIu64 " ops in %.0f us (sd %.0f us); "
+	    "%.0f ops/s; Samples: %zu\n",
+	    workload_size, duration_avg / 1000.0, duration_sigma / 1000.0,
+	    thruput_avg * 1000000000.0, run_count);
+}
+
+static bool run_benchmark(bench_env_t *env, benchmark_t *bench)
+{
+	printf("Warm up and determine workload size...\n");
+
+	/*
+	 * We share this buffer across all runs as we know that it is
+	 * used only on failure (and we abort after first error).
+	 */
+	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, "");
+
+	bench_run_t helper_run;
+	bench_run_init(&helper_run, error_msg, MAX_ERROR_STR_LENGTH);
+
+	bool ret = true;
+
+	if (bench->setup != NULL) {
+		ret = bench->setup(env, &helper_run);
+		if (!ret) {
+			goto leave_error;
+		}
+	}
+
+	/*
+	 * Find workload size that is big enough to last few seconds.
+	 * We also check that uint64_t is big enough.
+	 */
+	uint64_t workload_size = 0;
+	for (size_t bits = 0; bits <= 64; bits++) {
+		if (bits == 64) {
+			str_cpy(error_msg, MAX_ERROR_STR_LENGTH, "Workload too small even for 1 << 63");
+			goto leave_error;
+		}
+		workload_size = ((uint64_t) 1) << bits;
+
+		bench_run_t run;
+		bench_run_init(&run, error_msg, MAX_ERROR_STR_LENGTH);
+
+		bool ok = bench->entry(env, &run, workload_size);
+		if (!ok) {
+			goto leave_error;
+		}
+		short_report(&run, -1, bench, workload_size);
+
+		nsec_t duration = stopwatch_get_nanos(&run.stopwatch);
+		if (duration > env->minimal_run_duration_nanos) {
+			break;
+		}
+	}
+
+	printf("Workload size set to %" PRIu64 ", measuring %zu samples.\n",
+	    workload_size, env->run_count);
+
+	bench_run_t *runs = calloc(env->run_count, sizeof(bench_run_t));
+	if (runs == NULL) {
+		snprintf(error_msg, MAX_ERROR_STR_LENGTH, "failed allocating memory");
+		goto leave_error;
+	}
+	for (size_t i = 0; i < env->run_count; i++) {
+		bench_run_init(&runs[i], error_msg, MAX_ERROR_STR_LENGTH);
+
+		bool ok = bench->entry(env, &runs[i], workload_size);
+		if (!ok) {
+			free(runs);
+			goto leave_error;
+		}
+		short_report(&runs[i], i, bench, workload_size);
+	}
+
+	summary_stats(runs, env->run_count, bench, workload_size);
+	printf("\nBenchmark completed\n");
+
+	free(runs);
+
+	goto leave;
+
+leave_error:
+	printf("Error: %s\n", error_msg);
+	ret = false;
+
+leave:
+	if (bench->teardown != NULL) {
+		bool ok = bench->teardown(env, &helper_run);
+		if (!ok) {
+			printf("Error: %s\n", error_msg);
+			ret = false;
+		}
+	}
+
+	free(error_msg);
+
+	return ret;
+}
+
+static int run_benchmarks(bench_env_t *env)
+{
+	unsigned int count_ok = 0;
+	unsigned int count_fail = 0;
+
+	char *failed_names = NULL;
+
+	printf("\n*** Running all benchmarks ***\n\n");
+
+	for (size_t it = 0; it < benchmark_count; it++) {
+		printf("%s (%s)\n", benchmarks[it]->name, benchmarks[it]->desc);
+		if (run_benchmark(env, benchmarks[it])) {
+			count_ok++;
+			continue;
+		}
+
+		if (!failed_names) {
+			failed_names = str_dup(benchmarks[it]->name);
+		} else {
+			char *f = NULL;
+			asprintf(&f, "%s, %s", failed_names, benchmarks[it]->name);
+			if (!f) {
+				printf("Out of memory.\n");
+				abort();
+			}
+			free(failed_names);
+			failed_names = f;
+		}
+		count_fail++;
+	}
+
+	printf("\nCompleted, %u benchmarks run, %u succeeded.\n",
+	    count_ok + count_fail, count_ok);
+	if (failed_names)
+		printf("Failed benchmarks: %s\n", failed_names);
+
+	return count_fail;
+}
+
+static void list_benchmarks(void)
+{
+	size_t len = 0;
+	for (size_t i = 0; i < benchmark_count; i++) {
+		size_t len_now = str_length(benchmarks[i]->name);
+		if (len_now > len)
+			len = len_now;
+	}
+
+	assert(can_cast_size_t_to_int(len) && "benchmark name length overflow");
+
+	for (size_t i = 0; i < benchmark_count; i++)
+		printf("  %-*s %s\n", (int) len, benchmarks[i]->name, benchmarks[i]->desc);
+
+	printf("  %-*s Run all benchmarks\n", (int) len, "*");
+}
+
+static void print_usage(const char *progname)
+{
+	printf("Usage: %s [options] <benchmark>\n", progname);
+	printf("-h, --help                 "
+	    "Print this help and exit\n");
+	printf("-d, --duration MILLIS      "
+	    "Set minimal run duration (milliseconds)\n");
+	printf("-n, --count N              "
+	    "Set number of measured runs\n");
+	printf("-o, --output filename.csv  "
+	    "Store machine-readable data in filename.csv\n");
+	printf("-p, --param KEY=VALUE      "
+	    "Additional parameters for the benchmark\n");
+	printf("<benchmark> is one of the following:\n");
+	list_benchmarks();
+}
+
+static void handle_param_arg(bench_env_t *env, char *arg)
+{
+	char *value = NULL;
+	char *key = str_tok(arg, "=", &value);
+	bench_env_param_set(env, key, value);
+}
+
+int main(int argc, char *argv[])
+{
+	bench_env_t bench_env;
+	errno_t rc = bench_env_init(&bench_env);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed to initialize internal params structure: %s\n",
+		    str_error(rc));
+		return -5;
+	}
+
+	const char *short_options = "ho:p:n:d:";
+	struct option long_options[] = {
+		{ "duration", required_argument, NULL, 'd' },
+		{ "help", optional_argument, NULL, 'h' },
+		{ "count", required_argument, NULL, 'n' },
+		{ "output", required_argument, NULL, 'o' },
+		{ "param", required_argument, NULL, 'p' },
+		{ 0, 0, NULL, 0 }
+	};
+
+	char *csv_output_filename = NULL;
+
+	int opt = 0;
+	while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) > 0) {
+		switch (opt) {
+		case 'd':
+			errno = EOK;
+			bench_env.minimal_run_duration_nanos = MSEC2NSEC(atoll(optarg));
+			if ((errno != EOK) || (bench_env.minimal_run_duration_nanos <= 0)) {
+				fprintf(stderr, "Invalid -d argument.\n");
+				return -3;
+			}
+			break;
+		case 'h':
+			print_usage(*argv);
+			return 0;
+		case 'n':
+			errno = EOK;
+			bench_env.run_count = (nsec_t) atoll(optarg);
+			if ((errno != EOK) || (bench_env.run_count <= 0)) {
+				fprintf(stderr, "Invalid -n argument.\n");
+				return -3;
+			}
+			break;
+		case 'o':
+			csv_output_filename = optarg;
+			break;
+		case 'p':
+			handle_param_arg(&bench_env, optarg);
+			break;
+		case -1:
+		default:
+			break;
+		}
+	}
+
+	if (optind + 1 != argc) {
+		print_usage(*argv);
+		fprintf(stderr, "Error: specify one benchmark to run or * for all.\n");
+		return -3;
+	}
+
+	const char *benchmark = argv[optind];
+
+	if (csv_output_filename != NULL) {
+		errno_t rc = csv_report_open(csv_output_filename);
+		if (rc != EOK) {
+			fprintf(stderr, "Failed to open CSV report '%s': %s\n",
+			    csv_output_filename, str_error(rc));
+			return -4;
+		}
+	}
+
+	int exit_code = 0;
+
+	if (str_cmp(benchmark, "*") == 0) {
+		exit_code = run_benchmarks(&bench_env);
+	} else {
+		bool benchmark_exists = false;
+		for (size_t i = 0; i < benchmark_count; i++) {
+			if (str_cmp(benchmark, benchmarks[i]->name) == 0) {
+				benchmark_exists = true;
+				exit_code = run_benchmark(&bench_env, benchmarks[i]) ? 0 : -1;
+				break;
+			}
+		}
+		if (!benchmark_exists) {
+			printf("Unknown benchmark \"%s\"\n", benchmark);
+			exit_code = -2;
+		}
+	}
+
+	csv_report_close();
+	bench_env_cleanup(&bench_env);
+
+	return exit_code;
+}
+
+/** @}
+ */
Index: uspace/app/hbench/malloc/malloc1.c
===================================================================
--- uspace/app/hbench/malloc/malloc1.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/malloc/malloc1.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,64 @@
+/*
+ * 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 hbench
+ * @{
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "../hbench.h"
+
+static bool runner(bench_env_t *env, bench_run_t *run, uint64_t size)
+{
+	bench_run_start(run);
+	for (uint64_t i = 0; i < size; i++) {
+		void *p = malloc(1);
+		if (p == NULL) {
+			return bench_run_fail(run,
+			    "failed to allocate 1B in run %" PRIu64 " (out of %" PRIu64 ")",
+			    i, size);
+		}
+		free(p);
+	}
+	bench_run_stop(run);
+
+	return true;
+}
+
+benchmark_t benchmark_malloc1 = {
+	.name = "malloc1",
+	.desc = "User-space memory allocator benchmark, repeatedly allocate one block",
+	.entry = &runner,
+	.setup = NULL,
+	.teardown = NULL
+};
+
+/** @}
+ */
Index: uspace/app/hbench/malloc/malloc2.c
===================================================================
--- uspace/app/hbench/malloc/malloc2.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/malloc/malloc2.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,79 @@
+/*
+ * 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 hbench
+ * @{
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "../hbench.h"
+
+static bool runner(bench_env_t *env, bench_run_t *run, uint64_t niter)
+{
+	bench_run_start(run);
+
+	void **p = malloc(niter * sizeof(void *));
+	if (p == NULL) {
+		return bench_run_fail(run, "failed to allocate backend array (%" PRIu64 "B)",
+		    niter * sizeof(void *));
+	}
+
+	for (uint64_t count = 0; count < niter; count++) {
+		p[count] = malloc(1);
+		if (p[count] == NULL) {
+			for (uint64_t j = 0; j < count; j++) {
+				free(p[j]);
+			}
+			free(p);
+			return bench_run_fail(run,
+			    "failed to allocate 1B in run %" PRIu64 " (out of %" PRIu64 ")",
+			    count, niter);
+		}
+	}
+
+	for (uint64_t count = 0; count < niter; count++)
+		free(p[count]);
+
+	free(p);
+
+	bench_run_stop(run);
+
+	return true;
+}
+
+benchmark_t benchmark_malloc2 = {
+	.name = "malloc2",
+	.desc = "User-space memory allocator benchmark, allocate many small blocks",
+	.entry = &runner,
+	.setup = NULL,
+	.teardown = NULL
+};
+
+/** @}
+ */
Index: uspace/app/hbench/synch/fibril_mutex.c
===================================================================
--- uspace/app/hbench/synch/fibril_mutex.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/synch/fibril_mutex.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2019 Vojtech Horky
+ * 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 hbench
+ * @{
+ */
+
+#include <fibril_synch.h>
+#include <stdatomic.h>
+#include "../hbench.h"
+
+/*
+ * Simple benchmark for fibril mutexes. There are two fibrils that compete
+ * over the same mutex as that is the simplest scenario.
+ */
+
+typedef struct {
+	fibril_mutex_t mutex;
+	uint64_t counter;
+	atomic_bool done;
+} shared_t;
+
+static errno_t competitor(void *arg)
+{
+	shared_t *shared = arg;
+	fibril_detach(fibril_get_id());
+
+	while (true) {
+		fibril_mutex_lock(&shared->mutex);
+		uint64_t local = shared->counter;
+		fibril_mutex_unlock(&shared->mutex);
+		if (local == 0) {
+			break;
+		}
+	}
+
+	atomic_store(&shared->done, true);
+
+	return EOK;
+}
+
+static bool runner(bench_env_t *env, bench_run_t *run, uint64_t size)
+{
+	shared_t shared;
+	fibril_mutex_initialize(&shared.mutex);
+	shared.counter = size;
+	atomic_store(&shared.done, false);
+
+	fid_t other = fibril_create(competitor, &shared);
+	fibril_add_ready(other);
+
+	bench_run_start(run);
+	for (uint64_t i = 0; i < size; i++) {
+		fibril_mutex_lock(&shared.mutex);
+		shared.counter--;
+		fibril_mutex_unlock(&shared.mutex);
+	}
+	bench_run_stop(run);
+
+	while (!atomic_load(&shared.done)) {
+		fibril_yield();
+	}
+
+	return true;
+}
+
+benchmark_t benchmark_fibril_mutex = {
+	.name = "fibril_mutex",
+	.desc = "Speed of mutex lock/unlock operations",
+	.entry = &runner,
+	.setup = NULL,
+	.teardown = NULL
+};
+
+/** @}
+ */
Index: uspace/app/hbench/utils.c
===================================================================
--- uspace/app/hbench/utils.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/app/hbench/utils.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2019 Vojtech Horky
+ * 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 hbench
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "hbench.h"
+
+/** Initialize bench run structure.
+ *
+ * @param run Structure to intialize.
+ * @param error_buffer Pre-allocated memory to use for error messages.
+ * @param error_buffer_size Size of error_buffer.
+ */
+void bench_run_init(bench_run_t *run, char *error_buffer, size_t error_buffer_size)
+{
+	stopwatch_init(&run->stopwatch);
+	run->error_message = error_buffer;
+	run->error_message_buffer_size = error_buffer_size;
+}
+
+/** Format error message on benchmark failure.
+ *
+ * This function always returns false so it can be easily used in benchmark
+ * runners as
+ *
+ * @code
+ * if (error) {
+ *     return bench_run_fail(run, "we failed");
+ * }
+ * @endcode
+ *
+ * @param run Current benchmark run.
+ * @param fmt printf-style message followed by extra arguments.
+ * @retval false Always.
+ */
+bool bench_run_fail(bench_run_t *run, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	vsnprintf(run->error_message, run->error_message_buffer_size, fmt, args);
+	va_end(args);
+
+	return false;
+}
+
+/** @}
+ */
Index: pace/app/perf/Makefile
===================================================================
--- uspace/app/perf/Makefile	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,42 +1,0 @@
-#
-# 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/ns_ping.c \
-	ipc/ping_pong.c \
-	malloc/malloc1.c \
-	malloc/malloc2.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/app/perf/doc/doxygroups.h
===================================================================
--- uspace/app/perf/doc/doxygroups.h	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,4 +1,0 @@
-/** @addtogroup perf perf
- * @brief User space performance measuring tool
- * @ingroup apps
- */
Index: pace/app/perf/ipc/ns_ping.c
===================================================================
--- uspace/app/perf/ipc/ns_ping.c	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,134 +1,0 @@
-/*
- * 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.
- */
-
-#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_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: pace/app/perf/ipc/ns_ping.def
===================================================================
--- uspace/app/perf/ipc/ns_ping.def	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,5 +1,0 @@
-{
-	"ns_ping",
-	"Name service IPC ping-pong benchmark",
-	&bench_ns_ping
-},
Index: pace/app/perf/ipc/ping_pong.c
===================================================================
--- uspace/app/perf/ipc/ping_pong.c	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,149 +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 <ipc_test.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(ipc_test_t *test, uint64_t niter,
-    uint64_t *rduration)
-{
-	struct timespec start;
-	uint64_t count;
-
-	getuptime(&start);
-
-	for (count = 0; count < niter; count++) {
-		errno_t retval = ipc_test_ping(test);
-
-		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];
-	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: pace/app/perf/ipc/ping_pong.def
===================================================================
--- uspace/app/perf/ipc/ping_pong.def	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,5 +1,0 @@
-{
-	"ping_pong",
-	"IPC ping-pong benchmark",
-	&bench_ping_pong
-},
Index: pace/app/perf/malloc/malloc1.c
===================================================================
--- uspace/app/perf/malloc/malloc1.c	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,138 +1,0 @@
-/*
- * 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.
- */
-
-#include <math.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
-
-static errno_t malloc1_measure(uint64_t niter, uint64_t *rduration)
-{
-	struct timespec start;
-	uint64_t count;
-	void *p;
-
-	getuptime(&start);
-
-	for (count = 0; count < niter; count++) {
-		p = malloc(1);
-		if (p == NULL)
-			return ENOMEM;
-		free(p);
-	}
-
-	struct timespec now;
-	getuptime(&now);
-
-	*rduration = ts_sub_diff(&now, &start) / 1000;
-	return EOK;
-}
-
-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: pace/app/perf/malloc/malloc1.def
===================================================================
--- uspace/app/perf/malloc/malloc1.def	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,5 +1,0 @@
-{
-	"malloc1",
-	"User-space memory allocator benchmark, repeatedly allocate one block",
-	&bench_malloc1
-},
Index: pace/app/perf/malloc/malloc2.c
===================================================================
--- uspace/app/perf/malloc/malloc2.c	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,146 +1,0 @@
-/*
- * 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.
- */
-
-#include <math.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
-
-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;
-	}
-
-	for (count = 0; count < niter; count++)
-		free(p[count]);
-
-	free(p);
-
-	struct timespec now;
-	getuptime(&now);
-
-	*rduration = ts_sub_diff(&now, &start) / 1000;
-	return EOK;
-}
-
-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: pace/app/perf/malloc/malloc2.def
===================================================================
--- uspace/app/perf/malloc/malloc2.def	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,5 +1,0 @@
-{
-	"malloc2",
-	"User-space memory allocator benchmark, allocate many small blocks",
-	&bench_malloc2
-},
Index: pace/app/perf/perf.c
===================================================================
--- uspace/app/perf/perf.c	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,149 +1,0 @@
-/*
- * 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/ns_ping.def"
-#include "ipc/ping_pong.def"
-#include "malloc/malloc1.def"
-#include "malloc/malloc2.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: pace/app/perf/perf.h
===================================================================
--- uspace/app/perf/perf.h	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ 	(revision )
@@ -1,58 +1,0 @@
-/*
- * 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_malloc1(void);
-extern const char *bench_malloc2(void);
-extern const char *bench_ns_ping(void);
-extern const char *bench_ping_pong(void);
-
-extern benchmark_t benchmarks[];
-
-#endif
-
-/** @}
- */
Index: uspace/app/tester/tester.c
===================================================================
--- uspace/app/tester/tester.c	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ uspace/app/tester/tester.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -35,4 +35,5 @@
  */
 
+#include <assert.h>
 #include <stdio.h>
 #include <stddef.h>
@@ -40,4 +41,5 @@
 #include <str.h>
 #include <io/log.h>
+#include <types/casting.h>
 #include "tester.h"
 
@@ -144,15 +146,11 @@
 	}
 
-	unsigned int _len = (unsigned int) len;
-	if ((_len != len) || (((int) _len) < 0)) {
-		printf("Command length overflow\n");
-		return;
-	}
+	assert(can_cast_size_t_to_int(len) && "test name length overflow");
 
 	for (test = tests; test->name != NULL; test++)
-		printf("%-*s %s%s\n", _len, test->name, test->desc,
+		printf("%-*s %s%s\n", (int) len, test->name, test->desc,
 		    (test->safe ? "" : " (unsafe)"));
 
-	printf("%-*s Run all safe tests\n", _len, "*");
+	printf("%-*s Run all safe tests\n", (int) len, "*");
 }
 
Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ uspace/lib/c/Makefile	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -189,4 +189,5 @@
 TEST_SOURCES = \
 	test/adt/circ_buf.c \
+	test/casting.c \
 	test/fibril/timer.c \
 	test/main.c \
@@ -196,4 +197,5 @@
 	test/stdio/scanf.c \
 	test/odict.c \
+	test/perf.c \
 	test/perm.c \
 	test/qsort.c \
Index: uspace/lib/c/include/perf.h
===================================================================
--- uspace/lib/c/include/perf.h	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/lib/c/include/perf.h	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2018 Vojtech Horky
+ * 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 libc
+ * @{
+ */
+/** @file
+ * System performance measurement utilities.
+ */
+
+#ifndef LIBC_PERF_H_
+#define LIBC_PERF_H_
+
+#include <time.h>
+
+/** Stopwatch is THE way to measure elapsed time on HelenOS. */
+typedef struct {
+	struct timespec start;
+	struct timespec end;
+} stopwatch_t;
+
+#define STOPWATCH_INITIALIZE_STATIC { { 0, 0 }, { 0, 0 } }
+
+/** Initialize system stopwatch.
+ *
+ * @param stopwatch Stopwatch to initialize.
+ */
+static inline void stopwatch_init(stopwatch_t *stopwatch)
+{
+	stopwatch->start.tv_sec = 0;
+	stopwatch->start.tv_nsec = 0;
+	stopwatch->end.tv_sec = 0;
+	stopwatch->end.tv_nsec = 0;
+}
+
+/** Emulate elapsed time for use in tests.
+ *
+ * @param stopwatch Stopwatch to use.
+ * @param nanos Elapsed time in nanoseconds to set.
+ */
+static inline void stopwatch_set_nanos(stopwatch_t *stopwatch, nsec_t nanos)
+{
+	stopwatch->start.tv_sec = 0;
+	stopwatch->start.tv_nsec = 0;
+	stopwatch->end.tv_sec = NSEC2SEC(nanos);
+	stopwatch->end.tv_nsec = nanos - SEC2NSEC(stopwatch->end.tv_sec);
+}
+
+/** Start the stopwatch.
+ *
+ * Note that repeated starts/stops do NOT aggregate the elapsed time.
+ *
+ * @param stopwatch Stopwatch to start.
+ */
+static inline void stopwatch_start(stopwatch_t *stopwatch)
+{
+	getuptime(&stopwatch->start);
+}
+
+/** Stop the stopwatch.
+ *
+ * Note that repeated starts/stops do NOT aggregate the elapsed time.
+ *
+ * @param stopwatch Stopwatch to stop.
+ */
+static inline void stopwatch_stop(stopwatch_t *stopwatch)
+{
+	getuptime(&stopwatch->end);
+}
+
+/** Get elapsed time in nanoseconds.
+ *
+ * @param stopwatch Stopwatch to read from.
+ * @return Elapsed time in nanoseconds.
+ */
+static inline nsec_t stopwatch_get_nanos(stopwatch_t *stopwatch)
+{
+	return ts_sub_diff(&stopwatch->end, &stopwatch->start);
+}
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/types/casting.h
===================================================================
--- uspace/lib/c/include/types/casting.h	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/lib/c/include/types/casting.h	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 Vojtech Horky
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_TYPES_CASTING_H_
+#define LIBC_TYPES_CASTING_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+
+/**
+ * Checks that the size_t value can be casted to int without loss of data.
+ *
+ * @param val Value to test.
+ * @return Whether it is safe to typecast value to int.
+ */
+static inline bool can_cast_size_t_to_int(size_t val)
+{
+	unsigned int as_uint = (unsigned int) val;
+	return !((as_uint != val) || (((int) as_uint) < 0));
+}
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/test/casting.c
===================================================================
--- uspace/lib/c/test/casting.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/lib/c/test/casting.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Vojtech Horky
+ * 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 <pcut/pcut.h>
+#include <limits.h>
+#include <types/casting.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(casting);
+
+/*
+ * Following tests checks functionality of can_cast_size_t_to_int.
+ */
+
+PCUT_TEST(size_t_to_int_with_small)
+{
+	PCUT_ASSERT_TRUE(can_cast_size_t_to_int(0));
+	PCUT_ASSERT_TRUE(can_cast_size_t_to_int(128));
+}
+
+PCUT_TEST(size_t_to_int_with_biggest_int)
+{
+	PCUT_ASSERT_TRUE(can_cast_size_t_to_int(INT_MAX));
+}
+
+PCUT_TEST(size_t_to_int_with_biggest_size_t)
+{
+	PCUT_ASSERT_FALSE(can_cast_size_t_to_int(SIZE_MAX));
+}
+
+PCUT_EXPORT(casting);
Index: uspace/lib/c/test/main.c
===================================================================
--- uspace/lib/c/test/main.c	(revision 182487c6289bc890713fc7ccbab8a4ae126b05b7)
+++ uspace/lib/c/test/main.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -32,4 +32,5 @@
 PCUT_INIT;
 
+PCUT_IMPORT(casting);
 PCUT_IMPORT(circ_buf);
 PCUT_IMPORT(fibril_timer);
@@ -37,4 +38,5 @@
 PCUT_IMPORT(mem);
 PCUT_IMPORT(odict);
+PCUT_IMPORT(perf);
 PCUT_IMPORT(perm);
 PCUT_IMPORT(qsort);
Index: uspace/lib/c/test/perf.c
===================================================================
--- uspace/lib/c/test/perf.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
+++ uspace/lib/c/test/perf.c	(revision 3fea75289c10d725fe81e0e0e160535d3f80e6f6)
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2018 Vojtech Horky
+ * 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 <pcut/pcut.h>
+#include <fibril.h>
+#include <perf.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(perf);
+
+/* Checks that initialization zeroes out all entries. */
+PCUT_TEST(zero_diff)
+{
+	stopwatch_t sw;
+	stopwatch_init(&sw);
+	PCUT_ASSERT_INT_EQUALS(0, (int) stopwatch_get_nanos(&sw));
+}
+
+/* Checks that initialization zeroes out all entries. */
+PCUT_TEST(zero_diff_static)
+{
+	stopwatch_t sw = STOPWATCH_INITIALIZE_STATIC;
+	PCUT_ASSERT_INT_EQUALS(0, (int) stopwatch_get_nanos(&sw));
+}
+
+/* Checks that measuring 1s sleep does not give completely invalid results. */
+PCUT_TEST(stopwatch_smokes)
+{
+	stopwatch_t sw = STOPWATCH_INITIALIZE_STATIC;
+	stopwatch_start(&sw);
+	fibril_sleep(1);
+	stopwatch_stop(&sw);
+	nsec_t diff_nanos = stopwatch_get_nanos(&sw);
+	PCUT_ASSERT_TRUE(diff_nanos > MSEC2NSEC(500));
+	PCUT_ASSERT_TRUE(diff_nanos < SEC2NSEC(5));
+}
+
+/* Checks that setting time works for small values. */
+PCUT_TEST(stopwatch_emulation_works_small)
+{
+	stopwatch_t sw = STOPWATCH_INITIALIZE_STATIC;
+	stopwatch_set_nanos(&sw, 42);
+	PCUT_ASSERT_INT_EQUALS(42, (int) stopwatch_get_nanos(&sw));
+}
+
+/* Checks that setting time works for big values too. */
+PCUT_TEST(stopwatch_emulation_works_big)
+{
+	stopwatch_t sw = STOPWATCH_INITIALIZE_STATIC;
+	stopwatch_set_nanos(&sw, 4200000000021);
+	PCUT_ASSERT_EQUALS(4200000000021, (long long) stopwatch_get_nanos(&sw));
+}
+
+PCUT_EXPORT(perf);
