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

893 lines
20 KiB
C

/*
* memusage - memory usage accounting
*
* This program lists the memory used by processes and the kernel. It has
* a terse mode (the default), and a verbose mode. In verbose mode, it lists
* the memory allocated to each portion of a process. Memory for processes is
* listed in four columns: virtual, unique, shared, weighted. The virtual size
* is the total virtual address space. The unique size is the memory that is
* exclusively allocated for the particular process. The shared size is memory
* that the process is using, but is also being used by others. The weighted
* size is the unique memory plus a portion of the shared memory which is
* calculated as the size of the amount being shared divided by the number of
* people sharing it. All calculations are accurate to the 4K page size of the
* system, but are expressed as kilobytes. The sum of all of the weighted sizes
* is the total amount used by all processes.
*
* The program uses a data file that contains the pathnames of files likely
* to be executed. These are used to provide full names for shared libraries
* and the verbose format listing. A program, mkinodetbl, will generate the
* data file:
* mkinodetbl > /usr/tmp/memusage.inodes
*
* To determine the working set of a particular system configuration, perform
* the following steps:
*
* 1. Start with a system that has ample memory, like 1 1/2 times as much
* as will likely be needed.
* 2. Bring up all of the applications that you want to test.
* 3. Run the flushmem program several times to page everything out:
* flushmem & flushmem & flushmem
* 4. Do whatever tasks you want for your particular test. This will
* cause the system to page back in whatever pages are needed.
* 5. Run memusage to collect the working set data.
*
*
* $Revision: 1.5 $
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _KMEMUSER
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/region.h>
#include <sys/vnode.h>
#include <sys/vfs.h>
#include <sys/var.h>
#include <sys/pmap.h>
#include <sys/immu.h>
#include <sys/pfdat.h>
#include <sys/sysmacros.h>
#include <sys/prctl.h>
#include <sys/mbuf.h>
#include <nlist.h>
#include <getopt.h>
#include <sys/sysmp.h>
#include <sys/sysinfo.h>
int kmemfd; /* File descriptor of /dev/kmem */
struct pfdat * pfdat; /* same as kernel */
struct var vr; /* same as kernel v struct */
long maxclick, /* pfn of last page */
firstfree, /* pfn of first pageable page */
physmem, /* number of pages of memory in system */
heapmem, /* bytes claimed by kern_malloc */
zonemem, /* bytes claimed by zone malloc */
firstclick, /* maxclick-physmem (128Meg on MC machines) */
kernmag, /* contains value of _end */
bufmem; /* bytes claimed by fs_bio */
int tflg = 1; /* terse flag */
char * unixpath = "/unix";
char * corepath = "/dev/kmem";
char * nodepath = "/var/tmp/memusage.inodes";
struct mbstat mbstat; /* network buffer statistics */
pde_t * kptbl; /* k2 segment map */
/*
* As processes are scanned, the memory they reference
* is marked in the pfdat table with these values.
*/
#define MRK_UPAGE (pfd_t *)0xcafebabe
#define MRK_PROCESS (pfd_t *)0xbeefbabe
#define MRK_PTABLE (pfd_t *)0xbeabface
char *pregtypes[] = {
"UNUSED",
"TEXT",
"DATA",
"Stack",
"SHMEM",
"MEM",
"LibTxt",
"LibDat",
"GR",
"MapFile",
"PRDA"};
/*
* Abbreviated proc for internal use
*/
struct myproc {
char u_comm[PSCOMSIZ]; /* name */
long p_stat; /* flags */
long p_user; /* k2 address of u. */
pde_t p_upg0; /* user page */
pde_t p_upg1; /* kernel stack */
long p_size; /* virtual size */
void * p_shd; /* share structure */
long nproc;
struct pregion *p_region; /* private preg list */
struct pregion *p_sregion; /* shared preg list */
};
/*
* Table to correlate inode numbers to path names
*/
struct inrec {
struct inrec *next;
long inode;
dev_t rdev;
char name[128];
};
struct inrec *inodes;
char *
get_pregiontype(struct pregion *preg)
{
if (preg->p_type != PT_MAPFILE)
return pregtypes[preg->p_type];
if (preg->p_reg->r_flags & RG_TEXT)
return "Code";
if (preg->p_reg->r_flags & RG_PHYS)
return "I/O";
return "Data";
}
/*
* Get something from /dev/kmem (or core file)
*
* If the seek or read fails, give up on whole program
*/
readmem(long loc, void * buf, long buflen)
{
int ret;
if ((ret = lseek (kmemfd, loc, SEEK_SET)) == -1) {
perror ("lseek");
exit(1);
}
if ((ret = read (kmemfd, buf, buflen)) < 0) {
perror ("read");
exit(1);
}
return ret;
}
void
add_inode(long inode, dev_t rdev, 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;
}
/*
* Given a vnode at address vloc, find a corresponding path name
*/
char *
get_inode(long vloc)
{
struct vnode vn;
dev_t rdev;
static long first_call = 1;
FILE *fp;
long inode;
static char str[128];
struct inrec *current;
if (first_call) {
first_call = 0;
if ((fp = fopen (nodepath, "r")) != NULL)
while (fscanf(fp,"%d %d %s\n",&rdev,&inode,str) == 3)
add_inode (inode, rdev, str);
}
readmem(vloc, &vn, sizeof vn);
/*
* Hack to show /dev/zero which all programs have
*/
if (vn.v_type == VCHR) {
readmem(((long)vn.v_data) + 20, &rdev, sizeof rdev);
if (major(rdev) == 37 && minor(rdev) == 0)
return "/dev/zero";
sprintf(str, "/dev/(%d,%d)", major(rdev), minor(rdev));
return str;
}
/*
* Hard coded structure offsets!
*/
readmem(((long)vn.v_data) + 28, &inode, sizeof (inode));
readmem(((long)vn.v_data) + 32, &rdev, sizeof (rdev));
for (current = inodes; current; current = current->next)
if (current->inode == inode && current->rdev == rdev)
return current->name;
sprintf(str, "%d", inode);
return str;
}
/*
* Read in user page at location loc
*/
struct user *
readuser(long loc)
{
struct user *up;
if ((up = (struct user *)malloc(sizeof (struct user))) == NULL) {
fprintf(stderr, "readuser: malloc failed.\n");
return 0;
}
readmem(loc, up, sizeof (struct user));
return up;
}
/*
* Read in entire pfdat table
*/
struct pfdat *
readpfdat(long loc)
{
struct pfdat *pfdat;
if ((pfdat = (struct pfdat *)malloc(sizeof (struct pfdat)
* (maxclick - firstfree))) == NULL) {
fprintf(stderr, "readpfdat: malloc failed.\n");
return 0;
}
readmem(loc + (firstfree * sizeof (struct pfdat)),
pfdat, sizeof (struct pfdat) * (maxclick - firstfree));
return pfdat;
}
/*
* mark a pfdat at address kv with mark
* kv can be either a K0, K1, or K2 seg address.
*/
void
markpfdat(void * kv, pfd_t * mark)
{
unsigned pfn;
if (IS_KSEGDM(kv))
pfn = pnum(KDM_TO_PHYS(kv));
else
if (pnum((__psunsigned_t)kv - K2SEG) < vr.v_syssegsz)
pfn = pdetopfn(kvtokptbl(kv));
else {
fprintf(stderr, "Funny mark address %#x\n", kv);
return;
}
if (pfn < firstfree) {
fprintf(stderr, "Funny mark address %#x\n", kv);
return;
}
pfdat[pfn-firstfree].pf_hchain = mark;
}
/*
* read in a pmap and all of its page tables
*/
pmap_t *
readpmap(long loc)
{
pmap_t *pmap;
pte_t **ptt, *pte;
int idx;
/* get pmap struct */
if ((pmap = (pmap_t *)malloc(sizeof (pmap_t))) == NULL) {
fprintf(stderr, "readpmap: malloc failed.\n");
return 0;
}
readmem(loc, pmap, sizeof (pmap_t));
/* Segment table */
if ((ptt = (pte_t **)malloc(NPGTBLS * sizeof (pde_t *))) == NULL) {
fprintf(stderr, "readpmap: malloc failed.\n");
return 0;
}
readmem((long)pmap->pmap_ptr, ptt, NPGTBLS * sizeof (pde_t *));
/* Read each page table */
for (idx = 0; idx < NPGTBLS; idx++)
if (ptt[idx]) {
pte = (pte_t *)malloc(NBPP);
markpfdat(ptt[idx], MRK_PTABLE);
readmem((long)ptt[idx], pte, NBPP);
ptt[idx] = pte;
}
pmap->pmap_ptr = (void *)ptt;
return pmap;
}
struct pregion *
readregions(long loc)
{
struct pregion *preg, *head, *current;
struct region *reg;
pmap_t *pmap, *previous_pmap;
head = 0;
pmap = 0;
while (loc != 0) {
if (!(preg =(struct pregion *)malloc(sizeof(struct pregion)))) {
fprintf(stderr, "readregion: malloc failed.\n");
return 0;
}
readmem(loc, preg, sizeof (struct pregion));
if (head)
current->p_forw = preg;
else {
head = preg;
previous_pmap = preg->p_pmap;
}
current = preg;
loc = (long)current->p_forw;
if (!pmap || (previous_pmap != current->p_pmap)) {
if ((pmap = readpmap((long)current->p_pmap)) == NULL)
return 0;
}
previous_pmap = current->p_pmap;
current->p_pmap = pmap;
if (!(reg = (struct region *)malloc(sizeof (struct region)))) {
fprintf(stderr, "readregion: malloc failed.\n");
return 0;
}
readmem((long)current->p_reg, reg, sizeof (struct region));
current->p_reg = reg;
}
return head;
}
/*
* get pregion stats
*
* This routine calculates three size values: total valid pages,
* valid non-shared pages, and a weighted combination of the two.
*/
double
get_weighted_size(preg_t *preg, long *rsize, int *unique)
{
pte_t **ptt;
long idx, jdx;
long bucket, entry, len;
double size;
/* ptt is the segment table for the pmap belong to preg */
ptt = (pte_t **)preg->p_pmap->pmap_ptr;
/* starting offset into the segment table */
bucket = btost(preg->p_regva);
/* starting offset into the first page table */
entry = ((unsigned)preg->p_regva % NBPS) / NBPC;
/* total number of virtual pages in this pregion */
len = preg->p_pglen;
size = 0.0;
*rsize = 0;
jdx = entry;
for (idx = bucket; idx < NPGTBLS && len > 0; idx++) {
/* If page table is not yet allocated, skip it */
if (!ptt[idx]) {
len -= (NBPP / sizeof (pte_t) - jdx);
jdx = 0;
continue;
}
/* For each pte, find the corresponding pfdat and mark it. */
for (; jdx * sizeof (pte_t) < NBPP && len > 0; jdx++, len--)
if (ptt[idx][jdx].pte_sv) {
pfd_t *pfd = &pfdat[
ptt[idx][jdx].pte_pfn-firstfree];
if (pfd->pf_use == 0) {
/* I don't understand how this
* can happen, but it does.
*/
continue;
}
pfd->pf_hchain = MRK_PROCESS;
if (pfd->pf_use == 1)
*unique += 1;
(*rsize)++;
size = size + (NBPP/pfd->pf_use);
}
jdx = 0;
}
return size / NBPP;
}
/*
* Read the user process name
*/
char *
read_ucomm(long loc, char *comm)
{
struct user *user;
if (!loc || ((user = readuser(loc)) == NULL)) {
strcpy(comm, "defunct");
return comm;
}
strcpy(comm, user->u_comm);
free(user);
return comm;
}
/*
* Read the proc table entry at loc and map it into our own private
* style.
*/
struct myproc *
readproc(struct proc *loc)
{
struct proc pp;
struct myproc *mproc;
if (!(mproc = (struct myproc *)calloc(1, sizeof (struct myproc)))) {
fprintf(stderr, "readproc: malloc failed.\n");
return 0;
}
readmem((long)loc, &pp, sizeof (struct proc));
mproc->p_stat = pp.p_stat;
mproc->p_region = pp.p_region;
mproc->p_user = (long)pp.p_user;
mproc->p_upg0 = pp.p_upgs[0];
mproc->p_upg1 = pp.p_upgs[1];
mproc->p_size = pp.p_size;
read_ucomm(mproc->p_user, &(mproc->u_comm[0]));
/*
* If the process is part of a share group, get the shaddr struct
* so we can get the shared pregion list
*/
if (pp.p_shaddr && (pp.p_shmask & PR_SADDR)) {
struct shaddr_s shaddr;
readmem((long)pp.p_shaddr, &shaddr, sizeof shaddr);
mproc->p_shd = mproc->p_sregion = shaddr.s_region;
}
return mproc;
}
void
usage()
{
fprintf(stderr, "usage: memusage [-v] [-n unixpath] [-c corefile]\n");
exit(1);
}
main(int argc, char *argv[])
{
int i;
int c;
struct nlist nl[32];
struct myproc **proc;
struct proc *procloc;
long nprocs, pfdatloc;
long totalsize;
long totalhash;
long totalanonhash;
double totalweighted;
long pregtotal;
struct pregion *preg;
struct region *reg;
int totalfree;
int totalchunked;
int totalunacct;
int totalupages;
int ktotal = 0;
int ftotal = 0;
int total_pagetables = 0;
while ((c = getopt(argc,argv,"tvn:c:")) != EOF)
switch (c) {
case 'n':
unixpath = optarg;
break;
case 'c':
corepath = optarg;
break;
case 't':
tflg = 1;
break;
case 'v':
tflg = 0;
break;
case '?':
usage();
break;
}
if (optind != argc)
usage();
if ((kmemfd = open(corepath, O_RDONLY)) < 0) {
perror("open");
exit(1);
}
#define NL_PROC 0
#define NL_V 1
#define NL_MAXCLICK 2
#define NL_PFDAT 3
#define NL_FIRSTFREE 4
#define NL_PHYSMEM 5
#define NL_BUFMEM 6
#define NL_KERNMAG 7
#define NL_MBSTAT 8
#define NL_KPTBL 9
#define NL_END 10
#define NL_MEMBASE 11
#define NL_LAST 12
nl[NL_PROC].n_name = "proc";
nl[NL_V].n_name = "v";
nl[NL_MAXCLICK].n_name = "maxclick";
nl[NL_PFDAT].n_name = "pfdat";
nl[NL_FIRSTFREE].n_name = "firstfree";
nl[NL_PHYSMEM].n_name = "physmem";
nl[NL_BUFMEM].n_name = "bufmem";
nl[NL_KERNMAG].n_name = "kernel_magic";
nl[NL_MBSTAT].n_name = "mbstat";
nl[NL_KPTBL].n_name = "kptbl";
nl[NL_END].n_name = "end";
nl[NL_MEMBASE].n_name = "_physmem_start";
nl[NL_LAST].n_name = 0;
if (nlist(unixpath, nl) < 0) {
perror("nlist");
exit(1);
}
for ( i = 0; nl[i].n_name; i++ )
if (!nl[i].n_type) {
printf("%s not found\n", nl[i].n_name);
exit(1);
}
readmem( nl[NL_PROC].n_value, &procloc, sizeof procloc);
readmem( nl[NL_V].n_value, &vr, sizeof vr);
readmem( nl[NL_MAXCLICK].n_value, &maxclick, sizeof maxclick);
readmem( nl[NL_PFDAT].n_value, &pfdatloc, sizeof pfdatloc);
readmem(nl[NL_FIRSTFREE].n_value, &firstfree, sizeof maxclick);
readmem( nl[NL_BUFMEM].n_value, &bufmem, sizeof bufmem);
readmem( nl[NL_KERNMAG].n_value, &kernmag, sizeof kernmag);
readmem( nl[NL_PHYSMEM].n_value, &physmem, sizeof physmem);
readmem( nl[NL_MBSTAT].n_value, &mbstat, sizeof mbstat);
firstclick = btoc(nl[NL_MEMBASE].n_value);
if (nl[NL_END].n_value != kernmag) {
fprintf(stderr,"%s: %s does not match %s, try -n option\n",
argv[0], unixpath, corepath);
exit(1);
}
if ((pfdat = readpfdat(pfdatloc)) == NULL)
exit(1);
/* Read in system segment (K2) table */
{
pde_t *akptbl;
kptbl = (pde_t *)malloc(sizeof kptbl[0] * vr.v_syssegsz);
readmem(nl[NL_KPTBL].n_value, &akptbl, sizeof akptbl);
readmem((unsigned long)akptbl, kptbl, sizeof kptbl[0] * vr.v_syssegsz);
}
if (!(proc = (struct myproc **)malloc(vr.v_proc*sizeof *proc))) {
fprintf(stderr, "memusage: malloc of proc array failed.\n");
exit(1);
}
/* Read in all of the procs, regions, pmaps and page tables */
for (i = 0, nprocs = 0; i < vr.v_proc; i++, procloc += 1) {
if ((proc[nprocs] = readproc(procloc)) == NULL)
exit(1);
proc[nprocs]->nproc = nprocs;
if (proc[nprocs]->p_stat == 0)
free(proc[nprocs]);
else {
proc[nprocs]->p_region =
readregions((long)proc[nprocs]->p_region);
/* Eliminate all but the first proc
* in a shared address group
*/
if (proc[nprocs]->p_sregion) {
int j;
for ( j = 0; j < nprocs; j++ )
if (proc[j]->p_shd ==
proc[nprocs]->p_sregion) {
proc[nprocs]->p_sregion = 0;
break;
}
if (proc[nprocs]->p_sregion)
proc[nprocs]->p_sregion =
readregions((long)proc[nprocs]->p_sregion);
}
nprocs++;
}
}
totalweighted = 0.0;
totalsize = 0;
totalupages = 0;
for (i = 0; i < nprocs; i++) {
double procweighted = 0;
int procunique = 0;
int procshared = 0;
int nregs = 0;
/*
* Mark off upages in pfdat
*
* Defunct processes don't have these
*/
if (proc[i]->p_upg0.pte.pg_pfn) {
pfdat[proc[i]->p_upg0.pte.pg_pfn-firstfree].pf_hchain =
MRK_UPAGE;
pfdat[proc[i]->p_upg1.pte.pg_pfn-firstfree].pf_hchain =
MRK_UPAGE;
}
if (!(preg = proc[i]->p_region))
preg = proc[i]->p_sregion;
if (!preg)
continue;
if (tflg) {
if (totalsize==0)
printf("Name Virtual Unique Share Weight\n");
}
else {
printf(
"\nName Address Type Virtual Resident File"
"\n Unique Shared\n");
}
printf ("%-10.10s ", proc[i]->u_comm);
dopregs:
for (; preg; preg = preg->p_forw) {
double pregweight = 0;
int pregunique = 0;
reg = preg->p_reg;
if (reg->r_flags & RG_PHYS)
continue;
if (nregs&&!tflg)
printf(" ");
nregs++;
pregweight = get_weighted_size(preg,
&pregtotal,
&pregunique);
procunique += pregunique;
procshared += pregtotal-pregunique;
procweighted += pregweight;
if (!tflg)
printf (" 0x%08x %-8s ",
preg->p_regva,
get_pregiontype(preg));
if (tflg)
continue;
printf ("%8d %8d %8d %s\n",
preg->p_pglen * 4, /* Virtual size */
pregunique * 4,
(pregtotal-pregunique) * 4, /* shared */
(preg->p_reg->r_vnode == 0) ? "" :
get_inode((long)preg->p_reg->r_vnode));
}
if (preg = proc[i]->p_sregion) {
proc[i]->p_sregion = 0;
goto dopregs;
}
if (tflg)
printf(" %8d %8d %8d %7.0f\n",
proc[i]->p_size * 4,
procunique*4,
procshared*4,
procweighted*4);
else
printf(" %3d page tables %8d "
"%8d %8d Weighted Size: %.0f\n",
proc[i]->p_region?
proc[i]->p_region->p_pmap->pmap_scount: 0,
proc[i]->p_size * 4,
procunique*4,
procshared*4,
procweighted*4);
totalsize += proc[i]->p_size;
totalweighted += procweighted;
}
printf("\nProcess totals %6d %7.0f\n\n",
totalsize * 4,
totalweighted * 4);
/* Scan entire pfdat list to pick up strays */
totalhash = 0;
totalanonhash = 0;
totalfree = 0;
totalchunked = 0;
totalunacct = 0;
for (i = 0; i < (maxclick - firstfree); i ++)
if (pfdat[i].pf_use == -1) /* memory hole */
continue;
else
if (pfdat[i].pf_hchain == MRK_PROCESS)
continue;
else
if (pfdat[i].pf_hchain == MRK_UPAGE)
totalupages += 1;
else
if (pfdat[i].pf_hchain == MRK_PTABLE)
total_pagetables += 1;
else
if ((pfdat[i].pf_flags & (P_HASH|P_ANON)) ==
(P_HASH|P_ANON))
totalanonhash += 1;
else
if ((pfdat[i].pf_flags & (P_HASH|P_QUEUE)) ==
(P_HASH|P_QUEUE))
totalhash += 1;
else
if ((pfdat[i].pf_flags & (P_HASH|P_SQUEUE)) ==
(P_HASH|P_SQUEUE))
totalhash += 1;
else
if ((pfdat[i].pf_flags & (P_HASH|P_QUEUE)) ==
(P_QUEUE))
totalfree += 1;
else
if ((pfdat[i].pf_flags & (P_HASH|P_SQUEUE)) ==
(P_SQUEUE))
totalfree += 1;
else
if (pfdat[i].pf_flags & P_HASH)
totalchunked += 1;
else
totalunacct += 1;
{
int ktmp;
printf("Kernel\n");
printf(" Load size %8d\n",
ktmp = (btoc(K0_TO_PHYS(kernmag)) - firstclick) * 4);
ktotal += ktmp;
printf(" Fixed tables %8d\n",
ktmp = (firstfree - btoc(K0_TO_PHYS(kernmag))) *4);
ktotal += ktmp;
{
int minfosz;
struct minfo *mi;
minfosz = sysmp(MP_SASZ, MPSA_MINFO);
if (minfosz != sizeof *mi)
printf("Bad minfosz\n");
mi = calloc(1, sizeof *mi);
sysmp(MP_SAGET, MPSA_MINFO, mi, minfosz);
heapmem = mi->heapmem;
zonemem = mi->zonemem;
}
printf(" Dynamic tables %8d\n",
ktmp = (heapmem/1024)+(zonemem/1024));
ktotal += ktmp;
totalunacct -= ktmp/4;
printf(" Network buffers %8d\n",
ktmp = mbstat.m_clusters * 4);
ktotal += ktmp;
totalunacct -= bufmem;
printf(" Unaccounted %8d\n",
ktmp = totalunacct * 4);
ktotal += ktmp;
printf(" Process overhead %8d\n",
ktmp = (totalupages * 4) + (total_pagetables * 4));
ktotal += ktmp;
printf("\nKernel total: %8d\n", ktotal);
}
printf("\nMemory pool:\n");
printf(" Page cache: %8d\n", totalhash *4);
ftotal += totalhash * 4;
printf(" Free pages: %8d\n", totalfree *4);
ftotal += totalfree * 4;
printf(" Swap cache: %8d\n", totalanonhash *4);
ftotal += totalanonhash * 4;
printf(" Buffer cache: %8d\n", totalchunked *4);
ftotal += totalchunked * 4;
printf(" Filesystem meta cache: %8d\n", bufmem *4);
ftotal += bufmem * 4;
printf("\nMemory pool total: %8d\n", ftotal);
printf("\n\nAccounted memory total: %20.0f\n", ftotal+ktotal+(totalweighted*4));
if ((physmem * 4) - (ftotal+ktotal+(totalweighted*4)) < 0)
printf("Over accounted %20.0f\n",
(ftotal+ktotal+(totalweighted*4)) - (physmem * 4));
else
printf("Unaccounted %20.0f\n",
(physmem * 4) - (ftotal+ktotal+(totalweighted*4)));
return 0;
}