/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident "$Header: sslib.c 1.4 89/04/28 $"

/*
 * sslib.c
 *
 * Library routines to get performance monitors
 */
#include <sys/file.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/dir.h>
#include <sys/resource.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/vm.h>
#include <sys/buf.h>
#include <sys/stats_disk.h>
#include <sys/pte.h>
#include <sys/plocal.h>
#include <sys/engine.h>
#include <sys/timer.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/flock.h>
#include <sys/var.h>
#include <sys/sec.h>
#include <stdio.h>
#include <nlist.h>
#include "sslib.h"

char *namelist = "/unix";	/* default kernel namelist file */

struct nlist nl[] = {
#define X_NENGINE	0
	{ "Nengine" },
#define X_DS_LIST	1
	{ "ds_list" },
#define X_DS_NDISKS	2
	{ "ds_ndisks" },
#define X_DS_ARRAYSIZE	3
	{ "ds_arraysize" },
#define X_ENGINE	4
	{ "engine" },
#define X_NONLINE	5
	{ "nonline" },
#define X_V		6
	{ "v" },
#define X_PROC		7
	{ "proc" },
#define	X_AVENRUN	8
	{ "avenrun" },
#define	X_DEFICIT	9
	{ "deficit" },
#define	X_FLCKINFO	10
	{ "flckinfo" },
#define X_MEMSTATS	11
	{ "mem_stats" },
#define X_PS_STATS	12
	{ "procstate", },
	{ "" },
};


#define min(a,b)	((a) > (b) ? (b) : (a))

#define STEAL(offset, addr, size) \
	if (offset) { lseek(kmem, (off_t)offset, 0); read(kmem, (char *)addr, (unsigned)size); } \
	else (void) memset((char *)addr, 0, size);

extern off_t lseek();
extern char *calloc();
extern char *whoiam;

int kmem;
struct engine *engine;	/* array of engine structures */
struct engine *pengine;	/* kernel addresses of engine structures */
unsigned Nengine, nonline;

int ndisks;			/* number of disks monitored */
struct disk_stats_list *ds_list; /* kernel addr of first chunk in list */
unsigned ds_arraysize;

unsigned totprocs;	/* the total # of active procs */
struct proc *procp;
struct proc *proc;
struct var v;
int pagesize;

struct ppriv_pages *ppriv;
struct flckinfo *flckinfop;

get_sscfg(sc)
	struct ss_cfg *sc;
{
	extern char *mapkmem();

	/*
	 * Do the one time setup stuff
	 */
	if ((kmem = open("/dev/kmem", 0)) < 0) {
		fprintf(stderr, "%s: ", whoiam);
		perror("cannot open /dev/kmem");
		exit(1);
	}
	if (nlist(namelist, nl) < 0 || nl[X_NENGINE].n_value == 0) {
		fprintf(stderr, "%s: no namelist\n", whoiam);
		exit(1);
	}
	STEAL(nl[X_NENGINE].n_value, &Nengine, sizeof Nengine);

	STEAL(nl[X_DS_NDISKS].n_value, (char *) &ndisks, sizeof(ndisks));
	STEAL(nl[X_DS_LIST].n_value, (char *)&ds_list, sizeof(ds_list));
	STEAL(nl[X_DS_ARRAYSIZE].n_value, (char *)&ds_arraysize,
		sizeof(ds_arraysize));

	STEAL(nl[X_NONLINE].n_value, &nonline, sizeof nonline);
	if (nonline == 0)
		nonline = 1;

	STEAL(nl[X_ENGINE].n_value, &pengine, sizeof pengine);
	engine = (struct engine *)calloc(Nengine, sizeof(struct engine));
	if (engine == (struct engine *)NULL) {
		fprintf(stderr, "%s: No space for engine table\n", whoiam);
		exit(1);
	}
	STEAL(pengine, &engine[0], Nengine*sizeof(struct engine));
	/*
	 * Get the process table stuff
	 */
	STEAL(nl[X_V].n_value, &v, sizeof(v));
	STEAL(nl[X_PROC].n_value, &procp, sizeof(procp));

	/*
	 * Fill out the configuration stuff
	 */
	sc->Nengine = Nengine;
	sc->nonline = nonline;
	sc->ndisks = ndisks;
}

/* This function is called after all mallocs are done for this program.
 * mmap does not move the curbrk value and it is possible to sbrk over
 * the mmap'd area.
 */
mapmem()
{

	pagesize = getpagesize();

	proc = (struct proc *) mapkmem((char *)procp,
				(int)v.v_proc * sizeof(struct proc));

	ppriv = (struct ppriv_pages *) mapkmem((char *)engine[0].e_local,
				(int)Nengine * SZPPRIV);

	flckinfop = (struct flckinfo *) mapkmem((char *)nl[X_FLCKINFO].n_value,
				sizeof(struct flckinfo));

	free((char *)engine);
}

char *
mapkmem(kernaddr, size)
	char *kernaddr;
	int size;
{
	extern char *sbrk();
	off_t pos;
	int sz, off;
	char *retva;
	static char *va = 0;

	if (va == 0)
		va = (char *)(((unsigned)sbrk(0) + (pagesize-1)) &~ (pagesize-1));
	pos = (unsigned)kernaddr & ~(pagesize-1);
	off = (unsigned)kernaddr - pos;
	sz = size + off;
	sz = (sz+pagesize-1) & ~(pagesize-1); 
	if (mmap(va, sz, PROT_READ, MAP_SHARED, kmem, pos) != 0) {
		fprintf(stderr, "%s: mmap: ", whoiam);
		perror("");
		exit(1);
	}
	retva = va;
	va += sz;
	return((char *) (retva + off));
}

/*
 * Sample the data
 * nonline is also sampled here in case someone did online/offline.
 */

ss_sample(sc, vm, dsp, ps, memst,
	avenrun, deficitp, flp)
	struct ss_cfg *sc;
	register struct vmmeter *vm;
	struct disk_stats *dsp;
	struct procstate *ps;
	struct mem_stats *memst;
	double *avenrun;
	unsigned *deficitp;
	struct flckinfo *flp;
{
	register int i;
	register struct plocal *plp;
	register struct ppriv_pages *prp; 
	struct disk_stats_list *dl;
	struct disk_stats_list dlist;
	int n;
	long l_avenrun[3]; 

	for (i = 0, prp = ppriv; i < sc->Nengine; i++, prp++) {
		plp = (struct plocal *) prp->pp_local;
		*vm = plp->cnt;
		vm++;
	}
	STEAL(nl[X_MEMSTATS].n_value, memst, sizeof(struct mem_stats));

	STEAL(nl[X_NONLINE].n_value, &nonline, sizeof(nonline));
	sc->nonline = nonline;

	STEAL(nl[X_PS_STATS].n_value, ps, sizeof(struct procstate));
	totprocs = ps->ps_onproc	/* actually running */
		 + ps->ps_runq		/* not onproc, not swapped */
		 + ps->ps_fastwait	/* "negative" priority, not swapped */
		 + ps->ps_sleep		/* sleeping in memory */
		 + ps->ps_swap;		/* actually swapped */

	STEAL(nl[X_AVENRUN].n_value, l_avenrun, sizeof(l_avenrun));
	for (i = 0; i < 3; i++) {
		*avenrun++ = ((double)l_avenrun[i])/1000.0;
	}
	STEAL(nl[X_DEFICIT].n_value, deficitp, sizeof(*deficitp));
	*flp = *flckinfop;

	/*
	 * Read the disk stats by traversing the list of arrays of disk_stats
	 * structures.  Be careful to only read stats for the number of disks
	 * configured for when monitor started up in case one was added later.
	 */

	dl = ds_list;
	n = sc->ndisks;	/* only read stats for the number of disks cfg'd */

	while (dl != NULL && n > 0) {
		int n_to_read;

		if (lseek(kmem, (off_t)dl, 0) == -1) {
			fprintf(stderr, "%s: dlist lseek error: ", whoiam);
			perror("");
			exit(1);
		}
		if (read(kmem, (char *)&dlist, sizeof(struct disk_stats_list))
					!= sizeof(struct disk_stats_list)) {
			fprintf(stderr, "%s: dlist read error: ", whoiam);
			perror("");
			exit(1);
		}
		if (lseek(kmem, (off_t)dlist.dl_stats, 0) == -1) {
			fprintf(stderr, "%s: dstats lseek error: ", whoiam);
			perror("");
			exit(1);
		}
		n_to_read = min(n, dlist.dl_used);
		if (read(kmem, (char *)dsp, n_to_read *
				sizeof(struct disk_stats)) !=
				n_to_read * sizeof(struct disk_stats)) {
			fprintf(stderr, "%s: dstats read error: ", whoiam);
			perror("");
			exit(1);
		}
		dsp += n_to_read;
		n -= n_to_read;
		dl = dlist.dl_next;
	}
}


ss_sum(sc, sd, sum)
	struct ss_cfg *sc;		/* config stuff */
	register struct vmmeter *sd;	/* per processor data */
	struct vmmeter *sum;		/* put the sum here */
{
	register int i;
	register ulong *sp, *dp;

	(void) memset((char *)sum, 0, sizeof(struct vmmeter));
	/*
	 * Calculate the sum
	 */
	for (i = 0; i < sc->Nengine; i++, sd++) {
		sp = &(sum->v_first);
		for (dp = &(sd->v_first); dp <= &(sd->v_last); dp++, sp++)
			*sp += *dp;
	}
}
