1041 lines
25 KiB
C
1041 lines
25 KiB
C
/*
|
|
* process.c
|
|
*
|
|
* Code to collect information about memory usage in the system
|
|
*
|
|
*/
|
|
#define TILES_TO_LPAGES
|
|
#define _KMEMUSER
|
|
#include <sys/types.h>
|
|
#include <sys/procfs.h>
|
|
#include <sys/param.h>
|
|
#include <sys/sysmp.h>
|
|
#include <sys/sysinfo.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <sys/pfdat.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/syssgi.h>
|
|
#include <sys/tcpipstats.h>
|
|
#include <sys/capability.h>
|
|
|
|
#include <dirent.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#include "process.h"
|
|
#include "inode.h"
|
|
#include "pfdat3264.h"
|
|
|
|
#define PROCDIR "/proc"
|
|
|
|
/*
|
|
* dirp is for opening /proc
|
|
*/
|
|
static DIR *dirp = NULL;
|
|
static unsigned long kpstart, ktext, ketext, kend, kpbase, kpfdat, kpfdatsz;
|
|
static unsigned long kpagesize;
|
|
|
|
static int pgsize; /* page size in 1000 of bytes (ie. 4, 16, etc...) */
|
|
static int usize; /* number of pages for uarea */
|
|
|
|
/*
|
|
* static void
|
|
* KernelInfo()
|
|
*
|
|
* Description:
|
|
* Initialize a few constant global kernel variables
|
|
*/
|
|
static void
|
|
KernelInfo(void)
|
|
{
|
|
register long k0mask;
|
|
register int fd;
|
|
|
|
/* Only do once */
|
|
if (!kend) {
|
|
/* Handle transparent pointer sizing */
|
|
if (sysconf(_SC_KERN_POINTERS) > 32) {
|
|
k0mask = 0; /* Not required on 64bit kernels. */
|
|
kpfdatsz = sizeof (pfd64_t);
|
|
} else {
|
|
k0mask = 0x80000000L;
|
|
kpfdatsz = sizeof (pfd32_t);
|
|
}
|
|
|
|
/* Get some info from sysmp() */
|
|
if ((kpstart = sysmp(MP_KERNADDR, MPKA_PSTART)) == -1)
|
|
perror("sysmp(MP_KERNADDR, MPKA_PSTART)");
|
|
kpstart |= k0mask;
|
|
if ((ktext = sysmp(MP_KERNADDR, MPKA_TEXT)) == -1)
|
|
perror("sysmp(MP_KERNADDR, MPKA_TEXT)");
|
|
if ((ketext = sysmp(MP_KERNADDR, MPKA_ETEXT)) == -1)
|
|
perror("sysmp(MP_KERNADDR, MPKA_ETEXT)");
|
|
if ((kend = sysmp(MP_KERNADDR, MPKA_END)) == -1)
|
|
perror("sysmp(MP_KERNADDR, MPKA_END)");
|
|
if ((kpfdat = sysmp(MP_KERNADDR, MPKA_PFDAT)) == -1)
|
|
perror("sysmp(MP_KERNADDR, MPKA_PFDAT)");
|
|
if ((kpbase = sysmp(MP_KERNADDR, MPKA_KPBASE)) == -1)
|
|
perror("sysmp(MP_KERNADDR, MPKA_KPBASE)");
|
|
|
|
/* Read some stuff from /dev/kmem */
|
|
fd = open("/dev/kmem", 0);
|
|
if (fd != -1) {
|
|
/* Get value of KPBASE */
|
|
if (lseek(fd, kpbase, SEEK_SET) == -1)
|
|
perror("lseek");
|
|
if (read(fd, (caddr_t)&kpbase, sizeof kpbase) < 0)
|
|
perror("read");
|
|
kpbase |= k0mask;
|
|
close(fd);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* static PROGRAM *
|
|
* AddElem(PROGRAM *head, char *progName, char *mapName, char *mapType,
|
|
* long privSize, long weightSize, long resSize, long size,
|
|
* pid_t pid, void *vaddr, int markNProc)
|
|
*
|
|
* Description:
|
|
* Add an element to our program list.
|
|
*
|
|
* Parameters:
|
|
* head The list to add this element to
|
|
* progName Name of program to add
|
|
* privSize Size of private pages
|
|
* weightSize Weighted memusage size
|
|
* resSize resident size of program to add
|
|
* size total size of program to add
|
|
* pid process id of program to add
|
|
* vaddr virtual address of region if this is a region
|
|
* markNProc 1 if we care about keeping nProc up to date
|
|
*
|
|
* Returns:
|
|
* A new list (the element being added may go at the beginning)
|
|
*/
|
|
|
|
static PROGRAM *
|
|
AddElem(PROGRAM *head, char *progName, char *mapName, char *mapType,
|
|
long privSize, long weightSize, long resSize,
|
|
long size, pid_t pid, void *vaddr, int markNProc)
|
|
{
|
|
PROGRAM *elem, *prev, *pidPlace;
|
|
PIDLIST *pids;
|
|
int cmp;
|
|
|
|
elem = head;
|
|
prev = NULL;
|
|
pidPlace = NULL;
|
|
|
|
/*
|
|
* Look for another copy of the same program already running
|
|
*/
|
|
while (elem && (cmp = strcmp(progName, elem->progName)) != 0) {
|
|
/*
|
|
* Remember where the program should go (we sort by pid) in
|
|
* case this is the first of its kind that we've found
|
|
*/
|
|
if (pid < elem->pid && !pidPlace) {
|
|
pidPlace = prev;
|
|
}
|
|
prev = elem;
|
|
elem = elem->next;
|
|
}
|
|
|
|
if (elem && cmp == 0) {
|
|
/*
|
|
* Another copy is running; just add the new one's stats to
|
|
* the old one
|
|
*/
|
|
elem->privSize += privSize;
|
|
elem->weightSize += weightSize;
|
|
elem->resSize += resSize;
|
|
elem->size += size;
|
|
if (markNProc) {
|
|
elem->nProc++;
|
|
}
|
|
/*
|
|
* Add the new pid to the end of the pidlist for this program
|
|
* name.
|
|
*/
|
|
pids = elem->pids;
|
|
while (pids->next) {
|
|
pids = pids->next;
|
|
}
|
|
pids->next = malloc(sizeof *pids);
|
|
pids->next->pid = pid;
|
|
pids->next->prev = pids;
|
|
pids->next->next = NULL;
|
|
} else {
|
|
/*
|
|
* A new program is encountered. Allocate a new element, and
|
|
* insert it into the list.
|
|
*/
|
|
elem = malloc(sizeof *elem);
|
|
|
|
/*
|
|
* progName and mapName get strduped because the point to
|
|
* things like prinfo structs that change. mapType points to
|
|
* static storage that does not change. Be very careful if
|
|
* you change this, because FreeBloat (in this file) knows
|
|
* about this, AND SO DOES DrawSetup (in draw.c)!!!
|
|
*/
|
|
elem->progName = strdup(progName);
|
|
elem->mapName = mapName ? strdup(mapName) : NULL;
|
|
elem->mapType = mapType; /* Don't need to dup this; it points */
|
|
/* to static storage that won't change */
|
|
elem->privSize = privSize;
|
|
elem->weightSize = weightSize;
|
|
elem->resSize = resSize;
|
|
elem->size = size;
|
|
elem->vaddr = vaddr;
|
|
elem->nProc = 1;
|
|
elem->print = 1;
|
|
|
|
/*
|
|
* Set elem->pid for sorting and identification and posterity,
|
|
* and initialize the pidlist.
|
|
*/
|
|
elem->pid = pid;
|
|
elem->pids = malloc(sizeof *elem->pids);
|
|
elem->pids->prev = NULL;
|
|
elem->pids->next = NULL;
|
|
elem->pids->pid = pid;
|
|
|
|
if (!prev) {
|
|
elem->prev = NULL;
|
|
elem->next = head;
|
|
head = elem;
|
|
} else {
|
|
if (pidPlace) {
|
|
prev = pidPlace;
|
|
}
|
|
elem->next = prev->next;
|
|
elem->prev = prev;
|
|
prev->next = elem;
|
|
if (elem->next) {
|
|
elem->next->prev = elem;
|
|
}
|
|
}
|
|
}
|
|
return head;
|
|
}
|
|
|
|
/*
|
|
* static PROGRAM *
|
|
* SortList(PROGRAM *head, int flag)
|
|
*
|
|
* Description:
|
|
* Sort the element list by physical size (largest first)
|
|
*
|
|
* Parameters:
|
|
* head Start of the double linked list of elements
|
|
*
|
|
* Returns:
|
|
* New head of the list
|
|
*/
|
|
|
|
static PROGRAM *
|
|
SortList(PROGRAM *head, int flag)
|
|
{
|
|
PROGRAM *new = NULL, *prev, *walk, *next, *scan;
|
|
|
|
/* Walk the original list */
|
|
for (walk = head; walk != NULL; walk = next) {
|
|
/* Correct weightSize values */
|
|
if (flag != 0) {
|
|
walk->weightSize += MA_WSIZE_FRAC - 1;
|
|
walk->weightSize /= MA_WSIZE_FRAC;
|
|
}
|
|
|
|
/* Get it now, will be rewritten below */
|
|
next = walk->next;
|
|
|
|
/* No sort, just scale */
|
|
if (flag < 0) {
|
|
new = head;
|
|
continue;
|
|
}
|
|
|
|
/* Put into new sorted list */
|
|
for (prev = NULL, scan = new; scan != NULL;
|
|
scan = scan->next) {
|
|
if (walk->weightSize > scan->weightSize)
|
|
break;
|
|
prev = scan;
|
|
}
|
|
|
|
/* Insert into new list before current element */
|
|
if (scan) {
|
|
walk->next = scan;
|
|
walk->prev = scan->prev;
|
|
scan->prev = walk;
|
|
if (prev)
|
|
prev->next = walk;
|
|
else
|
|
new = walk;
|
|
} else if (new) {
|
|
walk->prev = prev;
|
|
prev->next = walk;
|
|
walk->next = NULL;
|
|
} else {
|
|
new = walk;
|
|
walk->prev = NULL;
|
|
walk->next = NULL;
|
|
}
|
|
}
|
|
|
|
return new;
|
|
}
|
|
|
|
/*
|
|
* Indentify PROGRAM text extent
|
|
*/
|
|
static int
|
|
isprgtxt(struct prmap_sgi *map)
|
|
{
|
|
if ((map->pr_mflags & (MA_EXEC|MA_PRIMARY)) == (MA_PRIMARY|MA_EXEC))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Indentify RLD text extent
|
|
*/
|
|
static int
|
|
isrldtxt(struct prmap_sgi *map)
|
|
{
|
|
if ((unsigned)map->pr_vaddr == 0x0fb60000)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Indentify RLD bss extents
|
|
*/
|
|
static isrldbss(struct prmap_sgi *map)
|
|
{
|
|
if (((unsigned)map->pr_vaddr >= 0x0fbc0000) &&
|
|
((unsigned)map->pr_vaddr < 0x0fc40000))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* static char *
|
|
* MapFlags(prmap_sgi_t *map)
|
|
*
|
|
* Description:
|
|
* Translate the flags for a region into a meaningful word that
|
|
* describes what type of region it is
|
|
*
|
|
* Parameters:
|
|
* map region pointer
|
|
*
|
|
* Returns:
|
|
* string describing the type of region
|
|
*/
|
|
|
|
static char *
|
|
MapFlags(prmap_sgi_t *map)
|
|
{
|
|
if (map->pr_mflags & MA_PHYS) {
|
|
return "Physical Device";
|
|
} else if (map->pr_mflags & MA_STACK) {
|
|
return "Stack";
|
|
} else if (map->pr_mflags & MA_BREAK) {
|
|
return "Break";
|
|
} else if (map->pr_mflags & MA_EXEC) {
|
|
return "Text";
|
|
} else if (map->pr_mflags & MA_COW) {
|
|
return "Data";
|
|
} else if (map->pr_mflags & MA_SHMEM) {
|
|
return "Shmem";
|
|
} else if ((map->pr_mflags & (MA_READ | MA_WRITE)) ==
|
|
(MA_READ | MA_WRITE)) {
|
|
return "RW";
|
|
} else if (map->pr_mflags & MA_READ) {
|
|
return "RO";
|
|
}
|
|
return "Other";
|
|
}
|
|
|
|
/*
|
|
* static int
|
|
* PteAccounting(prmap_sgi_t *maps, int nmaps)
|
|
*
|
|
* Description:
|
|
* Try and figure out how many page tables are in core
|
|
*
|
|
* Parameters:
|
|
* maps region pointer
|
|
* nmaps number of maps in region list
|
|
*/
|
|
static PteAccounting(prmap_sgi_t *maps, int nmaps)
|
|
{
|
|
unsigned pteblksize = (kpagesize / sizeof (caddr_t)) * kpagesize;
|
|
prmap_sgi_t *map;
|
|
long segno, refcnt, lrefcnt = 0, space = 0, ssize;
|
|
caddr_t vaddr;
|
|
int i, last = -1;
|
|
|
|
/* Search all the segments */
|
|
for (map = maps, i = 0; i < nmaps; ++map, ++i) {
|
|
/* Skip empty ones */
|
|
if (map->pr_vsize == 0)
|
|
continue;
|
|
|
|
/* Walk segment one segment at a time (XXX fix me) */
|
|
vaddr = map->pr_vaddr;
|
|
refcnt = map->pr_mflags >> MA_REFCNT_SHIFT;
|
|
for (ssize = map->pr_size; ssize > 0; ssize -= pteblksize) {
|
|
/* If not same as last time & incore, then bill me */
|
|
segno = (long)vaddr / pteblksize;
|
|
if (last == -1)
|
|
last = segno;
|
|
if (last != segno) {
|
|
space += kpagesize / lrefcnt;
|
|
last = segno;
|
|
lrefcnt = 0;
|
|
}
|
|
vaddr += pteblksize;
|
|
if (!lrefcnt || (refcnt < lrefcnt)) {
|
|
lrefcnt = refcnt;
|
|
}
|
|
}
|
|
}
|
|
if (lrefcnt) {
|
|
space += kpagesize / lrefcnt;
|
|
}
|
|
|
|
return space / 1024;
|
|
}
|
|
|
|
/*
|
|
* static void OpenProcDir(void)
|
|
*
|
|
* Description:
|
|
* Open /proc if it's not open, and rewind. dirp is a global
|
|
* variable.
|
|
*/
|
|
|
|
static void OpenProcDir(void)
|
|
{
|
|
struct paramconst p;
|
|
|
|
/* Get system page size */
|
|
if (!kpagesize) {
|
|
kpagesize = getpagesize();
|
|
|
|
if (syssgi(SGI_CONST, SGICONST_PARAM, &p, sizeof(p), 0) == -1) {
|
|
usize = 1; /* guess */
|
|
} else {
|
|
usize = p.p_usize + p.p_extusize;
|
|
}
|
|
pgsize = kpagesize/1024;
|
|
}
|
|
|
|
|
|
if (!dirp) {
|
|
if ((dirp = opendir(PROCDIR)) == NULL) {
|
|
perror(PROCDIR);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
rewinddir(dirp);
|
|
}
|
|
|
|
/*
|
|
* static void GetObjInfoPriv(char *objName, PROGRAM **all, PROGRAM **objp)
|
|
*
|
|
* Description:
|
|
* Get physical memory usage information for all mapped objects
|
|
* in the system.
|
|
*
|
|
* Parameters:
|
|
* objName name of object to get detailed information for
|
|
* all gets bloat for all objectss
|
|
* objp gets bloat for a specific object
|
|
*/
|
|
|
|
static void GetObjInfoPriv(char *objName, PROGRAM **all, PROGRAM **objp)
|
|
{
|
|
struct dirent *dent;
|
|
char procFile[MAXPATHLEN];
|
|
int status, fd, nmaps, i, pid = 1, ppid = 1;
|
|
register struct prmap_sgi *map, *amap, *rmap, *bmap, *maps;
|
|
prmap_sgi_arg_t maparg;
|
|
PROGRAM *list = NULL;
|
|
PROGRAM *plist = NULL;
|
|
unsigned long refCount;
|
|
double rss;
|
|
char *mname, *flagName;
|
|
struct prpsinfo info;
|
|
long kstack = 0, ptespace = 0;
|
|
|
|
|
|
OpenProcDir();
|
|
|
|
while ((dent = readdir(dirp)) != NULL) {
|
|
if (!isdigit(dent->d_name[0]))
|
|
continue;
|
|
|
|
snprintf(procFile, sizeof procFile, "%s/%s", PROCDIR,
|
|
dent->d_name);
|
|
|
|
status = (fd = open(procFile, O_RDONLY)) != -1
|
|
&& ioctl(fd, PIOCPSINFO, &info) != -1;
|
|
|
|
if (!status) {
|
|
close(fd);
|
|
continue;
|
|
}
|
|
|
|
if (ioctl(fd, PIOCNMAP, &nmaps) == -1) {
|
|
close(fd);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Dynamically determine the number of entries for the process and
|
|
* allocate that many map entries.
|
|
*/
|
|
if (nmaps) {
|
|
nmaps++; /* Allocate an extra entry for termination */
|
|
maps = malloc(sizeof(struct prmap_sgi) * nmaps);
|
|
maparg.pr_vaddr = (caddr_t)maps;
|
|
maparg.pr_size = sizeof(struct prmap_sgi)*nmaps;
|
|
|
|
if ((nmaps = ioctl(fd, PIOCMAP_SGI, &maparg)) == -1) {
|
|
free(maps);
|
|
close(fd);
|
|
continue;
|
|
}
|
|
}
|
|
close(fd);
|
|
|
|
/* charge for kernel stack */
|
|
kstack += (KSTKSIZE * kpagesize / 1024);
|
|
|
|
/* Try and figure out how many page tables are resident in memory */
|
|
ptespace += PteAccounting(maps, nmaps);
|
|
|
|
amap = NULL;
|
|
rmap = NULL;
|
|
|
|
/* Search regions to find app segment & rld segment */
|
|
for (map = maps, i = nmaps; i-- > 0; ++map) {
|
|
/* Hack for app region */
|
|
if (!amap && isprgtxt(map)) {
|
|
amap = map;
|
|
}
|
|
|
|
/* Remember primary BRK region */
|
|
if ((map->pr_mflags & (MA_PRIMARY|MA_BREAK)) ==
|
|
(MA_PRIMARY|MA_BREAK)) {
|
|
bmap = map;
|
|
}
|
|
|
|
/* Remember RLD region */
|
|
if (!rmap && isrldtxt(map)) {
|
|
rmap = map;
|
|
}
|
|
}
|
|
|
|
/* Scan the table */
|
|
for (map = maps, i = nmaps; i-- > 0; ++map) {
|
|
if (map->pr_mflags & MA_PHYS) {
|
|
continue;
|
|
}
|
|
|
|
/* Try and bill /dev/zero regions to real owners */
|
|
if (map->pr_mflags & MA_MAPZERO) {
|
|
/* Hack for rld's /dev/zero regions */
|
|
if (rmap && isrldbss(map)) {
|
|
map->pr_dev = rmap->pr_dev;
|
|
map->pr_ino = rmap->pr_ino;
|
|
map->pr_mflags |= MA_BREAK;
|
|
}
|
|
/* Otherwise bill to apps primary break region */
|
|
else if (bmap) {
|
|
map->pr_dev = bmap->pr_dev;
|
|
map->pr_ino = bmap->pr_ino;
|
|
map->pr_mflags |= MA_BREAK;
|
|
}
|
|
} else if (amap && ((map->pr_mflags & MA_STACK) ||
|
|
(map->pr_mflags & MA_BREAK))) {
|
|
map->pr_dev = amap->pr_dev;
|
|
map->pr_ino = amap->pr_ino;
|
|
}
|
|
|
|
if (amap &&
|
|
map->pr_dev == amap->pr_dev && map->pr_ino == amap->pr_ino) {
|
|
mname = info.pr_fname;
|
|
} else {
|
|
mname = map->pr_ino ?
|
|
InodeLookup(map->pr_dev, map->pr_ino) : NULL;
|
|
}
|
|
|
|
flagName = MapFlags(map);
|
|
refCount = map->pr_mflags >> MA_REFCNT_SHIFT;
|
|
rss = (double)map->pr_wsize;
|
|
rss *= pgsize; /* use 1KB resolution */
|
|
rss /= (double)refCount;
|
|
list = AddElem(list, mname ? mname : flagName,
|
|
mname, flagName,
|
|
(map->pr_psize * pgsize) / refCount, rss,
|
|
0, 0, pid++, NULL, 0);
|
|
if (objName && mname && strcmp(objName, mname) == 0) {
|
|
plist = AddElem(plist, flagName, mname, flagName,
|
|
(map->pr_psize * pgsize) / refCount, rss,
|
|
0, 0, ppid++, NULL, 0);
|
|
}
|
|
}
|
|
if (nmaps)
|
|
free(maps);
|
|
}
|
|
list = SortList(list, 1);
|
|
if (plist) plist = SortList(plist, -1);
|
|
|
|
list = AddElem(list, "Kernel Stacks", "Kernel Stacks", IRIX,
|
|
kstack, kstack, 0, 0, pid++, NULL, 0);
|
|
|
|
list = AddElem(list, "PTEs", "PTEs", IRIX,
|
|
ptespace, ptespace, 0, 0, pid++, NULL, 0);
|
|
|
|
*all = list;
|
|
*objp = plist;
|
|
}
|
|
|
|
/*
|
|
* static void
|
|
* GetProcInfoPriv(char *procName, pid_t pid, PROGRAM **all, PROGRAM **proc)
|
|
*
|
|
* Description:
|
|
* Get memory usage information for all processes in the system.
|
|
* In addition, collect detailed information for procName if it
|
|
* is non-NULL, or collect detailed information for pid if it is
|
|
* not -1.
|
|
*
|
|
* Parameters:
|
|
* procName name of program to get detailed information for
|
|
* pid process id to get detailed information for
|
|
* all gets bloat for all processes
|
|
* proc gets bloat for a specific process
|
|
*/
|
|
|
|
static void
|
|
GetProcInfoPriv(char *procName, pid_t pid, PROGRAM **all, PROGRAM **proc)
|
|
{
|
|
struct dirent *dent;
|
|
char procFile[MAXPATHLEN];
|
|
int fd, i;
|
|
struct prpsinfo info;
|
|
register prmap_sgi_t *map, *amap, *maps;
|
|
long *physUse;
|
|
prmap_sgi_arg_t maparg;
|
|
unsigned nmaps;
|
|
PROGRAM *list = NULL;
|
|
PROGRAM *plist = NULL;
|
|
char mapObjName[MAXPATHLEN + 20], *mname, *flagName;
|
|
int status;
|
|
struct tileinfo tinfo;
|
|
struct rminfo rminfo;
|
|
struct kna kna;
|
|
struct minfo minfo;
|
|
long userTotal, pteTotal, kstack, ptespace, wrss, kmem, mem, privSize;
|
|
long weighted, refCount;
|
|
long resident, total;
|
|
|
|
|
|
OpenProcDir();
|
|
|
|
userTotal = pteTotal = 0;
|
|
while ((dent = readdir(dirp)) != NULL) {
|
|
if (!isdigit(dent->d_name[0]))
|
|
continue;
|
|
|
|
snprintf(procFile, sizeof procFile, "%s/%s", PROCDIR,
|
|
dent->d_name);
|
|
|
|
status = (fd = open(procFile, O_RDONLY)) != -1
|
|
&& ioctl(fd, PIOCPSINFO, &info) != -1;
|
|
|
|
if (!status) {
|
|
close(fd);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Dynamically determine the number of entries for the process and
|
|
* allocate that many map entries.
|
|
*/
|
|
|
|
if (ioctl(fd, PIOCNMAP, &nmaps) == -1) {
|
|
close(fd);
|
|
continue;
|
|
}
|
|
|
|
maps = NULL;
|
|
physUse = NULL;
|
|
if (nmaps) {
|
|
nmaps++; /* Allocate an extra entry for termination */
|
|
maps = malloc(sizeof(struct prmap_sgi) * nmaps);
|
|
physUse = malloc(sizeof(long) *nmaps);
|
|
maparg.pr_vaddr = (caddr_t)maps;
|
|
maparg.pr_size = sizeof(struct prmap_sgi)*nmaps;
|
|
|
|
if ((nmaps = ioctl(fd, PIOCMAP_SGI, &maparg)) == -1) {
|
|
free(maps);
|
|
free(physUse);
|
|
close(fd);
|
|
continue;
|
|
}
|
|
}
|
|
close(fd);
|
|
|
|
wrss = 0;
|
|
privSize = 0;
|
|
/* Compute weighted rss from valid and usage counts */
|
|
for (map = maps, i = nmaps; i-- > 0; ++map) {
|
|
if ((map->pr_mflags & MA_PHYS) == 0) {
|
|
refCount = map->pr_mflags >> MA_REFCNT_SHIFT;
|
|
weighted = map->pr_wsize;
|
|
weighted *= pgsize; /* use 1KB resolution */
|
|
weighted /= MA_WSIZE_FRAC;
|
|
weighted /= refCount;
|
|
physUse[i] = (long)weighted;
|
|
wrss += weighted;
|
|
if ((map->pr_mflags & MA_SHMEM) == 0) {
|
|
privSize += (map->pr_psize * pgsize) / refCount;
|
|
}
|
|
} else {
|
|
physUse[i] = 0;
|
|
}
|
|
}
|
|
userTotal += wrss;
|
|
|
|
/* charge for kernel stack */
|
|
kstack = (KSTKSIZE * kpagesize / 1024);
|
|
userTotal += kstack;
|
|
privSize += kstack;
|
|
|
|
/* Try and figure out how many page tables are resident in memory */
|
|
ptespace = PteAccounting(maps, nmaps);
|
|
pteTotal += ptespace;
|
|
privSize += ptespace;
|
|
|
|
list = AddElem(list, info.pr_fname, NULL, NULL, privSize,
|
|
(long)wrss + kstack + ptespace,
|
|
info.pr_rssize * pgsize + kstack + ptespace,
|
|
info.pr_size * pgsize + kstack + ptespace,
|
|
info.pr_pid, NULL, 1);
|
|
|
|
if (pid == -1 && procName && strcmp(procName, info.pr_fname) == 0
|
|
|| pid != -1 && pid == info.pr_pid) {
|
|
amap = 0;
|
|
/*
|
|
* Find program text region, so we can get good names for
|
|
* the regions associated with each executable file even
|
|
* if that file isn't in our inode map.
|
|
*/
|
|
for (map = maps, i = nmaps; i-- > 0; ++map) {
|
|
if (isprgtxt(map)) {
|
|
amap = map;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* List all the address space segments in virtual order */
|
|
for (map = maps, i = nmaps; i-- > 0; ++map) {
|
|
if ((map->pr_mflags & MA_MAPZERO) && isrldbss(map)) {
|
|
mname = "rld";
|
|
map->pr_mflags |= MA_BREAK;
|
|
} else if ((map->pr_mflags & MA_STACK) ||
|
|
(map->pr_mflags & MA_BREAK) ||
|
|
(amap &&
|
|
map->pr_dev == amap->pr_dev && map->pr_ino ==
|
|
amap->pr_ino)) {
|
|
mname = procName;
|
|
} else {
|
|
mname = map->pr_ino ?
|
|
InodeLookup(map->pr_dev, map->pr_ino) : NULL;
|
|
}
|
|
flagName = MapFlags(map);
|
|
if (mname) {
|
|
snprintf(mapObjName, sizeof mapObjName, "%s (%s)",
|
|
flagName, mname);
|
|
if (strlen(mapObjName) > 15) {
|
|
mapObjName[15] = ')';
|
|
mapObjName[16] = '\0';
|
|
}
|
|
}
|
|
refCount = map->pr_mflags >> MA_REFCNT_SHIFT;
|
|
if (map->pr_mflags & MA_PHYS) {
|
|
resident = 0;
|
|
total = 0;
|
|
} else {
|
|
resident = map->pr_vsize * pgsize;
|
|
total = (map->pr_size + 1023) / 1024;
|
|
}
|
|
plist = AddElem(plist, mname ? mapObjName : flagName,
|
|
mname, flagName,
|
|
((map->pr_mflags & MA_PHYS) ||
|
|
(map->pr_mflags & MA_SHMEM)) ?
|
|
0 : (map->pr_psize * pgsize) / refCount,
|
|
physUse[i], resident, total, nmaps - i,
|
|
map->pr_vaddr, 0);
|
|
}
|
|
|
|
plist = AddElem(plist, "Kernel Stack", "Kernel Stack",
|
|
IRIX, kstack, kstack, kstack, kstack,
|
|
nmaps+1, NULL, 0);
|
|
plist = AddElem(plist, "PTEs", "PTEs",
|
|
IRIX, ptespace, ptespace, ptespace, ptespace,
|
|
nmaps+2, NULL, 0);
|
|
}
|
|
if (nmaps) {
|
|
free(maps);
|
|
free(physUse);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Special case if procName is "Irix". We get info from the sysmp
|
|
* call.
|
|
*/
|
|
if (procName && strcmp(procName, IRIX) == 0) {
|
|
KernelInfo();
|
|
if (sysmp(MP_SAGET, MPSA_RMINFO, &rminfo, sizeof rminfo) == -1
|
|
||sysmp(MP_SAGET, MPSA_MINFO, &minfo, sizeof minfo) == -1
|
|
||sysmp(MP_SAGET, MPSA_TCPIPSTATS, (char *)&kna,
|
|
sizeof(kna)) == -1) {
|
|
perror("sysmp");
|
|
} else {
|
|
i = 0;
|
|
|
|
/*
|
|
* Non-filesystem parts of kernel are as follows -
|
|
*/
|
|
kmem = (rminfo.physmem-(rminfo.availrmem+rminfo.bufmem)) * pgsize;
|
|
|
|
/*
|
|
* Subtract out PTEs found attached to user processes (see PMAP)
|
|
*/
|
|
kmem -= pteTotal;
|
|
|
|
/*
|
|
* Filesystem data & metadata -
|
|
*/
|
|
mem = ((rminfo.chunkpages + rminfo.dpages) +
|
|
rminfo.dchunkpages) * pgsize;
|
|
|
|
plist = AddElem(plist, "FS Cache", "FS Cache", IRIX, mem, mem,
|
|
0, 0, i++, NULL, 0);
|
|
|
|
mem = rminfo.bufmem * pgsize;
|
|
plist = AddElem(plist, "FS Control", "FS Control", IRIX,
|
|
mem, mem, 0, 0, i++, NULL, 0);
|
|
|
|
/*
|
|
* Subtract streams from heap, since streams memory is
|
|
* part of the heap
|
|
*/
|
|
mem = minfo.heapmem / 1024 - rminfo.strmem * pgsize;
|
|
plist = AddElem(plist, "Heap", "Heap", IRIX,
|
|
mem, mem, 0, 0, i++, NULL, 0);
|
|
kmem -= mem;
|
|
|
|
mem = rminfo.strmem * pgsize;
|
|
plist = AddElem(plist, "Streams", "Streams", IRIX,
|
|
mem, mem, 0, 0, i++, NULL, 0);
|
|
kmem -= mem;
|
|
|
|
mem = kna.mbstat.m_clusters * pgsize;
|
|
plist = AddElem(plist, "BSD Networking",
|
|
"BSD Networking", IRIX,
|
|
mem, mem, 0, 0, i++, NULL, 0);
|
|
kmem -= mem;
|
|
|
|
mem = ketext - kpstart; /* Kernel Text */
|
|
mem += kend - ketext; /* Kernel Data */
|
|
if (kpbase > kend) {
|
|
/* pfdat is backwards from kpbase relative to physmem */
|
|
kpfdat = kpbase - (rminfo.physmem -
|
|
((kpbase - kpstart) / (pgsize * 1024))) *
|
|
kpfdatsz;
|
|
mem += kpbase - kpfdat; /* Page Frames */
|
|
mem += kpfdat - kend; /* Kernel Tables */
|
|
}
|
|
mem /= 1024;
|
|
kmem -= mem;
|
|
|
|
/*
|
|
* (PMAP) Subtract pmap memory since we charge it to processes
|
|
*/
|
|
mem = rminfo.pmapmem * pgsize - pteTotal;
|
|
if (mem > 0) {
|
|
plist = AddElem(plist, "Pmap Overhead", "Pmap Overhead",
|
|
IRIX, mem, mem, 0, 0, i++, NULL, 0);
|
|
kmem -= mem;
|
|
}
|
|
|
|
#ifdef NOTYET
|
|
/*
|
|
* Digital media data structures
|
|
*/
|
|
mem = minfo.dmedia * pgsize;
|
|
if (mem > 0) {
|
|
plist = AddElem(plist, "Digital media", "Digital media",
|
|
IRIX, mem, mem, 0, 0, i++, NULL, 0);
|
|
kmem -= mem;
|
|
}
|
|
#endif
|
|
#ifdef TILES_TO_LPAGES
|
|
/*
|
|
* Tiles (Physically contiguous 64k chunks)
|
|
*/
|
|
if (sysmp(MP_SAGET, MPSA_TILEINFO, &tinfo, sizeof tinfo) != -1) {
|
|
mem = tinfo.ttile * pgsize;
|
|
if (mem > 0) {
|
|
plist = AddElem(plist, "Tiles", "Tiles", IRIX,
|
|
mem, mem, 0, 0, i++, NULL, 0);
|
|
kmem -= mem;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
plist = AddElem(plist, "Other", "Other", IRIX,
|
|
kmem, kmem, 0, 0, i++, NULL, 0);
|
|
|
|
if (kpbase > kend) {
|
|
if (kpbase > kpfdat && kpfdat > kend) {
|
|
mem = (kpbase - kpfdat) / 1024;
|
|
plist = AddElem(plist, "Page Frame Data",
|
|
"Page Frame Data",
|
|
IRIX, mem, mem, 0, 0, i++, NULL, 0);
|
|
mem = (kpfdat - kend) / 1024;
|
|
plist = AddElem(plist, "Kernel Tables", "Kernel Tables",
|
|
IRIX, mem, mem, 0, 0, i++, NULL, 0);
|
|
} else {
|
|
mem = (kpbase - kend) / 1024;
|
|
plist = AddElem(plist, "Kernel Tables", "Kernel Tables",
|
|
IRIX, mem, mem, 0, 0, i++, NULL, 0);
|
|
}
|
|
}
|
|
mem = (kend - ketext) / 1024;
|
|
plist = AddElem(plist, "Unix Data Space", "Unix Data Space",
|
|
IRIX, mem, mem, 0, 0, i++, NULL, 0);
|
|
if ((ktext - kpstart) > 2*kpagesize) {
|
|
mem = (ketext - ktext) / 1024;
|
|
plist = AddElem(plist, "Unix Code Space", "Unix Code Space",
|
|
IRIX, mem, mem, 0, 0, i++, NULL, 0);
|
|
mem = (ktext - kpstart) / 1024;
|
|
plist = AddElem(plist, "Symmon", "Symmon", IRIX,
|
|
mem, mem, 0, 0, i++, NULL, 0);
|
|
} else {
|
|
mem = (ketext - kpstart) / 1024;
|
|
plist = AddElem(plist, "Unix Code Space", "Unix Code Space",
|
|
IRIX, mem, mem, 0, 0, i++, NULL, 0);
|
|
}
|
|
}
|
|
}
|
|
*all = list;
|
|
*proc = plist;
|
|
}
|
|
|
|
/*
|
|
* void GetObjInfo(char *objName, PROGRAM **all, PROGRAM **objp)
|
|
*
|
|
* Description:
|
|
* Wrapper for GetObjInfoPriv; aquire capabilities first, drop
|
|
* them after.
|
|
*
|
|
* Parameters:
|
|
* objName name of object to get detailed information for
|
|
* all gets bloat for all objectss
|
|
* objp gets bloat for a specific object
|
|
*/
|
|
|
|
void GetObjInfo(char *objName, PROGRAM **all, PROGRAM **objp)
|
|
{
|
|
cap_t ocap;
|
|
const cap_value_t cap[3] = { CAP_DAC_WRITE,
|
|
CAP_DAC_READ_SEARCH,
|
|
CAP_FOWNER };
|
|
ocap = cap_acquire(sizeof cap/sizeof cap[0], cap);
|
|
|
|
GetObjInfoPriv(objName, all, objp);
|
|
|
|
cap_surrender(ocap);
|
|
}
|
|
|
|
/*
|
|
* void
|
|
* GetProcInfo(char *procName, pid_t pid, PROGRAM **all, PROGRAM **proc)
|
|
*
|
|
* Description:
|
|
* Wrapper for GetProcInfoPriv. Acquire capabilities before
|
|
* calling, drop them after.
|
|
*
|
|
* Parameters:
|
|
* procName name of program to get detailed information for
|
|
* pid process id to get detailed information for
|
|
* all gets bloat for all processes
|
|
* proc gets bloat for a specific process
|
|
*/
|
|
void
|
|
GetProcInfo(char *procName, pid_t pid, PROGRAM **all, PROGRAM **proc)
|
|
{
|
|
cap_t ocap;
|
|
const cap_value_t cap[3] = { CAP_DAC_WRITE,
|
|
CAP_DAC_READ_SEARCH,
|
|
CAP_FOWNER };
|
|
ocap = cap_acquire(sizeof cap/sizeof cap[0], cap);
|
|
GetProcInfoPriv(procName, pid, all, proc);
|
|
cap_surrender(ocap);
|
|
}
|
|
|
|
/*
|
|
* void
|
|
* FreeBloat(PROGRAM *bloat)
|
|
*
|
|
* Description:
|
|
* Free all the memory used by a bloat list, so we don't
|
|
* ourselves become too bloated :-)
|
|
*
|
|
* Parameters:
|
|
* bloat
|
|
*/
|
|
|
|
void
|
|
FreeBloat(PROGRAM *bloat)
|
|
{
|
|
PROGRAM *next;
|
|
PIDLIST *pids, *npid;
|
|
|
|
while (bloat) {
|
|
pids = bloat->pids;
|
|
while (pids) {
|
|
npid = pids->next;
|
|
free(pids);
|
|
pids = npid;
|
|
}
|
|
|
|
next = bloat->next;
|
|
free(bloat->progName);
|
|
if (bloat->mapName) {
|
|
free(bloat->mapName);
|
|
}
|
|
free(bloat);
|
|
bloat = next;
|
|
}
|
|
}
|