Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/sys/src/cmd/ostats.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <thread.h>
#include <ctype.h>
#include <auth.h>
#include <fcall.h>
#include <omero.h>
#include <error.h>
#include <b.h>

typedef struct Plot	Plot;
typedef struct Machine	Machine;

enum {
	Mbattery,
	Mcontext,
	Mether,
	Methererr,
	Metherin,
	Metherout,
	Mfault,
	Midle,
	Minintr,
	Mintr,
	Mload,
	Mmem,
	Mswap,
	Msyscall,
	Mtlbmiss,
	Mtlbpurge,
	Msignal,
	Mend,
};

enum
{
	/* /dev/swap */
	Mem		= 0,
	Maxmem,
	Swap,
	Maxswap,
	/* /dev/sysstats */
	Procno	= 0,
	Context,
	Interrupt,
	Syscall,
	Fault,
	TLBfault,
	TLBpurge,
	Load,
	Idle,
	InIntr,
	/* /net/ether0/stats */
	In		= 0,
	Link,
	Out,
	Err0,

	MAXNUM	= 10,
	Dot		= 2,	/* height of dot */
};

struct Plot
{
	int		*data;
	int		ndata;
	char		*label;
	int		overflow;
	void		(*newvalue)(Machine*, ulong*, ulong*, int);
	Panel*		graph;
};


struct Machine
{
	char		*name;
	int		remote;
	int		statsfd;
	int		swapfd;
	int		etherfd;
	int		ifstatsfd;
	int		batteryfd;
	int		bitsybatfd;
	int		disable;

	ulong		devswap[4];
	ulong		devsysstat[10];
	ulong		prevsysstat[10];
	int		nproc;
	ulong		netetherstats[8];
	ulong		prevetherstats[8];
	ulong		batterystats[2];
	ulong		netetherifstats[2];

	char		buf[1024];
	char		*bufp;
	char		*ebufp;
};

char	*mname[Mend+1] = {
	"battery",
	"context",
	"ether",
	"ethererr",
	"etherin",
	"etherout",
	"fault",
	"idle",
	"inintr",
	"intr",
	"load",
	"mem",
	"swap",
	"syscall",
	"tlbmiss",
	"tlbpurge",
	"802.11b",
	nil,
};

int	present[Mend];
Plot	*plot[20];
int	nplot;
int	sleeptime;
char*	machine;
Panel*	grow;
Machine	*m;
int vsize = 40;
int hsize = 100;


void
memval(Machine *m, ulong *v, ulong *vmax, int)
{
	*v = m->devswap[Mem];
	*vmax = m->devswap[Maxmem];
}

void
swapval(Machine *m, ulong *v, ulong *vmax, int)
{
	*v = m->devswap[Swap];
	*vmax = m->devswap[Maxswap];
}

void
contextval(Machine *m, ulong *v, ulong *vmax, int init)
{
	*v = m->devsysstat[Context]-m->prevsysstat[Context];
	*vmax = sleeptime*m->nproc;
	if(init)
		*vmax = sleeptime;
}

void
intrval(Machine *m, ulong *v, ulong *vmax, int init)
{
	*v = m->devsysstat[Interrupt]-m->prevsysstat[Interrupt];
	*vmax = sleeptime*m->nproc;
	if(init)
		*vmax = sleeptime;
}

void
syscallval(Machine *m, ulong *v, ulong *vmax, int init)
{
	*v = m->devsysstat[Syscall]-m->prevsysstat[Syscall];
	*vmax = sleeptime*m->nproc;
	if(init)
		*vmax = sleeptime;
}

void
faultval(Machine *m, ulong *v, ulong *vmax, int init)
{
	*v = m->devsysstat[Fault]-m->prevsysstat[Fault];
	*vmax = sleeptime*m->nproc;
	if(init)
		*vmax = sleeptime;
}

void
tlbmissval(Machine *m, ulong *v, ulong *vmax, int init)
{
	*v = m->devsysstat[TLBfault]-m->prevsysstat[TLBfault];
	*vmax = (sleeptime/1000)*10*m->nproc;
	if(init)
		*vmax = (sleeptime/1000)*10;
}

void
tlbpurgeval(Machine *m, ulong *v, ulong *vmax, int init)
{
	*v = m->devsysstat[TLBpurge]-m->prevsysstat[TLBpurge];
	*vmax = (sleeptime/1000)*10*m->nproc;
	if(init)
		*vmax = (sleeptime/1000)*10;
}

void
loadval(Machine *m, ulong *v, ulong *vmax, int init)
{
	*v = m->devsysstat[Load];
	*vmax = 1000*m->nproc;
	if(init)
		*vmax = 1000;
}

void
idleval(Machine *m, ulong *v, ulong *vmax, int)
{
	*v = m->devsysstat[Idle]/m->nproc;
	*vmax = 100;
}

void
inintrval(Machine *m, ulong *v, ulong *vmax, int)
{
	*v = m->devsysstat[InIntr]/m->nproc;
	*vmax = 100;
}

void
etherval(Machine *m, ulong *v, ulong *vmax, int init)
{
	*v = m->netetherstats[In]-m->prevetherstats[In] + m->netetherstats[Out]-m->prevetherstats[Out];
	*vmax = sleeptime*m->nproc;
	if(init)
		*vmax = sleeptime;
}

void
etherinval(Machine *m, ulong *v, ulong *vmax, int init)
{
	*v = m->netetherstats[In]-m->prevetherstats[In];
	*vmax = sleeptime*m->nproc;
	if(init)
		*vmax = sleeptime;
}

void
etheroutval(Machine *m, ulong *v, ulong *vmax, int init)
{
	*v = m->netetherstats[Out]-m->prevetherstats[Out];
	*vmax = sleeptime*m->nproc;
	if(init)
		*vmax = sleeptime;
}

void
ethererrval(Machine *m, ulong *v, ulong *vmax, int init)
{
	int i;

	*v = 0;
	for(i=Err0; i<nelem(m->netetherstats); i++)
		*v += m->netetherstats[i];
	*vmax = (sleeptime/1000)*10*m->nproc;
	if(init)
		*vmax = (sleeptime/1000)*10;
}

void
batteryval(Machine *m, ulong *v, ulong *vmax, int)
{
	*v = m->batterystats[0];
	if(m->bitsybatfd >= 0)
		*vmax = 184;		// at least on my bitsy...
	else
		*vmax = 100;
}

void
signalval(Machine *m, ulong *v, ulong *vmax, int)
{
	ulong l;

	*vmax = sleeptime;
	l = m->netetherifstats[0];
	/*
	 * Range is seen to be from about -45 (strong) to -95 (weak); rescale
	 */
	if(l == 0){	/* probably not present */
		*v = 0;
		return;
	}
	*v = 20*(l+95);
}

void	(*newvaluefn[Mend])(Machine*, ulong*, ulong*, int init) = {
	batteryval,
	contextval,
	etherval,
	ethererrval,
	etherinval,
	etheroutval,
	faultval,
	idleval,
	inintrval,
	intrval,
	loadval,
	memval,
	swapval,
	syscallval,
	tlbmissval,
	tlbpurgeval,
	signalval,
};

int
needswap(void)
{
	return present[Mmem] | present[Mswap];
}


int
needstat(void)
{
	return  present[Mcontext]  | present[Mfault] | present[Mintr] | present[Mload] | present[Midle] |
		present[Minintr] | present[Msyscall] | present[Mtlbmiss] | present[Mtlbpurge];
}


int
needether(void)
{
	return  present[Mether] | present[Metherin] | present[Metherout] | present[Methererr];
}

int
needbattery(void)
{
	return  present[Mbattery];
}

int
needsignal(void)
{
	return  present[Msignal];
}

int 
connectexportfs(char *addr)
{
	char buf[ERRMAX], dir[256], *na;
	int fd, n;
	char *tree;
	AuthInfo *ai;

	tree = "/";
	na = netmkaddr(addr, 0, "exportfs");
	if((fd = dial(na, 0, dir, 0)) < 0)
		return -1;

	ai = auth_proxy(fd, nil, "proto=p9any role=client");
	if(ai == nil)
		return -1;

	n = write(fd, tree, strlen(tree));
	if(n < 0){
		close(fd);
		return -1;
	}

	strcpy(buf, "can't read tree");
	n = read(fd, buf, sizeof buf - 1);
	if(n!=2 || buf[0]!='O' || buf[1]!='K'){
		buf[sizeof buf - 1] = '\0';
		werrstr("bad remote tree: %s\n", buf);
		close(fd);
		return -1;
	}

	return fd;
}

int
loadbuf(Machine *m, int *fd)
{
	int n;


	if(*fd < 0)
		return 0;
	seek(*fd, 0, 0);
	n = read(*fd, m->buf, sizeof m->buf);
	if(n <= 0){
		close(*fd);
		*fd = -1;
		return 0;
	}
	m->bufp = m->buf;
	m->ebufp = m->buf+n;
	return 1;
}

int
readnums(Machine* m, int n, ulong *a, int spanlines)
{
	int i;
	char *p, *ep;

	if(spanlines)
		ep = m->ebufp;
	else
		for(ep=m->bufp; ep<m->ebufp; ep++)
			if(*ep == '\n')
				break;
	p = m->bufp;
	for(i=0; i<n && p<ep; i++){
		while(p<ep && !isdigit(*p) && *p!='-')
			p++;
		if(p == ep)
			break;
		a[i] = strtoul(p, &p, 10);
	}
	if(ep < m->ebufp)
		ep++;
	m->bufp = ep;
	return i == n;
}

int
initmach(char *name)
{
	int n, fd;
	ulong a[MAXNUM];
	char *p, mpt[256], buf[256];

	p = strchr(name, '!');
	if(p)
		p++;
	else
		p = name;
	m->name = estrdup(p);
	m->remote = (strcmp(p, sysname()) != 0);
	if(m->remote == 0)
		strcpy(mpt, "");
	else{
		snprint(mpt, sizeof mpt, "/n/%s", p);
		fd = connectexportfs(name);
		if(fd < 0){
			fprint(2, "can't connect to %s: %r\n", name);
			return 0;
		}
		/* BUG? need to use amount() now? */
		if(mount(fd, -1, mpt, MREPL, "") < 0)
			return 0;
	}

	seprint(buf, buf+sizeof(buf), "%s/dev/swap", mpt);
	m->swapfd = open(buf, OREAD);
	if(loadbuf(m, &m->swapfd) && readnums(m, nelem(m->devswap), a, 0))
		memmove(m->devswap, a, sizeof m->devswap);
	else
		m->devswap[Maxmem] = m->devswap[Maxswap] = 100;

	snprint(buf, sizeof buf, "%s/dev/sysstat", mpt);
	m->statsfd = open(buf, OREAD);
	if(loadbuf(m, &m->statsfd)){
		for(n=0; readnums(m, nelem(m->devsysstat), a, 0); n++)
			;
		m->nproc = n;
	}else
		m->nproc = 1;

	snprint(buf, sizeof buf, "%s/net/ether0/stats", mpt);
	m->etherfd = open(buf, OREAD);
	if(loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1))
		memmove(m->netetherstats, a, sizeof m->netetherstats);

	snprint(buf, sizeof buf, "%s/net/ether0/ifstats", mpt);
	m->ifstatsfd = open(buf, OREAD);
	if(loadbuf(m, &m->ifstatsfd)){
		/* need to check that this is a wavelan interface */
		if(strncmp(m->buf, "Signal: ", 8) == 0 && readnums(m, nelem(m->netetherifstats), a, 1))
			memmove(m->netetherifstats, a, sizeof m->netetherifstats);
	}

	snprint(buf, sizeof buf, "%s/mnt/apm/battery", mpt);
	m->batteryfd = open(buf, OREAD);
	m->bitsybatfd = -1;
	if(m->batteryfd >= 0){
		if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
			memmove(m->batterystats, a, sizeof(m->batterystats));
	}else{
		snprint(buf, sizeof buf, "%s/dev/battery", mpt);
		m->bitsybatfd = open(buf, OREAD);
		if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
			memmove(m->batterystats, a, sizeof(m->batterystats));
	}
	return 1;
}

void
readmach(void)
{
	int n, i;
	ulong a[8];
	char buf[32];

	snprint(buf, sizeof buf, "%s", m->name);
	if (strcmp(m->name, buf) != 0){
		free(m->name);
		m->name = estrdup(buf);
	}
	if(needswap() && loadbuf(m, &m->swapfd) && readnums(m, nelem(m->devswap), a, 0))
		memmove(m->devswap, a, sizeof m->devswap);
	if(needstat() && loadbuf(m, &m->statsfd)){
		memmove(m->prevsysstat, m->devsysstat, sizeof m->devsysstat);
		memset(m->devsysstat, 0, sizeof m->devsysstat);
		for(n=0; n<m->nproc && readnums(m, nelem(m->devsysstat), a, 0); n++)
			for(i=0; i<nelem(m->devsysstat); i++)
				m->devsysstat[i] += a[i];
	}
	if(needether() && loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1)){
		memmove(m->prevetherstats, m->netetherstats, sizeof m->netetherstats);
		memmove(m->netetherstats, a, sizeof m->netetherstats);
	}
	if(needsignal() && loadbuf(m, &m->ifstatsfd) && strncmp(m->buf, "Signal: ", 8)==0 && readnums(m, nelem(m->netetherifstats), a, 1)){
		memmove(m->netetherifstats, a, sizeof m->netetherifstats);
	}
	if(needbattery() && loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
		memmove(m->batterystats, a, sizeof(m->batterystats));
	if(needbattery() && loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
		memmove(m->batterystats, a, sizeof(m->batterystats));

}

Plot*
newplot(int n)
{
	Plot *g;
	static int nadd;
	Panel*	w;
	Panel*  wl;
	char	txt[20];
	char	gname[20];

	assert(n < nelem(mname));
	/* avoid two adjacent plots of same color */
	g = emalloc(sizeof(Plot));
	memset(g, 0, sizeof(Plot));
	g->label = mname[n];
	g->data = malloc(hsize*sizeof(ulong));
	g->ndata = hsize;
	memset(g->data, 0, hsize*sizeof(ulong));
	seprint(txt, txt+sizeof(txt), "%s %s", machine, mname[n]);
	txt[13]=0;
	seprint(gname, gname+sizeof(gname), "col:%s", mname[n]);
	w = createsubpanel(grow, gname);
	seprint(gname, gname+sizeof(gname), "label:%s", mname[n]);
	wl = createsubpanel(w, gname);
	//graphctl(wl, "font S");
	openpanel(wl, OWRITE|OTRUNC);
	writepanel(wl, txt, strlen(txt));
	closepanel(wl);
	seprint(gname, gname+sizeof(gname), "draw:%s", mname[n]);
	g->graph = createsubpanel(w, gname);
	openpanelctl(w);
	panelctl(w, "notag");
	closepanelctl(w);
	g->newvalue = newvaluefn[n];
	present[n] = 1;
	plot[nplot++] = g;
	return g;
}

void
update(Plot *g, ulong v, ulong vmax)
{
	static char plot[32*1024];
	char*	s;
	double	y;
	int	d;
	int	i;

	memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
	g->data[0] = v;
	s = seprint(plot, plot+sizeof(plot),
		"rect %d %d %d %d grey\n", 0, 0, hsize+1, vsize+1);
	for (i = 0; i < g->ndata; i++){
		y = ((double)g->data[i])/(vmax*1);
		d = vsize * y;
		if (d < 0)
			d = 0;
		if (d > vsize)
			d = vsize;
		d = vsize - d;
		s = seprint(s, plot+sizeof(plot),
			"line %d %d %d %d 0 red\n", hsize - i, vsize, hsize - i, d);
	}
	openpanel(g->graph, OWRITE|OTRUNC);
	writepanel(g->graph, plot, strlen(plot));
	closepanel(g->graph);
}

void
usage(void)
{
	fprint(2, "usage: %s [-8bceEfiImlnpstw] [machine]\n", argv0);
	exits("usage");
}

void
omerogone(void)
{
	fprint(2, "ostats: graphgone\n");
	sysfatal("graphgone");
}

void
threadmain(int argc, char* argv[])
{
	ulong v, vmax, nargs;
	char* s;
	int	i;
	char args[20];
	char	argchars[] = "8bceEfiImlnpstw";
	extern int graphdebug;

	grow = createpanel("ostats", "row", nil);
	if (grow == nil)
		sysfatal("graphinit: %r\n");
	sleeptime = 2000;
	nargs = 0;
	ARGBEGIN{
	case 'd':
		omerodebug = 1;
		break;
	case 'T':
		s = EARGF(usage());
		i = atoi(s);
		if(i > 2)
			sleeptime = 1000*i;
		break;
	default:
		if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
			usage();
		args[nargs++] = ARGC();
	}ARGEND;
	if (argc == 1){
		machine = argv[0];
		argc--;
	} else
		machine = sysname();
	if (argc != 0)
		usage();

	m = malloc(sizeof(Machine));
	memset(m, 0, sizeof(Machine));
	initmach(machine);
	for(i=0; i<nargs; i++)
	switch(args[i]){
	case 'b':
		newplot(Mbattery);
		break;
	case 'c':
		newplot(Mcontext);
		break;
	case 'e':
		newplot(Mether);
		break;
	case 'E':
		newplot(Metherin);
		newplot(Metherout);
		break;
	case 'f':
		newplot(Mfault);
		break;
	case 'i':
		newplot(Mintr);
		break;
	case 'I':
		newplot(Mload);
		newplot(Midle);
		newplot(Minintr);
		break;
	case 'l':
		newplot(Mload);
		break;
	case 'm':
		newplot(Mmem);
		break;
	case 'n':
		newplot(Metherin);
		newplot(Metherout);
		newplot(Methererr);
		break;
	case 'p':
		newplot(Mtlbpurge);
		break;
	case 's':
		newplot(Msyscall);
		break;
	case 't':
		newplot(Mtlbmiss);
		newplot(Mtlbpurge);
		break;
	case '8':
		newplot(Msignal);
		break;
	case 'w':
		newplot(Mswap);
		break;
	}
	if(nplot == 0)
		newplot(Mload);
	closepanelctl(grow);
	for (;;){
		readmach();
		for(i=0; i<nplot; i++){
			plot[i]->newvalue(m, &v, &vmax, 0);
			update(plot[i], v, vmax);
		}
		sleep(sleeptime);
	}

}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.