/*
 *
 *	debugger
 *
 */
#include "defs.h"
#include "fns.h"

#define ptrace dbptrace

extern	int	infile;
extern	int	outfile;
extern	int	maxpos;

/* general printing routines ($) */

char	*Ipath = INCDIR;
static	int	tracetype;
static void	printfp(Map*, int);

/*
 *	callback on stack trace
 */
static int
ptrace(Map *map, Regs *regs, u64int pc, u64int nextpc, Symbol *sym, int depth)
{
	char buf[512];

	USED(map);
	if(sym){
		dprint("%s(", sym->name);
		printparams(sym, regs);
		dprint(") ");
	}else
		dprint("%#lux ", pc);
	printsource(pc);

	dprint(" called from ");
	symoff(buf, 512, nextpc, CTEXT);
	dprint("%s ", buf);
/*	printsource(nextpc); */
	dprint("\n");
	if(tracetype == 'C' && sym)
		printlocals(sym, regs);
	return depth<40;
}

static ulong *adrregvals;

static int
adrrw(Regs *regs, char *name, u64int *val, int isr)
{
	int i;

	if((i = windindex(name)) == -1)
		return correg->rw(correg, name, val, isr);
	if(isr){
		*val = adrregvals[i];
		return 0;
	}
	werrstr("saved registers are immutable");
	return -1;
}

Regs*
adrregs(void)
{
	int i;
	static Regs r;
	static u32int x;

	if(adrregvals== nil){
		adrregvals = malloc(mach->nwindreg*sizeof(adrregvals[0]));
		if(adrregvals == nil)
			error("%r");
	}
	for(i=0; i<mach->nwindreg; i++){
		if(get4(cormap, adrval+4*i, &x) < 0)
			error("%r");
		adrregvals[i] = x;
	}
	r.rw = adrrw;
	return &r;
}

void
printdollar(int modif)
{
	int	i;
	u32int u4;
	BKPT *bk;
	Symbol s;
	int	stack;
	char	*fname;
	char buf[512];
	Regs *r;

	if (cntflg==0)
		cntval = -1;
	switch (modif) {

	case '<':
		if (cntval == 0) {
			while (readchar() != EOR)
				;
			reread();
			break;
		}
		if (rdc() == '<')
			stack = 1;
		else {
			stack = 0;
			reread();
		}
		fname = getfname();
		redirin(stack, fname);
		break;

	case '>':
		fname = getfname();
		redirout(fname);
		break;

	case 'a':
		attachprocess();
		break;

/* maybe use this for lwpids?
	case 'A':
		attachpthread();
		break;
*/
	case 'k':
		kmsys();
		break;

	case 'q':
	case 'Q':
		done();

	case 'w':
		maxpos=(adrflg?adrval:MAXPOS);
		break;

	case 'S':
		printsym();
		break;

	case 's':
		maxoff=(adrflg?adrval:MAXOFF);
		break;

	case 'm':
		printmap("? map", symmap);
		printmap("/ map", cormap);
		break;

	case 0:
	case '?':
		if (pid)
			dprint("pid = %d\n",pid);
		else
			prints("no process\n");
		flushbuf();

	case 'r':
	case 'R':
		printregs(modif);
		return;

	case 'f':
	case 'F':
		printfp(cormap, modif);
		return;

	case 'c':
	case 'C':
		tracetype = modif;
		if (adrflg)
			r = adrregs();
		else
			r = correg;
		if(stacktrace(cormap, correg, ptrace) <= 0)
			error("no stack frame");
		break;

		/*print externals*/
	case 'e':
		for (i = 0; indexsym(i, &s)>=0; i++) {
			if (s.class==CDATA)
			if (s.loc.type==LADDR)
			if (get4(cormap, s.loc.addr, &u4) > 0)
				dprint("%s/%12t%#lux\n", s.name, (ulong)u4);
		}
		break;

		/*print breakpoints*/
	case 'b':
	case 'B':
		for (bk=bkpthead; bk; bk=bk->nxtbkpt)
			if (bk->flag) {
				symoff(buf, 512, (WORD)bk->loc, CTEXT);
				dprint(buf);
				if (bk->count != 1)
					dprint(",%d", bk->count);
				dprint(":%c %s", bk->flag == BKPTTMP ? 'B' : 'b', bk->comm);
			}
		break;

	case 'M':
		fname = getfname();
		if (machbyname(fname) == 0)
			dprint("unknown name\n");;
		break;
	default:
		error("bad `$' command");
	}
	USED(r);

}

char *
getfname(void)
{
	static char fname[ARB];
	char *p;

	if (rdc() == EOR) {
		reread();
		return (0);
	}
	p = fname;
	do {
		*p++ = lastc;
		if (p >= &fname[ARB-1])
			error("filename too long");
	} while (rdc() != EOR);
	*p = 0;
	reread();
	return (fname);
}

static void
printfp(Map *map, int modif)
{
	Regdesc *rp;
	int i;
	int ret;
	char buf[512];

	for (i = 0, rp = mach->reglist; rp->name; rp += ret) {
		ret = 1;
		if (!(rp->flags&RFLT))
			continue;
		ret = fpformat(map, rp, buf, sizeof(buf), modif);
		if (ret < 0) {
			werrstr("Register %s: %r", rp->name);
			error("%r");
		}
			/* double column print */
		if (i&0x01)
			dprint("%40t%-8s%-12s\n", rp->name, buf);
		else
			dprint("\t%-8s%-12s", rp->name, buf);
		i++;
	}
}

void
redirin(int stack, char *file)
{
	char pfile[ARB];

	if (file == 0) {
		iclose(-1, 0);
		return;
	}
	iclose(stack, 0);
	if ((infile = open(file, 0)) < 0) {
		strcpy(pfile, Ipath);
		strcat(pfile, "/");
		strcat(pfile, file);
		if ((infile = open(pfile, 0)) < 0) {
			infile = STDIN;
			error("cannot open");
		}
	}
}

void
printmap(char *s, Map *map)
{
	int i;

	if (!map)
		return;
	if (map == symmap)
		dprint("%s%12t`%s'\n", s, symfil==nil ? "-" : symfil);
	else if (map == cormap)
		dprint("%s%12t`%s'\n", s, corfil==nil ? "-" : corfil);
	else
		dprint("%s\n", s);
	for (i = 0; i < map->nseg; i++) {
		dprint("%s%8t%-16#lux %-16#lux %-16#lux %s\n", map->seg[i].name,
			map->seg[i].base, map->seg[i].base+map->seg[i].size, map->seg[i].offset,
			map->seg[i].file ? map->seg[i].file : "");
	}
}

/*
 *	dump the raw symbol table
 */
void
printsym(void)
{
	int i;
	Symbol *sp, s;

	for (i=0; indexsym(i, &s)>=0; i++){
		sp = &s;
		switch(sp->type) {
		case 't':
		case 'l':
			dprint("%8#lux t %s\n", sp->loc.addr, sp->name);
			break;
		case 'T':
		case 'L':
			dprint("%8#lux T %s\n", sp->loc.addr, sp->name);
			break;
		case 'D':
		case 'd':
		case 'B':
		case 'b':
		case 'a':
		case 'p':
		case 'm':
			dprint("%8#lux %c %s\n", sp->loc.addr, sp->type, sp->name);
			break;
		default:
			break;
		}
	}
}

#define	STRINGSZ	128

/*
 *	print the value of dot as file:line
 */
void
printsource(long dot)
{
	char str[STRINGSZ];

	if (fileline(dot, str, STRINGSZ) >= 0)
		dprint("%s", str);
}

void
printpc(void)
{
	char buf[512];
	u64int u;

	if(rget(correg, mach->pc, &u) < 0)
		error("%r");
	dot = u;
	if(dot){
		printsource((long)dot);
		printc(' ');
		symoff(buf, sizeof(buf), (long)dot, CTEXT);
		dprint("%s/", buf);
		if (mach->das(cormap, dot, 'i', buf, sizeof(buf)) < 0)
			error("%r");
		dprint("%16t%s\n", buf);
	}
}

void
printlocals(Symbol *fn, Regs *regs)
{
	int i;
	u32int v;
	Symbol s;

	for (i = 0; indexlsym(fn, i, &s)>=0; i++) {
		if (s.class != CAUTO)
			continue;
		if(lget4(cormap, regs, s.loc, &v) >= 0)
			dprint("%8t%s.%s/%10t%#lux\n", fn->name, s.name, v);
		else
			dprint("%8t%s.%s/%10t?\n", fn->name, s.name);
	}
}

void
printparams(Symbol *fn, Regs *regs)
{
	int i;
	Symbol s;
	u32int v;
	int first = 0;

	for (i = 0; indexlsym(fn, i, &s)>=0; i++) {
		if (s.class != CPARAM)
			continue;
		if (first++)
			dprint(", ");
		if(lget4(cormap, regs, s.loc, &v) >= 0)
			dprint("%s=%#lux", s.name, v);
		else
			dprint("%s=?", s.name);
	}
}