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

1494 lines
28 KiB
C

/*
* bufview - file system buffer cache activity monitor
*/
#define _KMEMUSER
#include <sys/types.h>
#include <sys/times.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/buf.h>
#include <sys/sysmp.h>
#include <sys/param.h>
#include <sys/ksa.h>
#include <sys/sysinfo.h>
#include <sys/sysget.h>
#include <sys/syssgi.h>
#include <assert.h>
#include <values.h>
#include <dirent.h>
#include <stdio.h>
#include <mntent.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include "display.h"
#include "bv.h"
#include "utils.h"
#include "machine.h"
#define KMEM "/dev/kmem"
slist_t shead, spark;
char header_aggregate[] =
" VNUMBER NAME DEVICE FSTYP NBUF SIZE DELWRI LOW HIGH";
/*
012345678901234567890123456789012345678901234567890123456789012345678901234567
10 20 30 40 50 60 70
*/
#define Buf_data_format \
"%10llx %-20.20s %-8.8s %-4.4s %5d %6.6s %6.6s %6.6s %6.6s"
#define Buf_ctl_format \
" system %-8.8s %-4.4s %5d %6.6s %6.6s %6.6s %6.6s"
/*
012345678901234567890123456789012345678901234567890123456789012345678901234567
10 20 30 40 50 60 70
*/
char header_separate[] =
" VNUMBER NAME/REF DEVICE FSTYP BUF SIZE OFFSET AGE FLAGS";
#define Buf_data_format_separate \
"%10llx %-13.13s %-8.8s %-4.4s %5d %6.6s %6.6s %10d %s"
#define Buf_ctl_format_separate \
" system %2d %-8.8s %-4.4s %5d %6.6s %6.6s %10d %s"
static char fmt[MAX_COLS + 2];
/* useful externals */
extern int errno;
extern char *sys_errlist[];
extern char *myname;
static int kmem;
static off64_t v_offset;
static float irix_ver; /* for easy numeric comparison */
static bnode_t *bnodebase;
static binfo_t *binfobase;
int pagesize;
/*
* The following arrays are mostly filled in by get_system_info.
*/
char *sysbuf_names[] = {
"J Bufs ", /* total # of buffers */
"P Mem ", /* system memory */
"P SMem ", /* memory used currently by system buffers */
"J Sys ", /* # of buffers mapping system data */
NULL
};
char *databuf_names[] = {
"J Data ", /* # of buffers mapping file data */
"P DMin ", /* minimum amount of memory file buffers */
/* can consume without regard for others */
"P DMem ", /* memory used currently by file buffers */
"P MaxR ", /* availrmem */
"P MaxS ", /* availsmem */
NULL
};
char *emptybuf_names[] = {
"J Empty ", /* # of empty buffers */
"P FrMin ", /* min_free_pages * pagesize */
"P MFree ", /* free memory */
"P MFreD ", /* free memory that caches file data */
"J Inact ", /* # of inactive buffers */
"K deact ", /* buffers inactivated per second */
NULL
};
char *getbuf_names[] = {
"K gtblk ", /* getblk calls per second */
"K found ", /* buffers found in cache by getblk, per sec */
"K relse ", /* buffers released because of getblk calls */
"K write ", /* delwri buffers written " " " " */
"K stky ", /* buffers refs decremented " " " " */
"K react ", /* buffers un-inactivated per second */
NULL
};
/*
* The bflag_list is ordered according to the enum in buf.h. The
* bufview flags must be first and in enum order since this table
* is indexed for flag comparisons.
*/
bprune_t bflag_list[] = {
{ 0, BV_FS_NONE, 1, ""},
{ 0, BV_FS_INO, B_FS_INO, "ino"},
{ 0, BV_FS_INOMAP, B_FS_INOMAP, "inomap"},
{ 0, BV_FS_DIR_BTREE, B_FS_DIR_BTREE, "dir_bt"},
{ 0, BV_FS_MAP, B_FS_MAP, "map"},
{ 0, BV_FS_ATTR_BTREE, B_FS_ATTR_BTREE, "attr_bt"},
{ 0, BV_FS_AGI, B_FS_AGI, "agi"},
{ 0, BV_FS_AGF, B_FS_AGF, "agf"},
{ 0, BV_FS_AGFL, B_FS_AGFL, "agfl"},
{ 0, BV_FS_DQUOT, B_FS_DQUOT, "dquot"},
{ 1, B_DELWRI, B_DELWRI, "dw"},
{ 1, B_BUSY, B_BUSY, "bsy"},
{ 1, B_SWAP, B_SWAP, "swp"},
{ 1, B_DELALLOC, B_DELALLOC, "da"},
{ 1, B_ASYNC, B_ASYNC, "as"},
{ 1, B_NFS_UNSTABLE, B_NFS_UNSTABLE, "nc"},
{ 1, B_NFS_ASYNC, B_NFS_ASYNC, "na"},
{ 1, B_INACTIVE, B_INACTIVE, "inact"},
{ 1, 0, 0, NULL } ,
};
#define STAT0 0
#define STAT1 1
#define STAT2 2
#define STAT3 3
#define STAT4 4
#define STAT5 5
/* abbreviated process states */
static char *state_abbrev[] =
{ "sys", "data", "empty", "inactive", NULL };
#define BS_METADATA 0
#define BS_DATA 1
#define BS_EMPTY 2
#define BS_INACTIVE 3
#define BS_BUSY 4
/*
* buf_state
* map the buffer state to an integer.
* used as an index into state_abbrev[]
* as well as an "order" key
*/
static int
buf_state(binfo_t *bp)
{
int flags = bp->binfo_flags;
if (flags & B_PAGEIO)
return BS_DATA;
if (flags & B_INACTIVE)
return BS_INACTIVE;
if (bp->binfo_dev == (dev_t)NODEV)
return BS_EMPTY;
return BS_METADATA;
}
/*
* allocate space for:
*
* binfo array
* bnode_t pointers to the above (used for sorting)
*
* Exactly n_bufs binfos are allocated, and to make things simple,
* exactly n_bufs sort bins will be allocated. The sort bins are
* where per-vnode and per-device buffers values are accumulated.
*/
void
allocate_buf_tables(void)
{
extern int n_bufs;
if (bnodebase != NULL)
return;
if (n_bufs <= 0) {
fprintf(stderr, "%s: nbufs %d?\n", myname, n_bufs);
exit(1);
}
binfobase = (binfo_t *) malloc(n_bufs * sizeof(binfo_t));
bnodebase = (bnode_t *)malloc(n_bufs * sizeof(bnode_t));
if (binfobase == NULL || bnodebase == NULL) {
(void) fprintf(stderr,
"%s: malloc: out of memory, n_buf = %d\n",
myname, n_bufs);
exit (1);
}
}
int n_bufs;
struct getblkstats *ogetblkp, *ngetblkp;
int getblkstatsz;
int
machine_init(void)
{
char tmpbuf[20];
pagesize = getpagesize();
if ((kmem = open(KMEM, O_RDONLY)) == -1)
{
perror(KMEM);
return -1;
}
if ((v_offset = sysmp(MP_KERNADDR, MPKA_VAR)) == -1)
{
perror("sysmp(MP_KERNADDR, MPKA_VAR)");
return -1;
}
getblkstatsz = (int)sysmp(MP_SASZ, MPSA_BUFINFO);
ngetblkp = calloc(1, getblkstatsz);
ogetblkp = calloc(1, getblkstatsz);
/*
* n_buf is the first integer in struct v
*/
(void) getkval(v_offset, (int *) &n_bufs, sizeof(n_bufs), "!v.v_buf");
allocate_buf_tables();
return (0);
}
int min_file_pages;
int min_free_pages;
long nticks, oticks, ticks;
void
get_system_info(si)
struct system_info *si;
{
int i;
struct rminfo realmem;
struct getblkstats *getblkp;
sgt_cookie_t ck;
struct tms tbuf;
extern void stg_sread(char *, void *, int);
nticks = times(&tbuf);
ticks = nticks - oticks;
oticks = nticks;
if (ticks < HZ)
ticks = HZ;
SGT_COOKIE_INIT(&ck);
getblkp = ogetblkp;
ogetblkp = ngetblkp;
ngetblkp = getblkp;
if (sysget(SGT_BUFINFO, (char *)getblkp, getblkstatsz,
SGT_READ|SGT_SUM, &ck) < 0)
{
perror("sysget");
fprintf(stderr, "SGT_READ of SGT_BUFINFO failed\n");
exit(1);
}
if (sysmp(MP_SAGET, MPSA_RMINFO, &realmem, sizeof(realmem)) == -1) {
perror("sysmp(MP_SAGET, MPSA_RMINFO)");
return;
}
stg_sread("min_file_pages",
(void *)&min_file_pages, sizeof(min_file_pages));
stg_sread("min_free_pages",
(void *)&min_free_pages, sizeof(min_free_pages));
/* first (buffer) column */
sysbuf_stats[STAT0] = n_bufs;
sysbuf_stats[STAT1] = realmem.physmem;
sysbuf_stats[STAT2] = realmem.bufmem;
sysbuf_stats[STAT3] = system_info.si_metabufs;
databuf_stats[STAT0] = system_info.si_fsbufs;
databuf_stats[STAT1] = min_file_pages;
databuf_stats[STAT2] = realmem.chunkpages;
databuf_stats[STAT3] = realmem.ravailrmem;
databuf_stats[STAT4] = realmem.availsmem;
emptybuf_stats[STAT0] = system_info.si_emptybufs;
emptybuf_stats[STAT1] = min_free_pages;
emptybuf_stats[STAT2] = realmem.freemem;
emptybuf_stats[STAT3] = realmem.freemem - realmem.emptymem;
emptybuf_stats[STAT4] = system_info.si_inactivebufs;
emptybuf_stats[STAT5] = (ngetblkp->inactive - ogetblkp->inactive) /
(ticks/HZ);
getbuf_stats[STAT0] = (ngetblkp->getblks - ogetblkp->getblks) /
(ticks/HZ);
getbuf_stats[STAT1] = (ngetblkp->getfound - ogetblkp->getfound) /
(ticks/HZ);
getbuf_stats[STAT2] =
(ngetblkp->getfreerelse - ogetblkp->getfreerelse) / (ticks/HZ);
getbuf_stats[STAT3] =
(ngetblkp->getfreedelwri - ogetblkp->getfreedelwri) /
(ticks/HZ);
getbuf_stats[STAT4] = (ngetblkp->getfreeref - ogetblkp->getfreeref) /
(ticks/HZ);
getbuf_stats[STAT5] = (ngetblkp->active - ogetblkp->active) /
(ticks/HZ);
#ifdef notdef
getbuf_stats[STAT5] =
(ngetblkp->getfreeempty - ogetblkp->getfreeempty) / (ticks/HZ);
#endif
return;
}
/*
* this code is 'borrowed' from osview
*/
typedef struct kname {
char * kn_name;
int kn_count;
} kname_t;
kname_t knames[] = {
{ KSYM_MIN_FILE_PAGES, -1 },
{ KSYM_MIN_FREE_PAGES, -1 },
{ KSYM_PDWRIMEM, -1 },
};
int num_knames = sizeof(knames) / sizeof(struct kname);
void
stg_sread(char *s, void *buf, int len)
{
sgt_cookie_t ck;
sgt_info_t info;
int i;
int flags;
for (i = 0; i < num_knames; i++) {
if (!strcmp(s, knames[i].kn_name)) {
break;
}
}
if (i == num_knames) {
fprintf(stderr, "stg_sread: %s is unknown\n", s);
quit(2);
}
if (knames[i].kn_count < 0) {
/* Find out how many of these exist */
SGT_COOKIE_INIT(&ck);
SGT_COOKIE_SET_KSYM(&ck, knames[i].kn_name);
if (sysget(SGT_KSYM, (char *)&info, sizeof(info), SGT_INFO, &ck) < 0) {
perror("sysget");
fprintf(stderr, "sgt_sread: SGT_INFO of %s failed\n",s);
quit(2);
}
knames[i].kn_count = info.si_num;
}
SGT_COOKIE_INIT(&ck);
SGT_COOKIE_SET_KSYM(&ck, knames[i].kn_name);
if (knames[i].kn_count > 1) {
/* There appear to be copies of this object on each cell.
* We will get the sum.
*/
flags = SGT_READ | SGT_SUM;
} else
flags = SGT_READ;
if (sysget(SGT_KSYM, buf, len, flags, &ck) < 0) {
perror("sysget");
fprintf(stderr, "stg_sread: SGT_READ for %s failed\n", s);
quit(2);
}
}
void
count_nodes(bnode_t *bp, int *cnt)
{
assert(bp);
if (bp->bn_prev)
count_nodes(bp->bn_prev, cnt);
(*cnt)++;
if (bp->bn_next)
count_nodes(bp->bn_next, cnt);
}
void
btree_sort_add(bnode_t **head, bnode_t *bp)
{
bnode_t *nodep;
slist_t *slp;
int direction;
bp->bn_next = bp->bn_prev = NULL;
if ((nodep = *head) == NULL)
{
*head = bp;
return;
}
for (slp = shead.sl_next; slp != &shead; slp = slp->sl_next)
{
direction = (*slp->sl_routine)(nodep, bp, slp->sl_tag);
if (direction == SL_CMP_GT) {
if (nodep->bn_prev) {
btree_sort_add(&nodep->bn_prev, bp);
} else {
nodep->bn_prev = bp;
}
return;
}
if (direction == SL_CMP_LT) {
if (nodep->bn_next) {
btree_sort_add(&nodep->bn_next, bp);
} else {
nodep->bn_next = bp;
}
return;
}
}
fprintf(stderr, "no comparisons -- bp %x nodep %x\n", bp, nodep);
exit(1);
}
/*
* Here we're walking through the bnode_t tree that's sorted in
* bn_key order. We're going to strip the nodes from the ends
* of the tree and put them onto *put, sorted in output order.
*/
void
sort_bnode_tree(bnode_t *nodep, bnode_t **put)
{
bnode_t *bp;
if (bp = nodep->bn_prev)
sort_bnode_tree(bp, put);
if (bp = nodep->bn_next)
sort_bnode_tree(bp, put);
btree_sort_add(put, nodep);
}
int collate_line;
#include "screen.h"
void
print_tree(bnode_t *nodep)
{
bnode_t *bp;
if (bp = nodep->bn_prev)
print_tree(bp);
Move_to(0, collate_line++);
printf("%x / %llx: %d [prev %x next %x]",
nodep, nodep->bn_key, nodep->bn_bufcnt,
nodep->bn_prev, nodep->bn_next);
if (bp = nodep->bn_next)
print_tree(bp);
}
int
btree_collate_add(bnode_t **head, bnode_t *bp)
{
bnode_t *nodep;
assert(bp->bn_prev == NULL);
assert(bp->bn_next == NULL);
if ((bst.bflags || bst.bvtype) &&
!(bp->bn_flags & bst.bflags) &&
!(REMAPFLAG & bst.bvtype))
return 0;
if ((nodep = *head) == NULL)
{
*head = bp;
bp->bn_bufcnt = 1;
return 1;
}
if (bp->bn_key < nodep->bn_key)
{
if (nodep->bn_prev)
return btree_collate_add(&nodep->bn_prev, bp);
else {
nodep->bn_prev = bp;
bp->bn_bufcnt = 1;
return 1;
}
}
if (bp->bn_key > nodep->bn_key)
{
if (nodep->bn_next)
return btree_collate_add(&nodep->bn_next, bp);
else {
nodep->bn_next = bp;
bp->bn_bufcnt = 1;
return 1;
}
}
/*
* Got a match -- add data.
*/
nodep->bn_mem += bp->bn_mem;
nodep->bn_dmem += bp->bn_dmem;
nodep->bn_bufcnt++;
/*
* If buffer representing nodep was locked at the time the
* kernel looked at it, the file name will be a construct.
* Patch it now.
*/
if ((nodep->bn_flags & B_PAGEIO) && !strcmp(nodep->bn_fname, "??"))
{
strncpy(nodep->bn_fname, bp->bn_fname, NE_NAME);
nodep->bn_fname[NE_NAME] = '\0';
}
if (nodep->bn_low > bp->bn_low)
nodep->bn_low = bp->bn_low;
if (nodep->bn_high < bp->bn_high)
nodep->bn_high = bp->bn_high;
return 0;
}
/*
* First pass of sort. Push binfo data into bpnodes.
* Generate simple sort key, then build tree in key order.
* If we're aggregating info, it is done here.
*
* Later, the bpnodes get sorted for output.
*/
void
collate_buf_table(
binfo_t *binfop,
bnode_t *bnodep,
bnode_t **bhp,
struct system_info *si)
{
int i;
binfo_t *bp;
bnode_t *bn;
extern void dev_to_names(dev_t, bnode_t *);
extern char *get_fsname(int);
extern int dev_list_rebuilt;
*bhp = NULL;
si->si_busers =
si->si_fusers =
si->si_fsbufs =
si->si_metabufs =
si->si_inactivebufs =
si->si_busybufs =
si->si_emptybufs = 0;
si->si_fssize =
si->si_fsdsize =
si->si_metasize =
si->si_metadsize = 0;
bzero(bnodep, n_bufs * sizeof(*bnodep));
for (bp = binfop, bn = bnodep, i = 0; i < n_bufs; i++, bp++, bn++)
{
int64_t key;
if (bp->binfo_flags & B_BUSY)
si->si_busybufs++;
switch (buf_state(bp))
{
case BS_DATA:
si->si_fsbufs++;
si->si_fssize += bp->binfo_size;
if (bp->binfo_flags & B_DELWRI)
{
si->si_fsdsize += bp->binfo_size;
bn->bn_dmem = bp->binfo_size;
}
if (bst.fsdata)
{
bn->bn_mem = bp->binfo_size;
bn->bn_low = BBTOB(bp->binfo_offset);
bn->bn_high = BBTOB(bp->binfo_offset) +
bp->binfo_size;
bn->bn_flags = bp->binfo_flags;
bn->bn_bvtype = bp->binfo_bvtype;
dev_to_names(bp->binfo_dev, bn);
if (prune_dev(bn->bn_devname))
continue;
strncpy(bn->bn_fname, bp->binfo_name, NE_NAME);
bn->bn_fname[NE_NAME] = '\0';
bn->bn_fstype = get_fsname(bp->binfo_fstype);
bn->bn_vnumber = bp->binfo_vnumber;
bn->bn_start = bp->binfo_start;
if (bst.separate)
{
key = bp->binfo_start;
bn->bn_key = (key << 32) | i;
}
else
{
bn->bn_key = bp->binfo_vp;
}
if (btree_collate_add(bhp, bn))
{
si->si_busers++;
si->si_fusers++;
}
}
break;
case BS_METADATA:
si->si_metabufs++;
si->si_metasize += bp->binfo_size;
if (bp->binfo_flags & B_DELWRI)
{
si->si_metadsize += bp->binfo_size;
bn->bn_dmem = bp->binfo_size;
}
if (bst.system)
{
bn->bn_mem = bp->binfo_size;
bn->bn_low = BBTOB(bp->binfo_blkno);
bn->bn_high = bn->bn_low + bp->binfo_size;
bn->bn_flags = bp->binfo_flags;
bn->bn_bvtype = bp->binfo_bvtype;
dev_to_names(bp->binfo_dev, bn);
if (prune_dev(bn->bn_devname))
continue;
bn->bn_start = bp->binfo_start;
bn->bn_fpass = bp->binfo_fpass;
if (bst.separate)
{
key = bp->binfo_start;
bn->bn_key = (key << 32) | i;
}
else
{
bn->bn_key = bp->binfo_dev;
}
if (bn->bn_key && btree_collate_add(bhp, bn))
si->si_busers++;
}
break;
case BS_INACTIVE:
si->si_inactivebufs++;
if (bst.bflags & B_INACTIVE)
{
key = bp->binfo_start;
bn->bn_key = (key << 32) | i;
bn->bn_mem = bp->binfo_size;
bn->bn_low = bp->binfo_blkno;
bn->bn_high = bp->binfo_blkno
+ BTOBB(bp->binfo_size);
bn->bn_flags = bp->binfo_flags;
bn->bn_bvtype = bp->binfo_bvtype;
bn->bn_vnumber = 0;
bn->bn_start = bp->binfo_start;
if (btree_collate_add(bhp, bn))
si->si_busers++;
}
break;
case BS_EMPTY:
si->si_emptybufs++;
break;
}
}
}
void
get_buffer_info(struct system_info *si)
{
bnode_t *bag;
static char first_screen = 1;
/* read all the buffer structures */
if (syssgi(SGI_BUFINFO, 0, n_bufs, binfobase) < 0) {
perror("syssgi(SGI_BUFINFO)");
exit(1);
}
collate_buf_table(binfobase, bnodebase, &bag, si);
si->si_bufs = NULL;
if (bag)
{
sort_bnode_tree(bag, &si->si_bufs);
}
first_screen = 0;
}
char *fstype_names;
int fstype_index;
#define FS_EXTRA 10
#define MAX_INDEX 4096
char *
get_fsname(int index)
{
char *fsname;
if (index > MAX_INDEX)
{
fprintf(stderr, "fs index %d\n", index);
return "huh?";
}
if (index >= fstype_index)
{
if (fstype_names)
free(fstype_names);
fstype_index = index + FS_EXTRA;
fstype_names = calloc(FSTYPSZ, fstype_index);
}
fsname = fstype_names + (FSTYPSZ * index);
if (*fsname == '\0')
{
if (sysfs(GETFSTYP, index, fsname))
strcpy(fsname, "???");
else
*(fsname+4) = '\0';
}
return fsname;
}
/*
* bufview keeps a list of the mounted file system names,
* which are displayed in the DEVICE column.
* The list of devices and their corresponding names are
* first built via getmntent(). If a buffer contains a
* device not on the list, the list is rebuilt (in case there's
* a newly-mounted file system). If that doesn't work, a fake
* device name is created for it.
*/
mnt_entry_t *mnt_head;
void
devlist_init(void)
{
FILE *f;
struct mntent *mntent;
struct stat statb;
mnt_entry_t *me;
char namebuf[256];
char *cp, *cp_slash;
int i;
int fd;
mnt_head = NULL;
f = setmntent("/etc/mtab", "r");
while (mntent = getmntent(f))
{
#ifdef notdef
fprintf(stderr, "stat %s %s\n",
mntent->mnt_dir,
mntent->mnt_fsname);
#endif
if (stat(mntent->mnt_dir, &statb))
{
fprintf(stderr, "couldn't stat %s\n", mntent->mnt_dir);
perror("stat");
continue;
}
if (statb.st_dev == 0)
continue;
me = malloc(sizeof(mnt_entry_t));
i = strlen(mntent->mnt_type);
if (i > FSTYPSZ)
i = FSTYPSZ;
strncpy(me->me_fstype, mntent->mnt_type, i);
me->me_fstype[3] = '\0';
strcpy(namebuf, mntent->mnt_fsname);
for (cp = namebuf; *cp; cp++)
;
cp_slash = NULL;
for (i = 0; i < ME_NAME; cp--, i++)
{
if (cp == namebuf)
break;
/*
* remember last pathname slash
*/
if (*cp == '/')
cp_slash = cp;
}
if (cp_slash && cp != namebuf)
cp = cp_slash+1;
strncpy(me->me_name, (const char *)cp, ME_NAME);
me->me_name[ME_NAME] = '\0';
me->me_dev = statb.st_dev;
me->me_next = mnt_head;
mnt_head = me;
#ifdef notdef
new_message(MT_standout, "fsname %s dir %s\n",
mntent->mnt_fsname,
mntent->mnt_dir);
#endif
}
(void) endmntent(f);
}
int dev_list_rebuilt;
void
dev_to_names(dev_t dev, bnode_t *bn)
{
mnt_entry_t *me;
char nameb[64];
for ( ; ; )
{
for (me = mnt_head; me; me = me->me_next)
{
if (me->me_dev == dev)
{
bn->bn_devname = me->me_name;
bn->bn_fstype = me->me_fstype;
return;
}
}
if (dev_list_rebuilt)
break;
dev_list_rebuilt = 1;
devlist_destroy();
devlist_init();
}
me = malloc(sizeof(mnt_entry_t));
sprintf(nameb, "%x", dev);
strncpy(me->me_name, nameb, ME_NAME);
me->me_name[ME_NAME] = '\0';
me->me_next = mnt_head;
strcpy(me->me_fstype, "???");
me->me_dev = dev;
mnt_head = me;
#ifdef notdef
new_message(MT_standout, "me_name %s dev %x\n",
me->me_name, me->me_dev);
#endif
bn->bn_devname = me->me_name;
bn->bn_fstype = me->me_fstype;
}
/*
* bufview allows the caller to specify which devices buffes to display
* (the default is all).
* If devices are specified, they're put on the dphead list.
* If anything's on the list, and a particular device isn't,
* the buffer won't get displayed.
*/
typedef struct dev_prune {
struct dev_prune *d_next;
char *d_name;
} dev_prune_t;
dev_prune_t *dphead;
int
prune_dev(char *devname)
{
dev_prune_t *dp;
int rv = dphead ? 1 : 0;
for (dp = dphead; dp; dp = dp->d_next)
{
if (!strcmp(devname, dp->d_name))
return 0;
}
return rv;
}
int
display_devs(void)
{
int n = 0;
if (dphead)
{
dev_prune_t *dp;
n += printf(" Display devices:");
for (dp = dphead; dp; dp = dp->d_next)
{
n += printf(" %s", dp->d_name);
}
n += printf(".");
}
return n;
}
void
display_only_dev(char *dpname)
{
char *cp;
dev_prune_t *dp;
for (dp = dphead; dp; dp = dp->d_next)
{
if (!strcmp(dpname, dp->d_name))
return;
}
cp = malloc(strlen(dpname)+1);
dp = malloc(sizeof(dev_prune_t));
strcpy(cp, dpname);
dp->d_name = cp;
dp->d_next = dphead;
dphead = dp;
}
void
display_all_devs(void)
{
dev_prune_t *dp;
while (dp = dphead)
{
dphead = dp->d_next;
free(dp->d_name);
free(dp);
}
}
void
devlist_destroy(void)
{
mnt_entry_t *me;
while (me = mnt_head)
{
mnt_head = me->me_next;
free(me);
}
}
/*
* Routines to format a buffer display line.
* There are separate format routines for data and fsctl buffers;
* each routine then choosed between aggregate or itemized mode.
*/
char *
format_data_bnode(bnode_t *bp)
{
/* format this entry */
if (bst.separate)
{
sprintf(fmt,
Buf_data_format_separate,
bp->bn_vnumber,
bp->bn_fname,
bp->bn_devname,
bp->bn_fstype,
(int)bp->bn_key & 0xffffff,
konvert(bp->bn_mem),
konvert(bp->bn_low),
bp->bn_start,
display_flags(bp->bn_flags,
REMAPFLAG, " "));
}
else
{
/* format this entry */
sprintf(fmt,
Buf_data_format,
bp->bn_vnumber,
bp->bn_fname,
bp->bn_devname,
bp->bn_fstype,
bp->bn_bufcnt,
konvert(bp->bn_mem),
konvert(bp->bn_dmem),
konvert(bp->bn_low),
konvert(bp->bn_high));
}
/* return the result */
return (fmt);
}
char *
format_ctl_bnode(bnode_t *bp)
{
/* format this entry */
if (bst.separate)
{
sprintf(fmt,
Buf_ctl_format_separate,
bp->bn_fpass,
bp->bn_devname,
bp->bn_fstype,
(int)bp->bn_key & 0xffffff,
konvert(bp->bn_mem),
konvert(bp->bn_low),
bp->bn_start,
display_flags(bp->bn_flags,
REMAPFLAG, " "));
}
else
{
/*
* Printing aggregated buffer data.
*/
sprintf(fmt,
Buf_ctl_format,
bp->bn_devname,
bp->bn_fstype,
bp->bn_bufcnt,
konvert(bp->bn_mem),
konvert(bp->bn_dmem),
konvert(bp->bn_low),
konvert(bp->bn_high));
}
/* return the result */
return (fmt);
}
static int display_line;
static int display_n;
void
display_bnodes(bnode_t *bp, int (*display_buf)())
{
assert(bp);
if (!bp) {
fprintf(stderr, "in display_bnodes without a bnode\n");
exit(1);
}
if (bp->bn_next) {
display_bnodes(bp->bn_next, display_buf);
if (display_n <= 0)
return;
}
if (bp->bn_flags & B_PAGEIO)
{
(*display_buf)(display_line++, format_data_bnode(bp));
}
else
{
(*display_buf)(display_line++, format_ctl_bnode(bp));
}
if (--display_n <= 0)
return;
if (bp->bn_prev)
display_bnodes(bp->bn_prev, display_buf);
}
/*
* print up to 'active_users' bnodes, in size order
* XXX Debugging.
*/
void
display_buffer_users(int active_users, int (*display_buf)())
{
int cnt = 0;
display_n = active_users;
display_line = 0;
if (system_info.si_bufs && (active_users > 0))
display_bnodes( system_info.si_bufs,
display_buf);
}
/*
* getkval(offset, ptr, size, refstr) - get a value out of the kernel.
* "offset" is the byte offset into the kernel for the desired value,
* "ptr" points to a buffer into which the value is retrieved,
* "size" is the size of the buffer (and the object to retrieve),
* "refstr" is a reference string used when printing error meessages,
* if "refstr" starts with a '!', then a failure on read will not
* be fatal (this may seem like a silly way to do things, but I
* really didn't want the overhead of another argument).
*/
int
getkval(off64_t offset, int *ptr, int size, char *refstr)
{
if (lseek64(kmem, offset, SEEK_SET) == -1) {
if (*refstr == '!')
refstr++;
(void) fprintf(stderr, "%s: %s: lseek to %s: %s\n",
myname, KMEM, refstr, strerror(errno));
exit(0);
}
if (read(kmem, (char *) ptr, size) == -1) {
if (*refstr == '!')
return (0);
else {
(void) fprintf(stderr,
"%s: %s: reading %s at 0x%llx: %s [%d]\n",
myname, KMEM, refstr, offset,
strerror(errno), errno);
exit(0);
}
}
return (1);
}
sltag_t
s_nbuf(bnode_t *t, bnode_t *bp, sltag_t tag)
{
int64_t n;
assert(tag != 0);
if (n = (t->bn_bufcnt - bp->bn_bufcnt)) {
return (n > 0) ? tag : -tag;
}
return 0;
}
sltag_t
s_size(bnode_t *t, bnode_t *bp, sltag_t tag)
{
int64_t n;
assert(tag != 0);
if (n = (t->bn_mem - bp->bn_mem)) {
return (n > 0) ? tag : -tag;
}
return 0;
}
sltag_t
s_key(bnode_t *t, bnode_t *bp, sltag_t tag)
{
int64_t n;
assert(tag != 0);
if (n = (t->bn_key - bp->bn_key)) {
return (n > 0) ? tag : -tag;
}
assert(0);
return 0;
}
sltag_t
s_age(bnode_t *t, bnode_t *bp, sltag_t tag)
{
int64_t n;
assert(tag != 0);
if (n = (t->bn_start - bp->bn_start)) { /* for now */
return (n > 0) ? tag : -tag;
}
return 0;
}
#define SL_NBUF 0
#define SL_SIZE 1
#define SL_AGE 2
#define SL_KEY 3
slist_t sort_list[] = {
{ s_nbuf, SL_CMP_GT, SF_AGG,
"m", /* most */
"l", /* least */
NULL, NULL} ,
{ s_size, SL_CMP_GT, SF_NONE,
"b", /* biggest */
"s", /* smallest */
NULL, NULL} ,
{ s_age, SL_CMP_GT, SF_IND,
"n", /* newest */
"o", /*oldest */
NULL, NULL} ,
{ s_key, SL_CMP_GT, SF_NONE,
" generic sort key", " ",
NULL, NULL} ,
{ NULL, SL_CMP_EQ, SF_NONE,
" ", " ",
NULL, NULL} ,
};
char dorder_buffer[MAX_COLS];
char *
display_order(void)
{
char *to = dorder_buffer;
const char *from;
slist_t *sp;
int cnt;
int first = 1;
for (sp = shead.sl_next; sp != &shead; sp = sp->sl_next)
{
if (sp->sl_tag == SL_CMP_GT)
from = sp->sl_sup;
else
from = sp->sl_inf;
if (*from == ' ')
break;
if (!first)
*to++ = '.';
else
first = 0;
cnt = strlen(from);
strncpy(to, from, cnt);
to += cnt;
}
*to++ = '.';
*to = '\0';
return dorder_buffer;
}
void
slist_delete(slist_t *sentry)
{
slist_t *sp = sentry->sl_prev;
slist_t *sn = sentry->sl_next;
sp->sl_next = sn;
sn->sl_prev = sp;
}
void
slist_insert(slist_t *slp, slist_t *sentry)
{
slist_t *sn;
sn = slp->sl_next;
slp->sl_next = sentry;
sentry->sl_next = sn;
sentry->sl_prev = slp;
sn->sl_prev = sentry;
}
/*
* Inialize, by hand, the sort list for buffer printing.
*/
void
sortlist_init(void)
{
shead.sl_next = shead.sl_prev = &shead;
spark.sl_next = spark.sl_prev = &spark;
slist_insert(&shead, &sort_list[SL_KEY]);
slist_insert(&shead, &sort_list[SL_SIZE]);
slist_insert(&shead, &sort_list[SL_NBUF]);
slist_insert(&spark, &sort_list[SL_AGE]);
}
void
slist_use_only_attr(short attr)
{
slist_t *sp;
short attr_not;
switch (attr)
{
case SF_AGG:
bst.separate = 0;
bst.bflags = 0;
bst.bvtype = 0;
attr_not = SF_IND;
break;
case SF_IND:
bst.separate = 1;
attr_not = SF_AGG;
break;
default:
return;
}
reset_display();
again:
for (sp = shead.sl_next; sp != &shead; sp = sp->sl_next)
{
if (sp->sl_attr & attr_not)
{
slist_delete(sp);
slist_insert(&spark, sp);
goto again;
}
}
assert(shead.sl_next != &shead);
}
char *
sortlist_reorder(char sort_tag)
{
slist_t *s_entry;
if (sort_tag == ' ') /* this is our stop char */
return (char *)NULL;
for (s_entry = &sort_list[0]; s_entry->sl_tag != SL_CMP_EQ; s_entry++)
{
if (sort_tag == s_entry->sl_sup[0])
{
slist_delete(s_entry);
s_entry->sl_tag = SL_CMP_GT;
if (s_entry->sl_attr & (SF_AGG|SF_IND))
{
slist_use_only_attr(s_entry->sl_attr);
}
slist_insert(&shead, s_entry);
return s_entry->sl_sup;
}
if (sort_tag == s_entry->sl_inf[0])
{
slist_delete(s_entry);
s_entry->sl_tag = SL_CMP_LT;
if (s_entry->sl_attr & (SF_AGG|SF_IND))
{
slist_use_only_attr(s_entry->sl_attr);
}
slist_insert(&shead, s_entry);
return s_entry->sl_inf;
}
}
return (char *)NULL;
}
char *
sortlist_bprune(char *fptr)
{
bprune_t *bprune;
for (bprune = bflag_list; bprune->bp_flag; bprune++)
{
if (!strcmp(fptr, bprune->bp_match))
{
if (bprune->bp_index)
bst.bflags |= bprune->bp_remap;
else
bst.bvtype |= bprune->bp_remap;
slist_use_only_attr(SF_IND);
return (char *)bprune->bp_match;
}
}
return (char *)NULL;
}
char bflags_buffer[MAX_COLS];
char *
display_flags(uint64_t flags, uint64_t bvtype, char *separator)
{
char *to = bflags_buffer;
bprune_t *bprune;
int cnt;
int first = 1;
for (bprune = bflag_list; bprune->bp_flag; bprune++)
{
if ((bprune->bp_index && (bprune->bp_remap & flags))
|| ((!bprune->bp_index) && (bprune->bp_remap & bvtype)))
{
if (!first)
{
cnt = strlen(separator);
strncpy(to, separator, cnt);
to += cnt;
} else {
first = 0;
}
cnt = strlen(bprune->bp_match);
strncpy(to, bprune->bp_match, cnt);
to += cnt;
}
}
*to = '\0';
return bflags_buffer;
}