pmap.c revision 7595fad9bab86562dbe8d6580c4b310bbad949b7
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdio_ext.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 <signal.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#include <sys/mman.h>
#include <sys/lgrp_user.h>
#include <libproc.h>
#include <libzonecfg.h>
#define KILOBYTE 1024
#define MEGABYTE (KILOBYTE * KILOBYTE)
#define GIGABYTE (KILOBYTE * KILOBYTE * KILOBYTE)
/*
* Round up the value to the nearest kilobyte
*/
#define ROUNDUP_KB(x) (((x) + (KILOBYTE - 1)) / KILOBYTE)
/*
* The alignment should be a power of 2.
*/
#define P2ALIGN(x, align) ((x) & -(align))
#define INVALID_ADDRESS (uintptr_t)(-1)
struct totals {
ulong_t total_size;
ulong_t total_swap;
ulong_t total_rss;
ulong_t total_anon;
ulong_t total_locked;
};
/*
* -L option requires per-page information. The information is presented in an
* array of page_descr structures.
*/
typedef struct page_descr {
uintptr_t pd_start; /* start address of a page */
size_t pd_pagesize; /* page size in bytes */
lgrp_id_t pd_lgrp; /* lgroup of memory backing the page */
int pd_valid; /* valid page description if non-zero */
} page_descr_t;
/*
* Per-page information for a memory chunk.
* The meminfo(2) system call accepts up to MAX_MEMINFO_CNT pages at once.
* When we need to scan larger ranges we divide them in MAX_MEMINFO_CNT sized
* chunks. The chunk information is stored in the memory_chunk structure.
*/
typedef struct memory_chunk {
page_descr_t page_info[MAX_MEMINFO_CNT];
uintptr_t end_addr;
uintptr_t chunk_start; /* Starting address */
uintptr_t chunk_end; /* chunk_end is always <= end_addr */
size_t page_size;
int page_index; /* Current page */
int page_count; /* Number of pages */
} memory_chunk_t;
static volatile int interrupt;
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 parse_addr_range(char *, uintptr_t *, uintptr_t *);
static void mem_chunk_init(memory_chunk_t *, uintptr_t, size_t);
static int perr(char *);
static void printK(long, int);
static char *mflags(uint_t);
static size_t get_contiguous_region(memory_chunk_t *, uintptr_t,
uintptr_t, size_t, lgrp_id_t *);
static void mem_chunk_get(memory_chunk_t *, uintptr_t);
static lgrp_id_t addr_to_lgrp(memory_chunk_t *, uintptr_t, size_t *);
static char *lgrp2str(lgrp_id_t);
static int address_in_range(uintptr_t, uintptr_t, size_t);
static size_t adjust_addr_range(uintptr_t, uintptr_t, size_t,
uintptr_t *, uintptr_t *);
static int lflag = 0;
static int Lflag = 0;
static int aflag = 0;
/*
* The -A address range is represented as a pair of addresses
* <start_addr, end_addr>. Either one of these may be unspecified (set to
* INVALID_ADDRESS). If both are unspecified, no address range restrictions are
* in place.
*/
static uintptr_t start_addr = INVALID_ADDRESS;
static uintptr_t end_addr = INVALID_ADDRESS;
static int addr_width, size_width;
static char *command;
static char *procname;
static struct ps_prochandle *Pr;
static void intr(int);
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;
boolean_t 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, Fflag = 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;
int prg_gflags = PGRAB_RDONLY;
int prr_flags = 0;
boolean_t use_agent_lwp = B_FALSE;
if ((command = strrchr(argv[0], '/')) != NULL)
command++;
else
command = argv[0];
while ((opt = getopt(argc, argv, "arsxSlLFA:")) != 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 'L': /* show lgroup information */
Lflag = 1;
use_agent_lwp = B_TRUE;
break;
case 'F': /* force grabbing (no O_EXCL) */
Fflag = PGRAB_FORCE;
break;
case 'A':
if (parse_addr_range(optarg, &start_addr, &end_addr)
!= 0)
errflg++;
break;
default:
errflg = 1;
break;
}
}
argc -= optind;
argv += optind;
if ((Sflag && (xflag || rflag || sflag)) || (xflag && rflag) ||
(aflag && (!xflag && !Sflag)) ||
(Lflag && (xflag || Sflag))) {
errflg = 1;
}
if (errflg || argc <= 0) {
(void) fprintf(stderr,
"usage:\t%s [-rslF] [-A start[,end]] { pid | core } ...\n",
command);
(void) fprintf(stderr,
"\t\t(report process address maps)\n");
(void) fprintf(stderr,
"\t%s -L [-rslF] [-A start[,end]] pid ...\n", command);
(void) fprintf(stderr,
"\t\t(report process address maps lgroups mappings)\n");
(void) fprintf(stderr,
"\t%s -x [-aslF] [-A start[,end]] pid ...\n", command);
(void) fprintf(stderr,
"\t\t(show resident/anon/locked mapping details)\n");
(void) fprintf(stderr,
"\t%s -S [-alF] [-A start[,end]] { 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");
(void) fprintf(stderr,
"\t-L: show lgroup mappings\n");
(void) fprintf(stderr,
"\t-A start,end: limit output to the specified range\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);
(void) enable_extended_FILE_stdio(-1, -1);
}
/*
* The implementation of -L option creates an agent LWP in the target
* process address space. The agent LWP issues meminfo(2) system calls
* on behalf of the target process. If we are interrupted prematurely,
* the target process remains in the stopped state with the agent still
* attached to it. To prevent such situation we catch signals from
* terminal and terminate gracefully.
*/
if (use_agent_lwp) {
/*
* Buffer output to stdout, stderr while process is grabbed.
* Prevents infamous deadlocks due to pmap `pgrep xterm` and
* other variants.
*/
(void) proc_initstdio();
prg_gflags = PGRAB_RETAIN | Fflag;
prr_flags = PRELEASE_RETAIN;
if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
(void) sigset(SIGHUP, intr);
if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
(void) sigset(SIGINT, intr);
if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
(void) sigset(SIGQUIT, intr);
(void) sigset(SIGPIPE, intr);
(void) sigset(SIGTERM, intr);
}
while (argc-- > 0) {
char *arg;
int gcode;
psinfo_t psinfo;
int tries = 0;
if (use_agent_lwp)
(void) proc_flushstdio();
if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_ANY,
prg_gflags, &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, prr_flags);
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 || Lflag) {
(void) printf(" -%c option is not compatible "
"with core files\n", xflag ? 'x' :
sflag ? 's' : rflag ? 'r' :
Lflag ? 'L' : 'S');
Prelease(Pr, prr_flags);
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, prr_flags);
(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, prr_flags);
(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) {
if (Lflag) {
(void) printf("%*s %*s %4s"
" %-6s %s %s\n",
addr_width, "Address",
size_width,
"Bytes", "Pgsz", "Mode ",
"Lgrp", "Mapped File");
rc += iter_xmap(look_smap, &t);
} else {
(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, prr_flags);
if (mapfd != -1)
(void) close(mapfd);
}
if (use_agent_lwp)
(void) proc_finistdio();
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);
const psinfo_t *pi = Ppsinfo(Pr);
char fname[100];
struct stat statb;
int len;
char zname[ZONENAME_MAX];
char zpath[PATH_MAX];
char objname[PATH_MAX];
if (!lflag && strcmp(mapname, "a.out") == 0 &&
Pexecname(Pr, buf, bufsz) != NULL)
return (buf);
if (Pobjname(Pr, addr, objname, sizeof (objname)) != NULL) {
(void) strncpy(buf, objname, bufsz);
if (lflag)
return (buf);
if ((len = resolvepath(buf, buf, bufsz)) > 0) {
buf[len] = '\0';
return (buf);
}
/*
* If the target is in a non-global zone, attempt to prepend
* the zone path in order to give the global-zone caller the
* real path to the file.
*/
if (getzonenamebyid(pi->pr_zoneid, zname,
sizeof (zname)) != -1 && strcmp(zname, "global") != 0) {
typedef int (*fptr)(char *, char *, size_t);
fptr zone_get_zonepath;
void *dlhdl;
if (((dlhdl =
dlopen(LIBZONECFG_PATH, RTLD_LAZY)) == NULL) ||
((zone_get_zonepath =
(fptr) dlsym(dlhdl, "zone_get_zonepath")) == NULL))
return (NULL);
if ((*zone_get_zonepath)(zname, zpath, sizeof (zpath))
== Z_OK) {
(void) strncat(zpath, "/root",
MAXPATHLEN - strlen(zpath));
if (bufsz <= strlen(zpath)) {
(void) dlclose(dlhdl);
return (NULL);
}
(void) strncpy(buf, zpath, bufsz);
(void) strncat(buf, objname,
bufsz - strlen(zpath));
}
(void) dlclose(dlhdl);
}
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/path/%s",
(int)Psp->pr_pid, mapname);
len = readlink(fname, buf, bufsz - 1);
if (len >= 0) {
buf[len] = '\0';
return (buf);
} else { /* there is no path and readlink() error */
(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);
}
}
/*
* Mark the last element.
*/
if (map_count > 0)
maps[map_count - 1].md_last = B_TRUE;
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;
char mname[PATH_MAX];
char *lname = NULL;
size_t psz = pmp->pr_pagesize;
uintptr_t vaddr = pmp->pr_vaddr;
uintptr_t segment_end = vaddr + pmp->pr_size;
lgrp_id_t lgrp;
memory_chunk_t mchunk;
/*
* 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) ||
segment_end <= 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);
}
/*
* Adjust the address range if -A is specified.
*/
size = adjust_addr_range(pmp->pr_vaddr, segment_end, psz,
&vaddr, &segment_end);
if (size == 0)
return (0);
if (!Lflag) {
/*
* Display the whole mapping
*/
size = ROUNDUP_KB(size);
(void) printf(lname ?
"%.*lX %*luK %-6s %s\n" :
"%.*lX %*luK %s\n",
addr_width, vaddr,
size_width - 1, size, mflags(pmp->pr_mflags), lname);
t->total_size += size;
return (0);
}
/*
* We need to display lgroups backing physical memory, so we break the
* segment into individual pages and coalesce pages with the same lgroup
* into one "segment".
*/
/*
* Initialize address descriptions for the mapping.
*/
mem_chunk_init(&mchunk, segment_end, psz);
size = 0;
/*
* Walk mapping (page by page) and display contiguous ranges of memory
* allocated to same lgroup.
*/
do {
size_t size_contig;
/*
* Get contiguous region of memory starting from vaddr allocated
* from the same lgroup.
*/
size_contig = get_contiguous_region(&mchunk, vaddr,
segment_end, pmp->pr_pagesize, &lgrp);
(void) printf(lname ? "%.*lX %*luK %-6s%s %s\n" :
"%.*lX %*luK %s %s\n",
addr_width, vaddr,
size_width - 1, size_contig / KILOBYTE,
mflags(pmp->pr_mflags),
lgrp2str(lgrp), lname);
vaddr += size_contig;
size += size_contig;
} while (vaddr < segment_end && !interrupt);
/* Update the total size */
t->total_size += ROUNDUP_KB(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 >= KILOBYTE && (pagesize % KILOBYTE) == 0) {
if ((pagesize % GIGABYTE) == 0)
(void) snprintf(buf, sizeof (buf), "%dG",
pagesize / GIGABYTE);
else if ((pagesize % MEGABYTE) == 0)
(void) snprintf(buf, sizeof (buf), "%dM",
pagesize / MEGABYTE);
else
(void) snprintf(buf, sizeof (buf), "%dK",
pagesize / KILOBYTE);
} 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;
char mname[PATH_MAX];
char *lname = NULL;
const char *format;
size_t psz = pmp->pr_pagesize;
uintptr_t vaddr = pmp->pr_vaddr;
uintptr_t segment_end = vaddr + pmp->pr_size;
lgrp_id_t lgrp;
memory_chunk_t mchunk;
/*
* 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);
}
/*
* Adjust the address range if -A is specified.
*/
size = adjust_addr_range(pmp->pr_vaddr, segment_end, psz,
&vaddr, &segment_end);
if (size == 0)
return (0);
if (!Lflag) {
/*
* Display the whole mapping
*/
if (lname != NULL)
format = "%.*lX %*luK %4s %-6s %s\n";
else
format = "%.*lX %*luK %4s %s\n";
size = ROUNDUP_KB(size);
(void) printf(format, addr_width, vaddr, size_width - 1, size,
pagesize(pmp), mflags(pmp->pr_mflags), lname);
t->total_size += size;
return (0);
}
if (lname != NULL)
format = "%.*lX %*luK %4s %-6s%s %s\n";
else
format = "%.*lX %*luK %4s%s %s\n";
/*
* We need to display lgroups backing physical memory, so we break the
* segment into individual pages and coalesce pages with the same lgroup
* into one "segment".
*/
/*
* Initialize address descriptions for the mapping.
*/
mem_chunk_init(&mchunk, segment_end, psz);
size = 0;
/*
* Walk mapping (page by page) and display contiguous ranges of memory
* allocated to same lgroup.
*/
do {
size_t size_contig;
/*
* Get contiguous region of memory starting from vaddr allocated
* from the same lgroup.
*/
size_contig = get_contiguous_region(&mchunk, vaddr,
segment_end, pmp->pr_pagesize, &lgrp);
(void) printf(format, addr_width, vaddr,
size_width - 1, size_contig / KILOBYTE,
pagesize(pmp), mflags(pmp->pr_mflags),
lgrp2str(lgrp), lname);
vaddr += size_contig;
size += size_contig;
} while (vaddr < segment_end && !interrupt);
t->total_size += ROUNDUP_KB(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(ROUNDUP_KB(pmp->pr_size), size_width);
printK(pmp->pr_rss * (pmp->pr_pagesize / KILOBYTE), size_width);
printK(ANON(pmp) * (pmp->pr_pagesize / KILOBYTE), size_width);
printK(pmp->pr_locked * (pmp->pr_pagesize / KILOBYTE), size_width);
(void) printf(lname ? " %4s %-6s %s\n" : " %4s %s\n",
pagesize(pmp), mflags(pmp->pr_mflags), lname);
t->total_size += ROUNDUP_KB(pmp->pr_size);
t->total_rss += pmp->pr_rss * (pmp->pr_pagesize / KILOBYTE);
t->total_anon += ANON(pmp) * (pmp->pr_pagesize / KILOBYTE);
t->total_locked += (pmp->pr_locked * (pmp->pr_pagesize / KILOBYTE));
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 / KILOBYTE;
t->total_size += ROUNDUP_KB(pmp->pr_size);
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(ROUNDUP_KB(prev_size), 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(ROUNDUP_KB(pmp->pr_size), 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;
/* Skip mappings which are outside the range specified by -A */
if (!address_in_range(map->pr_vaddr,
map->pr_vaddr + map->pr_size, map->pr_pagesize))
return (0);
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;
/* Skip mappings which are outside the range specified by -A */
if (!address_in_range(xmap->pr_vaddr,
xmap->pr_vaddr + xmap->pr_size, xmap->pr_pagesize))
return (0);
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 (interrupt)
break;
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 (interrupt)
break;
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);
}
/*
* Convert lgroup ID to string.
* returns dash when lgroup ID is invalid.
*/
static char *
lgrp2str(lgrp_id_t lgrp)
{
static char lgrp_buf[20];
char *str = lgrp_buf;
(void) sprintf(str, lgrp == LGRP_NONE ? " -" : "%4d", lgrp);
return (str);
}
/*
* Parse address range specification for -A option.
* The address range may have the following forms:
*
* address
* start and end is set to address
* address,
* start is set to address, end is set to INVALID_ADDRESS
* ,address
* start is set to 0, end is set to address
* address1,address2
* start is set to address1, end is set to address2
*
*/
static int
parse_addr_range(char *input_str, uintptr_t *start, uintptr_t *end)
{
char *startp = input_str;
char *endp = strchr(input_str, ',');
ulong_t s = (ulong_t)INVALID_ADDRESS;
ulong_t e = (ulong_t)INVALID_ADDRESS;
if (endp != NULL) {
/*
* Comma is present. If there is nothing after comma, the end
* remains set at INVALID_ADDRESS. Otherwise it is set to the
* value after comma.
*/
*endp = '\0';
endp++;
if ((*endp != '\0') && sscanf(endp, "%lx", &e) != 1)
return (1);
}
if (startp != NULL) {
/*
* Read the start address, if it is specified. If the address is
* missing, start will be set to INVALID_ADDRESS.
*/
if ((*startp != '\0') && sscanf(startp, "%lx", &s) != 1)
return (1);
}
/* If there is no comma, end becomes equal to start */
if (endp == NULL)
e = s;
/*
* ,end implies 0..end range
*/
if (e != INVALID_ADDRESS && s == INVALID_ADDRESS)
s = 0;
*start = (uintptr_t)s;
*end = (uintptr_t)e;
/* Return error if neither start nor end address were specified */
return (! (s != INVALID_ADDRESS || e != INVALID_ADDRESS));
}
/*
* Check whether any portion of [start, end] segment is within the
* [start_addr, end_addr] range.
*
* Return values:
* 0 - address is outside the range
* 1 - address is within the range
*/
static int
address_in_range(uintptr_t start, uintptr_t end, size_t psz)
{
int rc = 1;
/*
* Nothing to do if there is no address range specified with -A
*/
if (start_addr != INVALID_ADDRESS || end_addr != INVALID_ADDRESS) {
/* The segment end is below the range start */
if ((start_addr != INVALID_ADDRESS) &&
(end < P2ALIGN(start_addr, psz)))
rc = 0;
/* The segment start is above the range end */
if ((end_addr != INVALID_ADDRESS) &&
(start > P2ALIGN(end_addr + psz, psz)))
rc = 0;
}
return (rc);
}
/*
* Returns an intersection of the [start, end] interval and the range specified
* by -A flag [start_addr, end_addr]. Unspecified parts of the address range
* have value INVALID_ADDRESS.
*
* The start_addr address is rounded down to the beginning of page and end_addr
* is rounded up to the end of page.
*
* Returns the size of the resulting interval or zero if the interval is empty
* or invalid.
*/
static size_t
adjust_addr_range(uintptr_t start, uintptr_t end, size_t psz,
uintptr_t *new_start, uintptr_t *new_end)
{
uintptr_t from; /* start_addr rounded down */
uintptr_t to; /* end_addr rounded up */
/*
* Round down the lower address of the range to the beginning of page.
*/
if (start_addr == INVALID_ADDRESS) {
/*
* No start_addr specified by -A, the lower part of the interval
* does not change.
*/
*new_start = start;
} else {
from = P2ALIGN(start_addr, psz);
/*
* If end address is outside the range, return an empty
* interval
*/
if (end < from) {
*new_start = *new_end = 0;
return (0);
}
/*
* The adjusted start address is the maximum of requested start
* and the aligned start_addr of the -A range.
*/
*new_start = start < from ? from : start;
}
/*
* Round up the higher address of the range to the end of page.
*/
if (end_addr == INVALID_ADDRESS) {
/*
* No end_addr specified by -A, the upper part of the interval
* does not change.
*/
*new_end = end;
} else {
/*
* If only one address is specified and it is the beginning of a
* segment, get information about the whole segment. This
* function is called once per segment and the 'end' argument is
* always the end of a segment, so just use the 'end' value.
*/
to = (end_addr == start_addr && start == start_addr) ?
end :
P2ALIGN(end_addr + psz, psz);
/*
* If start address is outside the range, return an empty
* interval
*/
if (start > to) {
*new_start = *new_end = 0;
return (0);
}
/*
* The adjusted end address is the minimum of requested end
* and the aligned end_addr of the -A range.
*/
*new_end = end > to ? to : end;
}
/*
* Make sure that the resulting interval is legal.
*/
if (*new_end < *new_start)
*new_start = *new_end = 0;
/* Return the size of the interval */
return (*new_end - *new_start);
}
/*
* Initialize memory_info data structure with information about a new segment.
*/
static void
mem_chunk_init(memory_chunk_t *chunk, uintptr_t end, size_t psz)
{
chunk->end_addr = end;
chunk->page_size = psz;
chunk->page_index = 0;
chunk->chunk_start = chunk->chunk_end = 0;
}
/*
* Create a new chunk of addresses starting from vaddr.
* Pass the whole chunk to pr_meminfo to collect lgroup and page size
* information for each page in the chunk.
*/
static void
mem_chunk_get(memory_chunk_t *chunk, uintptr_t vaddr)
{
page_descr_t *pdp = chunk->page_info;
size_t psz = chunk->page_size;
uintptr_t addr = vaddr;
uint64_t inaddr[MAX_MEMINFO_CNT];
uint64_t outdata[2 * MAX_MEMINFO_CNT];
uint_t info[2] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE };
uint_t validity[MAX_MEMINFO_CNT];
uint64_t *dataptr = inaddr;
uint64_t *outptr = outdata;
uint_t *valptr = validity;
int i, j, rc;
chunk->chunk_start = vaddr;
chunk->page_index = 0; /* reset index for the new chunk */
/*
* Fill in MAX_MEMINFO_CNT wotrh of pages starting from vaddr. Also,
* copy starting address of each page to inaddr array for pr_meminfo.
*/
for (i = 0, pdp = chunk->page_info;
(i < MAX_MEMINFO_CNT) && (addr <= chunk->end_addr);
i++, pdp++, dataptr++, addr += psz) {
*dataptr = (uint64_t)addr;
pdp->pd_start = addr;
pdp->pd_lgrp = LGRP_NONE;
pdp->pd_valid = 0;
pdp->pd_pagesize = 0;
}
/* Mark the number of entries in the chunk and the last address */
chunk->page_count = i;
chunk->chunk_end = addr - psz;
if (interrupt)
return;
/* Call meminfo for all collected addresses */
rc = pr_meminfo(Pr, inaddr, i, info, 2, outdata, validity);
if (rc < 0) {
(void) perr("can not get memory information");
return;
}
/* Verify validity of each result and fill in the addrs array */
pdp = chunk->page_info;
for (j = 0; j < i; j++, pdp++, valptr++, outptr += 2) {
/* Skip invalid address pointers */
if ((*valptr & 1) == 0) {
continue;
}
/* Is lgroup information available? */
if ((*valptr & 2) != 0) {
pdp->pd_lgrp = (lgrp_id_t)*outptr;
pdp->pd_valid = 1;
}
/* Is page size informaion available? */
if ((*valptr & 4) != 0) {
pdp->pd_pagesize = *(outptr + 1);
}
}
}
/*
* Starting from address 'vaddr' find the region with pages allocated from the
* same lgroup.
*
* Arguments:
* mchunk Initialized memory chunk structure
* vaddr Starting address of the region
* maxaddr Upper bound of the region
* pagesize Default page size to use
* ret_lgrp On exit contains the lgroup ID of all pages in the
* region.
*
* Returns:
* Size of the contiguous region in bytes
* The lgroup ID of all pages in the region in ret_lgrp argument.
*/
static size_t
get_contiguous_region(memory_chunk_t *mchunk, uintptr_t vaddr,
uintptr_t maxaddr, size_t pagesize, lgrp_id_t *ret_lgrp)
{
size_t size_contig = 0;
lgrp_id_t lgrp; /* Lgroup of the region start */
lgrp_id_t curr_lgrp; /* Lgroup of the current page */
size_t psz = pagesize; /* Pagesize to use */
/* Set both lgroup IDs to the lgroup of the first page */
curr_lgrp = lgrp = addr_to_lgrp(mchunk, vaddr, &psz);
/*
* Starting from vaddr, walk page by page until either the end
* of the segment is reached or a page is allocated from a different
* lgroup. Also stop if interrupted from keyboard.
*/
while ((vaddr < maxaddr) && (curr_lgrp == lgrp) && !interrupt) {
/*
* Get lgroup ID and the page size of the current page.
*/
curr_lgrp = addr_to_lgrp(mchunk, vaddr, &psz);
/* If there is no page size information, use the default */
if (psz == 0)
psz = pagesize;
if (curr_lgrp == lgrp) {
/*
* This page belongs to the contiguous region.
* Increase the region size and advance to the new page.
*/
size_contig += psz;
vaddr += psz;
}
}
/* Return the region lgroup ID and the size */
*ret_lgrp = lgrp;
return (size_contig);
}
/*
* Given a virtual address, return its lgroup and page size. If there is meminfo
* information for an address, use it, otherwise shift the chunk window to the
* vaddr and create a new chunk with known meminfo information.
*/
static lgrp_id_t
addr_to_lgrp(memory_chunk_t *chunk, uintptr_t vaddr, size_t *psz)
{
page_descr_t *pdp;
lgrp_id_t lgrp = LGRP_NONE;
int i;
*psz = chunk->page_size;
if (interrupt)
return (0);
/*
* Is there information about this address? If not, create a new chunk
* starting from vaddr and apply pr_meminfo() to the whole chunk.
*/
if (vaddr < chunk->chunk_start || vaddr > chunk->chunk_end) {
/*
* This address is outside the chunk, get the new chunk and
* collect meminfo information for it.
*/
mem_chunk_get(chunk, vaddr);
}
/*
* Find information about the address.
*/
pdp = &chunk->page_info[chunk->page_index];
for (i = chunk->page_index; i < chunk->page_count; i++, pdp++) {
if (pdp->pd_start == vaddr) {
if (pdp->pd_valid) {
lgrp = pdp->pd_lgrp;
/*
* Override page size information if it is
* present.
*/
if (pdp->pd_pagesize > 0)
*psz = pdp->pd_pagesize;
}
break;
}
}
/*
* Remember where we ended - the next search will start here.
* We can query for the lgrp for the same address again, so do not
* advance index past the current value.
*/
chunk->page_index = i;
return (lgrp);
}
/* ARGSUSED */
static void
intr(int sig)
{
interrupt = 1;
}