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

625 lines
14 KiB
C

/*
* This program does a symbolic dump of the number of pages consumed by each
* mapped region in the system across all processes. For instance, if libc.so
* is mapped into seven processes, rgaccum will add all the in core counts
* from those processes that pertain to libc.so and display the totals.
*
* usage: rgaccum [-Dbcdfstz] [process-id|name]
* -D Don't collect any Data/Stack/Break/Etc region counts
* -a Don't collect Anonymous region counts
* -b Don't collect Break region counts
* -c Don't collect Cow region counts
* -d Don't collect Data region counts
* -f Don't collect File region counts
* -s Don't collect Stack region counts
* -t Don't collect Text region counts
* -z Don't report regions with total of zero
* id Optional process ID (in decimal)
* name Optional process base name (as in Xsgi, etc...)
*/
#include <stdio.h>
#include <stddef.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <ustat.h>
#include <ftw.h>
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <mntent.h>
#include <dirent.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <sys/procfs.h>
#include <paths.h>
#include <libw.h>
#include <locale.h>
#include <fmtmsg.h>
#include <stdarg.h>
#include <sgi_nl.h>
/*
* Path to /proc filesystem
*/
char prpathname[BUFSIZ];
/*
* process information structure from /proc
*/
struct prpsinfo info;
/*
* process region map information from /proc
*/
#define MAXMAPS 256
struct prmap_sgi maps[MAXMAPS];
/*
* Table to correlate region data
*/
static struct sregion {
dev_t dev;
ino_t ino;
u_long vsize, psize, rsize, msize, rss;
int use;
struct sregion *next;
} *sregion, *fregion;
static int nsrel;
#define FIXUP(val) ((val) = ((val) + 1) / MA_WSIZE_FRAC)
static void
add_region(struct prmap_sgi *map)
{
register u_long rss, refcnt, multi;
struct sregion *new;
/* Compute weighted rss from wsize and region reference count */
refcnt = map->pr_mflags >> MA_REFCNT_SHIFT;
multi = MA_WSIZE_FRAC / refcnt;
/* Search existing list */
for (new = sregion; new != NULL; new = new->next) {
if (new->dev == map->pr_dev && new->ino == map->pr_ino) {
new->vsize += map->pr_vsize * multi;
new->psize += map->pr_psize * multi;
new->rsize += map->pr_rsize * multi;
new->msize += map->pr_msize * multi;
new->rss += map->pr_wsize / refcnt;
new->use++;
return;
}
}
/* Nothing found, add new entry to sregion list */
new = (struct sregion *)malloc(sizeof (struct sregion));
new->next = sregion;
new->dev = map->pr_dev;
new->ino = map->pr_ino;
new->vsize = map->pr_vsize * multi;
new->psize = map->pr_psize * multi;
new->rsize = map->pr_rsize * multi;
new->msize = map->pr_msize * multi;
new->rss = map->pr_wsize / refcnt;
new->use = 1;
sregion = new;
++nsrel;
}
compare(const void *a, const void *b)
{
register struct sregion *s = (struct sregion *)a;
register struct sregion *t = (struct sregion *)b;
if (s->rss < t->rss)
return 1;
else
return -1;
}
static void
sort_regions()
{
register struct sregion *new, *tbl;
register int i;
/* Create new table for sorting */
fregion = (struct sregion *)malloc(nsrel * sizeof (struct sregion));
/* Copy existing list */
for (tbl = fregion, new = sregion; new != NULL; new = new->next) {
*tbl = *new;
tbl++;
}
/* Sort the table */
qsort(fregion, nsrel, sizeof (struct sregion), compare);
/* Fix table next pointers */
for (tbl = fregion, i = nsrel; --i > 0; tbl++) {
tbl->next = &tbl[1];
}
tbl->next = NULL;
/* install new table */
sregion = fregion;
}
/*
* Table to correlate inode numbers to path names
*/
struct inrec {
struct inrec *next;
ino_t inode;
dev_t rdev;
char name[256];
};
#define _PATH_INODES "/var/tmp/memusage.inodes"
static struct inrec *inodes;
static void
add_inode(dev_t rdev, ino_t inode, char *str)
{
struct inrec *new;
new = (struct inrec *)malloc(sizeof (struct inrec));
new->next = inodes;
new->inode = inode;
new->rdev = rdev;
strcpy (new->name, str);
inodes = new;
}
static
find_inode(char *str, dev_t *rdev, ino_t *ino)
{
struct inrec *current;
for (current = inodes; current; current = current->next)
if (strcmp(current->name, str) == NULL) {
if (rdev) *rdev = current->rdev;
if (ino) *ino = current->inode;
return 1;
}
return 0;
}
/*
* Given a vnode at address vloc, find a corresponding path name
*/
char *
prdevino(dev_t rdev, ino_t inode, int use)
{
static long first_call = 1;
static char str[128];
struct inrec *current;
FILE *fp;
if (first_call) {
first_call = 0;
if ((fp = fopen (_PATH_INODES, "r")) != NULL) {
while (fscanf(fp,"%d %llu %s\n",&rdev,&inode,str) == 3)
add_inode (rdev, inode, str);
fclose(fp);
}
}
for (current = inodes; current; current = current->next)
if (current->inode == inode && current->rdev == rdev) {
if (use > 1)
sprintf(str, "%s (%d)", current->name, use);
else
strcpy(str, current->name);
return str;
}
sprintf(str, "Dev # = %x, Inode # = %llu", rdev, inode);
return str;
}
/*
* Add dynamic paths into dev/ino map list
*/
static void
prdynpaths()
{
static char *paths[] = { "/var/tmp/.Xshmtrans0",
"/tmp/.cadminOSSharedArena",
"/var/tmp/.rtmond_shm_file",
"/var/tmp/.XsgiShmInfo0",
NULL };
register int i;
struct stat statd;
/* Add elements to the list */
for (i = 0; paths[i] != NULL; ++i) {
if (stat(paths[i], &statd) < 0)
continue;
add_inode (statd.st_dev, statd.st_ino, paths[i]);
}
}
/*
* Get page table data
*/
static prpgd_sgi_t *pgd = NULL, *pgds = NULL;
void
getpgdinfo(int fd, struct prmap_sgi *map)
{
register pgd_t *src, *dst;
register int i;
/* Allocate array(s) to hold data */
if (pgd == NULL) {
register int size;
size = sizeof (prpgd_sgi_t);
size += sizeof (pgd_t) * (map->pr_size / getpagesize());
pgd = (prpgd_sgi_t *)calloc(1, size);
pgds = (prpgd_sgi_t *)calloc(1, size);
}
/* Get page table data */
pgd->pr_vaddr = map->pr_vaddr;
if (ioctl(fd, PIOCPGD_SGI, pgd) < 0) {
return;
}
/* Sum page table data */
pgds->pr_pglen = pgd->pr_pglen;
src = pgd->pr_data; dst = pgds->pr_data;
for (i = 0; i < pgd->pr_pglen; ++i, ++dst, ++src) {
dst->pr_flags |= src->pr_flags;
}
}
void
prpgdinfo()
{
register pgd_t *data;
register int i;
/* Dump RLD page table info */
printf("RLD [DATA] page table - (%d)\n", pgd->pr_pglen);
for (i = 0, data = pgds->pr_data; i < pgds->pr_pglen; ++i, ++data) {
printf("%02x ", data->pr_flags);
}
printf("\n");
}
/*
* Identify PROGRAM text extent
*/
isprgtxt(struct prmap_sgi *map)
{
if ((map->pr_mflags & (MA_EXEC|MA_PRIMARY)) == (MA_PRIMARY|MA_EXEC))
return 1;
return 0;
}
/*
* Identify RLD text extent
*/
isrldtxt(struct prmap_sgi *map)
{
if ((ptrdiff_t)map->pr_vaddr == 0x0fb60000)
return 1;
return 0;
}
/*
* Identify RLD bss extents
*/
isrldbss(struct prmap_sgi *map)
{
if (((ptrdiff_t)map->pr_vaddr >= 0x0fbc0000) &&
((ptrdiff_t)map->pr_vaddr < 0x0fc40000))
return 1;
return 0;
}
/*
* Identify RLD bss extents
*/
isdevzero(struct prmap_sgi *map)
{
static dev_t zdev;
static ino_t zino;
/* Lookup /dev/zero */
if (!zdev) {
find_inode("/dev/zero", &zdev, &zino);
}
/* Match? */
if (map->pr_dev == zdev && map->pr_ino == zino)
return 1;
return 0;
}
static void
usage()
{
fprintf(stderr, "usage: rgaccum [-DMabcdfrtv] [process-id|name]\n");
exit(0);
}
main(int argc, char **argv)
{
register struct prmap_sgi *map, *amap, *rmap;
register int i, flags, fd;
register struct sregion *rgn;
register DIR *dirp;
prmap_sgi_arg_t maparg;
struct dirent *direntp;
int c, nmaps, me, pid = 0;
int Dflg = 1, Sflg = 1, aflg = 1, bflg = 1, cflg = 1, dflg = 1;
int fflg = 1, sflg = 1, tflg = 1, Mflg = 0, zflg = 0;
pgno_t tvsize = 0, tpsize = 0, trss = 0, tmsize = 0;
dev_t mdev = 0, fdev = 0;
ino_t mino = 0, fino = 0;
/* Setup region symbol table */
(void) prdevino(0, 0, 0);
(void) prdynpaths();
/* Check for command line options */
while ((c = getopt(argc, argv, "DM:Sabcdfstzr:")) != EOF) {
switch (c) {
case 'D':
Dflg = 0;
break;
case 'M':
switch (optarg[0]) {
case 'N': Mflg = MA_NOTCACHED; break;
case 'P': Mflg = MA_PRIMARY; break;
case 'S': Mflg = MA_SREGION; break;
case 'W': Mflg = MA_WRITE; break;
case 'b': Mflg = MA_BREAK; break;
case 'd': Mflg = MA_COW; break;
case 'h': Mflg = MA_SHMEM; break;
case 'p': Mflg = MA_PHYS; break;
case 's': Mflg = MA_STACK; break;
case 't': Mflg = MA_EXEC; break;
default : printf("unknown -M suffix '%c'\n",
optarg[0]); exit(0);
}
break;
case 'S':
Sflg = 0;
break;
case 'a':
aflg = 0;
break;
case 'b':
bflg = 0;
break;
case 'c':
cflg = 0;
break;
case 'd':
dflg = 0;
break;
case 'f':
fflg = 0;
break;
case 's':
sflg = 0;
break;
case 't':
tflg = 0;
break;
case 'z':
zflg = 1;
break;
case 'r':
if (!find_inode(optarg, &fdev, &fino)) {
printf("'%s' not found\n", optarg);
}
break;
case '?':
usage();
break;
}
}
/* Look for optional process id argument */
if (optind != argc) {
if (isdigit(argv[optind][0])) {
pid = strtol(argv[optind], &optarg, 10);
if (*optarg != NULL) {
fprintf(stderr, "ERROR: pid is not an integer\n");
usage();
}
} else if (isalpha(argv[optind][0])) {
optarg = argv[optind];
} else
usage();
} else {
pid = 0; optarg = NULL;
}
/* Open /proc directory */
if ((dirp = opendir("/proc")) == NULL) {
perror("opendir");
exit(-1);
}
/* Scan the entire directory */
while ((direntp = readdir(dirp)) != NULL) {
/* Create process path name */
sprintf(prpathname, "/proc/%s", direntp->d_name);
/* Skip . and .. */
if (direntp->d_name[0] == '.')
continue;
/* Skip junk in /proc directory */
if (!isdigit(direntp->d_name[0]))
continue;
/* Open process */
if ((fd = open(prpathname, 0)) < 0) {
continue;
}
/* Get process info structure */
me = 0;
if (ioctl(fd, PIOCPSINFO, &info) < 0) {
perror("ioctl(PIOCPSINFO)");
exit(-3);
}
if (pid && info.pr_pid != pid)
continue;
if (optarg && strcmp(info.pr_fname, optarg))
continue;
if (strcmp(info.pr_fname, "rgaccum") == 0) {
me = 1;
}
/* Get process map structures */
maparg.pr_vaddr = (caddr_t)maps;
maparg.pr_size = sizeof maps;
if ((nmaps = ioctl(fd, PIOCMAP_SGI, &maparg)) < 0) {
(void) close(fd);
continue;
}
/* Search regions to find app segment & rld segment */
rmap = amap = NULL;
for (map = maps, i = nmaps; i-- > 0; ++map) {
if (map->pr_ino) {
/* Hack for app region */
if (!amap && isprgtxt(map)) {
amap = map;
}
/* Use BRK as app region marker */
if (map->pr_mflags & MA_BREAK) {
amap = map;
if (me) {
mdev = map->pr_dev;
mino = map->pr_ino;
}
}
/* Remember RLD region */
if (!rmap && isrldtxt(map)) {
rmap = map;
}
}
}
/* Try and bill /dev/zero regions to real owners */
for (map = maps, i = nmaps; i-- > 0; ++map) {
/* Hack for rld's /dev/zero regions */
if (isdevzero(map)) {
if (rmap && isrldbss(map)) {
map->pr_dev = rmap->pr_dev;
map->pr_ino = rmap->pr_ino;
map->pr_mflags |= MA_BREAK;
}
}
}
/* Scan the table */
for (map = maps, i = nmaps; i-- > 0; ++map) {
if (Mflg && !(map->pr_mflags & Mflg))
continue;
#define MA_COREDATA (MA_SHMEM|MA_COW|MA_WRITE)
#define MA_DATA (MA_COREDATA|MA_BREAK|MA_STACK)
if (!Dflg && (map->pr_mflags & MA_DATA))
continue;
if (!Sflg && (map->pr_mflags & MA_SHMEM))
continue;
if (!aflg && !map->pr_dev && !map->pr_ino)
continue;
if (!bflg && (map->pr_mflags & MA_BREAK))
continue;
if (!cflg && (map->pr_mflags & MA_COW))
continue;
if (!dflg && (map->pr_mflags & MA_COREDATA))
continue;
if (!sflg && (map->pr_mflags & MA_STACK))
continue;
if (!tflg && (map->pr_mflags & MA_EXEC))
continue;
#define MA_FILE (MA_STACK|MA_BREAK|MA_SHMEM|MA_EXEC|MA_COW)
if (!fflg && map->pr_dev && map->pr_ino &&
(map->pr_mflags & MA_FILE) == 0)
continue;
if (map->pr_ino) {
/* Region filter */
if (fdev && fino) {
/* Reject if no match */
if (fdev != map->pr_dev ||
fino != map->pr_ino)
continue;
/* Use app name for instance */
if (!amap)
continue;
map->pr_dev = amap->pr_dev;
map->pr_ino = amap->pr_ino;
}
add_region(map);
} else if (amap && !fdev) {
/* add stack & junk into app region */
map->pr_dev = amap->pr_dev;
map->pr_ino = amap->pr_ino;
add_region(map);
}
}
(void) close(fd);
}
closedir(dirp);
/* Any data */
if (sregion == NULL)
exit(0);
/* Sort the table by weighted-rss sizes */
sort_regions();
/* Dump out the region data */
printf("\tMapped Objects\t\t\t\t\t Page Counts\n");
printf("Pathname\t\t\t\t\t Valid\tPrivate\t Weight\tWritten\n");
for (rgn = sregion; rgn != NULL; rgn = rgn->next) {
if (!zflg && rgn->vsize == 0)
continue;
if (rgn->dev == mdev && rgn->ino == mino)
printf(" %-46s", "<rgaccum - this program>");
else
printf(" %-46s", prdevino(rgn->dev,rgn->ino,rgn->use));
FIXUP(rgn->vsize);
printf("\t%7d", rgn->vsize);
tvsize += rgn->vsize;
FIXUP(rgn->psize);
printf("\t%7d", rgn->psize);
tpsize += rgn->psize;
FIXUP(rgn->rss);
printf("\t%7d", rgn->rss);
trss += rgn->rss;
FIXUP(rgn->msize);
printf("\t%7d", rgn->msize);
tmsize += rgn->msize;
printf("\n");
}
printf("Totals\t\t\t\t\t\t%7d\t%7d\t%7d\t%7d\n",
tvsize, tpsize, trss, tmsize);
exit(0);
}