/* cc -Wall -g -o p2 p2.c -ljemalloc -lpthread */

#include <err.h>
#include <malloc.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sysctl.h>

static void
memstats(FILE *fp)
{
	static int page_size = 0;
	int name[CTL_MAXNAME];
	size_t sz, allocated, active, metadata, resident, mapped;
	u_int namelen;
	uint64_t epoch;
	struct kinfo_proc2 p;

	if (page_size == 0) {
		sz = sizeof(page_size);
		namelen = 0;
		name[namelen++] = CTL_HW;
		name[namelen++] = HW_PAGESIZE;
		if (sysctl(&name[0], namelen, &page_size, &sz, NULL, 0) != 0)
			err(1, "sysctl pagesize");
		fprintf(fp, "%8s%8s%8s%8s%8s%8s%8s%8s%8s\n",
		    "dsize", "ssize", "msize", "rssize",
		    "alloca", "active", "meta", "resid", "mapped");
	}

	epoch = 1;
	sz = sizeof(epoch);
	if (mallctl("epoch", &epoch, &sz, &epoch, sz) != 0)
		err(1, "mallctl epoch");

#define MC(n, v) do { \
	sz = sizeof((v)); \
	if (mallctl((n), &(v), &sz, NULL, 0) != 0) \
		err(1, "mallctl " n); \
} while (0)

	MC("stats.allocated", allocated);
	MC("stats.active", active);
	MC("stats.metadata", metadata);
	MC("stats.resident", resident);
	MC("stats.mapped", mapped);

#undef MC

	sz = sizeof(p);
	namelen = 0;
	name[namelen++] = CTL_KERN;
	name[namelen++] = KERN_PROC2;
	name[namelen++] = KERN_PROC_PID;
	name[namelen++] = getpid();
	name[namelen++] = sz;
	name[namelen++] = 1;
	if (sysctl(&name[0], namelen, &p, &sz, NULL, 0) != 0)
		err(1, "sysctl proc2");

#define B2KB(v) (((v) + 1023) / 1024)
#define P2KB(f) (((int64_t)p.f * page_size + 1023) / 1024)
	fprintf(fp, "%8" PRId64 "%8" PRId64 "%8" PRId64 "%8" PRId64 "%8zu%8zu%8zu%8zu%8zu\n",
	    P2KB(p_vm_dsize), P2KB(p_vm_ssize), P2KB(p_vm_msize),  P2KB(p_vm_rssize),
	    B2KB(allocated), B2KB(active), B2KB(metadata), B2KB(resident), B2KB(mapped));
#undef B2KB
#undef P2KB
	fflush(fp);
}

static void *
worker(void *arg)
{
	char *buf;

	buf = malloc(8192);
	buf[0] = 2;

	usleep(10000);

	free(buf);

	return NULL;
}

int
main(int argc, char **argv)
{
	unsigned i, n;
	pthread_t thread;

	if (argv[1] == NULL || (n = atoi(argv[1])) <= 1)
		n = UINT_MAX;
	for (i = 0; i < n; i++) {
		if ((i % 100) == 0)
			memstats(stdout);
		if (pthread_create(&thread, NULL, worker, NULL) != 0)
			err(1, "pthread_create");
		if (pthread_detach(thread) != 0)
			err(1, "pthread_detach");
		usleep(10000);
	}
	memstats(stdout);

	return 0;
}
