/*
 * functions for running the debugged process
 */

#include "defs.h"
#include "fns.h"


int child;
int msgfd = -1;
int notefd = -1;
int pcspid = -1;
int pcsactive = 0;

void
setpcs(void)
{
	char buf[128];

	if(pid && pid != pcspid){
		if(msgfd >= 0){
			close(msgfd);
			msgfd = -1;
		}
		if(notefd >= 0){
			close(notefd);
			notefd = -1;
		}
		pcspid = -1;
		sprint(buf, "/proc/%d/ctl", pid);
		msgfd = open(buf, OWRITE);
		if(msgfd < 0)
			error("can't open control file");
		sprint(buf, "/proc/%d/note", pid);
		notefd = open(buf, ORDWR);
		if(notefd < 0)
			error("can't open note file");
		pcspid = pid;
	}
}

void
msgpcs(char *msg)
{
	char err[ERRMAX];

	setpcs();
	if(write(msgfd, msg, strlen(msg)) < 0 && !ending){
		errstr(err, sizeof err);
		if(strcmp(err, "interrupted") != 0)
			endpcs();
		errors("can't write control file", err);
	}
}

/*
 * empty the note buffer and toss pending breakpoint notes
 */
void
unloadnote(void)
{
	char err[ERRMAX];

	setpcs();
	for(; nnote<NNOTE; nnote++){
		switch(read(notefd, note[nnote], sizeof note[nnote])){
		case -1:
			errstr(err, sizeof err);
			if(strcmp(err, "interrupted") != 0)
				endpcs();
			errors("can't read note file", err);
		case 0:
			return;
		}
		note[nnote][ERRMAX-1] = '\0';
		if(strncmp(note[nnote], "sys: breakpoint", 15) == 0)
			--nnote;
	}
}

/*
 * reload the note buffer
 */
void
loadnote(void)
{
	int i;
	char err[ERRMAX];

	setpcs();
	for(i=0; i<nnote; i++){
		if(write(notefd, note[i], strlen(note[i])) < 0){
			errstr(err, sizeof err);
			if(strcmp(err, "interrupted") != 0)
				endpcs();
			errors("can't write note file", err);
		}
	}
	nnote = 0;
}

void
notes(void)
{
	int n;

	if(nnote == 0)
		return;
	dprint("notes:\n");
	for(n=0; n<nnote; n++)
		dprint("%d:\t%s\n", n, note[n]);
}

void
killpcs(void)
{
	msgpcs("kill");
}

void
grab(void)
{
	flush();
	msgpcs("stop");
	bpwait();
}

void
ungrab(void)
{
	msgpcs("start");
}

void
doexec(void)
{
	char *argl[MAXARG];
	char args[LINSIZ];
	char *p;
	char **ap;
	char *thisarg;

	ap = argl;
	p = args;
	*ap++ = symfil;
	for (rdc(); lastc != EOR;) {
		thisarg = p;
		if (lastc == '<' || lastc == '>') {
			*p++ = lastc;
			rdc();
		}
		while (lastc != EOR && lastc != SPC && lastc != TB) {
			*p++ = lastc;
			readchar();
		}
		if (lastc == SPC || lastc == TB)
			rdc();
		*p++ = 0;
		if (*thisarg == '<') {
			close(0);
			if (open(&thisarg[1], OREAD) < 0) {
				print("%s: cannot open\n", &thisarg[1]);
				_exits(0);
			}
		}
		else if (*thisarg == '>') {
			close(1);
			if (create(&thisarg[1], OWRITE, 0666) < 0) {
				print("%s: cannot create\n", &thisarg[1]);
				_exits(0);
			}
		}
		else
			*ap++ = thisarg;
	}
	*ap = 0;
	exec(symfil, argl);
	perror(symfil);
}

char	procname[100];

void
startpcs(void)
{
	if ((pid = fork()) == 0) {
		pid = getpid();
		msgpcs("hang");
		doexec();
		exits(0);
	}

	if (pid == -1)
		error("can't fork");
	child++;
	sprint(procname, "/proc/%d/mem", pid);
	corfil = procname;
	msgpcs("waitstop");
	bpwait();
	if (adrflg)
		rput(cormap, mach->pc, adrval);
	while (rdc() != EOR)
		;
	reread();
}

void
runstep(uvlong loc, int keepnote)
{
	int nfoll;
	uvlong foll[3];
	BKPT bkpt[3];
	int i;

	if(machdata->foll == 0){
		dprint("stepping unimplemented; assuming not a branch\n");
		nfoll = 1;
		foll[0] = loc+mach->pcquant;
	}else {
		nfoll = machdata->foll(cormap, loc, rget, foll);
		if (nfoll < 0)
			error("%r");
	}
	memset(bkpt, 0, sizeof bkpt);
	for(i=0; i<nfoll; i++){
		if(foll[i] == loc)
			error("can't single step: next instruction is dot");
		bkpt[i].loc = foll[i];
		bkput(&bkpt[i], 1);
	}
	runrun(keepnote);
	for(i=0; i<nfoll; i++)
		bkput(&bkpt[i], 0);
}

void
bpwait(void)
{
	setcor();
	unloadnote();
}

void
runrun(int keepnote)
{
	int on;

	on = nnote;
	unloadnote();
	if(on != nnote){
		notes();
		error("not running: new notes pending");
	}
	if(keepnote)
		loadnote();
	else
		nnote = 0;
	flush();
	msgpcs("startstop");
	bpwait();
}

void
bkput(BKPT *bp, int install)
{
	char buf[256];
	ADDR loc;
	int ret;

	errstr(buf, sizeof buf);
	if(machdata->bpfix)
		loc = (*machdata->bpfix)(bp->loc);
	else
		loc = bp->loc;
	if(install){
		ret = get1(cormap, loc, bp->save, machdata->bpsize);
		if (ret > 0)
			ret = put1(cormap, loc, machdata->bpinst, machdata->bpsize);
	}else
		ret = put1(cormap, loc, bp->save, machdata->bpsize);
	if(ret < 0){
		sprint(buf, "can't set breakpoint at %#llux: %r", bp->loc);
		print(buf);
		read(0, buf, 100);
	}
}
