1
0
Files
irix-657m-src/eoe/cmd/sa/sadc.c
2022-09-29 17:59:04 +03:00

851 lines
19 KiB
C

/* Copyright (c) 1984 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
/* #ident "@(#)sa:sadc.c 1.25" */
#ident "$Revision: 1.62 $"
/* sadc.c 1.25 of 11/27/85 */
/*
sadc.c - writes system activity binary data from /dev/kmem to a
file or stdout.
Usage: sadc [t n] [file]
if t and n are not specified, it writes
a dummy record to data file. This usage is
particularly used at system booting.
If t and n are specified, it writes system data n times to
file every t seconds.
In both cases, if file is not specified, it writes
data to stdout.
*/
#undef SA_DEBUG
#define _KMEMUSER
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <stddef.h>
#include <sys/var.h>
#include <sys/sema.h>
#include <sys/iobuf.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/sbd.h>
#include <sys/immu.h>
#include <sys/sysmp.h>
#include <sys/syssgi.h>
#include <sys/sysinfo.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/flock.h>
#include <sys/kmem.h>
#include <sys/utsname.h>
#include <sys/sysget.h>
#include "sa.h"
#include <sys/buf.h>
#include <sys/dvh.h>
#include <sys/scsi.h>
#include <sys/dksc.h>
#include <sys/dmamap.h>
#include <sys/ksa.h>
#include <sys/errno.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/attributes.h>
#include <sys/invent.h>
#include <sys/conf.h>
#include <sys/iograph.h>
#include <diskinfo.h>
#include <ftw.h>
#include <fcntl.h>
#include <stddef.h>
#include <string.h>
#include <strings.h>
struct disk_iostat_info *Ndiosetup, *CurNdiosetup, *AllocdNdiosetup;
int NdiosetupIndex;
struct sgidio *Sgidio;
struct stat ubuf,syb;
struct var *tbl;
struct syserr *err;
struct vnodestats vnodestat;
struct sa *d;
struct sysinfo *si;
struct minfo *mi;
struct rminfo *ri;
struct dinfo *di;
struct gfxinfo *gi;
struct infomap infomap;
int i,j,k;
int nsites;
int sysinfo_sz;
int f;
void sgi_dio_setup(int count);
struct disk_iostat_info *sgi_dio_alloc();
void sgi_disk_read(void), sgi_sys_setup(void);
void write_rec(char *, char *, int, int, char *);
void kread(char *, void **, int, int);
void ksaread(void);
int ncpus, ndisk;
struct sysinfo_cpu *cst;
sgt_stat_t *cstat;
int fp;
extern int errno;
#ifdef SA_DEBUG
void sadc_iotime_print(struct sgidio *);
#endif /* SA_DEBUG */
static struct pageconst pageconst;
/*
* These defines have been added based on the use of
* the following pagesize-dependent macros:
*
* ctob
* dtop
*/
#define _PAGESZ pageconst.p_pagesz
#define PNUMSHFT pageconst.p_pnumshft
struct kname {
char *name;
int count;
};
struct kname knames[] = {
"v", -1,
"_physmem_start", -1,
"vn_vnumber", -1,
"vn_nfree", -1,
"vn_epoch", -1,
"syssegsz", -1,
"file_zone", -1,
"gfxinfo", -1,
"min_file_pages", -1,
};
int num_knames = sizeof(knames) / sizeof(struct kname);
static int proctbl(int);
static int zonefree(char *);
main(int argc, char *const argv[])
{
int ct;
unsigned ti;
time_t min;
struct stat buf;
char *fname;
struct sysinfo_cpu *cstp;
#ifdef SA_DEBUG
printf("sadc : argv[1] = %s, argv[2] = %s\n",argv[1],argv[2]);
#endif
ct = argc >= 3? atoi(argv[2]): 0;
min = time((time_t *) 0);
ti = argc >= 3? atoi(argv[1]): 0;
if (syssgi(SGI_CONST, SGICONST_PAGESZ, &pageconst,
sizeof(pageconst), 0) == -1)
perrexit("");
sgi_dio_setup(0);
/* Save machine/unix info. This will only change at restarts */
sgi_sys_setup();
if (uname(&infomap.uname) < 0) {
perrexit("uname failed");
}
/* Save machine attributes */
infomap.clk_tck = HZ;
infomap.long_sz = sizeof(long);
if (argc == 3 || argc == 1){
/* no data file is specified, direct data to stdout */
signal(SIGPIPE, SIG_IGN); /* For write to fail on pipe close */
fp = 1;
write_rec(INFO_MAGIC, (char *)&infomap, sizeof(infomap),
1, "Could not write header record");
#ifdef NOTYET
write_rec(SCNAME_MAGIC, (char *)syscallname, MAXSCNAME,
MAXSYSENT, "Error writing syscallname");
write_rec(LIDNAME_MAGIC, (char *)lid_name, MAXLIDNAME,
MAXLID, "Error writing lid_name");
#endif
}
else {
fname = argc==2? argv[1]: argv[3];
/* check if the data file is there */
/* check if data file is too old */
if ((stat(fname, &buf) == -1) ||
((min - buf.st_mtime) > 86400))
goto credfl;
if ((fp = open(fname, O_RDWR)) == -1){
credfl:
/* data file does not exist:
* create one and write the header record.
*/
if ((fp = creat(fname,00644)) == -1)
perrexit(fname);
close(fp);
fp = open (fname,2);
lseek(fp,0L,SEEK_SET);
write_rec(INFO_MAGIC, (char *)&infomap, sizeof(infomap),
1, "Could not write header record");
#ifdef NOTYET
write_rec(SCNAME_MAGIC, (char *)syscallname, MAXSCNAME,
MAXSYSENT, "Error writing syscallname");
write_rec(LIDNAME_MAGIC, (char *)lid_name, MAXLIDNAME,
MAXLID, "Error writing lid_name");
#endif
}
else{
/* position the write pointer to end of the file */
lseek(fp, 0, SEEK_END);
}
}
/* if n =0 , write the additional dummy record */
if (ct == 0){
d->apstate = CPUDUMMYTIME;
d->ts = min;
/* Save uname data in case unix has changed */
strcpy(((struct revname *)d)->release, infomap.uname.release);
strcpy(((struct revname *)d)->version, infomap.uname.version);
for (i = 1; i < nsites; i++) {
/* Copy this too all them */
d[i] = *d;
}
write_rec(SA_MAGIC, (char *)d, sizeof(struct sa),
nsites, "Could not write dummy record");
#ifdef SA_DEBUG
printf("sadc : wrote dummy fd = %d ,d.numdisks = %d\n",fp,d.numdisks);
#endif
/* clean it out once we've written it */
for (i = 0; i < nsites; i++) {
memset(&d[i], 0, sizeof(struct revname));
}
}
sgi_neo_disk_setup(sgi_dio_alloc);
/* get memory for tables */
kread("v", (void **)&tbl, sizeof(struct var), 1);
for(;;) {
/* read sysinfo, minfo, dinfo, syserr, vopinfo */
ksaread();
sgi_disk_read();
/* compute size of system tables */
for (i = 0; i < nsites; i++) {
d[i].szfile = si[i].filecnt;
d[i].mszfile = d[i].szfile +
zonefree("file_zone");
d[i].szproc = proctbl(i);
/* record system table sizes */
d[i].szlckr = si[i].reccnt;
d[i].mszlckr = d[i].szlckr;
/* record maximum sizes of system tables */
d[i].mszproc = tbl[i].v_proc;
/* get time stamp */
if (i == 0) {
d->ts = time ((time_t *) 0);
}
else {
d[i].ts = d->ts;
}
}
i=0;
/* write data to data file from structure d */
write_rec(SA_MAGIC, (char *)d, sizeof(struct sa), nsites,
"Could not write sa record");
write_rec(SINFO_MAGIC, (char *)si, sizeof(struct sysinfo),
nsites, "Could not write sysinfo record");
write_rec(MINFO_MAGIC, (char *)mi, sizeof(struct minfo),
nsites, "Could not write minfo record");
write_rec(DINFO_MAGIC, (char *)di, sizeof(struct dinfo),
nsites, "Could not write dinfo record");
write_rec(GFX_MAGIC, (char *)gi, sizeof(struct gfxinfo),
nsites, "Could not write gfxinfo record");
write_rec(RMINFO_MAGIC, (char *)ri, sizeof(struct rminfo),
nsites, "Cound not write rminfo record");
for (i = 0, cstp = cst; i < nsites; i++) {
/* For each site we write a CPU record */
write_rec(CPU_MAGIC, (char *)cstp,
sizeof(struct sysinfo_cpu), d[i].apstate,
"Could not write cpu record");
cstp += d[i].apstate;
}
write_rec(NEODISK_MAGIC, (char *)Sgidio, sizeof(struct sgidio),
ndisk, "Could not write disk record");
#ifdef SA_DEBUG
{
static int th=0;
printf("sadc : wrote %dth time to fd = %d d.numdisks = %d\n",
++th,fp,ndisk);
}
#endif
if(--ct > 0)
sleep(ti);
else {
close(fp);
exit(0);
}
}
}
/*ARGSUSED*/
static int
zonefree(char *sym)
{
#if 0
/*
* zone_t structure is not visible to the user level programs anymore
* It's a kernel private data structure, and should be treated like
* that.
* Anyway, the mechanism here is broken. file_zone does not give any
* idea of the maximum number of files that can be opened. This
* needs to be fixed.
*/
zone_t file_zone, *addr1;
lseek(f, addr, 0);
read(f, &addr1, sizeof(addr1));
lseek(f, (long)addr1, 0);
read(f, &file_zone, sizeof(file_zone));
return(file_zone.zone_freesize);
#else
return 0;
#endif
}
#include <paths.h>
#include <dirent.h>
static DIR *dd = NULL;
static int
proctbl(int x)
{
int nactive = 0;
struct dirent *ent;
if (x > 0) {
/* We don't have a way to get procs per cell yet */
return 0;
}
if (!dd) {
if ((dd = opendir(_PATH_PROCFSPI)) == NULL) {
fprintf(stderr, "sadc: can't read process status\n");
perrexit("");
}
}
rewinddir(dd);
while (ent = readdir(dd)) {
if (ent->d_name[0] == '.')
continue;
nactive++;
}
return nactive;
}
void
perrexit(const char *mesg)
{
fprintf(stderr, "sadc: ");
perror(mesg);
exit(2);
}
void
getgfxinfo(void)
{
static gfxinfo_t *gfxinfo;
int i, j, start;
if (!gi) {
gi = (struct gfxinfo *)calloc(sizeof(struct gfxinfo),
d->apstate);
if (!gi) {
fprintf(stderr, "Can't alloc gfxinfo\n");
perrexit("getgfxinfo");
}
}
kread("gfxinfo", (void **)&gfxinfo, sizeof(gfxinfo_t), 0);
if (!gfxinfo) {
return;
}
for (j = 0; j < nsites; j++) {
start = j * d->apstate;
gi[j].gswitch = gfxinfo[start].gswitch;
gi[j].griioctl = gfxinfo[start].griioctl;
gi[j].gintr = gfxinfo[start].gintr;
gi[j].gswapbuf = gfxinfo[start].gswapbuf;
gi[j].fifowait = gfxinfo[start].fifowait;
gi[j].fifonowait = gfxinfo[start].fifonowait;
for (i=1; i< d->apstate; i++) {
gi[j].gswitch += gfxinfo[start + i].gswitch;
gi[j].griioctl += gfxinfo[start + i].griioctl;
gi[j].gintr += gfxinfo[start + i].gintr;
gi[j].gswapbuf += gfxinfo[start + i].gswapbuf;
gi[j].fifowait += gfxinfo[start + i].fifowait;
gi[j].fifonowait += gfxinfo[start + i].fifonowait;
}
}
}
/*
* ksaread - read info from kernel (rather than using kmem)
*/
void
ksaread(void)
{
static long *vn_vnumber = (long *)0;
static int *vn_epoch = (int *)0;
static int *vn_nfree = (int *)0;
static int *min_file_pages = (int *)0;
int i, tcpus;
sgt_cookie_t ck;
for (i = 0, tcpus = 0; i < nsites; i++) {
/* how many processors on-line */
d[i].apstate = cstat[i].info.si_num;
tcpus += d[i].apstate;
}
if (ncpus != tcpus) {
cst = realloc(cst, (sizeof(struct sysinfo_cpu) * tcpus));
if (!cst) {
perrexit("");
}
ncpus = tcpus;
}
if (nsites == 1) {
if (sysmp(MP_SAGET,MPSA_SINFO,si,sizeof(struct sysinfo)))
perrexit("");
if (sysmp(MP_SAGET,MPSA_MINFO,mi,sizeof(struct minfo)))
perrexit("");
if (sysmp(MP_SAGET,MPSA_RMINFO,ri,sizeof(struct rminfo)))
perrexit("");
if (sysmp(MP_SAGET,MPSA_DINFO,di,sizeof(struct dinfo)))
perrexit("");
if (sysmp(MP_SAGET,MPSA_SERR,err,sizeof(struct syserr)))
perrexit("");
for (i = 0; i < d->apstate; i++) {
/* The cpu stats are at the front of sysinfo */
if (sysmp(MP_SAGET1, MPSA_SINFO, &cst[i],
sizeof(struct sysinfo_cpu), i) < 0) {
continue;
}
}
}
else {
/* Get data for each cell */
#ifdef NOTYET
/* The performace of this looks terrible. It might be better
* to consolidate all this info into one structure to minimize
* the number of sysget calls.
*/
#endif
SGT_COOKIE_INIT(&ck);
if (sysget(SGT_SINFO, (char *)si, sysinfo_sz * nsites,
SGT_READ, &ck) < 0) {
perrexit("SGT_SINFO failed");
}
SGT_COOKIE_INIT(&ck);
if (sysget(SGT_MINFO, (char *)mi, (int)sizeof(struct minfo) * nsites,
SGT_READ, &ck) < 0) {
perrexit("SGT_MINFO failed");
}
SGT_COOKIE_INIT(&ck);
if (sysget(SGT_RMINFO, (char *)ri, (int)sizeof(struct rminfo) *
nsites, SGT_READ, &ck) < 0) {
perrexit("SGT_RMINFO failed");
}
SGT_COOKIE_INIT(&ck);
if (sysget(SGT_DINFO, (char *)di, (int)sizeof(struct dinfo) * nsites,
SGT_READ, &ck) < 0) {
perrexit("SGT_DINFO failed");
}
SGT_COOKIE_INIT(&ck);
if (sysget(SGT_SERR, (char *)err, (int)sizeof(struct syserr) *
nsites, SGT_READ, &ck) < 0) {
perrexit("SGT_SERR failed");
}
SGT_COOKIE_INIT(&ck);
if (sysget(SGT_SINFO_CPU, (char *)cst,
(int)sizeof(struct sysinfo_cpu) * ncpus,
SGT_READ | SGT_CPUS, &ck) < 0) {
perrexit("SGT_SINFO_CPU failed");
}
}
kread("vn_vnumber", (void **)&vn_vnumber, sizeof(vn_vnumber), 1);
kread("vn_epoch", (void **)&vn_epoch, sizeof(vn_epoch), 1);
kread("vn_nfree", (void **)&vn_nfree, sizeof(vn_nfree), 1);
kread("min_file_pages", (void **)&min_file_pages,
sizeof(min_file_pages), 1);
for (i = 0; i < nsites; i++) {
d[i].inodeovf = (long)err[i].inodeovf;
d[i].fileovf = (long)err[i].fileovf;
d[i].procovf = (long)err[i].procovf;
d[i].mszinode = (int)(vn_vnumber[i] - vn_epoch[i]);
d[i].szinode = d[i].mszinode - vn_nfree[i];
/* translate from clicks to disk blocks */
si[i].bswapin=(unsigned)(ctob(si[i].bswapin)/NBPSCTR); /*BSUZE to NBPSCTR*/
si[i].bswapout=(unsigned)(ctob(si[i].bswapout)/NBPSCTR);/*BSIZE to NBPSCTR*/
/* Fix availsmem to take into account min_file_pages cutoff */
ri[i].availsmem -= min_file_pages[i];
}
getgfxinfo();
}
#include <sys/iograph.h>
#ifdef SA_DEBUG
void
sadc_iotime_print(struct sgidio *ds)
{
printf("ds->sdio_lname = %s \n",ds->sdio_lname);
printf("io_ops=%d,io_misc=%d,io_qcnt=%d,io_unlog=%d,io_bcnt=%d\n"
",io_resp=%ld,io_act=%ld,io_wops=%d,io_wbcnt=%d\n",
ds->sdio_iotim.ios.io_ops,
ds->sdio_iotim.ios.io_misc,
ds->sdio_iotim.ios.io_qcnt,
ds->sdio_iotim.ios.io_unlog,
ds->sdio_iotim.io_bcnt,
ds->sdio_iotim.io_resp,
ds->sdio_iotim.io_act,
ds->sdio_iotim.io_wops,
ds->sdio_iotim.io_wbcnt);
}
#endif /* SA_DEBUG */
/*
* read an iotime struct for each drive and store its counters and name in d
*/
void
sgi_disk_read(void)
{
register int c;
struct disk_iostat_info *ds;
c = ndisk;
/*
* Allocate the table, if needed.
*/
if (!Sgidio) {
Sgidio = (struct sgidio *)calloc(1, c * sizeof(struct sgidio));
if (!Sgidio) {
fprintf(stderr, "No memory for sgidio table (%d)\n",
c);
perrexit("");
}
}
for (i = 0, ds = Ndiosetup; (i < c) && ds; i++, ds = ds->next) {
strncpy(SGIDIO_DNAME(Sgidio[i]), ds->diskname, SDIO_NAMESIZE);
SGIDIO_DNAME(Sgidio[i])[SDIO_NAMESIZE-1] = '\0';
dkiotime_neoget(ds->devicename, &(Sgidio[i].sdio_iotim));
#ifdef SA_DEBUG
sadc_iotime_print(&Sgidio[i]);
#endif
}
return;
}
/*
* This routine sets up the initial dio data structures
*/
/*ARGSUSED*/
void
sgi_dio_setup(int count)
{
/*
* Allocate space for the first NDEVS elements.
*/
AllocdNdiosetup = (struct disk_iostat_info *)
calloc(1, NDEVS * sizeof(struct disk_iostat_info));
if (!AllocdNdiosetup) {
fprintf(stderr, "No memory for disk_iostat_info table (%d)\n",
ndisk);
perrexit("");
}
/*
* Set the initial index to zero.
*/
NdiosetupIndex = 0;
return;
}
/*
* Allocate another element.
*/
struct disk_iostat_info *
sgi_dio_alloc()
{
static int count;
struct disk_iostat_info *ds;
#ifdef SA_DEBUG
{
static int call_num = 0;
printf("sgi_dio_alloc called %dth time\n",++call_num);
}
#endif /* SA_DEBUG */
/*
* Check to see if we're past the end of allocated elements.
*/
if (NdiosetupIndex >= NDEVS) {
/*
* We need to allocate a new chunk of elements.
*/
count++;
sgi_dio_setup(count);
}
/*
* We should have an element. These should be zero'd out by
* sgi_dio_setup.
*/
ds = &AllocdNdiosetup[NdiosetupIndex++];
if (Ndiosetup) {
CurNdiosetup->next = ds;
} else {
Ndiosetup = ds;
}
CurNdiosetup = ds;
/*
* Increment the # disks counter.
*/
ndisk++;
return ds;
}
void
write_rec(char *id, char *buf, int recsize, int nrec, char *errmessage)
{
struct rec_header header;
int bufsize = recsize * nrec;
strncpy(header.magic, SAR_MAGIC, SAR_MAGIC_LEN);
strncpy(header.id, id, SAR_MAGIC_LEN);
header.recsize = recsize;
header.nrec = nrec;
if (write(fp, &header, sizeof(header)) != sizeof(header)) {
perrexit("Can't write rec header");
}
if (write(fp, buf, bufsize) != bufsize) {
perrexit(errmessage);
}
}
void
kread(char *sym, void **buf, int len, int required)
{
sgt_cookie_t ck;
sgt_info_t info;
int i;
int flags;
for (i = 0; i < num_knames; i++) {
if (!strcmp(sym, knames[i].name)) {
break;
}
}
if (i == num_knames) {
fprintf(stderr, "kread: %s is unknown\n", sym);
perrexit("");
}
if (knames[i].count < 0) {
/* Find out how many of these exist */
SGT_COOKIE_INIT(&ck);
SGT_COOKIE_SET_KSYM(&ck, sym);
if (sysget(SGT_KSYM, (char *)&info, sizeof(info), SGT_INFO,
&ck) < 0) {
if (!required && errno == ENOENT) {
/* sym not configured so skip it */
knames[i].count = 0;
return;
}
fprintf(stderr, "sgt_sread: SGT_INFO of %s failed\n",
sym);
perrexit("sysget");
}
knames[i].count = info.si_num;
*buf = (void *)calloc(len, info.si_num);
if (!*buf) {
fprintf(stderr, "kread: can't alloc space for %s\n",
sym);
perrexit("");
}
}
else if (!knames[i].count) {
/* Not configured */
return;
}
flags = SGT_READ;
SGT_COOKIE_INIT(&ck);
SGT_COOKIE_SET_KSYM(&ck, sym);
if (sysget(SGT_KSYM, *buf, len * knames[i].count, flags, &ck) < 0) {
fprintf(stderr, "kread: SGT_READ for %s failed\n", sym);
perrexit("sysget");
}
}
void
sgi_sys_setup(void)
{
sgt_cookie_t ck;
sgt_info_t info;
SGT_COOKIE_INIT(&ck);
if (sysget(SGT_SINFO, (char *)&info, sizeof(info), SGT_INFO, &ck) < 0) {
perrexit("SGT_SINFO failed");
}
nsites = info.si_num;
d = (struct sa *)calloc(sizeof(struct sa), nsites);
if (!d) {
perrexit("Can't alloc space for sa");
}
/* Allocate space for structures that are cpu related. We make
* a special effort for sysinfo to use the size returned by
* the kernel instead of the one we are compiled with.
*/
cstat = (sgt_stat_t *)calloc(sizeof(sgt_stat_t), nsites);
if (!cstat) {
perrexit("Can't alloc space for cstat");
}
if (nsites > 1) {
SGT_COOKIE_INIT(&ck);
if (sysget(SGT_SINFO, (char *)cstat,
(int)sizeof(sgt_stat_t) * nsites,
SGT_STAT | SGT_CPUS, &ck) < 0) {
perrexit("SGT_STAT failed");
}
}
else {
cstat[0].info.si_num = (int)sysmp(MP_NPROCS);
}
SGT_COOKIE_INIT(&ck);
if (nsites > 1) {
SGT_COOKIE_SET_CELL(&ck, SC_MYCELL);
}
if (sysget(SGT_SINFO, (char *)&info, sizeof(info), SGT_INFO, &ck) < 0) {
perrexit("SGT_SINFO failed");
}
sysinfo_sz = info.si_size;
si = (struct sysinfo *)calloc(sysinfo_sz, nsites);
if (!si) {
perrexit("Can't alloc space for sysinfo");
}
mi = (struct minfo *)calloc(sizeof(struct minfo), nsites);
if (!mi) {
perrexit("Can't alloc space for minfo");
}
di = (struct dinfo *)calloc(sizeof(struct dinfo), nsites);
if (!di) {
perrexit("Can't alloc space for dinfo");
}
err = (struct syserr *)calloc(sizeof(struct syserr), nsites);
if (!err) {
perrexit("Can't alloc space for err");
}
ri = (struct rminfo *)calloc(sizeof(struct rminfo), nsites);
if (!ri) {
perrexit("Can't alloc space for rminfo");
}
}