/* Copyright 1985, Massachusetts Institute of Technology */

/*
 * X Unix performance monitor.
 */

#ifndef lint
static char *rcsid_xperfmon_c = "$Header: xperfmon.c,v 10.13 86/11/25 18:31:37 jg Rel $";
#endif	lint

/*
 * Simple graphical performance monitor for system-wide data.
 */

#include <stdio.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/vm.h>
#include <sys/dk.h>
#include <nlist.h>
#include <sys/buf.h>
#ifdef vax
#include <vaxuba/ubavar.h>
#include <vaxmba/mbavar.h>
#endif vax
#ifdef sun
#include <sundev/mbvar.h>
#endif sun
#ifdef ibm032	/* IBM RT/PC */
#include <caio/ioccvar.h>
#endif ibm032
#include <X/Xlib.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/file.h>
#include <sys/time.h>

#include <strings.h>

#define	USEC_INC	50000
#define	SEC_INC		1

struct packet {
	int	input, output, collisions
};
static	struct packet packets, old_packets;

#define NUM_VALS_PER	1000
struct statistic {
	int	min_val, max_val;
	int	value[NUM_VALS_PER];
	char	*label, *label2;
};

#define SECS_PER_TIME_TICK	10
static	char do_time[NUM_VALS_PER];
static	struct timeval current_time, saved_time;
static	struct timezone dummy_zone;

short gray_bits[16] = {
    0xaaaa, 0x5555, 0xaaaa, 0x5555,
    0xaaaa, 0x5555, 0xaaaa, 0x5555,
    0xaaaa, 0x5555, 0xaaaa, 0x5555,
    0xaaaa, 0x5555, 0xaaaa, 0x5555};

/*
 * The array stats always has valid info for stats[i], 0 <= i < num_stats.
 * For each valid stats[i], stats[i].value[j] is valid for 0 <= j < num_of_val.
 * The info for the k-th possible statistic of interest is recorded, if it is
 *   recorded at all, in stats[possible_stats[k]].
 */

#define	NO_STAT			-1
#define USER_CPU_PERCENTAGE	0
#define SYSTEM_CPU_PERCENTAGE	1
#define IDLE_CPU_PERCENTAGE	2
#define FREE_MEM		3
#define DISK_TRANSFERS		4
#define INTERRUPTS		5
#define INPUT_PACKETS		6
#define OUTPUT_PACKETS		7
#define COLLISION_PACKETS	8
#define NUM_POSSIBLE_STATS	9
static	int			possible_stats[NUM_POSSIBLE_STATS];
#define WANT_STAT(x)		(possible_stats[(x)] != NO_STAT)

#define	MAX_STATS		10

#define DEFAULT_BORDER_WIDTH	3
#define DEFAULT_POSITION	"=%dx%d-0+0"

static	struct statistic	stats[MAX_STATS];

static	struct timeval timeout = {
	SEC_INC,USEC_INC};
static	int num_stats, num_of_val = 0;
static	int graph_x_offset = 0;
WindowInfo	WInfo;

Window Win;
char Host[40];
char *font_name = "6x10";
int background;			    	    /* color of background */
int foreground;			    	    /* color of graph */
int highlight;				    /* color of text, scale */
FontInfo *finfo;			    /* font information needed */
int debug = 0;
#define max(a,b) (a>b ? a:b)

OpaqueFrame win;

#define FORALLPOSSIBLESTATS(stat)\
	for (stat = 0; stat < NUM_POSSIBLE_STATS; stat++)
#define FORALLSTATS(stat) for (stat = 0; stat < num_stats; stat++)

struct nlist nl[] = {
#define X_CPTIME	0
	{ "_cp_time" },
#define X_RATE	  1
	{ "_rate" },
#define X_TOTAL	 2
	{ "_total" },
#define X_DEFICIT       3
	{ "_deficit" },
#define X_FORKSTAT      4
	{ "_forkstat" },
#define X_SUM	   5
	{ "_sum" },
#define X_FIRSTFREE     6
	{ "_firstfree" },
#define X_MAXFREE       7
	{ "_maxfree" },
#define X_BOOTTIME      8
	{ "_boottime" },
#define X_DKXFER	9
	{ "_dk_xfer" },
#define X_REC	   10
	{ "_rectime" },
#define X_PGIN	  11
	{ "_pgintime" },
#define X_HZ	    12
	{ "_hz" },
#define X_MBDINIT       13
	{ "_mbdinit" },
#define N_IFNET       14
	{ "_ifnet" },
#define X_UBDINIT	15
	{ "_ubdinit" },
#define X_IOCINIT	16
	{ "_ioccdinit" },
	{ "" },
};

char dr_name[DK_NDRIVE][10];
char dr_unit[DK_NDRIVE];
double  stat1();
int     maxfree;
int     hz;
struct
{
	int     busy;
	long    time[CPUSTATES];
	long    xfer[DK_NDRIVE];
	struct  vmmeter Rate;
	struct  vmtotal Total;
	struct  vmmeter Sum;
	struct  forkstat Forkstat;
	unsigned rectime;
	unsigned pgintime;
} 
s, s1;
#define rate	    s.Rate
#define total	   s.Total
#define sum	     s.Sum
#define forkstat	s.Forkstat

struct  vmmeter osum;
int     deficit;
double  etime;
int     mf;
int     swflag;

int nintv;
long t;

#define steal(where, var) lseek(mf, where, 0); read(mf, &var, sizeof var);
#define pgtok(a) ((a)*NBPG/1024)

char	*options[NUM_POSSIBLE_STATS+1] = {
	"user", "system", "idle", "free", "disk", "interrupts",
	"input", "output", "collision",
	0  /* Terminator! */  };

short  arrow []=  {0x0000, 0x0020, 0x0070, 0x00f8, 0x01fc, 0x03fe, 0x0070,
		   0x0070, 0x0070, 0x0070, 0x0070, 0x0070, 0x0070, 0x0000};
short  mask []=   {0x0020, 0x0070, 0x00f8, 0x01fc, 0x03fe, 0x07ff, 0x07ff,
		   0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8};


main(argc, argv)
int argc;
char **argv;
{
	int	stat;
	int	have_disk;
	struct 	timeval timeleft;
	char 	display[40];
	char 	*strind;
	int 	Select_mask, select_mask = 0;
	int 	maxplus1, n;
	Cursor 	cursor;
	char 	*geometry = NULL;		/* location of window */
	char 	def[32];
	int 	reverse = 0;
	double 	update = -1.;
	double	atof();
	char 	*border_color;
	char 	*fore_color;
	char 	*back_color;
	char 	*high_color;
	Pixmap 	border_pixmap;
	char 	*option;
	int 	opt;
	int	i;
	int	minheight, minwidth;
	
	Color cdef;
	int border_width = DEFAULT_BORDER_WIDTH;

	display[0] = '\0';

	if ((option = XGetDefault(argv[0],"ReverseVideo")) != NULL )
		if (strcmp (option, "on") == 0)
			reverse = 1;
	if ((option = XGetDefault(argv[0],"BorderWidth")) != NULL)
		border_width = atoi(option);
	if ((option = XGetDefault(argv[0],"BodyFont")) != NULL)
		font_name = option;
	if ((border_color = XGetDefault(argv[0],"Border")) == NULL)
		border_color = XGetDefault(argv[0],"BorderColor");
	back_color = XGetDefault(argv[0],"Background");
	fore_color = XGetDefault(argv[0],"Foreground");
	high_color = XGetDefault(argv[0],"Highlight");
	if ((option = XGetDefault(argv[0],"Update")) != NULL)
		update = atof(option);

	nintv = get_namelist("/vmunix", "/dev/kmem");
	collect_stats();
	etime = 1.0;
	have_disk = (total_disk_transfers() ? 1 : 0);

	/* Initialize stats */
	FORALLPOSSIBLESTATS(stat)
		possible_stats[stat] = NO_STAT;
	num_stats = 0;
	for (i = 1; i < argc; i++) {  	                /* Parse line */
		if (argv[i][0] == '=') {
			geometry = argv[i];
			continue;
		}
		if (index(argv[i], ':') != NULL) {	/* host:display */
			strncpy(display, argv[i], sizeof(display));
			continue;
		}
		if (strcmp(argv[i], "-rv") == 0 ||
		strcmp(argv[i], "-reverse") == 0) {	/* black on white */
			reverse = 1;
			continue;
		}
		if (strcmp(argv[i], "-fw") == 0 ||
		strcmp(argv[i], "-forward") == 0) {	/* white on black */
			reverse = 0;
			continue;
		}
		if (strcmp(argv[i], "-bw") == 0 ||
		strcmp(argv[i], "-border") == 0) {	/* border width */
			if (++i >= argc) usage();
			border_width = atoi(argv[i]);
			continue;
		}
		if (strcmp(argv[i], "-fn") == 0 ||
		strcmp(argv[i], "-font") == 0) {	/* host name font */
			if (++i >= argc) usage();
			font_name = argv[i];
			continue;
		}
		if (strcmp(argv[i], "-bd") == 0 ||
		strcmp(argv[i], "-color") == 0) {	/* border color */
			if (++i >= argc) usage();
			border_color = argv[i];
			continue;
		}
		if (strcmp(argv[i], "-fg") == 0 ||
		strcmp(argv[i], "-foreground") == 0) {  /* foreground color */
			if (++i >= argc) usage();
			fore_color = argv[i];
			continue;
		}
		if (strcmp(argv[i], "-bg") == 0 ||
		strcmp(argv[i], "-background") == 0) {  /* background color */
			if (++i >= argc) usage();
			back_color = argv[i];
			continue;
		}
		if (strcmp(argv[i], "-hl") == 0 ||
		strcmp(argv[i], "-highlight") == 0) {   /* highlight color */
			if (++i >= argc) usage();
			high_color = argv[i];
			continue;
		}
		if (strcmp(argv[i], "-u") == 0 ||
		strcmp(argv[i], "-update") == 0) {	/* update interval */
			if (++i >= argc) usage();
			update = atof(argv[i]);
			continue;
		}
		opt = getcmd(argv[i], options);
		if (opt >= 0 && opt < NUM_POSSIBLE_STATS) {
			if (num_stats == MAX_STATS) {
				fprintf(stderr,
				    "MAX_STATS exceeded, please recompile!\n");
			}
			else possible_stats[opt] = num_stats++;
			continue;
		}
		usage();
	}
	
	if (num_stats == 0)
		FORALLPOSSIBLESTATS(stat) {
			if ((stat == DISK_TRANSFERS) && (have_disk == 0)) continue;
			possible_stats[stat] = num_stats++;
			if (num_stats == MAX_STATS) break;
		}
	have_disk = 0;	/* so max # of packets = 40 */
	init_stat(USER_CPU_PERCENTAGE, 100, "User", " CPU");
	init_stat(SYSTEM_CPU_PERCENTAGE, 100, "System", " CPU");
	init_stat(IDLE_CPU_PERCENTAGE, 100, "Idle", " CPU");
	init_stat(FREE_MEM, pgtok(maxfree), "Free", " memory");
	init_stat(DISK_TRANSFERS, 40, "Disk", " transfers");
	init_stat(INTERRUPTS, 60, "Interrupts", "");
	init_stat(INPUT_PACKETS, (have_disk ? 20 : 40), "Input", " packets");
	init_stat(OUTPUT_PACKETS, (have_disk ? 20 : 40), "Output", " packets");
	init_stat(COLLISION_PACKETS, 10, "Collision", " packets");
	if (border_width < 0) border_width = DEFAULT_BORDER_WIDTH;
	if (update > .09) {
		timeout.tv_sec = update;
		timeout.tv_usec = (update - (double)((int)update)) * 1000000.;
	}
	if (!XOpenDisplay (display)){
 	    fprintf(stderr, "%s: Can't open display '%s'\n",
		    argv[0], XDisplayName(display));
	    exit(1);
	}
	if ((finfo = XOpenFont(font_name)) == NULL) {
		fprintf(stderr, "Can't load font %s!\n", font_name);
		exit(1);
	}
	gethostname(Host, sizeof (Host));
	strcat(Host, ":");
	FORALLSTATS(stat) {
		int s_width;
		s_width = XStringWidth (stats[stat].label, finfo, 0, 0);
		graph_x_offset = max(graph_x_offset, s_width);
		s_width = XStringWidth (stats[stat].label2, finfo, 0, 0);
		graph_x_offset = max(graph_x_offset, s_width);
	}
	graph_x_offset += 15;
	if(debug) fprintf(stderr, "graph_x_offset=%d\n", graph_x_offset);
	gettimeofday(&saved_time, &dummy_zone);

	if (border_color && DisplayCells() > 2 &&
    		XParseColor(border_color, &cdef) && XGetHardwareColor(&cdef))
		border_pixmap = XMakeTile(cdef.pixel);
	else if (border_color && strcmp(border_color, "black") == 0)
		border_pixmap = BlackPixmap;
	else if (border_color && strcmp(border_color, "white") == 0)
		border_pixmap = WhitePixmap;
	else	
    		border_pixmap = XMakePixmap (
			(Bitmap) XStoreBitmap (16, 16, gray_bits),
					BlackPixel, WhitePixel);



	if (back_color && DisplayCells() > 2 &&
	    XParseColor(back_color, &cdef) && XGetHardwareColor(&cdef)) {
		background = cdef.pixel;
	} else if (back_color && (strcmp(back_color, "white") == 0)) {
		background = WhitePixel;
		reverse = 0;
	} else if (back_color && (strcmp(back_color, "black") == 0)) {
		background = BlackPixel;
		reverse = 0;
	} else
	    background = BlackPixel;

	if (fore_color && DisplayCells() > 2 &&
	    XParseColor(fore_color, &cdef) && XGetHardwareColor(&cdef)) {
		foreground = cdef.pixel;
	} else if (fore_color && (strcmp(fore_color, "black") == 0)) {
		foreground = BlackPixel;
		reverse = 0;
	} else if (fore_color && (strcmp(fore_color, "white") == 0)) {
		foreground = WhitePixel;
		reverse = 0;
	} else
	    foreground = WhitePixel;

	if (high_color && DisplayCells() > 2 &&
	    XParseColor(high_color, &cdef) && XGetHardwareColor(&cdef)) {
		highlight = cdef.pixel;
	} else
	    highlight = foreground;

	if (reverse) {
		highlight = background;
		background = foreground;
		foreground = highlight;
	}
	win.bdrwidth = border_width;
	win.border = border_pixmap;
	win.background = XMakeTile(background);

	minheight = (finfo->height * 2 + 2) * num_stats;
	minwidth  = graph_x_offset + 100;
	sprintf(def, DEFAULT_POSITION, minwidth+100, 
		(finfo->height * 3 + 3) * num_stats);

	Win = XCreate ("Performance Monitor", argv[0], geometry, def, &win,
		minwidth, minheight);

	win.height -= 10;
	XMapWindow (Win);
	cursor = XCreateCursor (11, 14, arrow, mask, 5, 1, 1, 0, GXcopyInverted);
	XDefineCursor (Win, cursor);

	redisplay (Win);
	timeleft = timeout;
	Select_mask = 1<<dpyno();
	maxplus1 = 1+dpyno();
	XSelectInput(Win, KeyPressed | ExposeWindow | ExposeCopy);
	while(1) {
		select_mask = Select_mask;
		if(debug) fprintf(stderr, "time=[%d,%d]\n",
		timeleft.tv_sec, timeleft.tv_usec);
		XFlush();
		if ((n = select(maxplus1, &select_mask, NULL, NULL, &timeleft))
		    < 0) exit(46);
		if(debug)
			fprintf(stderr,"selected n=%d mask=0x%x, time=[%d,%d]\n",
			n, select_mask, timeleft.tv_sec, timeleft.tv_usec);
		if (perf_mon_selected (Win, n, select_mask, &timeleft)
		    < 0) break;
	}
}

getcmd(to_match, table)			/* Modified from ucb/lpr/lpc.c */
register char *to_match;
register char **table;
{
	register char *p, *q;
	int found, index, nmatches, longest;

	longest = nmatches = 0;
	found = index = -1;
	for (p = *table; p; p = *(++table)) {
		index++;
		for (q = to_match; *q == *p++; q++)
			if (*q == 0)		/* exact match? */
				return(index);
		if (!*q) {			/* the to_match was a prefix */
			if (q - to_match > longest) {
				longest = q - to_match;
				nmatches = 1;
				found = index;
			} 
			else if (q - to_match == longest)
				nmatches++;
		}
	}
	if (nmatches > 1)
		return(-1);
	return(found);
}

init_stat(index, maxval, label_1, label_2)
int index, maxval;
char *label_1, *label_2;
{
	if WANT_STAT(index) {
		index = possible_stats[index];
		stats[index].max_val = maxval;
		stats[index].label = label_1;
		stats[index].label2 = label_2;
	}
}

#define	TIMER_EXPIRED(timer)						\
(*timer && ((*timer)->tv_sec == 0) && ((*timer)->tv_usec == 0))

int perf_mon_selected(w, number, mask, timer)
int mask, number;
Window w;
struct	timeval *timer;
{
	if(number == 0) {	/*timer expired */
		int *target[CPUSTATES-1], trash;
		collect_stats();
		for (trash = 0; trash < CPUSTATES-1; trash++)
			target[trash] = &trash;
		if WANT_STAT(USER_CPU_PERCENTAGE)
			target[0] =
			    &stats[possible_stats[USER_CPU_PERCENTAGE]].value[num_of_val];
		if WANT_STAT(SYSTEM_CPU_PERCENTAGE)
			target[1] =
			    &stats[possible_stats[SYSTEM_CPU_PERCENTAGE]].value[num_of_val];
		if WANT_STAT(IDLE_CPU_PERCENTAGE)
			target[2] =
			    &stats[possible_stats[IDLE_CPU_PERCENTAGE]].value[num_of_val];
		copy_cpu_stats(target);
		if WANT_STAT(FREE_MEM)
			stats[possible_stats[FREE_MEM]].value[num_of_val] =
			    pgtok(total.t_free);
		if WANT_STAT(DISK_TRANSFERS)
			stats[possible_stats[DISK_TRANSFERS]].value[num_of_val] =
			    total_disk_transfers();
		if WANT_STAT(INTERRUPTS)
			stats[possible_stats[INTERRUPTS]].value[num_of_val] =
			    (rate.v_intr/nintv) - hz;
		if WANT_STAT(INPUT_PACKETS)
			stats[possible_stats[INPUT_PACKETS]].value[num_of_val] =
			    packets.input - old_packets.input;
		if WANT_STAT(OUTPUT_PACKETS)
			stats[possible_stats[OUTPUT_PACKETS]].value[num_of_val] =
			    packets.output - old_packets.output;
		if WANT_STAT(COLLISION_PACKETS)
			stats[possible_stats[COLLISION_PACKETS]].value[num_of_val] =
			    packets.collisions - old_packets.collisions;
		gettimeofday(&current_time, &dummy_zone);
		if (current_time.tv_sec < saved_time.tv_sec) {
			/* Super-user must have set the clock back */
			saved_time = current_time;
			saved_time.tv_sec -= SECS_PER_TIME_TICK;
		}
		if (saved_time.tv_sec+SECS_PER_TIME_TICK <= current_time.tv_sec) {
			saved_time = current_time;
			do_time[num_of_val] = 1;
		} 
		else
			do_time[num_of_val] = 0;
		next_display(w);
	}
	if (mask & (1 << dpyno())){
		XEvent event;
		XEvent pevent;
		XExposeWindowEvent *exp_event;
		int key;
		if(!XPending()) return (-1); /* end of file on connection */
		while (XPending())
		  {
			XNextEvent (&event);
			switch (event.type) {
			case KeyPressed:
				if ((key = mapkey(((XKeyPressedEvent *)&event)->detail)) > 0)
					switch(key){
					case 'f': /* faster usec timeout */
						if (timeout.tv_usec >= USEC_INC)
							timeout.tv_usec -= USEC_INC;
						else {
							if (timeout.tv_sec >= SEC_INC) {
								timeout.tv_sec -= SEC_INC;
								timeout.tv_usec = 1000000-USEC_INC;
							}
						}
						break;
					case 's': /* slower usec timeout */
						if (timeout.tv_usec < 1000000-USEC_INC)
							timeout.tv_usec += USEC_INC;
						else {
							timeout.tv_usec = 0;
							timeout.tv_sec += 1;
						}
						break;
					case 'F': /* faster sec timeout */
						if (timeout.tv_sec >= SEC_INC)
							timeout.tv_sec -= SEC_INC;
						break;
					case 'S': /* slower sec timeout */
						timeout.tv_sec += SEC_INC;
						break;
					case 'R': /* reset */
						timeout.tv_sec = SEC_INC;
						timeout.tv_usec = USEC_INC;
						num_of_val = 0;
						redisplay(w);
						break;
					case 'h':
					case 'H':
					case '?': /* Help */
						printf("%s\n%s\n%s\n%s\n%s\n%s\n",
						"'s' slower usec timeout",
						"'f' faster usec timeout",
						"'S' slower sec timeout",
						"'F' faster sec timeout",
						"'R' reset timeout and display",
						"'q' or 'Q' quit");
						/*
						 * Don't reset timeout
						 */
						return(0);
					case 'q':
					case 'Q':
						return(-1);
					}	/* switch(key) */
				break;
			case ExposeWindow:
				XSync(0);
				while (XPending() != 0) {
					XPeekEvent (&pevent);
					if (pevent.type != ExposeWindow) break;
					XNextEvent(&event);
				}

				exp_event = (XExposeWindowEvent *) &event;
				win.x = exp_event->x;
				win.y = exp_event->y;
				win.width = exp_event->width;
				win.height = exp_event->height - 10;
				redisplay(w);
				break;
			default:
				break;
			}
		}
	}
	*timer = timeout;
	return(0);
}

int total_disk_transfers()
{
	register int i, total_xfers = 0;

	for(i=0; i < DK_NDRIVE; i++)
		total_xfers += s.xfer[i];
	return(total_xfers/etime);
}

copy_cpu_stats(stat)
int *stat[CPUSTATES-1];
{
	register int i;

	for(i=0; i<CPUSTATES; i++) {
		float f = stat1(i);
		if (i == 0) {           /* US+NI */
			i++;
			f += stat1(i);
		}
		if (stat[i-1] != 0)
			*stat[i-1] = f;
	}
}

collect_stats()
{

	off_t ifnetaddr = (long)nl[N_IFNET].n_value;

	register int i;

	lseek(mf, (long)nl[X_CPTIME].n_value, 0);
	read(mf, s.time, sizeof s.time);
	lseek(mf, (long)nl[X_DKXFER].n_value, 0);
	read(mf, s.xfer, sizeof s.xfer);
	if (nintv != 1) {
		steal((long)nl[X_SUM].n_value, rate);
	} 
	else {
		steal((long)nl[X_RATE].n_value, rate);
	}
	steal((long)nl[X_TOTAL].n_value, total);
	osum = sum;
	steal((long)nl[X_SUM].n_value, sum);
	steal((long)nl[X_DEFICIT].n_value, deficit);
	etime = 0;
	for (i=0; i < DK_NDRIVE; i++) {
		t = s.xfer[i];
		s.xfer[i] -= s1.xfer[i];
		s1.xfer[i] = t;
	}
	for (i=0; i < CPUSTATES; i++) {
		t = s.time[i];
		s.time[i] -= s1.time[i];
		s1.time[i] = t;
		etime += s.time[i];
	}
	if(etime == 0.)
		etime = 1.;
	etime /= 60.;
	nintv = 1;

	if (nl[N_IFNET].n_value != 0) {
		struct ifnet ifnet;
		steal((long)nl[N_IFNET].n_value, ifnetaddr);
		old_packets = packets;
		packets.input = packets.output = packets.collisions = 0;
		while (ifnetaddr) {
			steal(ifnetaddr, ifnet);
			packets.input += ifnet.if_ipackets;
			packets.output += ifnet.if_opackets;
			packets.collisions += ifnet.if_collisions;
			ifnetaddr = (off_t) ifnet.if_next;
		}
	}

}

min (a, b)
	int a,b;
{
	return(a<b ? a:b);
}

#define	YORIGIN_FOR_STAT(num)	((((num)*win.height)/num_stats)+3)
#define	YMIDPOINT_FOR_STAT(num)	((((num)*win.height+win.height/2)/num_stats) + 5)
#define Y_FOR_STAT_VAL(stat, num_of_val)				\
	y_base - min(height_of_stat, (	\
		height_of_stat*(					\
		  stats[stat].value[num_of_val]-stats[stat].min_val)/(	\
		  stats[stat].max_val-stats[stat].min_val)))
#define First_Point(v, xv, yv) {v->x = xv; v->y = yv;\
		v++->flags = VertexDontDraw; }
#define Next_Point(v, xv, yv) {v->x = xv; v->y = yv;\
		v++->flags = VertexRelative | VertexDrawLastPoint; }

display_dividers(w, clear_first)
int clear_first;
Window w;
{
	register int	i, stat;
	register int lwidth = win.width - graph_x_offset;
	Vertex v[NUM_VALS_PER];
	register Vertex *vp;

	if(debug) fprintf(stderr, "num_of_val=%d\n", num_of_val);
	FORALLSTATS(stat) {
		register int y_org = YORIGIN_FOR_STAT(stat+1);
		vp = v;
		if (clear_first)
			XPixSet(w, graph_x_offset, y_org-2, lwidth, 5, background);
		/* Draw the horizontal line and then add the tick marks */
		XLine(w, graph_x_offset, y_org, win.width, y_org, 1, 1, 
		    foreground, GXcopy, ~0);
		for (i = 0; i < num_of_val; i++) {
			if (do_time[i]){
				First_Point(vp, graph_x_offset + i, y_org - 2);
				Next_Point(vp, 0, 4);
			}
		}
		if (vp != v)
			XDraw(w, v, vp-v, 1, 1, foreground, GXcopy, ~0);
	}
}

redisplay(w)
Window w;
{
	register int height_of_stat, stat;

	XClear (w);
	display_dividers(w, 0);
	height_of_stat = YORIGIN_FOR_STAT(1) - YORIGIN_FOR_STAT(0) - 10;
	XTextMask (w, 0, 0, Host, strlen (Host), finfo->id, highlight);
	FORALLSTATS(stat) {
		register int y_origin_of_stat = YORIGIN_FOR_STAT(stat);
		int text_size;
		char temp[20];
		XTextMask (w, 0, YMIDPOINT_FOR_STAT(stat),
		    stats[stat].label, strlen (stats[stat].label), finfo->id,
		    highlight);
		XTextMask (w, 0, YMIDPOINT_FOR_STAT(stat)+10,
		    stats[stat].label2, strlen (stats[stat].label2), finfo->id, 
		    highlight);
		sprintf(temp, "%d", stats[stat].max_val);
		text_size = XStringWidth (temp, finfo, 0, 0);
		XTextMask (w, graph_x_offset-5-text_size, y_origin_of_stat+5,
			temp, strlen (temp), finfo->id, highlight);
		sprintf(temp, "%d", stats[stat].min_val);
		text_size = XStringWidth (temp, finfo, 0, 0);
		XTextMask (w, graph_x_offset-5-text_size,
		    y_origin_of_stat-1+height_of_stat, temp, strlen (temp),
		    finfo->id, highlight);
	}
	if (num_of_val > 0) FORALLSTATS(stat) 
		redisplay_stat_values(w, height_of_stat, stat, num_of_val);

}

redisplay_stat_values(w, height_of_stat, stat, stop_plus_one)
Window w;
int height_of_stat, stat, stop_plus_one;
{
	register int j, newY;
	Vertex v[NUM_VALS_PER];
	register Vertex *vp = v;
	int y_base = YORIGIN_FOR_STAT(stat+1)-5;
	newY = Y_FOR_STAT_VAL(stat, 0);
	First_Point(vp, graph_x_offset, newY);
	for (j = 1; j < stop_plus_one; ) {
		register int npts = 0, oldY = newY;
		do {
			newY = Y_FOR_STAT_VAL(stat, j);
			j++; 
			npts++;
		} 
		while ((oldY == newY) && (j < stop_plus_one));
		if (--npts)
			Next_Point(vp, npts, 0);
		Next_Point(vp, 1, newY - oldY);
	}
	if (vp != v)
		XDraw(w, v, vp-v, 1, 1, foreground, GXcopy, ~0);
}

next_display(w)
Window w;
{
	int stat, height_of_stat, redisp = 0;

	height_of_stat = YORIGIN_FOR_STAT(1) - YORIGIN_FOR_STAT(0) - 10;
	FORALLSTATS(stat) {
		int newY, oldY;
		int y_base = YORIGIN_FOR_STAT(stat+1)-5;
		newY = Y_FOR_STAT_VAL(stat, num_of_val);
		if (num_of_val == 0)
			oldY = newY;
		else
			oldY = Y_FOR_STAT_VAL(stat, num_of_val-1);
		XLine(w, graph_x_offset+num_of_val, oldY,
		    graph_x_offset+num_of_val+1, newY, 1, 1, foreground, 
		    GXcopy, ~0);
		if (do_time[num_of_val]) {
			y_base += 5;
			XLine(w, graph_x_offset+num_of_val, y_base-2,
			    graph_x_offset+num_of_val, y_base+2,
			    1, 1, foreground, GXcopy, ~0);
		}
	}
	if (++num_of_val >= NUM_VALS_PER ||
	    num_of_val >= win.width-graph_x_offset) {
		int num_shift_left = (win.width-graph_x_offset)/2;
		int width = (win.width-graph_x_offset) - num_shift_left;
		register int j;
		for (j = num_shift_left; j < num_of_val; j++)
			do_time[j-num_shift_left] = do_time[j];
		FORALLSTATS(stat) {
			register int ys = YORIGIN_FOR_STAT(stat)+5, nmax = 1, t;
			for (j = num_shift_left; j < num_of_val; j++) {
				t = stats[stat].value[j-num_shift_left] =
				    stats[stat].value[j];
				nmax = nmax > t ? nmax : t;
			}
			if (stat >= FREE_MEM && stat < COLLISION_PACKETS  && nmax != stats[stat].max_val) {
				stats[stat].max_val = nmax;
				redisp = 1;
			}
			if (!redisp) {
				XMoveArea(w, graph_x_offset+num_shift_left,
				ys, graph_x_offset, ys, width, height_of_stat+2);
				XPixSet(w, graph_x_offset+num_shift_left,
          			  ys, width, height_of_stat+2, background);

			}
		}
		num_of_val -= num_shift_left+1;
		if (redisp)
			redisplay(w);
		else
			display_dividers(w, 1);
	}
}

int get_namelist(kernel_name, memory_name)
char *kernel_name, *memory_name;
{
	time_t now;
	time_t boottime;
	register int i;
	int nintv;

	nlist(kernel_name, nl);
	if(nl[0].n_type == 0) {
		fprintf(stderr, "no %s namelist\n", kernel_name);
		exit(1);
	}
	mf = open(memory_name, 0);
	if (mf < 0) {
		fprintf(stderr, "cannot open %s\n", memory_name);
		exit(1);
	}
	steal((long)nl[X_MAXFREE].n_value, maxfree);
	steal((long)nl[X_BOOTTIME].n_value, boottime);
	steal((long)nl[X_HZ].n_value, hz);
	for (i = 0; i < DK_NDRIVE; i++) {
		strcpy(dr_name[i], "xx");
		dr_unit[i] = i;
	}
	read_names();
	time(&now);
	nintv = now - boottime;
	if (nintv <= 0 || nintv > 60*60*24*365*10) {
		fprintf(stderr,
		"Time makes no sense... namelist must be wrong.\n");
		exit(1);
	}
	return(nintv);
}

double
stat1(row)
{
	double t;
	register i;

	t = 0;
	for(i=0; i<CPUSTATES; i++)
		t += s.time[i];
	if(t == 0.)
		t = 1.;
	return(s.time[row]*100./t);
}

#ifdef vax
read_names()
{
	struct mba_device mdev;
	register struct mba_device *mp;
	struct mba_driver mdrv;
	short two_char;
	char *cp = (char *) &two_char;
	struct uba_device udev, *up;
	struct uba_driver udrv;

	mp = (struct mba_device *) nl[X_MBDINIT].n_value;
	up = (struct uba_device *) nl[X_UBDINIT].n_value;
	if (up == 0) {
		fprintf(stderr, "perfmon: Disk init info not in namelist\n");
		exit(1);
	}
	if(mp) for (;;) {
		steal(mp++, mdev);
		if (mdev.mi_driver == 0)
			break;
		if (mdev.mi_dk < 0 || mdev.mi_alive == 0)
			continue;
		steal(mdev.mi_driver, mdrv);
		steal(mdrv.md_dname, two_char);
		sprintf(dr_name[mdev.mi_dk], "%c%c", cp[0], cp[1]);
		dr_unit[mdev.mi_dk] = mdev.mi_unit;
	}
	if(up) for (;;) {
		steal(up++, udev);
		if (udev.ui_driver == 0)
			break;
		if (udev.ui_dk < 0 || udev.ui_alive == 0)
			continue;
		steal(udev.ui_driver, udrv);
		steal(udrv.ud_dname, two_char);
		sprintf(dr_name[udev.ui_dk], "%c%c", cp[0], cp[1]);
		dr_unit[udev.ui_dk] = udev.ui_unit;
	}
}
#endif vax

#ifdef sun
read_names()
{
	struct mb_device mdev;
	register struct mb_device *mp;
	struct mb_driver mdrv;
	short two_char;
	char *cp = (char *) &two_char;

	mp = (struct mb_device *) nl[X_MBDINIT].n_value;
	if (mp == 0) {
		fprintf(stderr, "vmstat: Disk init info not in namelist\n");
		exit(1);
	}
	for (;;) {
		steal(mp++, mdev);
		if (mdev.md_driver == 0)
			break;
		if (mdev.md_dk < 0 || mdev.md_alive == 0)
			continue;
		steal(mdev.md_driver, mdrv);
		steal(mdrv.mdr_dname, two_char);
		sprintf(dr_name[mdev.md_dk], "%c%c", cp[0], cp[1]);
		dr_unit[mdev.md_dk] = mdev.md_unit;
	}
}
#endif sun

#ifdef ibm032
read_names()
{
	struct iocc_device mdev;
	register struct iocc_device *mp;
	struct iocc_driver mdrv;
	short two_char;
	char *cp = (char *) &two_char;

	mp = (struct iocc_device *) nl[X_IOCINIT].n_value;
	if (mp == 0) {
		fprintf(stderr, "vmstat: Disk init info not in namelist\n");
		exit(1);
	}
	for (;;) {
		steal(mp++, mdev);
		if (mdev.iod_driver == 0)
			break;
		if (mdev.iod_dk < 0 || mdev.iod_alive == 0)
			continue;
		steal(mdev.iod_driver, mdrv);
		steal(mdrv.idr_dname, two_char);
		sprintf(dr_name[mdev.iod_dk], "%c%c", cp[0], cp[1]);
		dr_unit[mdev.iod_dk] = mdev.iod_unit;
	}
}
#endif ibm032


usage()
{
	fprintf(stderr,
		"Usage: xperfmon [host:display] option option .....\n");
	exit(1);
}

