Initial pull from http://www.etalabs.net/releases/
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ee31086
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+
+SRCS = $(sort $(wildcard *.c))
+OBJS = $(SRCS:.c=.o)
+
+CFLAGS = -Os
+LDFLAGS = -static
+LIBS = -lpthread -lrt -lpthread
+
+
+all: libc-bench
+
+clean:
+	rm -f $(OBJS) libc-bench
+
+test: all
+	./libc-bench
+
+libc-bench: $(OBJS)
+	$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+%.o: %.c
+	$(CC) $(CFLAGS) -c -o $@ $<
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..d5f55cd
--- /dev/null
+++ b/main.c
@@ -0,0 +1,96 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+void print_stats(struct timespec tv0)
+{
+	FILE *f;
+	char buf[256];
+	struct timespec tv;
+	int maj, min, in_heap=0;
+	unsigned long l;
+	size_t vm_size=0, vm_rss=0, vm_priv_dirty=0;
+
+	clock_gettime(CLOCK_REALTIME, &tv);
+	tv.tv_sec -= tv0.tv_sec;
+	if ((tv.tv_nsec -= tv0.tv_nsec) < 0) {
+		tv.tv_nsec += 1000000000;
+		tv.tv_sec--;
+	}
+
+	f = fopen("/proc/self/smaps", "rb");
+	if (f) while (fgets(buf, sizeof buf, f)) {
+		if (sscanf(buf, "%*lx-%*lx %*s %*lx %x:%x %*lu %*s", &maj, &min)==2)
+			in_heap = (!maj && !min && !strstr(buf, "---p") && (strstr(buf, "[heap]") || !strchr(buf, '[')));
+		if (in_heap) {
+			if (sscanf(buf, "Size: %lu", &l)==1) vm_size += l;
+			else if (sscanf(buf, "Rss: %lu", &l)==1) vm_rss += l;
+			else if (sscanf(buf, "Private_Dirty: %lu", &l)==1) vm_priv_dirty += l;
+		}
+	}
+	if (f) fclose(f);
+	printf("  time: %ld.%.9ld, virt: %zu, res: %zu, dirty: %zu\n\n",
+		(long)tv.tv_sec, (long)tv.tv_nsec,
+		vm_size, vm_rss, vm_priv_dirty);
+}
+
+int run_bench(const char *label, size_t (*bench)(void *), void *params)
+{
+	struct timespec tv0;
+	pid_t p = fork();
+	if (p) {
+		int status;
+		wait(&status);
+		return status;
+	}
+
+	puts(label);
+	clock_gettime(CLOCK_REALTIME, &tv0);
+	bench(params);
+	print_stats(tv0);
+	exit(0);
+}
+
+#define RUN(a, b) \
+	extern size_t (a)(void *); \
+	run_bench(#a " (" #b ")", (a), (b))
+
+int main()
+{
+	RUN(b_malloc_sparse, 0);
+	RUN(b_malloc_bubble, 0);
+	RUN(b_malloc_tiny1, 0);
+	RUN(b_malloc_tiny2, 0);
+	RUN(b_malloc_big1, 0);
+	RUN(b_malloc_big2, 0);
+	RUN(b_malloc_thread_stress, 0);
+	RUN(b_malloc_thread_local, 0);
+
+	RUN(b_string_strstr, "abcdefghijklmnopqrstuvwxyz");
+	RUN(b_string_strstr, "azbycxdwevfugthsirjqkplomn");
+	RUN(b_string_strstr, "aaaaaaaaaaaaaacccccccccccc");
+	RUN(b_string_strstr, "aaaaaaaaaaaaaaaaaaaaaaaaac");
+	RUN(b_string_strstr, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac");
+	RUN(b_string_memset, 0);
+	RUN(b_string_strchr, 0);
+	RUN(b_string_strlen, 0);
+
+	RUN(b_pthread_createjoin_serial1, 0);
+	RUN(b_pthread_createjoin_serial2, 0);
+	RUN(b_pthread_create_serial1, 0);
+	RUN(b_pthread_uselesslock, 0);
+
+	RUN(b_utf8_bigbuf, 0);
+	RUN(b_utf8_onebyone, 0);
+
+	RUN(b_stdio_putcgetc, 0);
+	RUN(b_stdio_putcgetc_unlocked, 0);
+
+	RUN(b_regex_compile, "(a|b|c)*d*b");
+	RUN(b_regex_search, "(a|b|c)*d*b");
+	RUN(b_regex_search, "a{25}b");
+}
+
diff --git a/malloc.c b/malloc.c
new file mode 100644
index 0000000..79cc88b
--- /dev/null
+++ b/malloc.c
@@ -0,0 +1,154 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+size_t b_malloc_sparse(void *dummy)
+{
+	void *p[10000];
+	size_t i;
+	for (i=0; i<sizeof p/sizeof *p; i++) {
+		p[i] = malloc(4000);
+		memset(p[i], 0, 4000);
+	}
+	for (i=0; i<sizeof p/sizeof *p; i++)
+		if (i%150) free(p[i]);
+	return 0;
+}
+
+size_t b_malloc_bubble(void *dummy)
+{
+	void *p[10000];
+	size_t i;
+	for (i=0; i<sizeof p/sizeof *p; i++) {
+		p[i] = malloc(4000);
+		memset(p[i], 0, 4000);
+	}
+	for (i=0; i<sizeof p/sizeof *p-1; i++)
+		free(p[i]);
+	return 0;
+}
+
+size_t b_malloc_tiny1(void *dummy)
+{
+	void *p[10000];
+	size_t i;
+	for (i=0; i<sizeof p/sizeof *p; i++) {
+		p[i] = malloc((i%4+1)*16);
+	}
+	for (i=0; i<sizeof p/sizeof *p; i++) {
+		free(p[i]);
+	}
+	return 0;
+}
+
+size_t b_malloc_tiny2(void *dummy)
+{
+	void *p[10000];
+	size_t i;
+	for (i=0; i<sizeof p/sizeof *p; i++) {
+		p[i] = malloc((i%4+1)*16);
+	}
+	for (i=1; i; i = (i+57)%(sizeof p/sizeof *p))
+		free(p[i]);
+	return 0;
+}
+
+size_t b_malloc_big1(void *dummy)
+{
+	void *p[2000];
+	size_t i;
+	for (i=0; i<sizeof p/sizeof *p; i++) {
+		p[i] = malloc((i%4+1)*16384);
+	}
+	for (i=0; i<sizeof p/sizeof *p; i++) {
+		free(p[i]);
+	}
+	return 0;
+}
+
+size_t b_malloc_big2(void *dummy)
+{
+	void *p[2000];
+	size_t i;
+	for (i=0; i<sizeof p/sizeof *p; i++) {
+		p[i] = malloc((i%4+1)*16384);
+	}
+	for (i=1; i; i = (i+57)%(sizeof p/sizeof *p))
+		free(p[i]);
+	return 0;
+}
+
+
+#define LOOPS 100000
+#define SH_COUNT 300
+#define PV_COUNT 300
+#define MAX_SZ 500
+#define DEF_SZ 40
+
+struct foo {
+	pthread_mutex_t lock;
+	void *mem;
+};
+
+static unsigned rng(unsigned *r)
+{
+	return *r = *r * 1103515245 + 12345;
+}
+
+
+static void *stress(void *arg)
+{
+	struct foo *foo = arg;
+	unsigned r = (unsigned)pthread_self();
+	int i, j;
+	size_t sz;
+	void *p;
+
+	for (i=0; i<LOOPS; i++) {
+		j = rng(&r) % SH_COUNT;
+		sz = rng(&r) % MAX_SZ;
+		pthread_mutex_lock(&foo[j].lock);
+		p = foo[j].mem;
+		foo[j].mem = 0;
+		pthread_mutex_unlock(&foo[j].lock);
+		free(p);
+		if (!p) {
+			p = malloc(sz);
+			pthread_mutex_lock(&foo[j].lock);
+			if (!foo[j].mem) foo[j].mem = p, p = 0;
+			pthread_mutex_unlock(&foo[j].lock);
+			free(p);
+		}
+	}
+	return 0;
+}
+
+size_t b_malloc_thread_stress(void *dummy)
+{
+	struct foo foo[SH_COUNT] = {0};
+	pthread_t td1, td2;
+	void *res;
+	int i;
+
+	pthread_create(&td1, 0, stress, foo);
+	pthread_create(&td2, 0, stress, foo);
+	pthread_join(td1, &res);
+	pthread_join(td2, &res);
+	return 0;
+}
+
+size_t b_malloc_thread_local(void *dummy)
+{
+	struct foo foo1[SH_COUNT] = {0};
+	struct foo foo2[SH_COUNT] = {0};
+	pthread_t td1, td2;
+	void *res;
+	int i;
+
+	pthread_create(&td1, 0, stress, foo1);
+	pthread_create(&td2, 0, stress, foo2);
+	pthread_join(td1, &res);
+	pthread_join(td2, &res);
+	return 0;
+}
diff --git a/pthread.c b/pthread.c
new file mode 100644
index 0000000..c160314
--- /dev/null
+++ b/pthread.c
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+void *emptyfunc(void *dummy)
+{
+	return 0;
+}
+
+size_t b_pthread_createjoin_serial1(void *dummy)
+{
+	size_t i;
+	pthread_t td;
+	for (i=0; i<2500; i++) {
+		pthread_create(&td, 0, emptyfunc, 0);
+		pthread_join(td, &dummy);
+	}
+	return 0;
+}
+
+size_t b_pthread_createjoin_serial2(void *dummy)
+{
+	size_t i, j;
+	pthread_t td[50];
+	for (j=0; j<50; j++) {
+		for (i=0; i<sizeof td/sizeof *td; i++)
+			pthread_create(td+i, 0, emptyfunc, 0);
+		for (i=0; i<sizeof td/sizeof *td; i++)
+			pthread_join(td[i], &dummy);
+	}
+	return 0;
+}
+
+size_t b_pthread_create_serial1(void *dummy)
+{
+	size_t i;
+	pthread_attr_t attr;
+	pthread_t td;
+	pthread_attr_init(&attr);
+	pthread_attr_setstacksize(&attr, 16384);
+	for (i=0; i<2500; i++)
+		pthread_create(&td, &attr, emptyfunc, 0);
+	return 0;
+}
+
+void *lockunlock(void *mut)
+{
+	size_t i;
+	for (i=0; i<1000000; i++) {
+		pthread_mutex_lock(mut);
+		pthread_mutex_unlock(mut);
+	}
+	return 0;
+}
+
+size_t b_pthread_uselesslock(void *dummy)
+{
+	pthread_t td;
+	pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
+	pthread_create(&td, 0, lockunlock, &mut);
+	pthread_join(td, &dummy);
+	return 0;
+}
diff --git a/regex.c b/regex.c
new file mode 100644
index 0000000..46cfe37
--- /dev/null
+++ b/regex.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <regex.h>
+#include <locale.h>
+
+size_t b_regex_compile(void *s)
+{
+	regex_t re;
+	size_t i;
+	setlocale(LC_CTYPE, "");
+	for (i=0; i<1000; i++) {
+		regcomp(&re, s, REG_EXTENDED);
+		regfree(&re);
+	}
+}
+
+size_t b_regex_search(void *s)
+{
+	char buf[260000];
+	regex_t re;
+	size_t i;
+	setlocale(LC_CTYPE, "");
+	memset(buf, 'a', sizeof(buf)-2);
+	buf[sizeof buf - 2] = 'b';
+	buf[sizeof buf - 1] = 0;
+	regcomp(&re, s, REG_EXTENDED);
+	regexec(&re, buf, 0, 0, 0);
+	regfree(&re);
+}
diff --git a/stdio.c b/stdio.c
new file mode 100644
index 0000000..0d41f86
--- /dev/null
+++ b/stdio.c
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+size_t b_stdio_putcgetc(void *dummy)
+{
+	FILE *f = tmpfile();
+	size_t i;
+	size_t cs;
+
+	for (i=0; i<5000000; i++)
+		putc('x', f);
+	fseeko(f, 0, SEEK_SET);
+	for (i=0; i<5000000; i++)
+		cs += getc(f);
+	fclose(f);
+
+	return cs;
+}
+
+size_t b_stdio_putcgetc_unlocked(void *dummy)
+{
+	FILE *f = tmpfile();
+	size_t i;
+	size_t cs;
+
+	for (i=0; i<5000000; i++)
+		putc_unlocked('x', f);
+	fseeko(f, 0, SEEK_SET);
+	for (i=0; i<5000000; i++)
+		cs += getc_unlocked(f);
+	fclose(f);
+
+	return cs;
+}
diff --git a/string.c b/string.c
new file mode 100644
index 0000000..158b828
--- /dev/null
+++ b/string.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define BUFLEN 500000
+
+size_t b_string_strstr(void *dummy)
+{
+	const char *needle = dummy;
+	size_t l = strlen(needle);
+	size_t i;
+	size_t cnt = 10000;
+	size_t cs = 0;
+	char *haystack = malloc(l * cnt + 1);
+	for (i=0; i<cnt-1; i++) {
+		memcpy(haystack + l*i, needle, l);
+		haystack[l*i+l-1] ^= 1;
+	}
+	memcpy(haystack + l*i, needle, l+1);
+	for (i=0; i<50; i++) {
+		haystack[0]^=1;
+		cs += (int)strstr(haystack, needle);
+	}
+	free(haystack);
+	return cs;
+}
+
+size_t b_string_memset(void *dummy)
+{
+	char *buf = malloc(BUFLEN);
+	size_t i;
+	for (i=0; i<100; i++)
+		memset(buf+i, i, BUFLEN-i);
+	free(buf);
+	return 0;
+}
+
+size_t b_string_strchr(void *dummy)
+{
+	char *buf = malloc(BUFLEN);
+	size_t i;
+	size_t cs;
+	memset(buf, 'a', BUFLEN);
+	buf[BUFLEN-1] = 0;
+	buf[BUFLEN-2] = 'b';
+	for (i=0; i<100; i++) {
+		buf[i] = '0'+i%8;
+		cs += (int)strchr(buf, 'b');
+	}
+	free(buf);
+	return cs;
+}
+
+size_t b_string_strlen(void *dummy)
+{
+	char *buf = malloc(BUFLEN);
+	size_t i;
+	size_t cs = 0;
+
+	memset(buf, 'a', BUFLEN-1);
+	buf[BUFLEN-1] = 0;
+	for (i=0; i<100; i++) {
+		buf[i] = '0'+i%8;
+		cs += strlen(buf);
+	}
+	free(buf);
+	return cs;
+}
diff --git a/utf8.c b/utf8.c
new file mode 100644
index 0000000..5a675ed
--- /dev/null
+++ b/utf8.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <locale.h>
+#include <langinfo.h>
+
+size_t b_utf8_bigbuf(void *dummy)
+{
+	char *buf;
+	wchar_t *wbuf;
+	size_t i, j, k, l;
+	size_t cs;
+
+	setlocale(LC_CTYPE, "C.UTF-8")
+	|| setlocale(LC_CTYPE, "en_US.UTF-8")
+	|| setlocale(LC_CTYPE, "en_GB.UTF-8")
+	|| setlocale(LC_CTYPE, "en.UTF-8")
+	|| setlocale(LC_CTYPE, "de_DE-8")
+	|| setlocale(LC_CTYPE, "fr_FR-8");
+	if (strcmp(nl_langinfo(CODESET), "UTF-8")) return -1;
+
+	buf = malloc(500000);
+	wbuf = malloc(500000*sizeof(wchar_t));
+	l = 0;
+	for (i=0xc3; i<0xe0; i++)
+		for (j=0x80; j<0xc0; j++)
+			buf[l++] = i, buf[l++] = j;
+	for (i=0xe1; i<0xed; i++)
+		for (j=0x80; j<0xc0; j++)
+			for (k=0x80; k<0xc0; k++)
+				buf[l++] = i, buf[l++] = j, buf[l++] = k;
+	for (i=0xf1; i<0xf4; i++)
+		for (j=0x80; j<0xc0; j++)
+			for (k=0x80; k<0xc0; k++)
+				buf[l++] = i, buf[l++] = j, buf[l++] = 0x80, buf[l++] = k;
+	buf[l++] = 0;
+	for (i=0; i<50; i++)
+		cs += mbstowcs(wbuf, buf, 500000);
+	free(wbuf);
+	free(buf);
+	return cs;
+}
+
+size_t b_utf8_onebyone(void *dummy)
+{
+	char *buf;
+	wchar_t wc;
+	size_t i, j, k, l;
+	size_t cs;
+	mbstate_t st = {0};
+
+	setlocale(LC_CTYPE, "C.UTF-8")
+	|| setlocale(LC_CTYPE, "en_US.UTF-8")
+	|| setlocale(LC_CTYPE, "en_GB.UTF-8")
+	|| setlocale(LC_CTYPE, "en.UTF-8")
+	|| setlocale(LC_CTYPE, "de_DE-8")
+	|| setlocale(LC_CTYPE, "fr_FR-8");
+	if (strcmp(nl_langinfo(CODESET), "UTF-8")) return -1;
+
+	buf = malloc(500000);
+	l = 0;
+	for (i=0xc3; i<0xe0; i++)
+		for (j=0x80; j<0xc0; j++)
+			buf[l++] = i, buf[l++] = j;
+	for (i=0xe1; i<0xed; i++)
+		for (j=0x80; j<0xc0; j++)
+			for (k=0x80; k<0xc0; k++)
+				buf[l++] = i, buf[l++] = j, buf[l++] = k;
+	for (i=0xf1; i<0xf4; i++)
+		for (j=0x80; j<0xc0; j++)
+			for (k=0x80; k<0xc0; k++)
+				buf[l++] = i, buf[l++] = j, buf[l++] = 0x80, buf[l++] = k;
+	buf[l++] = 0;
+	for (i=0; i<50; i++) {
+		for (j=0; buf[j]; j+=mbrtowc(&wc, buf+j, 4, &st));
+	}
+	free(buf);
+	return cs;
+}