#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"


/*
 * reported times can be translated to a more readable format by
 * using something like:
 * awk '{printf("print(\"%s: %s times; %s us worst; %s ws total\");\nsrc(%s)\n",
 *	 $1, $3, $4, $5, $2); }'  | acid ../k10/9k8cpu
 * on the wsdata file, after doing a sort +2nr on it.
 */

enum{
	WSdirqid,
	WSdataqid,
	WSctlqid,
};

Dirtab Wstab[]={
	".",		{WSdirqid, 0, QTDIR},0,	DMDIR|0550,
	"wsdata",	{WSdataqid},		0,	0600,
	"wsctl",	{WSctlqid},		0,	0600,
};


/*
 * waitstats functions are in taslock.c, because they use Locks but
 * callers in taslock.c must not call them to avoid
 * a loop.
 * This is only the user interface.
 */

static char*
collect(void)
{
	extern Lock waitstatslk;
	char *buf, *s;
	int i, n;
	static char *wname[] = {
	[WSlock] "lock",
	[WSqlock] "qlock",
	[WSslock] "slock",
	};

	n = waitstats.npcs * (strlen("slock") + 1 + 19 * 3 + 1) + 1;
	buf = smalloc(n);
	s = buf;
	lock(&waitstatslk);
	for(i = 0; i < NWstats; i++)
		if(waitstats.pcs[i] != 0)
			s = seprint(s, buf+n, "%s %#llux %d %#llud %#llud\n",
				wname[waitstats.type[i]],
				waitstats.pcs[i], waitstats.ns[i], waitstats.wait[i],
				waitstats.total[i]);
	unlock(&waitstatslk);
	if(s == buf + n)
		print("collect: fix devws.c, buffer was too short");
	return buf;
}

static Chan*
wsattach(char *spec)
{
	return devattach('W', spec);
}

static Walkqid*
wswalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, Wstab, nelem(Wstab), devgen);
}

static long
wsstat(Chan *c, uchar *db, long n)
{
	return devstat(c, db, n, Wstab, nelem(Wstab), devgen);
}

static Chan*
wsopen(Chan *c, int omode)
{
	if(c->qid.type & QTDIR){
		if(omode != OREAD)
			error(Eperm);
	}
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	c->aux = nil;
	if(c->qid.path == WSdataqid)
		c->aux = collect();
	return c;
}

static void
wsclose(Chan *c)
{
	free(c->aux);
}

static long
wsread(Chan *c, void *va, long n, vlong off)
{

	switch((int)c->qid.path){
	case WSdirqid:
		n = devdirread(c, va, n, Wstab, nelem(Wstab), devgen);
		break;
	case WSdataqid:
		n = readstr(off, va, n, c->aux);
		break;
	default:
		n = 0;
	}
	return n;
}

static long
wswrite(Chan *c, void *a, long n, vlong)
{
	char *buf;

	switch((int)(c->qid.path)){
	case WSctlqid:
		buf = smalloc(n + 1);
		memmove(buf, a, n);
		buf[n] = 0;
		if(n > 0 && buf[n-1] == '\n')
			buf[n-1] = 0;
		if(strcmp(buf, "clear") == 0){
			lockstats.locks = lockstats.glare = lockstats.inglare = 0;
			qlockstats.qlock = qlockstats.qlockq = 0;
			clearwaitstats();
		}else if(strcmp(buf, "start") == 0)
			startwaitstats(1);
		else if(strcmp(buf, "stop") == 0)
			startwaitstats(0);
		else{
			free(buf);
			error(Ebadctl);
		}
		free(buf);
		break;
	default:
		error(Ebadusefd);
	}
	return n;
}

Dev wsdevtab = {
	'W',
	"waitstats",

	devreset,
	devinit,
	devshutdown,
	wsattach,
	wswalk,
	wsstat,
	wsopen,
	devcreate,
	wsclose,
	wsread,
	devbread,
	wswrite,
	devbwrite,
	devremove,
	devwstat,
};
