#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 <locale.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#include <assert.h>
#include <libproc.h>
#include <libgen.h>
#include <signal.h>
#include "pmap_common.h"
#ifndef TEXT_DOMAIN
#define TEXT_DOMAIN "SYS_TEST"
#endif
#define KILOBYTE 1024
#define ROUNDUP_KB(x) (((x) + (KILOBYTE - 1)) / KILOBYTE)
#define INVALID_ADDRESS (uintptr_t)(-1)
#define NO_ADVICE 0
#define NODUPS 1
#define YESDUPS 0
#define GRP1_ADV (1 << MADV_NORMAL | 1 << MADV_RANDOM | \
1 << MADV_SEQUENTIAL)
#define GRP2_ADV (1 << MADV_WILLNEED | 1 << MADV_DONTNEED | \
1 << MADV_FREE | 1 << MADV_PURGE)
#define GRP3_ADV (1 << MADV_ACCESS_DEFAULT | 1 << MADV_ACCESS_LWP | \
1 << MADV_ACCESS_MANY)
static int create_maplist(void *, const prmap_t *, const char *);
static int pr_madvise(struct ps_prochandle *, caddr_t, size_t, int);
static char *mflags(uint_t);
static char *advtostr(int);
static int lflag = 0;
static int addr_width, size_width;
static char *progname;
static struct ps_prochandle *Pr;
static lwpstack_t *stacks;
static uint_t nstacks;
static uintptr_t comm_page = INVALID_ADDRESS;
static char *suboptstr[] = {
"private",
"shared",
"heap",
"stack",
NULL
};
int generic_adv[] = {NO_ADVICE, NO_ADVICE, NO_ADVICE, NO_ADVICE};
int at_map = 0;
typedef struct saddr_struct {
uintptr_t addr;
size_t length;
int adv;
struct saddr_struct *next;
} saddr_t;
static int apply_advice(saddr_t **);
static void set_advice(int *, int);
static void create_choplist(saddr_t **, saddr_t *);
saddr_t *rawadv_list = NULL;
saddr_t *merged_list = NULL;
saddr_t *chopped_list = NULL;
typedef struct mapnode_struct {
prmap_t *pmp;
char label[PATH_MAX];
int mtypes;
struct mapnode_struct *next;
} mapnode_t;
mapnode_t *maplist_head = NULL;
mapnode_t *maplist_tail = NULL;
static void print_advice(saddr_t *, mapnode_t *);
int opt_verbose;
static char *advicestr[] = {
"normal",
"random",
"sequential",
"willneed",
"dontneed",
"free",
"access_default",
"access_lwp",
"access_many"
};
static int interrupt = 0;
static void intr(int);
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);
}
static void
usage()
{
(void) fprintf(stderr,
gettext("usage:\t%s [-o option[,option]] [-Flv] pid ...\n"),
progname);
(void) fprintf(stderr,
gettext(" (Give \"advice\" about a process's memory)\n"
" -o option[,option]: options are\n"
" private=<advice>\n"
" shared=<advice>\n"
" heap=<advice>\n"
" stack=<advice>\n"
" <segaddr>[:<length>]=<advice>\n"
" valid <advice> is one of:\n"
" normal, random, sequential, willneed, dontneed,\n"
" free, access_lwp, access_many, access_default\n"
" -v: verbose output\n"
" -F: force grabbing of the target process(es)\n"
" -l: show unresolved dynamic linker map names\n"
" pid: process id list\n"));
exit(2);
}
static int
get_advice(char *optarg)
{
if (strcmp(optarg, "access_default") == 0)
return (1 << MADV_ACCESS_DEFAULT);
else if (strcmp(optarg, "access_many") == 0)
return (1 << MADV_ACCESS_MANY);
else if (strcmp(optarg, "access_lwp") == 0)
return (1 << MADV_ACCESS_LWP);
else if (strcmp(optarg, "sequential") == 0)
return (1 << MADV_SEQUENTIAL);
else if (strcmp(optarg, "willneed") == 0)
return (1 << MADV_WILLNEED);
else if (strcmp(optarg, "dontneed") == 0)
return (1 << MADV_DONTNEED);
else if (strcmp(optarg, "random") == 0)
return (1 << MADV_RANDOM);
else if (strcmp(optarg, "normal") == 0)
return (1 << MADV_NORMAL);
else if (strcmp(optarg, "free") == 0)
return (1 << MADV_FREE);
else if (strcmp(optarg, "purge") == 0)
return (1 << MADV_PURGE);
else {
(void) fprintf(stderr, gettext("%s: invalid advice: %s\n"),
progname, optarg);
usage();
return (-1);
}
}
static size_t
atosz(char *optarg, char **endptr)
{
size_t sz = 0;
if (optarg == NULL || optarg[0] == '\0')
return (0);
sz = strtoll(optarg, endptr, 0);
switch (**endptr) {
case 'E':
case 'e':
sz *= KILOBYTE;
case 'P':
case 'p':
sz *= KILOBYTE;
case 'T':
case 't':
sz *= KILOBYTE;
case 'G':
case 'g':
sz *= KILOBYTE;
case 'M':
case 'm':
sz *= KILOBYTE;
case 'K':
case 'k':
sz *= KILOBYTE;
case 'B':
case 'b':
(*endptr)++;
default:
break;
}
return (sz);
}
static void
insert_addr(saddr_t **list, saddr_t *newaddr, int dups)
{
saddr_t *prev = *list;
saddr_t *psaddr;
if (*list == NULL) {
newaddr->next = *list;
*list = newaddr;
return;
}
for (psaddr = (*list)->next; psaddr != NULL; psaddr = psaddr->next) {
if ((dups == NODUPS) && (psaddr->addr == newaddr->addr)) {
free(newaddr);
return;
}
if ((psaddr->addr > newaddr->addr) ||
(psaddr->addr == newaddr->addr &&
psaddr->length < newaddr->length))
break;
prev = psaddr;
}
prev->next = newaddr;
newaddr->next = psaddr;
}
static void
delete_addr(saddr_t **list, saddr_t *delme)
{
saddr_t *prev = *list;
if (delme == *list) {
*list = delme->next;
free(delme);
return;
}
while (prev != NULL && prev->next != delme) {
prev = prev->next;
}
if (prev) {
prev->next = delme->next;
free(delme);
}
}
static void
delete_list(saddr_t **list)
{
saddr_t *psaddr = *list;
while (psaddr != NULL) {
saddr_t *temp = psaddr;
psaddr = psaddr->next;
free(temp);
}
*list = NULL;
}
static saddr_t *
parse_suboptions(char *value)
{
char *endptr;
saddr_t *psaddr = malloc(sizeof (saddr_t));
psaddr->addr =
strtoull(value, &endptr, 16);
if ((psaddr->addr == 0) || (*endptr != ':' && *endptr != '=')) {
free(psaddr);
(void) fprintf(stderr,
gettext("%s: invalid option %s\n"),
progname, value);
usage();
} else {
psaddr->length = 0;
psaddr->adv = NO_ADVICE;
psaddr->next = NULL;
value = endptr;
if (*value == ':') {
value++;
psaddr->length = atosz(value, &endptr);
}
if (*endptr != '=') {
(void) fprintf(stderr,
gettext("%s: invalid option %s\n"),
progname, value);
free(psaddr);
usage();
}
value = endptr + 1;
at_map |= (1 << AT_SEG);
psaddr->adv =
get_advice(value);
}
return (psaddr);
}
static int
create_maplist(void *arg, const prmap_t *pmp, const char *object_name)
{
const pstatus_t *Psp = Pstatus(Pr);
mapnode_t *newmap = malloc(sizeof (mapnode_t));
saddr_t *newaddr;
saddr_t *psaddr;
char *lname = NULL;
int i;
if (interrupt)
return (0);
newmap->pmp = malloc(sizeof (prmap_t));
newmap->label[0] = '\0';
newmap->mtypes = 0;
newmap->next = NULL;
(void) memcpy(newmap->pmp, pmp, sizeof (prmap_t));
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, lflag, pmp->pr_vaddr, pmp->pr_mapname,
newmap->label, sizeof (newmap->label));
if (pmp->pr_mflags & MA_SHARED)
newmap->mtypes |= 1 << AT_SHARED;
else
newmap->mtypes |= 1 << AT_PRIVM;
}
if (lname == NULL && (pmp->pr_mflags & MA_ANON)) {
lname = anon_name(newmap->label, Psp, stacks, nstacks,
pmp->pr_vaddr, pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid,
&newmap->mtypes);
}
if (lname == NULL && comm_page != INVALID_ADDRESS &&
pmp->pr_vaddr == comm_page) {
(void) strlcpy(newmap->label, " [ comm ]",
sizeof (newmap->label));
lname = newmap->label;
}
psaddr = rawadv_list;
while (psaddr && psaddr->addr < pmp->pr_vaddr)
psaddr = psaddr->next;
while (psaddr && psaddr->addr < (pmp->pr_vaddr + pmp->pr_size)) {
newaddr = malloc(sizeof (saddr_t));
(void) memcpy(newaddr, psaddr, sizeof (saddr_t));
insert_addr(&merged_list, newaddr, YESDUPS);
if ((pmp->pr_vaddr == psaddr->addr) && (psaddr->length == 0))
newaddr->length = pmp->pr_size;
psaddr = psaddr->next;
}
newaddr = malloc(sizeof (saddr_t));
newaddr->addr = pmp->pr_vaddr;
newaddr->length = pmp->pr_size;
newaddr->adv = NO_ADVICE;
insert_addr(&merged_list, newaddr, YESDUPS);
newmap->mtypes &= at_map;
for (i = AT_STACK; i >= AT_PRIVM; i--) {
if (newmap->mtypes & (1 << i)) {
assert(generic_adv[i] != NO_ADVICE);
newaddr->adv = generic_adv[i];
break;
}
}
if (maplist_tail == NULL) {
maplist_head = maplist_tail = newmap;
} else {
maplist_tail->next = newmap;
maplist_tail = newmap;
}
return (0);
}
static int
apply_advice(saddr_t **advicelist)
{
saddr_t *psaddr = *advicelist;
saddr_t *next;
int i;
while (!interrupt && psaddr != NULL) {
next = psaddr->next;
if (psaddr->adv != NO_ADVICE) {
for (i = MADV_NORMAL; i <= MADV_PURGE; i++) {
if ((psaddr->adv & (1 << i)) &&
(pr_madvise(Pr, (caddr_t)psaddr->addr,
psaddr->length, i) < 0)) {
(void) fprintf(stderr,
gettext("Error applying "
"advice (%s) to memory range "
"[%lx, %lx):\n"),
advicestr[i], (ulong_t)psaddr->addr,
(ulong_t)psaddr->addr +
psaddr->length);
perror("madvise");
psaddr->adv &= ~(1 << i);
if (psaddr->adv == 0) {
delete_addr(advicelist, psaddr);
break;
}
}
}
}
psaddr = next;
}
return (0);
}
static void
set_advice(int *combined_adv, int new_adv)
{
if (*combined_adv == -1)
*combined_adv = 0;
if (new_adv & GRP1_ADV)
*combined_adv &= ~GRP1_ADV;
else if (new_adv & GRP2_ADV)
*combined_adv &= ~GRP2_ADV;
else
*combined_adv &= ~GRP3_ADV;
*combined_adv |= new_adv;
}
static void
create_choplist(saddr_t **choppedlist, saddr_t *mergedlist)
{
saddr_t *mlptr, *clptr;
for (mlptr = mergedlist; mlptr != NULL; mlptr = mlptr->next) {
clptr = malloc(sizeof (saddr_t));
clptr->addr = mlptr->addr;
clptr->length = 0;
clptr->adv = -1;
clptr->next = NULL;
insert_addr(choppedlist, clptr, NODUPS);
clptr = malloc(sizeof (saddr_t));
clptr->addr = mlptr->addr + mlptr->length;
clptr->length = 0;
clptr->adv = -1;
clptr->next = NULL;
insert_addr(choppedlist, clptr, NODUPS);
}
for (clptr = *choppedlist; clptr != NULL; clptr = clptr->next) {
if (clptr->next) {
clptr->length = clptr->next->addr - clptr->addr;
} else {
delete_addr(choppedlist, clptr);
break;
}
}
for (mlptr = mergedlist; mlptr != NULL; mlptr = mlptr->next) {
for (clptr = *choppedlist; clptr != NULL; clptr = clptr->next) {
if (mlptr->addr <= clptr->addr &&
mlptr->addr + mlptr->length >=
clptr->addr + clptr->length)
set_advice(&clptr->adv, mlptr->adv);
if (mlptr->addr + mlptr->length <
clptr->addr)
break;
}
}
}
static void
print_advice(saddr_t *advlist, mapnode_t *maplist)
{
saddr_t *psaddr = advlist;
mapnode_t *pmapnode;
char *advice;
pmapnode = maplist;
while (psaddr) {
if (psaddr->adv == -1) {
psaddr = psaddr->next;
continue;
}
while (pmapnode && (pmapnode->pmp->pr_vaddr +
pmapnode->pmp->pr_size <= psaddr->addr))
pmapnode = pmapnode->next;
advice = advtostr(psaddr->adv);
if (strlen(advice) > 0) {
(void) printf("%.*lX %*uK %6s %s\t%s\n",
addr_width, (ulong_t)psaddr->addr, size_width - 1,
(int)ROUNDUP_KB(psaddr->length),
mflags(pmapnode->pmp->pr_mflags), pmapnode->label,
advice);
} else {
(void) printf("%.*lX %*uK %6s %s\n",
addr_width, (ulong_t)psaddr->addr, size_width - 1,
(int)ROUNDUP_KB(psaddr->length),
mflags(pmapnode->pmp->pr_mflags), pmapnode->label);
}
psaddr = psaddr->next;
}
}
static int
pr_madvise(struct ps_prochandle *Pr, caddr_t addr, size_t len, int advice)
{
return (pr_memcntl(Pr, addr, len, MC_ADVISE,
(caddr_t)(uintptr_t)advice, 0, 0));
}
static char *
mflags(uint_t arg)
{
static char code_buf[80];
(void) snprintf(code_buf, sizeof (code_buf), "%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' : '-');
return (code_buf);
}
static char *
advtostr(int adv)
{
static char buf[50];
int i;
*buf = '\0';
if (adv != NO_ADVICE) {
for (i = MADV_NORMAL; i <= MADV_PURGE; i++) {
if (adv & (1 << i)) {
if (*buf == '\0') {
(void) snprintf(buf, sizeof (buf) - 1,
"<= %s", advicestr[i]);
} else {
(void) strlcat(buf, ",", sizeof (buf));
(void) strlcat(buf, advicestr[i],
sizeof (buf));
}
}
}
}
return (buf);
}
static void
intr(int sig)
{
interrupt++;
}
int
main(int argc, char **argv)
{
int Fflag = 0;
int rc = 0;
int opt, subopt;
int tmpadv;
char *options, *value;
saddr_t *psaddr;
mapnode_t *pmapnode, *tempmapnode;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
progname = basename(argv[0]);
if (argc == 1)
usage();
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 ((opt = getopt(argc, argv, "Flo:v")) != EOF) {
switch (opt) {
case 'o':
options = optarg;
while (*options != '\0') {
subopt = getsubopt(&options, suboptstr,
&value);
switch (subopt) {
case AT_PRIVM:
case AT_HEAP:
case AT_SHARED:
case AT_STACK:
at_map |= (1 << subopt);
tmpadv = get_advice(value);
set_advice(&generic_adv[subopt],
tmpadv);
break;
default:
at_map |= (1 << AT_SEG);
psaddr = parse_suboptions(value);
if (psaddr == NULL) {
usage();
} else {
insert_addr(&rawadv_list,
psaddr, YESDUPS);
}
break;
}
}
break;
case 'v':
opt_verbose = 1;
break;
case 'F':
Fflag = PGRAB_FORCE;
break;
case 'l':
lflag = 1;
break;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (argc <= 0) {
usage();
}
(void) proc_initstdio();
while (!interrupt && argc-- > 0) {
char *arg;
int gcode;
psinfo_t psinfo;
(void) proc_flushstdio();
if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_PIDS,
PGRAB_RETAIN | Fflag, &gcode)) == NULL) {
(void) fprintf(stderr,
gettext("%s: cannot examine %s: %s\n"),
progname, arg, Pgrab_error(gcode));
rc++;
continue;
}
addr_width =
(Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 16 : 8;
size_width =
(Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 11 : 8;
(void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t));
if (opt_verbose) {
proc_unctrl_psinfo(&psinfo);
(void) printf("%d:\t%.70s\n",
(int)psinfo.pr_pid, psinfo.pr_psargs);
}
if (!(Pstatus(Pr)->pr_flags & PR_ISSYS)) {
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);
}
if (Pgetauxval(Pr, AT_BASE) != -1L &&
Prd_agent(Pr) == NULL) {
(void) fprintf(stderr,
gettext("%s: warning: "
"librtld_db failed to initialize; "
"shared library information will not "
"be available\n"),
progname);
}
comm_page = Pgetauxval(Pr, AT_SUN_COMMPAGE);
assert(merged_list == NULL);
maplist_head = maplist_tail = NULL;
rc += Pmapping_iter(Pr, (proc_map_f *)create_maplist,
NULL);
(void) apply_advice(&merged_list);
if (opt_verbose) {
assert(chopped_list == NULL);
create_choplist(&chopped_list, merged_list);
print_advice(chopped_list, maplist_head);
delete_list(&chopped_list);
}
delete_list(&merged_list);
pmapnode = maplist_head;
while (pmapnode) {
tempmapnode = pmapnode;
pmapnode = pmapnode->next;
free(tempmapnode);
}
if (stacks != NULL) {
free(stacks);
stacks = NULL;
}
}
Prelease(Pr, 0);
}
(void) proc_finistdio();
return (rc);
}