[comp.sys.encore] inq_stats

loverso@encore.UUCP (John LoVerso) (11/06/87)

In a recent article George Jones <george@fubar.cis.ohio-state.EDU> writes:
> >Xload needs a bit of work since the load average is computed differently.
> >Nlist() is not a valid operation and avenrun does not contain the load.
> 
> I have a quick and dirty xload (X10) that picks up the load from the stdout
> of "uptime" if anyone is interested.

The problem with this is the lack of documentation on the inq_stats(2)
system call.  There is quite a bit of information available by using it,
if you know what to look for.  Below, I've included the source to a version
of `uptime' I've written using inq_stats.  It produces output identical
to uptime (see note below).  The get_stats() routine can be grafted into
xload (and/or anything else you want), and should be a clear example
of how to use inq_stats().

There is a bug in probably all released versions of w/uptime and rwhod
since 2.0.  It causes them to compute the load average incorrectly.
Basically, the ps_nrun[] array is a circular buffer holding the number
of runnable jobs, as seen every 5 seconds.  To compute the 1/5/15
minute load avgs, you just average the last 12/60/180 entries.  The bug
was that w/uptime and rwhod thought the size of the array was 180
entries, rather than the 300 it really is.  When they have to wrap
around the start of the array, they will avg the wrong entries.  This
happened because the size of ps_nrun[] was 180 until R2.0 or so.
However, this value was put directly into w/uptime and rwhod.  A
constant, PS_NRUNSIZE, wasn't made up until R3.0, and rwhod & w/uptime
were only fixed recently.  Corrected versions should be seen with the
final release of 3.1.

The code below is not an official product of Encore, nor is it supported
or guaranteed to be correct.  I'm only providing it to show an example
use of inq_stats.  Please direct any comments about it to my address below.

John Robert LoVerso, Encore Computer Corp
encore!loverso, loverso@multimax.arpa


/*
 * `uptime' for UMAX4.2 using inq_stats(2) syscall
 */
#include <sys/time.h>
#include <stdio.h>
#include <sys/param.h>

time_t time();

main()
{
	double	avenrun[3];	/* load avgs over last 1, 5, 15 mins */
	time_t	uptime;		/* time of last reboot & elapsed time since */
	int	nusers;		/* number of users logged in now */
	int	days, hrs, mins;

	/*
	 * get uptime and load avgs
	 */
	get_stats(avenrun,&uptime);
	
	/* 
	 * Print time of day
	 */
	printtod(time((time_t *)0));	/* silly lint */

	/*
	 * Print how long system has been up.
	 */
	uptime += 30;
	days = uptime / (60 * 60 * 24);
	uptime %= (60 * 60 * 24);
	hrs = uptime / (60 * 60);
	uptime %= (60 * 60);
	mins = uptime / 60;

	printf("  up");
	if (days > 0)
		printf(" %d day%s,", days, days>1?"s":"");
	if (hrs > 0 && mins > 0)
		printf(" %2d:%02d,", hrs, mins);
	else {
		if (hrs > 0)
			printf(" %d hr%s,", hrs, hrs>1?"s":"");
		if (mins > 0)
			printf(" %d min%s,", mins, mins>1?"s":"");
	}

	/*
	 * Print number logged in users
	 */
	count_users(&nusers);
	if (nusers > 1)
		printf("  %d users", nusers);
	else
		printf("  %d user", nusers);

	/*
	 * Print 1, 5, and 15 minute load averages.
	 */
	printf(",  load average: %.2f %.2f %.2f\n",
		avenrun[0], avenrun[1], avenrun[2]);
}

/*
 * Print time of day in 12 hour format
 */
printtod(tod)
time_t tod;
{
	register int hours;
	register char *cycle;
	struct tm *timep;

	timep = localtime(&tod);
	hours = timep->tm_hour;
	if (hours > 11) {
		cycle = "pm";
		hours -= 12;
	} else
		cycle = "am";
	if (hours == 0)
		hours = 12;
	printf("%3d:%02d%s", hours, timep->tm_min, cycle);
}

/*
 *	read utmp to count number of users logged in
 */

#include <utmp.h>

#define UTMP "/etc/utmp"

count_users(nusers)
register int *nusers;
{
	FILE *ut = fopen(UTMP,"r");
	struct utmp utmp;

	*nusers = 0;
	while (fread((char *)&utmp, sizeof(utmp), 1, ut))
		if (utmp.ut_name[0] != '\0')
			(*nusers)++;
	(void) fclose(ut);
}

/*
 *	Use inq_stats() to determine boottime, load average
 */

#include <sys/stat.h>
#include <sys/statistics.h>
#include <sys/sysdefs.h>
#include <sys/procstats.h>
#include <sys/msgbuf.h>
#include <sys/syscall.h>
#include <sys/sysstats.h>
#ifndef PS_NRUNSIZE
#define PS_NRUNSIZE 300		/* see dimension of ps_nrun[] in procstats.h */
#endif

get_stats(avenrun,uptime)
double avenrun[];
time_t *uptime;
{
	struct stat_descr	Boot_info;
	struct sys_boot		Boot_data;
	struct stat_descr	Proc_info;
	struct proc_summary	Proc_sum_data;

	struct timeval		boottime;

	register int count, index, interval;

	/*
	 * struct to get system boot data,
	 * esp boottime
	 */
	Boot_info.sd_next = &Proc_info;
	Boot_info.sd_subsys = SUBSYS_SYS;
	Boot_info.sd_type = SYSTYPE_BOOT;
	Boot_info.sd_addr = (char *) &Boot_data;
	Boot_info.sd_size = sizeof (struct sys_boot);
	Boot_info.sd_sizeused = 0;

	/*
	 * struct to get process summary data,
	 * esp circular buffer containing counts of runnable processes
	 */
	Proc_info.sd_next = NULL;
	Proc_info.sd_subsys = SUBSYS_PROC;
	Proc_info.sd_type = PROCTYPE_SUMMARY;
	Proc_info.sd_addr = (char *) &Proc_sum_data;
	Proc_info.sd_size = sizeof (struct proc_summary);
	Proc_info.sd_sizeused = 0;

	/*
	 * fill in Boot_data and Proc_sum_data structures
	 */
	if (inq_stats(1, &Boot_info) != 0) {
		perror("inq_stats");
		exit(1);
	}

	/*
	 * Compute seconds since last boot
	 */
	boottime = Boot_data.sb_boottime;
	*uptime = Proc_info.sd_time.tv_sec - boottime.tv_sec;

	/*
	 * each entry in ps_nrun[] is a count of number of processes.
	 * a new entry is added every 5 seconds.
	 * period[] is the number of entries to scan for 1,5,15 min intervals
	 */
	index = Proc_sum_data.ps_nrunidx;
	interval = 0;
	avenrun[0] = 0.0;
	for (count = 0; count < 3; count++) {
		static int period[] = { 12, 60, 180 };

		for (; interval < period[count]; interval++) {
			avenrun[count] += Proc_sum_data.ps_nrun[index];
			if (--index < 0)
				index = PS_NRUNSIZE-1;
		}

		if (count < 2)
			avenrun[count+1] = avenrun[count];
		avenrun[count] /= period[count];
	}
}