/*
 * Copyright (C) 2006-2012 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include <sys/mman.h>
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char _end;

enum type { END, HEAP, MMAP, LIB, STACK, COUNT };

static void
test(void)
{
	int var;
	enum type t;

	for (t = 0; t < COUNT; t++) {
		switch (t) {
		case END:
			printf("(%p)\n", &_end);
			break;
		case HEAP:
			printf("(%p)\n", malloc(1));
			break;
		case MMAP:
			printf("(%p)\n", mmap(NULL, 4096, PROT_READ | PROT_WRITE,
						MAP_PRIVATE | MAP_ANONYMOUS,
						-1, 0));
			break;
		case STACK:
			printf("(%p)\n", &var);
			break;
		default:
			break;
		}
	}
}

static uintptr_t
valread(FILE *fp)
{
	char line[1024];

	if (! fgets(line, sizeof(line) - 1, fp)) {
		return -1;
	}
	line[sizeof(line) - 1] = '\0';

	assert(strchr(line, '('));

	return strtoul(strchr(line, '(') + 1, NULL, 0);
}

static void
eval(void)
{
	const char *name[COUNT] = {
		[END] = "end",
		[HEAP] = "heap",
		[MMAP] = "mmap",
		[LIB] = "lib",
		[STACK] = "stack",
		[LIB] = "lib",
	};
	uintptr_t min[COUNT], max[COUNT];
	uintptr_t gap[COUNT];
	unsigned int count;
	enum type t;

	for (t = 0; t < COUNT; t++) {
		min[t] = -1; max[t] = 0;
	}

	for (count = 0; count < 10000; count++) {
		FILE *fp;
		uintptr_t curr;

		/* Get addresses of end/heap/mmap/stack. */
		fp = popen("./vm-test -s", "r");
		assert(fp);

		for (t = 0; t < COUNT; t++) {
			if (t == LIB) continue;

			curr = valread(fp);
			if (curr < min[t]) min[t] = curr;
			if (max[t] < curr) max[t] = curr;
		}

		pclose(fp);

		/* Get addresses of mapped libraries. */
		fp = popen("ldd ./vm-test", "r");

		for (;;) {
			curr = valread(fp);
			if (curr == (uintptr_t) -1) {
				break;
			}
			if (curr < min[LIB]) min[LIB] = curr;
			if (max[LIB] < curr) max[LIB] = curr;
		}

		pclose(fp);
	}

	for (t = 0; t < COUNT; t++) {
		uintptr_t next;
		enum type t2;

		next = max[STACK];
		for (t2 = 0; t2 < COUNT; t2++) {
			if (t2 == t) continue;

			if (min[t] <= min[t2]
			 && min[t2] <= max[t]) {
				/* Regions overlap. */
				next = max[t];
			} else if (max[t] < min[t2]
				&& min[t2] < next) {
				next = min[t2];
			}
		}
		gap[t] = next - max[t];
	}

	for (t = 0; t < COUNT; t++) {
		printf("0x%lx <= %s <= 0x%lx (gap 0x%lx)\n",
				(unsigned long) min[t],
				name[t],
				(unsigned long) max[t],
				(unsigned long) gap[t]);
	}
}

int
main(int argc, char **argv)
{
	if (1 < argc) {
		test();
	} else {
		eval();
	}

	return 0;
}
