include("/sys/src/libc/port/pool.acid");

aggr Byte {
	'b' 0 byte;
};

defn
byteat(addr)
{
	local x;
	complex Byte addr;
	x = addr.byte;
	return x\d;
}

defn
B2T(addr) {
	complex Bhdr addr;
	addr = addr+addr.size-sizeofBtail;
	complex Btail addr;
	return addr;
}

defn
B2D(addr) {
	local x;
	x = addr+sizeofBhdr;
	return x\X;
}

defn
D2B(addr) {
	local x;
	x = addr-sizeofBhdr;
	complex Bhdr x;
	return x;
}

defn
B2NB(addr) {
	complex Bhdr addr;
	addr = addr+addr.size;
	complex Bhdr addr;
	return addr;
}

defn
A2TB(addr) {
	local b;
	complex Arena addr;
	b = addr+addr.asize-sizeofBhdr;
	complex Bhdr b;
	return b;
}

defn
A2B(addr) {
	return B2NB(addr);
}

defn
B2PT(addr) {
	complex Bhdr addr;
	addr = addr-sizeofBtail;
	complex Btail addr;
	return addr;
}

defn
SHORT(addr) {
	local hi, lo;

	hi = byteat(addr);
	lo = byteat(addr+1);
	return lo+hi*256;
}

defn
Btail(addr) {
	complex Btail addr;
	print("	magic0	", addr.magic0, "\n");
	print("	datadiff	", SHORT(addr.datasize), "\n");
	print("	magic1	", addr.magic1, "\n");
	print("	size	", addr.size\X, "\n");
	print("	hdr	", addr+sizeofBtail-addr.size\X, "\n");
};

defn
Tail(addr)
{
	print("	", B2T(addr)\X, "\n");
	Btail(B2T(addr));
}

defn
Magic(m)
{
	if m == FREE_MAGIC then
		return "free";
	if m == ARENA_MAGIC then
		return "arena";
	if m == UNKEMPT_MAGIC then
		return "unkempt";
	if m == KEMPT_MAGIC then
		return "kempt";
	if m == ARENATAIL_MAGIC then
		return "arenatail";
	if m == DEAD_MAGIC then
		return "dead";
	return "unknown magic";
}

defn
Block(addr)
{
	complex Bhdr addr;
	print("	", Magic(addr.magic), "\n");
	print("	data ", B2D(addr), "\n");
	print("	datasize ", getdsize(addr), "\n");
	Bhdr(addr);
	Tail(addr);
}

defn
getdsize(addr)
{
	complex Bhdr addr;
	local x;

	x = addr.size\d;
	x = x-SHORT(B2T(addr).datasize);
	return x\d;
}

defn
datamagic(x)
{
	x = x%4;
	if x == 0 then return 0xFE;
	if x == 1 then return 0xF1;
	if x == 2 then return 0xF0;
	if x == 3 then return 0xFA;
}

defn
checkblock(addr)
{
	local badmagic, datamagic, a, b, t, q, n, dsize, taddr, checked;
	complex Bhdr addr;
	taddr = B2T(addr);
	complex Btail taddr;

	if addr.magic == FREE_MAGIC || addr.magic == UNKEMPT_MAGIC then {
		if taddr.magic0 != TAIL_MAGIC0 || taddr.magic1 != TAIL_MAGIC1 then
			print(addr\X, " corrupt tail magic\n");
		if taddr.size != addr.size then
			print(addr\X, " corrupt tail header pointer\n");
	}

	if addr.magic == ARENA_MAGIC then {
		taddr = A2TB(addr);
		if taddr.magic != ARENATAIL_MAGIC then
			print(addr\X, " arena with bad tail block\n");
		else
			addr = taddr;
	}

	if addr.magic == ARENATAIL_MAGIC then {
		if addr.size != 0 then
			print(addr\X, " bad size in arena tail\n");
	}

	if addr.magic == KEMPT_MAGIC then {
		a = addr;
		complex Alloc a;
		if a.size > 1024*1024*1024 then 
			print(addr\X, " block ridiculously large\n");
		t = B2T(addr);
		if t.magic0 != TAIL_MAGIC0 || t.magic1 != TAIL_MAGIC1 then
			print(addr\X, " bad tail magic\n");
		if t.size != addr.size then
			print(addr\X, " bad tail pointer\n");
		dsize = getdsize(a);
		if dsize > a.size then
			print(addr\X, " too much data in block\n");
		q = B2D(a)\X+dsize;
		n = 4;
		if q+4 > t then
			n = t-q;
		badmagic = 0;
		loop 0,n-1 do {
			if byteat(q) != datamagic(q) then {
				badmagic=1;
			}
			q = q+1;
		}
		if badmagic then
			print(addr\X, " size ", dsize, " user has overwritten boundary\n");
	}
}

defn
checkarena(arena)
{
	local atail, b;

	atail = A2TB(arena);
	complex Bhdr arena;
	b = arena;
	while b.magic != ARENATAIL_MAGIC && b < atail do {
		checkblock(b);
		if B2NB(b) == b then {
			print("B2NB(", b\X, ") = b\n");
			b = atail;	// end loop
		}
		b = B2NB(b);
	}

	checkblock(b);
	if b != atail then
		print("found wrong tail to arena ", arena\X, "\n");
}

defn
checkpool(p)
{
	complex Pool p;
	local a;
	a = p.arenalist;

	while a != 0 do {
		complex Arena a;
		checkarena(a);
		a = a.down;
	}
}

defn
gendumptree(f, in, s)
{
	complex Free f;

	loop 1,in do {print(" ");}
	print(s, " size ", f.size\D, " left ", f.left\X, " right ", f.right\X, "\n");
	if f.left != 0 && f.left < 0x7FFFFFFF then
		gendumptree(f.left, in+1, "l");
	if f.right != 0 && f.right < 0x7FFFFFFF then
		gendumptree(f.right, in+1, "r");
}

defn
dumptree(f)
{
	gendumptree(f, 0, "*");
}

defn
poolwhopointsat(p, addr)
{
	complex Pool p;
	local a;

	a = p.arenalist;
	while a != 0 do {
		complex Arena a;
		arenawhopointsat(a, addr);
		a = a.down;
	}
}

defn 
arenawhopointsat(arena, addr)
{
	local atail, b;

	atail = A2TB(arena);
	complex Bhdr arena;
	b = arena;
	while b < atail do {
		if *b == addr then 
			print(b\X, "\n");
		b = b+4;
	}
}

defn
whopointsat(addr)
{
	poolwhopointsat(*mainmem, addr);
}

defn
blockhdr(addr)
{
	addr = addr & ~3;

	while *addr != FREE_MAGIC 
		&& *addr !=  ARENA_MAGIC
		&& *addr != UNKEMPT_MAGIC
		&& *addr != KEMPT_MAGIC
		&& *addr != ARENATAIL_MAGIC
	do
		addr = addr-4;
	return addr;
}

