pmap.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#include <limits.h>
#include <link.h>
#include <libelf.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#include <libproc.h>
struct totals {
ulong_t total_size;
ulong_t total_swap;
ulong_t total_rss;
ulong_t total_anon;
ulong_t total_locked;
};
typedef int proc_xmap_f(void *, const prxmap_t *, const char *, int, int);
static int xmapping_iter(struct ps_prochandle *, proc_xmap_f *, void *,
int);
static int rmapping_iter(struct ps_prochandle *, proc_map_f *, void *);
static int look_map(void *, const prmap_t *, const char *);
static int look_smap(void *, const prxmap_t *, const char *, int, int);
static int look_xmap(void *, const prxmap_t *, const char *, int, int);
static int look_xmap_nopgsz(void *, const prxmap_t *, const char *,
int, int);
static int gather_map(void *, const prmap_t *, const char *);
static int gather_xmap(void *, const prxmap_t *, const char *, int, int);
static int iter_map(proc_map_f *, void *);
static int iter_xmap(proc_xmap_f *, void *);
static int perr(char *);
static void printK(long, int);
static char *mflags(uint_t);
static int lflag = 0;
static int aflag = 0;
static int addr_width, size_width;
static char *command;
static char *procname;
static struct ps_prochandle *Pr;
typedef struct lwpstack {
lwpid_t lwps_lwpid;
stack_t lwps_stack;
} lwpstack_t;
typedef struct {
prxmap_t md_xmap;
prmap_t md_map;
char *md_objname;
int md_last;
int md_doswap;
} mapdata_t;
static mapdata_t *maps;
static int map_count;
static int map_alloc;
static lwpstack_t *stacks = NULL;
static uint_t nstacks = 0;
#define MAX_TRIES 5
static int
getstack(void *data, const lwpstatus_t *lsp)
{
int *np = (int *)data;
if (Plwp_alt_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) {
stacks[*np].lwps_stack.ss_flags |= SS_ONSTACK;
stacks[*np].lwps_lwpid = lsp->pr_lwpid;
(*np)++;
}
if (Plwp_main_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) {
stacks[*np].lwps_lwpid = lsp->pr_lwpid;
(*np)++;
}
return (0);
}
/*
* We compare the high memory addresses since stacks are faulted in from
* high memory addresses to low memory addresses, and our prmap_t
* structures identify only the range of addresses that have been faulted
* in so far.
*/
static int
cmpstacks(const void *ap, const void *bp)
{
const lwpstack_t *as = ap;
const lwpstack_t *bs = bp;
uintptr_t a = (uintptr_t)as->lwps_stack.ss_sp + as->lwps_stack.ss_size;
uintptr_t b = (uintptr_t)bs->lwps_stack.ss_sp + bs->lwps_stack.ss_size;
if (a < b)
return (1);
if (a > b)
return (-1);
return (0);
}
int
main(int argc, char **argv)
{
int rflag = 0, sflag = 0, xflag = 0;
int errflg = 0, Sflag = 0;
int rc = 0;
int opt;
const char *bar8 = "-------";
const char *bar16 = "----------";
const char *bar;
struct rlimit rlim;
struct stat64 statbuf;
char buf[128];
int mapfd;
if ((command = strrchr(argv[0], '/')) != NULL)
command++;
else
command = argv[0];
while ((opt = getopt(argc, argv, "arsxSlF")) != EOF) {
switch (opt) {
case 'a': /* include shared mappings in -[xS] */
aflag = 1;
break;
case 'r': /* show reserved mappings */
rflag = 1;
break;
case 's': /* show hardware page sizes */
sflag = 1;
break;
case 'S': /* show swap reservations */
Sflag = 1;
break;
case 'x': /* show extended mappings */
xflag = 1;
break;
case 'l': /* show unresolved link map names */
lflag = 1;
break;
case 'F':
/*
* Since we grab the process readonly now, the -F flag
* is meaningless. Consume it anyway it for backwards
* compatbility.
*/
break;
default:
errflg = 1;
break;
}
}
argc -= optind;
argv += optind;
if ((Sflag && (xflag || rflag || sflag)) || (xflag && rflag) ||
(aflag && (!xflag && !Sflag))) {
errflg = 1;
}
if (errflg || argc <= 0) {
(void) fprintf(stderr,
"usage:\t%s [-rslF] { pid | core } ...\n", command);
(void) fprintf(stderr,
"\t\t(report process address maps)\n");
(void) fprintf(stderr,
"\t%s -x [-aslF] pid ...\n", command);
(void) fprintf(stderr,
"\t\t(show resident/anon/locked mapping details)\n");
(void) fprintf(stderr,
"\t%s -S [-alF] { pid | core } ...\n", command);
(void) fprintf(stderr,
"\t\t(show swap reservations)\n\n");
(void) fprintf(stderr,
"\t-a: include shared mappings in -[xS] summary\n");
(void) fprintf(stderr,
"\t-r: show reserved address maps\n");
(void) fprintf(stderr,
"\t-s: show hardware page sizes\n");
(void) fprintf(stderr,
"\t-l: show unresolved dynamic linker map names\n");
(void) fprintf(stderr,
"\t-F: force grabbing of the target process\n");
return (2);
}
/*
* Make sure we'll have enough file descriptors to handle a target
* that has many many mappings.
*/
if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
rlim.rlim_cur = rlim.rlim_max;
(void) setrlimit(RLIMIT_NOFILE, &rlim);
}
while (argc-- > 0) {
char *arg;
int gcode;
psinfo_t psinfo;
int tries = 0;
if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_ANY,
PGRAB_RDONLY, &gcode)) == NULL) {
(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
command, arg, Pgrab_error(gcode));
rc++;
continue;
}
procname = arg; /* for perr() */
addr_width = (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 16 : 8;
size_width = (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 11 : 8;
bar = addr_width == 8 ? bar8 : bar16;
(void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t));
proc_unctrl_psinfo(&psinfo);
if (Pstate(Pr) != PS_DEAD) {
(void) snprintf(buf, sizeof (buf),
"/proc/%d/map", (int)psinfo.pr_pid);
if ((mapfd = open(buf, O_RDONLY)) < 0) {
(void) fprintf(stderr, "%s: cannot "
"examine %s: lost control of "
"process\n", command, arg);
rc++;
Prelease(Pr, 0);
continue;
}
} else {
mapfd = -1;
}
again:
map_count = 0;
if (Pstate(Pr) == PS_DEAD) {
(void) printf("core '%s' of %d:\t%.70s\n",
arg, (int)psinfo.pr_pid, psinfo.pr_psargs);
if (rflag || sflag || xflag || Sflag) {
(void) printf(" -%c option is not compatible "
"with core files\n", xflag ? 'x' :
sflag ? 's' : rflag ? 'r' : 'S');
Prelease(Pr, 0);
rc++;
continue;
}
} else {
(void) printf("%d:\t%.70s\n",
(int)psinfo.pr_pid, psinfo.pr_psargs);
}
if (!(Pstatus(Pr)->pr_flags & PR_ISSYS)) {
struct totals t;
/*
* Since we're grabbing the process readonly, we need
* to make sure the address space doesn't change during
* execution.
*/
if (Pstate(Pr) != PS_DEAD) {
if (tries++ == MAX_TRIES) {
Prelease(Pr, 0);
(void) close(mapfd);
(void) fprintf(stderr, "%s: cannot "
"examine %s: address space is "
"changing\n", command, arg);
continue;
}
if (fstat64(mapfd, &statbuf) != 0) {
Prelease(Pr, 0);
(void) close(mapfd);
(void) fprintf(stderr, "%s: cannot "
"examine %s: lost control of "
"process\n", command, arg);
continue;
}
}
nstacks = psinfo.pr_nlwp * 2;
stacks = calloc(nstacks, sizeof (stacks[0]));
if (stacks != NULL) {
int n = 0;
(void) Plwp_iter(Pr, getstack, &n);
qsort(stacks, nstacks, sizeof (stacks[0]),
cmpstacks);
}
(void) memset(&t, 0, sizeof (t));
if (Pgetauxval(Pr, AT_BASE) != -1L &&
Prd_agent(Pr) == NULL) {
(void) fprintf(stderr, "%s: warning: "
"librtld_db failed to initialize; "
"shared library information will not be "
"available\n", command);
}
/*
* Gather data
*/
if (xflag)
rc += xmapping_iter(Pr, gather_xmap, NULL, 0);
else if (Sflag)
rc += xmapping_iter(Pr, gather_xmap, NULL, 1);
else {
if (rflag)
rc += rmapping_iter(Pr, gather_map,
NULL);
else if (sflag)
rc += xmapping_iter(Pr, gather_xmap,
NULL, 0);
else
rc += Pmapping_iter(Pr, gather_map,
NULL);
}
/*
* Ensure mappings are consistent.
*/
if (Pstate(Pr) != PS_DEAD) {
struct stat64 newbuf;
if (fstat64(mapfd, &newbuf) != 0 ||
memcmp(&newbuf.st_mtim, &statbuf.st_mtim,
sizeof (newbuf.st_mtim)) != 0) {
if (stacks != NULL) {
free(stacks);
stacks = NULL;
}
goto again;
}
}
/*
* Display data.
*/
if (xflag) {
(void) printf("%*s%*s%*s%*s%*s "
"%sMode Mapped File\n",
addr_width, "Address",
size_width, "Kbytes",
size_width, "RSS",
size_width, "Anon",
size_width, "Locked",
sflag ? "Pgsz " : "");
rc += iter_xmap(sflag ? look_xmap :
look_xmap_nopgsz, &t);
(void) printf("%s%s %s %s %s %s\n",
addr_width == 8 ? "-" : "------",
bar, bar, bar, bar, bar);
(void) printf("%stotal Kb", addr_width == 16 ?
" " : "");
printK(t.total_size, size_width);
printK(t.total_rss, size_width);
printK(t.total_anon, size_width);
printK(t.total_locked, size_width);
(void) printf("\n");
} else if (Sflag) {
(void) printf("%*s%*s%*s Mode Mapped File\n",
addr_width, "Address",
size_width, "Kbytes",
size_width, "Swap");
rc += iter_xmap(look_xmap_nopgsz, &t);
(void) printf("%s%s %s %s\n",
addr_width == 8 ? "-" : "------",
bar, bar, bar);
(void) printf("%stotal Kb", addr_width == 16 ?
" " : "");
printK(t.total_size, size_width);
printK(t.total_swap, size_width);
(void) printf("\n");
} else {
if (rflag) {
rc += iter_map(look_map, &t);
} else if (sflag) {
(void) printf("%*s %*s %4s %-6s %s\n",
addr_width, "Address", size_width,
"Bytes", "Pgsz", "Mode ",
"Mapped File");
rc += iter_xmap(look_smap, &t);
} else {
rc += iter_map(look_map, &t);
}
(void) printf(" %stotal %*luK\n",
addr_width == 16 ?
" " : "",
size_width, t.total_size);
}
if (stacks != NULL) {
free(stacks);
stacks = NULL;
}
}
Prelease(Pr, 0);
if (mapfd != -1)
(void) close(mapfd);
}
return (rc);
}
static char *
make_name(struct ps_prochandle *Pr, uintptr_t addr, const char *mapname,
char *buf, size_t bufsz)
{
const pstatus_t *Psp = Pstatus(Pr);
char fname[100];
struct stat statb;
int len;
if (!lflag && strcmp(mapname, "a.out") == 0 &&
Pexecname(Pr, buf, bufsz) != NULL)
return (buf);
if (Pobjname(Pr, addr, buf, bufsz) != NULL) {
if (lflag)
return (buf);
if ((len = resolvepath(buf, buf, bufsz)) > 0) {
buf[len] = '\0';
return (buf);
}
}
if (Pstate(Pr) != PS_DEAD && *mapname != '\0') {
(void) snprintf(fname, sizeof (fname), "/proc/%d/object/%s",
(int)Psp->pr_pid, mapname);
if (stat(fname, &statb) == 0) {
dev_t dev = statb.st_dev;
ino_t ino = statb.st_ino;
(void) snprintf(buf, bufsz, "dev:%lu,%lu ino:%lu",
(ulong_t)major(dev), (ulong_t)minor(dev), ino);
return (buf);
}
}
return (NULL);
}
static char *
anon_name(char *name, const pstatus_t *Psp,
uintptr_t vaddr, size_t size, int mflags, int shmid)
{
if (mflags & MA_ISM) {
if (shmid == -1)
(void) snprintf(name, PATH_MAX, " [ %s shmid=null ]",
(mflags & MA_NORESERVE) ? "ism" : "dism");
else
(void) snprintf(name, PATH_MAX, " [ %s shmid=0x%x ]",
(mflags & MA_NORESERVE) ? "ism" : "dism", shmid);
} else if (mflags & MA_SHM) {
if (shmid == -1)
(void) sprintf(name, " [ shmid=null ]");
else
(void) sprintf(name, " [ shmid=0x%x ]", shmid);
} else if (vaddr + size > Psp->pr_stkbase &&
vaddr < Psp->pr_stkbase + Psp->pr_stksize) {
(void) strcpy(name, " [ stack ]");
} else if ((mflags & MA_ANON) &&
vaddr + size > Psp->pr_brkbase &&
vaddr < Psp->pr_brkbase + Psp->pr_brksize) {
(void) strcpy(name, " [ heap ]");
} else {
lwpstack_t key, *stk;
key.lwps_stack.ss_sp = (void *)vaddr;
key.lwps_stack.ss_size = size;
if (nstacks > 0 &&
(stk = bsearch(&key, stacks, nstacks, sizeof (stacks[0]),
cmpstacks)) != NULL) {
(void) snprintf(name, PATH_MAX, " [ %s tid=%d ]",
(stk->lwps_stack.ss_flags & SS_ONSTACK) ?
"altstack" : "stack",
stk->lwps_lwpid);
} else if (Pstate(Pr) != PS_DEAD) {
(void) strcpy(name, " [ anon ]");
} else {
return (NULL);
}
}
return (name);
}
static int
rmapping_iter(struct ps_prochandle *Pr, proc_map_f *func, void *cd)
{
char mapname[PATH_MAX];
int mapfd, nmap, i, rc;
struct stat st;
prmap_t *prmapp, *pmp;
ssize_t n;
(void) snprintf(mapname, sizeof (mapname),
"/proc/%d/rmap", (int)Pstatus(Pr)->pr_pid);
if ((mapfd = open(mapname, O_RDONLY)) < 0 || fstat(mapfd, &st) != 0) {
if (mapfd >= 0)
(void) close(mapfd);
return (perr(mapname));
}
nmap = st.st_size / sizeof (prmap_t);
prmapp = malloc((nmap + 1) * sizeof (prmap_t));
if ((n = pread(mapfd, prmapp, (nmap + 1) * sizeof (prmap_t), 0L)) < 0) {
(void) close(mapfd);
free(prmapp);
return (perr("read rmap"));
}
(void) close(mapfd);
nmap = n / sizeof (prmap_t);
for (i = 0, pmp = prmapp; i < nmap; i++, pmp++) {
if ((rc = func(cd, pmp, NULL)) != 0) {
free(prmapp);
return (rc);
}
}
free(prmapp);
return (0);
}
static int
xmapping_iter(struct ps_prochandle *Pr, proc_xmap_f *func, void *cd, int doswap)
{
char mapname[PATH_MAX];
int mapfd, nmap, i, rc;
struct stat st;
prxmap_t *prmapp, *pmp;
ssize_t n;
(void) snprintf(mapname, sizeof (mapname),
"/proc/%d/xmap", (int)Pstatus(Pr)->pr_pid);
if ((mapfd = open(mapname, O_RDONLY)) < 0 || fstat(mapfd, &st) != 0) {
if (mapfd >= 0)
(void) close(mapfd);
return (perr(mapname));
}
nmap = st.st_size / sizeof (prxmap_t);
nmap *= 2;
again:
prmapp = malloc((nmap + 1) * sizeof (prxmap_t));
if ((n = pread(mapfd, prmapp, (nmap + 1) * sizeof (prxmap_t), 0)) < 0) {
(void) close(mapfd);
free(prmapp);
return (perr("read xmap"));
}
if (nmap < n / sizeof (prxmap_t)) {
free(prmapp);
nmap *= 2;
goto again;
}
(void) close(mapfd);
nmap = n / sizeof (prxmap_t);
for (i = 0, pmp = prmapp; i < nmap; i++, pmp++) {
if ((rc = func(cd, pmp, NULL, i == nmap - 1, doswap)) != 0) {
free(prmapp);
return (rc);
}
}
free(prmapp);
return (0);
}
/*ARGSUSED*/
static int
look_map(void *data, const prmap_t *pmp, const char *object_name)
{
struct totals *t = data;
const pstatus_t *Psp = Pstatus(Pr);
size_t size = (pmp->pr_size + 1023) / 1024;
char mname[PATH_MAX];
char *lname = NULL;
/*
* If the mapping is not anon or not part of the heap, make a name
* for it. We don't want to report the heap as a.out's data.
*/
if (!(pmp->pr_mflags & MA_ANON) ||
pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase ||
pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) {
lname = make_name(Pr, pmp->pr_vaddr, pmp->pr_mapname,
mname, sizeof (mname));
}
if (lname == NULL &&
((pmp->pr_mflags & MA_ANON) || Pstate(Pr) == PS_DEAD)) {
lname = anon_name(mname, Psp, pmp->pr_vaddr,
pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid);
}
(void) printf(lname ? "%.*lX %*luK %-6s %s\n" : "%.*lX %*luK %s\n",
addr_width, (uintptr_t)pmp->pr_vaddr,
size_width - 1, size, mflags(pmp->pr_mflags), lname);
t->total_size += size;
return (0);
}
static void
printK(long value, int width)
{
if (value == 0)
(void) printf(width == 8 ? " -" : " -");
else
(void) printf(" %*lu", width - 1, value);
}
static const char *
pagesize(const prxmap_t *pmp)
{
int pagesize = pmp->pr_hatpagesize;
static char buf[32];
if (pagesize == 0) {
return ("-"); /* no underlying HAT mapping */
}
if (pagesize >= 1024 && (pagesize % 1024) == 0) {
if ((pagesize % (1024 * 1024 * 1024)) == 0)
(void) snprintf(buf, sizeof (buf), "%dG",
pagesize / (1024 * 1024 * 1024));
else if ((pagesize % (1024 * 1024)) == 0)
(void) snprintf(buf, sizeof (buf), "%dM",
pagesize / (1024 * 1024));
else
(void) snprintf(buf, sizeof (buf), "%dK",
pagesize / 1024);
} else
(void) snprintf(buf, sizeof (buf), "%db", pagesize);
return (buf);
}
/*ARGSUSED*/
static int
look_smap(void *data,
const prxmap_t *pmp,
const char *object_name,
int last, int doswap)
{
struct totals *t = data;
const pstatus_t *Psp = Pstatus(Pr);
size_t size = (pmp->pr_size + 1023) / 1024;
char mname[PATH_MAX];
char *lname = NULL;
const char *format;
/*
* If the mapping is not anon or not part of the heap, make a name
* for it. We don't want to report the heap as a.out's data.
*/
if (!(pmp->pr_mflags & MA_ANON) ||
pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase ||
pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) {
lname = make_name(Pr, pmp->pr_vaddr, pmp->pr_mapname,
mname, sizeof (mname));
}
if (lname == NULL &&
((pmp->pr_mflags & MA_ANON) || Pstate(Pr) == PS_DEAD)) {
lname = anon_name(mname, Psp, pmp->pr_vaddr,
pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid);
}
if (lname != NULL)
format = "%.*lX %*luK %4s %-6s %s\n";
else
format = "%.*lX %*luK %4s %s\n";
(void) printf(format, addr_width, (uintptr_t)pmp->pr_vaddr,
size_width - 1, size,
pagesize(pmp), mflags(pmp->pr_mflags), lname);
t->total_size += size;
return (0);
}
#define ANON(x) ((aflag || (((x)->pr_mflags & MA_SHARED) == 0)) ? \
((x)->pr_anon) : 0)
/*ARGSUSED*/
static int
look_xmap(void *data,
const prxmap_t *pmp,
const char *object_name,
int last, int doswap)
{
struct totals *t = data;
const pstatus_t *Psp = Pstatus(Pr);
char mname[PATH_MAX];
char *lname = NULL;
char *ln;
/*
* If the mapping is not anon or not part of the heap, make a name
* for it. We don't want to report the heap as a.out's data.
*/
if (!(pmp->pr_mflags & MA_ANON) ||
pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase ||
pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) {
lname = make_name(Pr, pmp->pr_vaddr, pmp->pr_mapname,
mname, sizeof (mname));
}
if (lname != NULL) {
if ((ln = strrchr(lname, '/')) != NULL)
lname = ln + 1;
} else if ((pmp->pr_mflags & MA_ANON) || Pstate(Pr) == PS_DEAD) {
lname = anon_name(mname, Psp, pmp->pr_vaddr,
pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid);
}
(void) printf("%.*lX", addr_width, (ulong_t)pmp->pr_vaddr);
printK((pmp->pr_size + 1023) / 1024, size_width);
printK(pmp->pr_rss * (pmp->pr_pagesize / 1024), size_width);
printK(ANON(pmp) * (pmp->pr_pagesize / 1024), size_width);
printK(pmp->pr_locked * (pmp->pr_pagesize / 1024), size_width);
(void) printf(lname ? " %4s %-6s %s\n" : " %4s %s\n",
pagesize(pmp), mflags(pmp->pr_mflags), lname);
t->total_size += (pmp->pr_size + 1023) / 1024;
t->total_rss += pmp->pr_rss * (pmp->pr_pagesize / 1024);
t->total_anon += ANON(pmp) * (pmp->pr_pagesize / 1024);
t->total_locked += (pmp->pr_locked * (pmp->pr_pagesize / 1024));
return (0);
}
/*ARGSUSED*/
static int
look_xmap_nopgsz(void *data,
const prxmap_t *pmp,
const char *object_name,
int last, int doswap)
{
struct totals *t = data;
const pstatus_t *Psp = Pstatus(Pr);
char mname[PATH_MAX];
char *lname = NULL;
char *ln;
static uintptr_t prev_vaddr;
static size_t prev_size;
static offset_t prev_offset;
static int prev_mflags;
static char *prev_lname;
static char prev_mname[PATH_MAX];
static ulong_t prev_rss;
static ulong_t prev_anon;
static ulong_t prev_locked;
static ulong_t prev_swap;
int merged = 0;
static int first = 1;
ulong_t swap = 0;
int kperpage;
/*
* Calculate swap reservations
*/
if (pmp->pr_mflags & MA_SHARED) {
if (aflag && (pmp->pr_mflags & MA_NORESERVE) == 0) {
/* Swap reserved for entire non-ism SHM */
swap = pmp->pr_size / pmp->pr_pagesize;
}
} else if (pmp->pr_mflags & MA_NORESERVE) {
/* Swap reserved on fault for each anon page */
swap = pmp->pr_anon;
} else if (pmp->pr_mflags & MA_WRITE) {
/* Swap reserve for entire writable segment */
swap = pmp->pr_size / pmp->pr_pagesize;
}
/*
* If the mapping is not anon or not part of the heap, make a name
* for it. We don't want to report the heap as a.out's data.
*/
if (!(pmp->pr_mflags & MA_ANON) ||
pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase ||
pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) {
lname = make_name(Pr, pmp->pr_vaddr, pmp->pr_mapname,
mname, sizeof (mname));
}
if (lname != NULL) {
if ((ln = strrchr(lname, '/')) != NULL)
lname = ln + 1;
} else if ((pmp->pr_mflags & MA_ANON) || Pstate(Pr) == PS_DEAD) {
lname = anon_name(mname, Psp, pmp->pr_vaddr,
pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid);
}
kperpage = pmp->pr_pagesize / 1024;
t->total_size += (pmp->pr_size + 1023) / 1024;
t->total_rss += pmp->pr_rss * kperpage;
t->total_anon += ANON(pmp) * kperpage;
t->total_locked += pmp->pr_locked * kperpage;
t->total_swap += swap * kperpage;
if (first == 1) {
first = 0;
prev_vaddr = pmp->pr_vaddr;
prev_size = pmp->pr_size;
prev_offset = pmp->pr_offset;
prev_mflags = pmp->pr_mflags;
if (lname == NULL) {
prev_lname = NULL;
} else {
(void) strcpy(prev_mname, lname);
prev_lname = prev_mname;
}
prev_rss = pmp->pr_rss * kperpage;
prev_anon = ANON(pmp) * kperpage;
prev_locked = pmp->pr_locked * kperpage;
prev_swap = swap * kperpage;
if (last == 0) {
return (0);
}
merged = 1;
} else if (prev_vaddr + prev_size == pmp->pr_vaddr &&
prev_mflags == pmp->pr_mflags &&
((prev_mflags & MA_ISM) ||
prev_offset + prev_size == pmp->pr_offset) &&
((lname == NULL && prev_lname == NULL) ||
(lname != NULL && prev_lname != NULL &&
strcmp(lname, prev_lname) == 0))) {
prev_size += pmp->pr_size;
prev_rss += pmp->pr_rss * kperpage;
prev_anon += ANON(pmp) * kperpage;
prev_locked += pmp->pr_locked * kperpage;
prev_swap += swap * kperpage;
if (last == 0) {
return (0);
}
merged = 1;
}
(void) printf("%.*lX", addr_width, (ulong_t)prev_vaddr);
printK((prev_size + 1023) / 1024, size_width);
if (doswap)
printK(prev_swap, size_width);
else {
printK(prev_rss, size_width);
printK(prev_anon, size_width);
printK(prev_locked, size_width);
}
(void) printf(prev_lname ? " %-6s %s\n" : " %s\n",
mflags(prev_mflags), prev_lname);
if (last == 0) {
prev_vaddr = pmp->pr_vaddr;
prev_size = pmp->pr_size;
prev_offset = pmp->pr_offset;
prev_mflags = pmp->pr_mflags;
if (lname == NULL) {
prev_lname = NULL;
} else {
(void) strcpy(prev_mname, lname);
prev_lname = prev_mname;
}
prev_rss = pmp->pr_rss * kperpage;
prev_anon = ANON(pmp) * kperpage;
prev_locked = pmp->pr_locked * kperpage;
prev_swap = swap * kperpage;
} else if (merged == 0) {
(void) printf("%.*lX", addr_width, (ulong_t)pmp->pr_vaddr);
printK((pmp->pr_size + 1023) / 1024, size_width);
if (doswap)
printK(swap * kperpage, size_width);
else {
printK(pmp->pr_rss * kperpage, size_width);
printK(ANON(pmp) * kperpage, size_width);
printK(pmp->pr_locked * kperpage, size_width);
}
(void) printf(lname ? " %-6s %s\n" : " %s\n",
mflags(pmp->pr_mflags), lname);
}
if (last != 0)
first = 1;
return (0);
}
static int
perr(char *s)
{
if (s)
(void) fprintf(stderr, "%s: ", procname);
else
s = procname;
perror(s);
return (1);
}
static char *
mflags(uint_t arg)
{
static char code_buf[80];
char *str = code_buf;
/*
* rwxsR
*
* r - segment is readable
* w - segment is writable
* x - segment is executable
* s - segment is shared
* R - segment is mapped MAP_NORESERVE
*
*/
(void) sprintf(str, "%c%c%c%c%c%c",
arg & MA_READ ? 'r' : '-',
arg & MA_WRITE ? 'w' : '-',
arg & MA_EXEC ? 'x' : '-',
arg & MA_SHARED ? 's' : '-',
arg & MA_NORESERVE ? 'R' : '-',
arg & MA_RESERVED1 ? '*' : ' ');
return (str);
}
static mapdata_t *
nextmap(void)
{
mapdata_t *newmaps;
int next;
if (map_count == map_alloc) {
if (map_alloc == 0)
next = 16;
else
next = map_alloc * 2;
newmaps = realloc(maps, next * sizeof (mapdata_t));
if (newmaps == NULL) {
(void) perr("failed to allocate maps");
exit(1);
}
(void) memset(newmaps + map_alloc, '\0',
(next - map_alloc) * sizeof (mapdata_t));
map_alloc = next;
maps = newmaps;
}
return (&maps[map_count++]);
}
/*ARGSUSED*/
static int
gather_map(void *ignored, const prmap_t *map, const char *objname)
{
mapdata_t *data = nextmap();
data->md_map = *map;
if (data->md_objname != NULL)
free(data->md_objname);
data->md_objname = objname ? strdup(objname) : NULL;
return (0);
}
/*ARGSUSED*/
static int
gather_xmap(void *ignored, const prxmap_t *xmap, const char *objname,
int last, int doswap)
{
mapdata_t *data = nextmap();
data->md_xmap = *xmap;
if (data->md_objname != NULL)
free(data->md_objname);
data->md_objname = objname ? strdup(objname) : NULL;
data->md_last = last;
data->md_doswap = doswap;
return (0);
}
static int
iter_map(proc_map_f *func, void *data)
{
int i;
int ret;
for (i = 0; i < map_count; i++) {
if ((ret = func(data, &maps[i].md_map,
maps[i].md_objname)) != 0)
return (ret);
}
return (0);
}
static int
iter_xmap(proc_xmap_f *func, void *data)
{
int i;
int ret;
for (i = 0; i < map_count; i++) {
if ((ret = func(data, &maps[i].md_xmap, maps[i].md_objname,
maps[i].md_last, maps[i].md_doswap)) != 0)
return (ret);
}
return (0);
}