// trace user malloc pool - trace malloc, realloc, and free calls
// if trumpsbrk is set, we trace sbrkalloc and sbrkmerge too.

_stoprunning = 0;
trumphexaddrs = 0;
trumpsbrk = 0;

defn stopped(pid) {
	local l;
	local pc;
	pc = *PC;
	if notes then {
		if (notes[0]!="sys: breakpoint") then
		{
			print(pid,": ",reason(*TRAP),"\t");
			print(fmt(pc,97),"\t",fmt(pc,105),"\n");
			print("Notes pending:\n");
			l = notes;
			while l do
			{
				print("\t",head l,"\n");
				l = tail l;
			}
			_stoprunning = 1;
		}
	}
}

defn printstack() {
	local frame, stk, pcs, lst, x;

	pcs = {*PC};
	stk = strace(*PC,*SP,0);
	while stk do {
		pcs = append pcs, stk[0][1];
		stk = tail stk;
	}

	print(" #");
	lst = pcs;
	while lst do {
		if trumphexaddrs != 0 then
			x = lst[0]\X;
		else
			x = lst[0]\a;
		print(" src(", x, ");");
		lst = tail lst;
	}
	print("\n");
}

defn setuptrump() {
	mallocPC = malloc;
	malloczPC = mallocz;
	freePC = free;
	reallocPC = realloc;
	sbrkallocPC = sbrkalloc;
	sbrkmergePC = sbrkmerge;

	// linker might fill delay slot with first instruction
	if objtype == "mips" then {
		mallocPC = mallocPC+4;
		malloczPC = malloczPC+4;
		freePC = freePC+4;
		reallocPC = reallocPC+4;
		sbrkallocPC = sbrkallocPC+4;
		sbrkmergePC = sbrkmergePC+4;
	}

	bpset(mallocPC);
	bpset(malloczPC);
	bpset(freePC);
	bpset(reallocPC);
	if trumpsbrk then {
		bpset(sbrkallocPC);
		bpset(sbrkmergePC);
	}
}

defn cleantrump() {
	stop(pid);

	bpdel(mallocPC);
	bpdel(malloczPC);
	bpdel(freePC);
	bpdel(reallocPC);
	bpdel(sbrkallocPC);
	bpdel(sbrkmergePC);
}

defn trumpflush() {
	stop(pid);		// already stopped, but flushes output
}

defn new() {
	bplist = {};
	newproc(progargs);
	bpset(follow(main)[0]);
	cont();
	bpdel(*PC);
	// clear the hang bit, which is left set by newproc, so programs we fork/exec don't hang
	printto("/proc/"+itoa(pid)+"/ctl", "nohang");
}

defn trumpfninfo() {
	local arg0, arg1, stk, retpc, params;

	stk = strace(*PC, *SP, 0);
	retpc = stk[0][1];
	params = stk[0][2];
	arg0 = params[0][1];
	arg1 = 0;
	if tail params != {} then
		arg1 = params[1][1];
	return {arg0, arg1, retpc};
}

defn trumpretval() {
	if objtype=="386" then
		return *AX;
	if objtype=="mips" then
		return *R1;
	if objtype=="power" || objtype=="alpha" then
		return *R0;
}

defn trump() {
	local arg0, arg1, pc, ret, x;

	stop(pid);
	_stoprunning = 0;
	setuptrump();
	while !_stoprunning do {
		cont();
		if notes[0]!="sys: breakpoint" then {
			cleantrump();
			return {};
		}

		pc = *PC;
		x = trumpfninfo();
		arg0 = x[0];
		if pc == reallocPC || pc == sbrkmergePC then 
			arg1 = x[1];
		bpset(x[2]);
		cont();
		bpdel(x[2]);
		ret = trumpretval();
		if pc == mallocPC then
			print(ret\X, " malloc ", arg0\D);
		if pc == malloczPC then
			print(ret\X, " mallocz ", arg0\D);
		if pc == freePC then
			print(arg0\X, " free");
		if pc == reallocPC then
			print(ret\X, " realloc ", arg0\X, " ", arg1\D);
		if pc == sbrkallocPC then
			print(ret\X, " sbrkalloc ", arg0\D);
		if pc == sbrkmergePC then
			print("sbrkmerge ", arg0\X, " ", arg1\X, " = ", ret\D);
		printstack();
		trumpflush();
	}
}

defn untrump() {
	cleantrump();
	start(pid);
}

print(acidfile);
