xref: /onnv-gate/usr/src/cmd/ptools/pmadvise/pmadvise.c (revision 7032:f2e60d02d9a9)
12685Sakolb /*
22685Sakolb  * CDDL HEADER START
32685Sakolb  *
42685Sakolb  * The contents of this file are subject to the terms of the
52685Sakolb  * Common Development and Distribution License (the "License").
62685Sakolb  * You may not use this file except in compliance with the License.
72685Sakolb  *
82685Sakolb  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92685Sakolb  * or http://www.opensolaris.org/os/licensing.
102685Sakolb  * See the License for the specific language governing permissions
112685Sakolb  * and limitations under the License.
122685Sakolb  *
132685Sakolb  * When distributing Covered Code, include this CDDL HEADER in each
142685Sakolb  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152685Sakolb  * If applicable, add the following below this CDDL HEADER, with the
162685Sakolb  * fields enclosed by brackets "[]" replaced with your own identifying
172685Sakolb  * information: Portions Copyright [yyyy] [name of copyright owner]
182685Sakolb  *
192685Sakolb  * CDDL HEADER END
202685Sakolb  */
212685Sakolb 
222685Sakolb /*
23*7032Sakolb  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
242685Sakolb  * Use is subject to license terms.
252685Sakolb  */
262685Sakolb 
272685Sakolb #pragma ident	"%Z%%M%	%I%	%E% SMI"
282685Sakolb 
292685Sakolb /*
302685Sakolb  * pmadvise
312685Sakolb  *
322685Sakolb  * ptool wrapper for madvise(3C) to apply memory advice to running processes
332685Sakolb  *
342685Sakolb  * usage:	pmadvise -o option[,option] [-v] [-F] pid ...
352685Sakolb  *  (Give "advice" about a process's memory)
362685Sakolb  *  -o option[,option]: options are
372685Sakolb  *      private=<advice>
382685Sakolb  *      shared=<advice>
392685Sakolb  *      heap=<advice>
402685Sakolb  *      stack=<advice>
412685Sakolb  *      <segaddr>[:<length>]=<advice>
422685Sakolb  *     valid <advice> is one of:
432685Sakolb  *      normal, random, sequential, willneed, dontneed,
442685Sakolb  *      free, access_lwp, access_many, access_default
452685Sakolb  *  -v: verbose output
462685Sakolb  *  -F: force grabbing of the target process(es)
472685Sakolb  *  pid: process id list
482685Sakolb  *
492685Sakolb  *
502685Sakolb  * Advice passed to this tool are organized into various lists described here:
512685Sakolb  *  rawadv_list: includes all specific advice from command line (specific
522685Sakolb  *               advice being those given to a particular address range rather
532685Sakolb  *               than a type like "heap" or "stack".  In contrast, these
542685Sakolb  *               types are referred to as generic advice). Duplicates allowed.
552685Sakolb  *               List ordered by addr, then by size (largest size first).
562685Sakolb  *               Created once per run.
572685Sakolb  *  merged_list: includes all specific advice from the rawadv_list as well as
582685Sakolb  *               all generic advice.  This must be recreated for each process
592685Sakolb  *               as the generic advice will apply to different regions for
602685Sakolb  *               different processes. Duplicates allowed. List ordered by addr,
612685Sakolb  *               then by size (largest size first). Created once per pid.
622685Sakolb  *  chopped_list: used for verbose output only. This list parses the merged
632685Sakolb  *                list such that it eliminates any overlap and combines the
642685Sakolb  *                advice. Easiest to think of this visually: if you take all
652685Sakolb  *                the advice in the merged list and lay them down on a memory
662685Sakolb  *                range of the entire process (laying on top of each other when
672685Sakolb  *                necessary), then flatten them into one layer, combining advice
682685Sakolb  *                in the case of overlap, you get the chopped_list of advice.
692685Sakolb  *                Duplicate entries not allowed (since there is no overlap by
702685Sakolb  *                definition in this list).  List ordered by addr. Created once
712685Sakolb  *                per pid.
722685Sakolb  *
732685Sakolb  *                Example:
742685Sakolb  *                   merged_list:   |-----adv1----|---------adv3---------|
752685Sakolb  *                                       |--adv2--|--adv4--|-----adv5----|
762685Sakolb  *                                                  ||
772685Sakolb  *                                                  \/
782685Sakolb  *                   chopped_list:  |adv1|-adv1,2-|-adv3,4-|----adv3,5---|
792685Sakolb  *
802685Sakolb  *  maplist: list of memory mappings for a particular process. Used to create
812685Sakolb  *           generic advice entries for merged_list and for pmap like verbose
822685Sakolb  *           output. Created once per pid.
832685Sakolb  *
842685Sakolb  * Multiple lists are necessary because the actual advice applied given a set
852685Sakolb  * of generic and specific advice changes from process to process, so for each
862685Sakolb  * pid pmadvise is passed, it must create a new merged_list from which to apply
872685Sakolb  * advice (and a new chopped_list if verbose output is requested).
882685Sakolb  *
892685Sakolb  * Pseudo-code:
902685Sakolb  * I.	Input advice from command line
912685Sakolb  * II.	Create [raw advice list] of specific advice
922685Sakolb  * III.	Iterate through PIDs:
932685Sakolb  *	A.	Create [map list]
942685Sakolb  *	B.	Merge generic advice and [raw advice list] into [merged list]
952685Sakolb  *	C.	Apply advice from [merged list]; upon error:
962685Sakolb  *		i.	output madvise error message
972685Sakolb  *		ii.	remove element from [merged list]
982685Sakolb  *	D.	If verbose output:
992685Sakolb  *		i.	Create [chopped list] from [merged list]
1002685Sakolb  *		ii.	Iterate through [map list]:
1012685Sakolb  *			a.	output advice as given by [merged list]
1022685Sakolb  *		iii.	Delete [chopped list]
1032685Sakolb  *	E.	Delete [merged list]
1042685Sakolb  *	F.	Delete [map list]
1052685Sakolb  */
1062685Sakolb 
1072685Sakolb #include <stdio.h>
1082685Sakolb #include <stdlib.h>
1092685Sakolb #include <unistd.h>
1102685Sakolb #include <ctype.h>
1112685Sakolb #include <fcntl.h>
1122685Sakolb #include <string.h>
1132685Sakolb #include <dirent.h>
1142685Sakolb #include <limits.h>
1152685Sakolb #include <link.h>
1162685Sakolb #include <libelf.h>
1172685Sakolb #include <locale.h>
1182685Sakolb #include <sys/types.h>
1192685Sakolb #include <sys/mman.h>
1202685Sakolb #include <sys/stat.h>
1212685Sakolb #include <sys/mkdev.h>
1222685Sakolb #include <assert.h>
1232685Sakolb #include <libproc.h>
1242685Sakolb #include <libgen.h>
1252685Sakolb #include <signal.h>
1262685Sakolb 
1272685Sakolb #ifndef	TEXT_DOMAIN			/* should be defined by cc -D */
1282685Sakolb #define	TEXT_DOMAIN	"SYS_TEST"	/* use this only if it wasn't */
1292685Sakolb #endif
1302685Sakolb 
1312685Sakolb #define	KILOBYTE	1024
1322685Sakolb 
1332685Sakolb /*
1342685Sakolb  * Round up the value to the nearest kilobyte
1352685Sakolb  */
1362685Sakolb #define	ROUNDUP_KB(x)	(((x) + (KILOBYTE - 1)) / KILOBYTE)
1372685Sakolb 
1382685Sakolb #define	NO_ADVICE		0
1392685Sakolb 
1402685Sakolb /*
1412685Sakolb  * The following definitions are used as the third argument in insert_addr()
1422685Sakolb  *   NODUPS = no duplicates are not allowed, thus if the addr being inserted
1432685Sakolb  *   already exists in the list, return without inserting again.
1442685Sakolb  *
1452685Sakolb  *   YESDUPS = yes duplicates are allowed, thus always insert the addr
1462685Sakolb  *   regardless of whether it already exists in the list or not.
1472685Sakolb  */
1482685Sakolb #define	NODUPS	1
1492685Sakolb #define	YESDUPS	0
1502685Sakolb 
1512685Sakolb /*
1522685Sakolb  * Advice that can be passed to madvise fit into three groups that each
1532685Sakolb  * contain 3 mutually exclusive options.  These groups are defined below:
1542685Sakolb  *   Group 1: normal, random, sequential
1552685Sakolb  *   Group 2: willneed, dontneed, free
1562685Sakolb  *   Group 3: default, accesslwp, accessmany
1572685Sakolb  * Thus, advice that includes (at most) one from each group is valid.
1582685Sakolb  *
1592685Sakolb  * The following #define's are used as masks to determine which group(s) a
1602685Sakolb  * particular advice fall under.
1612685Sakolb  */
1622685Sakolb 
1632685Sakolb #define	GRP1_ADV	(1 << MADV_NORMAL | 1 << MADV_RANDOM | \
1642685Sakolb 			1 << MADV_SEQUENTIAL)
1652685Sakolb #define	GRP2_ADV	(1 << MADV_WILLNEED | 1 << MADV_DONTNEED | \
1662685Sakolb 			1 << MADV_FREE)
1672685Sakolb #define	GRP3_ADV	(1 << MADV_ACCESS_DEFAULT | 1 << MADV_ACCESS_LWP | \
1682685Sakolb 			1 << MADV_ACCESS_MANY)
1692685Sakolb 
1702685Sakolb static	int	create_maplist(void *, const prmap_t *, const char *);
1712685Sakolb static	int	pr_madvise(struct ps_prochandle *, caddr_t, size_t, int);
1722685Sakolb 
1732685Sakolb static	char	*mflags(uint_t);
1742685Sakolb static	char	*advtostr(int);
1752685Sakolb 
1762685Sakolb static	int	addr_width, size_width;
1772685Sakolb static	char	*progname;
1782685Sakolb static	struct ps_prochandle *Pr;
1792685Sakolb 
1802685Sakolb typedef struct lwpstack {
1812685Sakolb 	lwpid_t	lwps_lwpid;
1822685Sakolb 	stack_t	lwps_stack;
1832685Sakolb } lwpstack_t;
1842685Sakolb 
1852685Sakolb static	lwpstack_t *stacks;
1862685Sakolb static	uint_t	nstacks;
1872685Sakolb 
1882685Sakolb /*
1892685Sakolb  * Used to set the advice type var (at_map) when parsing the arguments to
1902685Sakolb  * pmadvise.  Later, when creating the map list, at_map is used as a mask
1912685Sakolb  * to determine if any generic advice applies to each memory mapping.
1922685Sakolb  */
1932685Sakolb enum	atype_enum {
1942685Sakolb 	AT_PRIVM,
1952685Sakolb 	AT_SHARED,
1962685Sakolb 	AT_HEAP,
1972685Sakolb 	AT_STACK,
1982685Sakolb 	AT_SEG,
1992685Sakolb 	AT_NTYPES
2002685Sakolb };
2012685Sakolb 
2022685Sakolb static char	*suboptstr[] = {
2032685Sakolb 	"private",
2042685Sakolb 	"shared",
2052685Sakolb 	"heap",
2062685Sakolb 	"stack",
2072685Sakolb 	NULL
2082685Sakolb };
2092685Sakolb 
2102685Sakolb 
2112685Sakolb int	generic_adv[] = {NO_ADVICE, NO_ADVICE, NO_ADVICE, NO_ADVICE};
2122685Sakolb int	at_map = 0;
2132685Sakolb 
2142685Sakolb typedef struct saddr_struct {
2152685Sakolb 	uintptr_t	addr;
2162685Sakolb 	size_t		length;
2172685Sakolb 	int		adv;
2182685Sakolb 	struct saddr_struct	*next;
2192685Sakolb } saddr_t;
2202685Sakolb static int	apply_advice(saddr_t **);
2212685Sakolb static void	set_advice(int *, int);
2222685Sakolb static void	create_choplist(saddr_t **, saddr_t *);
2232685Sakolb 
2242685Sakolb /*
2252685Sakolb  * The segment address advice from the command line
2262685Sakolb  */
2272685Sakolb saddr_t	*rawadv_list = NULL;
2282685Sakolb /*
2292685Sakolb  * The rawadv_list + list entries for the generic advice (if any).
2302685Sakolb  * This must be recreated for each PID as the memory maps might be different.
2312685Sakolb  */
2322685Sakolb saddr_t *merged_list = NULL;
2332685Sakolb /*
2342685Sakolb  * The merged_list cut up so as to remove all overlap
2352685Sakolb  * e.g. if merged_list contained two entries:
2362685Sakolb  *
2372685Sakolb  * [0x38000:0x3e000) = adv1
2382685Sakolb  * [0x3a000:0x3c000) = adv2
2392685Sakolb  *
2402685Sakolb  * the chopped list will contain three entries:
2412685Sakolb  *
2422685Sakolb  * [0x38000:0x3a000) = adv1
2432685Sakolb  * [0x3a000:0x3c000) = adv1,adv2
2442685Sakolb  * [0x3c000:0x3e000) = adv1
2452685Sakolb  *
2462685Sakolb  */
2472685Sakolb saddr_t *chopped_list = NULL;
2482685Sakolb 
2492685Sakolb typedef struct mapnode_struct {
2502685Sakolb 	prmap_t			*pmp;
2512685Sakolb 	char			label[PATH_MAX];
2522685Sakolb 	int			mtypes;
2532685Sakolb 	struct mapnode_struct	*next;
2542685Sakolb } mapnode_t;
2552685Sakolb 
2562685Sakolb mapnode_t *maplist_head = NULL;
2572685Sakolb mapnode_t *maplist_tail = NULL;
2582685Sakolb static void	print_advice(saddr_t *, mapnode_t *);
2592685Sakolb 
2602685Sakolb int	opt_verbose;
2612685Sakolb 
2622685Sakolb static char	*advicestr[] = {
2632685Sakolb 	"normal",
2642685Sakolb 	"random",
2652685Sakolb 	"sequential",
2662685Sakolb 	"willneed",
2672685Sakolb 	"dontneed",
2682685Sakolb 	"free",
2692685Sakolb 	"access_default",
2702685Sakolb 	"access_lwp",
2712685Sakolb 	"access_many"
2722685Sakolb };
2732685Sakolb 
2742685Sakolb /*
2752685Sakolb  * How many signals caught from terminal
2762685Sakolb  * We bail out as soon as possible when interrupt is set
2772685Sakolb  */
2782685Sakolb static int	interrupt = 0;
2792685Sakolb 
2802685Sakolb /*
2812685Sakolb  * Interrupt handler
2822685Sakolb  */
2832685Sakolb static void	intr(int);
2842685Sakolb 
2852685Sakolb /*
2862685Sakolb  * Iterative function passed to Plwp_iter to
2872685Sakolb  * get alt and main stacks for given lwp.
2882685Sakolb  */
2892685Sakolb static int
2902685Sakolb getstack(void *data, const lwpstatus_t *lsp)
2912685Sakolb {
2922685Sakolb 	int *np = (int *)data;
2932685Sakolb 
2942685Sakolb 	if (Plwp_alt_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) {
2952685Sakolb 		stacks[*np].lwps_stack.ss_flags |= SS_ONSTACK;
2962685Sakolb 		stacks[*np].lwps_lwpid = lsp->pr_lwpid;
2972685Sakolb 		(*np)++;
2982685Sakolb 	}
2992685Sakolb 
3002685Sakolb 	if (Plwp_main_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) {
3012685Sakolb 		stacks[*np].lwps_lwpid = lsp->pr_lwpid;
3022685Sakolb 		(*np)++;
3032685Sakolb 	}
3042685Sakolb 
3052685Sakolb 	return (0);
3062685Sakolb }
3072685Sakolb 
3082685Sakolb /*
3092685Sakolb  * We compare the high memory addresses since stacks are faulted in from
3102685Sakolb  * high memory addresses to low memory addresses, and our prmap_t
3112685Sakolb  * structures identify only the range of addresses that have been faulted
3122685Sakolb  * in so far.
3132685Sakolb  */
3142685Sakolb static int
3152685Sakolb cmpstacks(const void *ap, const void *bp)
3162685Sakolb {
3172685Sakolb 	const lwpstack_t *as = ap;
3182685Sakolb 	const lwpstack_t *bs = bp;
3192685Sakolb 	uintptr_t a = (uintptr_t)as->lwps_stack.ss_sp + as->lwps_stack.ss_size;
3202685Sakolb 	uintptr_t b = (uintptr_t)bs->lwps_stack.ss_sp + bs->lwps_stack.ss_size;
3212685Sakolb 
3222685Sakolb 	if (a < b)
3232685Sakolb 		return (1);
3242685Sakolb 	if (a > b)
3252685Sakolb 		return (-1);
3262685Sakolb 	return (0);
3272685Sakolb }
3282685Sakolb 
3292685Sakolb /*
3302685Sakolb  * Prints usage and exits
3312685Sakolb  */
3322685Sakolb static void
3332685Sakolb usage()
3342685Sakolb {
3352685Sakolb 	(void) fprintf(stderr,
3362685Sakolb 	    gettext("usage:\t%s -o option[,option] [-v] [-F] pid ...\n"),
3372685Sakolb 	    progname);
3382685Sakolb 	(void) fprintf(stderr,
3392685Sakolb 	    gettext("    (Give \"advice\" about a process's memory)\n"
3402685Sakolb 		"    -o option[,option]: options are\n"
3412685Sakolb 		"        private=<advice>\n"
3422685Sakolb 		"        shared=<advice>\n"
3432685Sakolb 		"        heap=<advice>\n"
3442685Sakolb 		"        stack=<advice>\n"
3452685Sakolb 		"        <segaddr>[:<length>]=<advice>\n"
3462685Sakolb 		"       valid <advice> is one of:\n"
3472685Sakolb 		"        normal, random, sequential, willneed, dontneed,\n"
3482685Sakolb 		"        free, access_lwp, access_many, access_default\n"
3492685Sakolb 		"    -v: verbose output\n"
3502685Sakolb 		"    -F: force grabbing of the target process(es)\n"
3512685Sakolb 		"    pid: process id list\n"));
3522685Sakolb 	exit(2);
3532685Sakolb }
3542685Sakolb 
3552685Sakolb /*
3562685Sakolb  * Function to parse advice from options string
3572685Sakolb  */
3582685Sakolb static int
3592685Sakolb get_advice(char *optarg)
3602685Sakolb {
3612685Sakolb 	/*
3622685Sakolb 	 * Determine which advice is given, we use shifted values as
3632685Sakolb 	 * multiple pieces of advice may apply for a particular region.
3642685Sakolb 	 * (See comment above regarding GRP[1,2,3]_ADV definitions for
3652685Sakolb 	 * breakdown of advice groups).
3662685Sakolb 	 */
3672685Sakolb 	if (strcmp(optarg, "access_default") == 0)
3682685Sakolb 		return (1 << MADV_ACCESS_DEFAULT);
3692685Sakolb 	else if (strcmp(optarg, "access_many") == 0)
3702685Sakolb 		return (1 << MADV_ACCESS_MANY);
3712685Sakolb 	else if (strcmp(optarg, "access_lwp") == 0)
3722685Sakolb 		return (1 << MADV_ACCESS_LWP);
3732685Sakolb 	else if (strcmp(optarg, "sequential") == 0)
3742685Sakolb 		return (1 << MADV_SEQUENTIAL);
3752685Sakolb 	else if (strcmp(optarg, "willneed") == 0)
3762685Sakolb 		return (1 << MADV_WILLNEED);
3772685Sakolb 	else if (strcmp(optarg, "dontneed") == 0)
3782685Sakolb 		return (1 << MADV_DONTNEED);
3792685Sakolb 	else if (strcmp(optarg, "random") == 0)
3802685Sakolb 		return (1 << MADV_RANDOM);
3812685Sakolb 	else if (strcmp(optarg, "normal") == 0)
3822685Sakolb 		return (1 << MADV_NORMAL);
3832685Sakolb 	else if (strcmp(optarg, "free") == 0)
3842685Sakolb 		return (1 << MADV_FREE);
3852685Sakolb 	else {
3862685Sakolb 		(void) fprintf(stderr, gettext("%s: invalid advice: %s\n"),
3872685Sakolb 		    progname, optarg);
3882685Sakolb 		usage();
3892685Sakolb 		return (-1);
3902685Sakolb 	}
3912685Sakolb }
3922685Sakolb 
3932685Sakolb /*
3942685Sakolb  * Function to convert character size indicators into actual size
3952685Sakolb  * (i.e., 123M => sz = 123 * 1024 * 1024)
3962685Sakolb  */
3972685Sakolb static size_t
3982685Sakolb atosz(char *optarg, char **endptr)
3992685Sakolb {
4002685Sakolb 	size_t	sz = 0;
4012685Sakolb 
4022685Sakolb 	if (optarg == NULL || optarg[0] == '\0')
4032685Sakolb 		return (0);
4042685Sakolb 
4052685Sakolb 	sz = strtoll(optarg, endptr, 0);
4062685Sakolb 
4072685Sakolb 	switch (**endptr) {
4082685Sakolb 	case 'E':
4092685Sakolb 	case 'e':
4102685Sakolb 		sz *= KILOBYTE;
4112685Sakolb 		/* FALLTHRU */
4122685Sakolb 	case 'P':
4132685Sakolb 	case 'p':
4142685Sakolb 		sz *= KILOBYTE;
4152685Sakolb 		/* FALLTHRU */
4162685Sakolb 	case 'T':
4172685Sakolb 	case 't':
4182685Sakolb 		sz *= KILOBYTE;
4192685Sakolb 		/* FALLTHRU */
4202685Sakolb 	case 'G':
4212685Sakolb 	case 'g':
4222685Sakolb 		sz *= KILOBYTE;
4232685Sakolb 		/* FALLTHRU */
4242685Sakolb 	case 'M':
4252685Sakolb 	case 'm':
4262685Sakolb 		sz *= KILOBYTE;
4272685Sakolb 		/* FALLTHRU */
4282685Sakolb 	case 'K':
4292685Sakolb 	case 'k':
4302685Sakolb 		sz *= KILOBYTE;
4312685Sakolb 		/* FALLTHRU */
4322685Sakolb 	case 'B':
4332685Sakolb 	case 'b':
4342685Sakolb 		(*endptr)++;
4352685Sakolb 		/* FALLTHRU */
4362685Sakolb 	default:
4372685Sakolb 		break;
4382685Sakolb 	}
4392685Sakolb 	return (sz);
4402685Sakolb }
4412685Sakolb 
4422685Sakolb /*
4432685Sakolb  * Inserts newaddr into list.  dups indicates whether we allow duplicate
4442685Sakolb  * addr entries in the list (valid values are NODUPS and YESDUPS).
4452685Sakolb  */
4462685Sakolb static void
4472685Sakolb insert_addr(saddr_t **list, saddr_t *newaddr, int dups)
4482685Sakolb {
4492685Sakolb 	saddr_t *prev = *list;
4502685Sakolb 	saddr_t *psaddr;
4512685Sakolb 
4522685Sakolb 	if (*list == NULL) {
4532685Sakolb 		newaddr->next = *list;
4542685Sakolb 		*list = newaddr;
4552685Sakolb 		return;
4562685Sakolb 	}
4572685Sakolb 
4582685Sakolb 	for (psaddr = (*list)->next; psaddr != NULL; psaddr = psaddr->next) {
4592685Sakolb 		if ((dups == NODUPS) && (psaddr->addr == newaddr->addr)) {
4602685Sakolb 			free(newaddr);
4612685Sakolb 			return;
4622685Sakolb 		}
4632685Sakolb 
4642685Sakolb 		/*
4652685Sakolb 		 * primary level of comparison is by address; smaller addr 1st
4662685Sakolb 		 * secondary level of comparison is by length; bigger length 1st
4672685Sakolb 		 */
4682685Sakolb 		if ((psaddr->addr > newaddr->addr) ||
4692685Sakolb 		    (psaddr->addr == newaddr->addr &&
4702685Sakolb 		    psaddr->length < newaddr->length))
4712685Sakolb 			break;
4722685Sakolb 
4732685Sakolb 		prev = psaddr;
4742685Sakolb 	}
4752685Sakolb 
4762685Sakolb 	prev->next = newaddr;
4772685Sakolb 	newaddr->next = psaddr;
4782685Sakolb }
4792685Sakolb 
4802685Sakolb /*
4812685Sakolb  * Deletes given element from list
4822685Sakolb  */
4832685Sakolb static void
4842685Sakolb delete_addr(saddr_t **list, saddr_t *delme)
4852685Sakolb {
4862685Sakolb 	saddr_t	*prev = *list;
4872685Sakolb 
4882685Sakolb 	if (delme == *list) {
4892685Sakolb 		*list = delme->next;
4902685Sakolb 		free(delme);
4912685Sakolb 		return;
4922685Sakolb 	}
4932685Sakolb 
4942685Sakolb 	while (prev != NULL && prev->next != delme) {
4952685Sakolb 		prev = prev->next;
4962685Sakolb 	}
4972685Sakolb 
4982685Sakolb 	if (prev) {
4992685Sakolb 		prev->next = delme->next;
5002685Sakolb 		free(delme);
5012685Sakolb 	}
5022685Sakolb }
5032685Sakolb 
5042685Sakolb /*
5052685Sakolb  * Delete entire list
5062685Sakolb  */
5072685Sakolb static void
5082685Sakolb delete_list(saddr_t **list)
5092685Sakolb {
5102685Sakolb 	saddr_t *psaddr = *list;
5112685Sakolb 
5122685Sakolb 	while (psaddr != NULL) {
5132685Sakolb 		saddr_t *temp = psaddr;
5142685Sakolb 
5152685Sakolb 		psaddr = psaddr->next;
5162685Sakolb 		free(temp);
5172685Sakolb 	}
5182685Sakolb 	*list = NULL;
5192685Sakolb }
5202685Sakolb 
5212685Sakolb static saddr_t *
5222685Sakolb parse_suboptions(char *value)
5232685Sakolb {
5242685Sakolb 	char	*endptr;
5252685Sakolb 	saddr_t *psaddr = malloc(sizeof (saddr_t));
5262685Sakolb 
5272685Sakolb 	/*
5282685Sakolb 	 * This must (better) be a segment addr
5292685Sakolb 	 */
5302685Sakolb 	psaddr->addr =
5312685Sakolb 	    strtoull(value, &endptr, 16);
5322685Sakolb 
5332685Sakolb 	/*
5342685Sakolb 	 * Check to make sure strtoul worked correctly (a properly formatted
5352685Sakolb 	 * string will terminate in a ':' (if size is given) or an '=' (if size
5362685Sakolb 	 * is not specified). Also check to make sure a 0 addr wasn't returned
5372685Sakolb 	 * indicating strtoll was unable to convert).
5382685Sakolb 	 */
5392685Sakolb 	if ((psaddr->addr == 0) || (*endptr != ':' && *endptr != '=')) {
5402685Sakolb 		free(psaddr);
5412685Sakolb 		(void) fprintf(stderr,
5422685Sakolb 		    gettext("%s: invalid option %s\n"),
5432685Sakolb 		    progname, value);
5442685Sakolb 		usage();
5452685Sakolb 	} else {
5462685Sakolb 		/* init other fields */
5472685Sakolb 		psaddr->length = 0;
5482685Sakolb 		psaddr->adv = NO_ADVICE;
5492685Sakolb 		psaddr->next = NULL;
5502685Sakolb 
5512685Sakolb 		/* skip past address */
5522685Sakolb 		value = endptr;
5532685Sakolb 
5542685Sakolb 		/* check for length */
5552685Sakolb 		if (*value == ':') {
5562685Sakolb 			/* skip the ":" */
5572685Sakolb 			value++;
5582685Sakolb 			psaddr->length = atosz(value, &endptr);
5592685Sakolb 		}
5602685Sakolb 
5612685Sakolb 		if (*endptr != '=') {
5622685Sakolb 			(void) fprintf(stderr,
5632685Sakolb 			    gettext("%s: invalid option %s\n"),
5642685Sakolb 			    progname, value);
5652685Sakolb 			/*
5662685Sakolb 			 * if improperly formatted, free mem, print usage, and
5672685Sakolb 			 * exit Note: usage ends with a call to exit()
5682685Sakolb 			 */
5692685Sakolb 			free(psaddr);
5702685Sakolb 			usage();
5712685Sakolb 		}
5722685Sakolb 		/* skip the "=" */
5732685Sakolb 		value = endptr + 1;
5742685Sakolb 		at_map |= (1 << AT_SEG);
5752685Sakolb 		psaddr->adv =
5762685Sakolb 		    get_advice(value);
5772685Sakolb 	}
5782685Sakolb 
5792685Sakolb 	return (psaddr);
5802685Sakolb }
5812685Sakolb 
5822685Sakolb /*
5832685Sakolb  * Create labels for non-anon, non-heap mappings
5842685Sakolb  */
5852685Sakolb static char *
5862685Sakolb make_name(struct ps_prochandle *Pr, uintptr_t addr, const char *mapname,
5872685Sakolb 	char *buf, size_t bufsz)
5882685Sakolb {
5892685Sakolb 	const pstatus_t *Psp = Pstatus(Pr);
5902685Sakolb 	char fname[100];
5912685Sakolb 	struct stat statb;
5922685Sakolb 	int len;
5932685Sakolb 
5942685Sakolb 	if (strcmp(mapname, "a.out") == 0 &&
5952685Sakolb 	    Pexecname(Pr, buf, bufsz) != NULL)
5962685Sakolb 		return (buf);
5972685Sakolb 
5982685Sakolb 	if (Pobjname(Pr, addr, buf, bufsz) != NULL) {
5992685Sakolb 		if ((len = resolvepath(buf, buf, bufsz)) > 0) {
6002685Sakolb 			buf[len] = '\0';
6012685Sakolb 			return (buf);
6022685Sakolb 		}
6032685Sakolb 	}
6042685Sakolb 
6052685Sakolb 	if (*mapname != '\0') {
6062685Sakolb 		(void) snprintf(fname, sizeof (fname), "/proc/%d/object/%s",
6072685Sakolb 			(int)Psp->pr_pid, mapname);
6082685Sakolb 		if (stat(fname, &statb) == 0) {
6092685Sakolb 			dev_t dev = statb.st_dev;
6102685Sakolb 			ino_t ino = statb.st_ino;
6112685Sakolb 			(void) snprintf(buf, bufsz, "dev:%lu,%lu ino:%lu",
6122685Sakolb 				(ulong_t)major(dev), (ulong_t)minor(dev), ino);
6132685Sakolb 			return (buf);
6142685Sakolb 		}
6152685Sakolb 	}
6162685Sakolb 
6172685Sakolb 	return (NULL);
6182685Sakolb }
6192685Sakolb 
6202685Sakolb /*
6212685Sakolb  * Create label for anon mappings
6222685Sakolb  */
6232685Sakolb static char *
6242685Sakolb anon_name(char *name, const pstatus_t *Psp,
6252685Sakolb     uintptr_t vaddr, size_t size, int mflags, int shmid, int *mtypes)
6262685Sakolb {
6272685Sakolb 	if (mflags & MA_ISM) {
6282685Sakolb 		if (shmid == -1)
6292685Sakolb 			(void) snprintf(name, PATH_MAX, "  [ %s shmid=null ]",
6302685Sakolb 			    (mflags & MA_NORESERVE) ? "ism" : "dism");
6312685Sakolb 		else
6322685Sakolb 			(void) snprintf(name, PATH_MAX, "  [ %s shmid=0x%x ]",
6332685Sakolb 			    (mflags & MA_NORESERVE) ? "ism" : "dism", shmid);
6342685Sakolb 		*mtypes |= (1 << AT_SHARED);
6352685Sakolb 	} else if (mflags & MA_SHM) {
6362685Sakolb 		if (shmid == -1)
6372685Sakolb 			(void) sprintf(name, "  [ shmid=null ]");
6382685Sakolb 		else
6392685Sakolb 			(void) sprintf(name, "  [ shmid=0x%x ]", shmid);
6402685Sakolb 		*mtypes |= (1 << AT_SHARED);
6412685Sakolb 
6422685Sakolb 	} else if (vaddr + size > Psp->pr_stkbase &&
6432685Sakolb 	    vaddr < Psp->pr_stkbase + Psp->pr_stksize) {
6442685Sakolb 		(void) strcpy(name, "  [ stack ]");
6452685Sakolb 		*mtypes |= (1 << AT_STACK);
6462685Sakolb 
6472685Sakolb 	} else if ((mflags & MA_ANON) &&
6482685Sakolb 	    vaddr + size > Psp->pr_brkbase &&
6492685Sakolb 	    vaddr < Psp->pr_brkbase + Psp->pr_brksize) {
6502685Sakolb 		(void) strcpy(name, "  [ heap ]");
6512685Sakolb 		*mtypes |= (1 << AT_HEAP);
6522685Sakolb 
6532685Sakolb 	} else {
6542685Sakolb 		lwpstack_t key, *stk;
6552685Sakolb 
6562685Sakolb 		key.lwps_stack.ss_sp = (void *)vaddr;
6572685Sakolb 		key.lwps_stack.ss_size = size;
6582685Sakolb 		if (nstacks > 0 &&
6592685Sakolb 		    (stk = bsearch(&key, stacks, nstacks, sizeof (stacks[0]),
6602685Sakolb 		    cmpstacks)) != NULL) {
6612685Sakolb 			(void) snprintf(name, PATH_MAX, "  [ %s tid=%d ]",
6622685Sakolb 			    (stk->lwps_stack.ss_flags & SS_ONSTACK) ?
6632685Sakolb 			    "altstack" : "stack",
6642685Sakolb 			    stk->lwps_lwpid);
6652685Sakolb 			*mtypes |= (1 << AT_STACK);
6662685Sakolb 		} else {
6672685Sakolb 			(void) strcpy(name, "  [ anon ]");
6682685Sakolb 			*mtypes |= (1 << AT_PRIVM);
6692685Sakolb 		}
6702685Sakolb 	}
6712685Sakolb 
6722685Sakolb 	return (name);
6732685Sakolb }
6742685Sakolb 
6752685Sakolb /*
6762685Sakolb  * Create linked list of mappings for current process
6772685Sakolb  * In addition, add generic advice and raw advice
6782685Sakolb  * entries to merged_list.
6792685Sakolb  */
6802685Sakolb /* ARGSUSED */
6812685Sakolb static int
6822685Sakolb create_maplist(void *arg, const prmap_t *pmp, const char *object_name)
6832685Sakolb {
6842685Sakolb 	const 		pstatus_t *Psp = Pstatus(Pr);
6852685Sakolb 	mapnode_t *newmap = malloc(sizeof (mapnode_t));
6862685Sakolb 	saddr_t	*newaddr;
6872685Sakolb 	saddr_t	*psaddr;
6882685Sakolb 	char	*lname = NULL;
6892685Sakolb 	int	i;
6902685Sakolb 
6912685Sakolb 	if (interrupt)
6922685Sakolb 		return (0);
6932685Sakolb 
6942685Sakolb 	newmap->pmp = malloc(sizeof (prmap_t));
6952685Sakolb 	newmap->label[0] = '\0';
6962685Sakolb 	newmap->mtypes = 0;
6972685Sakolb 	newmap->next = NULL;
6982685Sakolb 	(void) memcpy(newmap->pmp, pmp, sizeof (prmap_t));
6992685Sakolb 
7002685Sakolb 	/*
7012685Sakolb 	 * If the mapping is not anon or not part of the heap, make a name
7022685Sakolb 	 * for it.  We don't want to report the heap as a.out's data.
7032685Sakolb 	 */
7042685Sakolb 	if (!(pmp->pr_mflags & MA_ANON) ||
7052685Sakolb 	    (pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase ||
7062685Sakolb 	    pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize)) {
7072685Sakolb 		lname = make_name(Pr, pmp->pr_vaddr, pmp->pr_mapname,
7082685Sakolb 		    newmap->label, sizeof (newmap->label));
7092685Sakolb 		if (pmp->pr_mflags & MA_SHARED)
7102685Sakolb 			newmap->mtypes |= 1 << AT_SHARED;
7112685Sakolb 		else
7122685Sakolb 			newmap->mtypes |= 1 << AT_PRIVM;
7132685Sakolb 	}
7142685Sakolb 
7152685Sakolb 	if (lname == NULL && (pmp->pr_mflags & MA_ANON)) {
7162685Sakolb 		lname = anon_name(newmap->label, Psp, pmp->pr_vaddr,
7172685Sakolb 		    pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid,
7182685Sakolb 		    &newmap->mtypes);
7192685Sakolb 	}
7202685Sakolb 
7212685Sakolb 	/*
7222685Sakolb 	 * Add raw advice that applies to this mapping to the merged_list
7232685Sakolb 	 */
7242685Sakolb 	psaddr = rawadv_list;
7252685Sakolb 	/*
7262685Sakolb 	 * Advance to point in rawadv_list that applies to this mapping
7272685Sakolb 	 */
7282685Sakolb 	while (psaddr && psaddr->addr < pmp->pr_vaddr)
7292685Sakolb 		psaddr = psaddr->next;
7302685Sakolb 	/*
7312685Sakolb 	 * Copy over to merged_list, check to see if size needs to be filled in
7322685Sakolb 	 */
7332685Sakolb 	while (psaddr && psaddr->addr < (pmp->pr_vaddr + pmp->pr_size)) {
7342685Sakolb 		newaddr = malloc(sizeof (saddr_t));
7352685Sakolb 		(void) memcpy(newaddr, psaddr, sizeof (saddr_t));
7362685Sakolb 		insert_addr(&merged_list, newaddr, YESDUPS);
7372685Sakolb 		/*
7382685Sakolb 		 * For raw advice that is given without size, try to default
7392685Sakolb 		 * size to size of mapping (only allowed if raw adv addr is
7402685Sakolb 		 * equal to beginning of mapping). Don't change the entry
7412685Sakolb 		 * in rawadv_list, only in the merged_list as the mappings
7422685Sakolb 		 * (and thus the default sizes) will be different for
7432685Sakolb 		 * different processes.
7442685Sakolb 		 */
7452685Sakolb 		if ((pmp->pr_vaddr == psaddr->addr) && (psaddr->length == 0))
7462685Sakolb 			newaddr->length = pmp->pr_size;
7472685Sakolb 		psaddr = psaddr->next;
7482685Sakolb 	}
7492685Sakolb 
7502685Sakolb 	/*
7512685Sakolb 	 * Put mapping into merged list with no advice, then
7522685Sakolb 	 * check to see if any generic advice applies.
7532685Sakolb 	 */
7542685Sakolb 	newaddr = malloc(sizeof (saddr_t));
7552685Sakolb 	newaddr->addr = pmp->pr_vaddr;
7562685Sakolb 	newaddr->length = pmp->pr_size;
7572685Sakolb 	newaddr->adv = NO_ADVICE;
7582685Sakolb 	insert_addr(&merged_list, newaddr, YESDUPS);
7592685Sakolb 
7602685Sakolb 	newmap->mtypes &= at_map;
7612685Sakolb 	for (i = AT_STACK; i >= AT_PRIVM; i--) {
7622685Sakolb 		if (newmap->mtypes & (1 << i)) {
7632685Sakolb 			assert(generic_adv[i] != NO_ADVICE);
7642685Sakolb 			newaddr->adv = generic_adv[i];
7652685Sakolb 			break;
7662685Sakolb 		}
7672685Sakolb 	}
7682685Sakolb 
7692685Sakolb 	/*
7702685Sakolb 	 * Add to linked list of mappings
7712685Sakolb 	 */
7722685Sakolb 	if (maplist_tail == NULL) {
7732685Sakolb 		maplist_head = maplist_tail = newmap;
7742685Sakolb 	} else {
7752685Sakolb 		maplist_tail->next = newmap;
7762685Sakolb 		maplist_tail = newmap;
7772685Sakolb 	}
7782685Sakolb 
7792685Sakolb 
7802685Sakolb 	return (0);
7812685Sakolb }
7822685Sakolb 
7832685Sakolb /*
7842685Sakolb  * Traverse advice list and apply all applicable advice to each region
7852685Sakolb  */
7862685Sakolb static int
7872685Sakolb apply_advice(saddr_t **advicelist)
7882685Sakolb {
7892685Sakolb 	saddr_t	*psaddr = *advicelist;
7902685Sakolb 	saddr_t	*next;
7912685Sakolb 	int	i;
7922685Sakolb 
7932685Sakolb 
7942685Sakolb 	while (!interrupt && psaddr != NULL) {
7952685Sakolb 		/*
7962685Sakolb 		 * Save next pointer since element may be removed before
7972685Sakolb 		 * we get a chance to advance psaddr.
7982685Sakolb 		 */
7992685Sakolb 		next = psaddr->next;
8002685Sakolb 
8012685Sakolb 		/*
8022685Sakolb 		 * Since mappings have been added to the merged list
8032685Sakolb 		 * even if no generic advice was given for the map,
8042685Sakolb 		 * check to make sure advice exists before bothering
8052685Sakolb 		 * with the for loop.
8062685Sakolb 		 */
8072685Sakolb 		if (psaddr->adv != NO_ADVICE) {
8082685Sakolb 			for (i = MADV_NORMAL; i <= MADV_ACCESS_MANY; i++) {
8092685Sakolb 				if ((psaddr->adv & (1 << i)) &&
8102685Sakolb 				    (pr_madvise(Pr, (caddr_t)psaddr->addr,
8112685Sakolb 				    psaddr->length, i) < 0)) {
8122685Sakolb 					/*
8132685Sakolb 					 * madvise(3C) call failed trying to
8142685Sakolb 					 * apply advice output error and remove
8152685Sakolb 					 * from advice list
8162685Sakolb 					 */
8172685Sakolb 					(void) fprintf(stderr,
8182685Sakolb 					    gettext("Error applying "
8192685Sakolb 						"advice (%s) to memory range "
8202685Sakolb 						"[%lx, %lx):\n"),
8212685Sakolb 					    advicestr[i], (ulong_t)psaddr->addr,
8222685Sakolb 					    (ulong_t)psaddr->addr +
8232685Sakolb 					    psaddr->length);
8242685Sakolb 					perror("madvise");
8252685Sakolb 					/*
8262685Sakolb 					 * Clear this advice from the advice
8272685Sakolb 					 * mask. If no more advice is given
8282685Sakolb 					 * for this element, remove element
8292685Sakolb 					 * from list.
8302685Sakolb 					 */
8312685Sakolb 					psaddr->adv &= ~(1 << i);
8322685Sakolb 					if (psaddr->adv == 0) {
8332685Sakolb 						delete_addr(advicelist, psaddr);
8342685Sakolb 						break;
8352685Sakolb 					}
8362685Sakolb 				}
8372685Sakolb 			}
8382685Sakolb 		}
8392685Sakolb 		psaddr = next;
8402685Sakolb 	}
8412685Sakolb 	return (0);
8422685Sakolb }
8432685Sakolb 
8442685Sakolb /*
8452685Sakolb  * Set advice but keep mutual exclusive property of advice groupings
8462685Sakolb  */
8472685Sakolb static void
8482685Sakolb set_advice(int *combined_adv, int new_adv) {
8492685Sakolb 	/*
8502685Sakolb 	 * Since advice falls in 3 groups of mutually exclusive options,
8512685Sakolb 	 * clear previous value if new advice overwrites that group.
8522685Sakolb 	 */
8532685Sakolb 
8542685Sakolb 	/*
8552685Sakolb 	 * If this is the first advice to be applied, clear invalid value (-1)
8562685Sakolb 	 */
8572685Sakolb 	if (*combined_adv == -1)
8582685Sakolb 		*combined_adv = 0;
8592685Sakolb 
8602685Sakolb 	if (new_adv & GRP1_ADV)
8612685Sakolb 		*combined_adv &= ~GRP1_ADV;
8622685Sakolb 	else if (new_adv & GRP2_ADV)
8632685Sakolb 		*combined_adv &= ~GRP2_ADV;
8642685Sakolb 	else
8652685Sakolb 		*combined_adv &= ~GRP3_ADV;
8662685Sakolb 
8672685Sakolb 	*combined_adv |= new_adv;
8682685Sakolb }
8692685Sakolb 
8702685Sakolb /*
8712685Sakolb  * Create chopped list from merged list for use with verbose output
8722685Sakolb  */
8732685Sakolb static void
8742685Sakolb create_choplist(saddr_t **choppedlist, saddr_t *mergedlist)
8752685Sakolb {
8762685Sakolb 	saddr_t	*mlptr, *clptr;
8772685Sakolb 
8782685Sakolb 	for (mlptr = mergedlist; mlptr != NULL; mlptr = mlptr->next) {
8792685Sakolb 		clptr = malloc(sizeof (saddr_t));
8802685Sakolb 		clptr->addr = mlptr->addr;
8812685Sakolb 		clptr->length = 0;
8822685Sakolb 		/*
8832685Sakolb 		 * Initialize the adv to -1 as an indicator for invalid
8842685Sakolb 		 * elements in the chopped list (created from gaps between
8852685Sakolb 		 * memory maps).
8862685Sakolb 		 */
8872685Sakolb 		clptr->adv = -1;
8882685Sakolb 		clptr->next = NULL;
8892685Sakolb 		insert_addr(choppedlist, clptr, NODUPS);
8902685Sakolb 
8912685Sakolb 		clptr = malloc(sizeof (saddr_t));
8922685Sakolb 		clptr->addr = mlptr->addr + mlptr->length;
8932685Sakolb 		clptr->length = 0;
8942685Sakolb 		/*
8952685Sakolb 		 * Again, initialize to -1 as an indicatorfor invalid elements
8962685Sakolb 		 */
8972685Sakolb 		clptr->adv = -1;
8982685Sakolb 		clptr->next = NULL;
8992685Sakolb 		insert_addr(choppedlist, clptr, NODUPS);
9002685Sakolb 	}
9012685Sakolb 
9022685Sakolb 	for (clptr = *choppedlist; clptr != NULL; clptr = clptr->next) {
9032685Sakolb 		if (clptr->next)
9042685Sakolb 			clptr->length = clptr->next->addr - clptr->addr;
9052685Sakolb 		else {
9062685Sakolb 			/*
9072685Sakolb 			 * must be last element, now that we've calculated
9082685Sakolb 			 * all segment lengths, we can remove this node
9092685Sakolb 			 */
9102685Sakolb 			delete_addr(choppedlist, clptr);
9112685Sakolb 		}
9122685Sakolb 	}
9132685Sakolb 
9142685Sakolb 	for (mlptr = mergedlist; mlptr != NULL; mlptr = mlptr->next) {
9152685Sakolb 		for (clptr = *choppedlist; clptr != NULL; clptr = clptr->next) {
9162685Sakolb 			if (mlptr->addr <= clptr->addr &&
9172685Sakolb 			    mlptr->addr + mlptr->length >=
9182685Sakolb 			    clptr->addr + clptr->length)
9192685Sakolb 				/*
9202685Sakolb 				 * set_advice() will take care of conflicting
9212685Sakolb 				 * advice by taking only the last advice
9222685Sakolb 				 * applied for each of the 3 groups of advice.
9232685Sakolb 				 */
9242685Sakolb 				set_advice(&clptr->adv, mlptr->adv);
9252685Sakolb 			if (mlptr->addr + mlptr->length <
9262685Sakolb 			    clptr->addr)
9272685Sakolb 				break;
9282685Sakolb 		}
9292685Sakolb 	}
9302685Sakolb }
9312685Sakolb 
9322685Sakolb /*
9332685Sakolb  * Print advice in pmap style for verbose output
9342685Sakolb  */
9352685Sakolb static void
9362685Sakolb print_advice(saddr_t *advlist, mapnode_t *maplist)
9372685Sakolb {
9382685Sakolb 	saddr_t		*psaddr = advlist;
9392685Sakolb 	mapnode_t	*pmapnode;
9402685Sakolb 	char		*advice;
9412685Sakolb 
9422685Sakolb 	pmapnode = maplist;
9432685Sakolb 
9442685Sakolb 	while (psaddr) {
9452685Sakolb 		/*
9462685Sakolb 		 * Using indicator flag from create_choppedlist, we know
9472685Sakolb 		 * which entries in the chopped_list are gaps and should
9482685Sakolb 		 * not be printed.
9492685Sakolb 		 */
9502685Sakolb 		if (psaddr->adv == -1) {
9512685Sakolb 			psaddr = psaddr->next;
9522685Sakolb 			continue;
9532685Sakolb 		}
9542685Sakolb 
9552685Sakolb 		while (pmapnode && (pmapnode->pmp->pr_vaddr +
9562685Sakolb 		    pmapnode->pmp->pr_size <= psaddr->addr))
9572685Sakolb 			pmapnode = pmapnode->next;
9582685Sakolb 
9592685Sakolb 		advice = advtostr(psaddr->adv);
9602685Sakolb 
9612685Sakolb 		/*
9622685Sakolb 		 * Print segment mapping and advice if there is any, or just a
9632685Sakolb 		 * segment mapping.
9642685Sakolb 		 */
9652685Sakolb 		if (strlen(advice) > 0) {
9662685Sakolb 			(void) printf("%.*lX %*uK %6s %s\t%s\n",
9672685Sakolb 			    addr_width, (ulong_t)psaddr->addr, size_width - 1,
9682685Sakolb 			    (int)ROUNDUP_KB(psaddr->length),
9692685Sakolb 			    mflags(pmapnode->pmp->pr_mflags), pmapnode->label,
9702685Sakolb 			    advice);
9712685Sakolb 		} else {
9722685Sakolb 			(void) printf("%.*lX %*uK %6s %s\n",
9732685Sakolb 			    addr_width, (ulong_t)psaddr->addr, size_width - 1,
9742685Sakolb 			    (int)ROUNDUP_KB(psaddr->length),
9752685Sakolb 			    mflags(pmapnode->pmp->pr_mflags), pmapnode->label);
9762685Sakolb 		}
9772685Sakolb 		psaddr = psaddr->next;
9782685Sakolb 
9792685Sakolb 	}
9802685Sakolb }
9812685Sakolb 
9822685Sakolb /*
9832685Sakolb  * Call madvise(3c) in the context of the target process
9842685Sakolb  */
9852685Sakolb static int
9862685Sakolb pr_madvise(struct ps_prochandle *Pr, caddr_t addr, size_t len, int advice)
9872685Sakolb {
9882685Sakolb 	return (pr_memcntl(Pr, addr, len, MC_ADVISE,
9892685Sakolb 		    (caddr_t)(uintptr_t)advice, 0, 0));
9902685Sakolb }
9912685Sakolb 
9922685Sakolb static char *
9932685Sakolb mflags(uint_t arg)
9942685Sakolb {
9952685Sakolb 	static char code_buf[80];
9962685Sakolb 
9972685Sakolb 	/*
9982685Sakolb 	 * rwxsR
9992685Sakolb 	 *
10002685Sakolb 	 * r - segment is readable
10012685Sakolb 	 * w - segment is writable
10022685Sakolb 	 * x - segment is executable
10032685Sakolb 	 * s - segment is shared
10042685Sakolb 	 * R - segment is mapped MAP_NORESERVE
10052685Sakolb 	 *
10062685Sakolb 	 */
10072685Sakolb 	(void) snprintf(code_buf, sizeof (code_buf), "%c%c%c%c%c ",
10082685Sakolb 	    arg & MA_READ ? 'r' : '-',
10092685Sakolb 	    arg & MA_WRITE ? 'w' : '-',
10102685Sakolb 	    arg & MA_EXEC ? 'x' : '-',
10112685Sakolb 	    arg & MA_SHARED ? 's' : '-',
10122685Sakolb 	    arg & MA_NORESERVE ? 'R' : '-');
10132685Sakolb 
10142685Sakolb 	return (code_buf);
10152685Sakolb }
10162685Sakolb 
10172685Sakolb /*
10182685Sakolb  * Convert advice to a string containing a commented list of applicable advice
10192685Sakolb  */
10202685Sakolb static char *
10212685Sakolb advtostr(int adv)
10222685Sakolb {
10232685Sakolb 	static char buf[50];
10242685Sakolb 	int i;
10252685Sakolb 
10262685Sakolb 	*buf = '\0';
10272685Sakolb 
10282685Sakolb 	if (adv != NO_ADVICE) {
10292685Sakolb 		for (i = MADV_NORMAL; i <= MADV_ACCESS_MANY; i++) {
10302685Sakolb 			if (adv & (1 << i)) {
10312685Sakolb 				/*
10322685Sakolb 				 * check if it's the first advice entry
10332685Sakolb 				 */
10342685Sakolb 				if (*buf == '\0')
10352685Sakolb 					(void) snprintf(buf, sizeof (buf) - 1,
10362685Sakolb 					    "<= %s", advicestr[i]);
10372685Sakolb 				else
10382685Sakolb 					(void) snprintf(buf, sizeof (buf) - 1,
10392685Sakolb 					    "%s,%s", buf, advicestr[i]);
10402685Sakolb 			}
10412685Sakolb 		}
10422685Sakolb 	}
10432685Sakolb 
10442685Sakolb 	return (buf);
10452685Sakolb }
10462685Sakolb 
10472685Sakolb /*
10482685Sakolb  * Handler for catching signals from terminal
10492685Sakolb  */
10502685Sakolb /* ARGSUSED */
10512685Sakolb static void
10522685Sakolb intr(int sig)
10532685Sakolb {
10542685Sakolb 	interrupt++;
10552685Sakolb }
10562685Sakolb 
10572685Sakolb int
10582685Sakolb main(int argc, char **argv)
10592685Sakolb {
10602685Sakolb 	int Fflag = 0;
10612685Sakolb 	int rc = 0;
10622685Sakolb 	int opt, subopt;
10632685Sakolb 	int tmpadv;
10642685Sakolb 	char	*options, *value;
10652685Sakolb 	saddr_t	*psaddr;
10662685Sakolb 	mapnode_t *pmapnode, *tempmapnode;
10672685Sakolb 
10682685Sakolb 	(void) setlocale(LC_ALL, "");
10692685Sakolb 	(void) textdomain(TEXT_DOMAIN);
10702685Sakolb 
10712685Sakolb 	/*
10722685Sakolb 	 * Get name of program for error messages
10732685Sakolb 	 */
10742685Sakolb 	progname = basename(argv[0]);
10752685Sakolb 
10762685Sakolb 	/*
10772685Sakolb 	 * Not much to do when only name of program given
10782685Sakolb 	 */
10792685Sakolb 	if (argc == 1)
10802685Sakolb 		usage();
10812685Sakolb 
10822685Sakolb 	/*
10832685Sakolb 	 * Catch signals from terminal, so they can be handled asynchronously
10842685Sakolb 	 * when we're ready instead of when we're not (;-)
10852685Sakolb 	 */
10862685Sakolb 	if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
10872685Sakolb 		(void) sigset(SIGHUP, intr);
10882685Sakolb 	if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
10892685Sakolb 		(void) sigset(SIGINT, intr);
10902685Sakolb 	if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
10912685Sakolb 		(void) sigset(SIGQUIT, intr);
10922685Sakolb 	(void) sigset(SIGPIPE, intr);
10932685Sakolb 	(void) sigset(SIGTERM, intr);
10942685Sakolb 
10952685Sakolb 	/*
10962685Sakolb 	 * Parse options, record generic advice if any and create
10972685Sakolb 	 * rawadv_list from specific address advice.
10982685Sakolb 	 */
10992685Sakolb 
11002685Sakolb 	while ((opt = getopt(argc, argv, "Fo:v")) != EOF) {
11012685Sakolb 		switch (opt) {
11022685Sakolb 		case 'o':
11032685Sakolb 			options = optarg;
11042685Sakolb 			while (*options != '\0') {
11052685Sakolb 				subopt = getsubopt(&options, suboptstr,
11062685Sakolb 				    &value);
11072685Sakolb 				switch (subopt) {
11082685Sakolb 				case AT_PRIVM:
11092685Sakolb 				case AT_HEAP:
11102685Sakolb 				case AT_SHARED:
11112685Sakolb 				case AT_STACK:
11122685Sakolb 					at_map |= (1 << subopt);
11132685Sakolb 					tmpadv = get_advice(value);
11142685Sakolb 					set_advice(&generic_adv[subopt],
11152685Sakolb 					    tmpadv);
11162685Sakolb 					break;
11172685Sakolb 				default:
11182685Sakolb 					at_map |= (1 << AT_SEG);
11192685Sakolb 					psaddr = parse_suboptions(value);
11202685Sakolb 					if (psaddr == NULL) {
11212685Sakolb 						usage();
11222685Sakolb 					} else {
11232685Sakolb 						insert_addr(&rawadv_list,
11242685Sakolb 						    psaddr, YESDUPS);
11252685Sakolb 					}
11262685Sakolb 					break;
11272685Sakolb 				}
11282685Sakolb 			}
11292685Sakolb 			break;
11302685Sakolb 		case 'v':
11312685Sakolb 			opt_verbose = 1;
11322685Sakolb 			break;
11332685Sakolb 		case 'F':		/* force grabbing (no O_EXCL) */
11342685Sakolb 			Fflag = PGRAB_FORCE;
11352685Sakolb 			break;
11362685Sakolb 		default:
11372685Sakolb 			usage();
11382685Sakolb 			break;
11392685Sakolb 		}
11402685Sakolb 	}
11412685Sakolb 
11422685Sakolb 	argc -= optind;
11432685Sakolb 	argv += optind;
11442685Sakolb 
11452685Sakolb 	if (argc <= 0) {
11462685Sakolb 		usage();
11472685Sakolb 	}
11482685Sakolb 
1149*7032Sakolb 	(void) proc_initstdio();
1150*7032Sakolb 
11512685Sakolb 	/*
11522685Sakolb 	 * Iterate through all pid arguments, create new merged_list, maplist,
11532685Sakolb 	 * (and chopped_list if using verbose output) based on each process'
11542685Sakolb 	 * memory map.
11552685Sakolb 	 */
11562685Sakolb 
11572685Sakolb 	while (!interrupt && argc-- > 0) {
11582685Sakolb 		char *arg;
11592685Sakolb 		int gcode;
11602685Sakolb 		psinfo_t psinfo;
11612685Sakolb 
1162*7032Sakolb 		(void) proc_flushstdio();
1163*7032Sakolb 
11642685Sakolb 		if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_PIDS,
11652685Sakolb 		    PGRAB_RETAIN | Fflag, &gcode)) == NULL) {
11662685Sakolb 			(void) fprintf(stderr,
11672685Sakolb 			    gettext("%s: cannot examine %s: %s\n"),
11682685Sakolb 			    progname, arg, Pgrab_error(gcode));
11692685Sakolb 			rc++;
11702685Sakolb 			continue;
11712685Sakolb 		}
11722685Sakolb 
11732685Sakolb 
11742685Sakolb 		addr_width =
11752685Sakolb 		    (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 16 : 8;
11762685Sakolb 		size_width =
11772685Sakolb 		    (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 11 : 8;
11782685Sakolb 		(void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t));
11792685Sakolb 
11802685Sakolb 		if (opt_verbose) {
11812685Sakolb 			proc_unctrl_psinfo(&psinfo);
11822685Sakolb 			(void) printf("%d:\t%.70s\n",
11832685Sakolb 			    (int)psinfo.pr_pid, psinfo.pr_psargs);
11842685Sakolb 		}
11852685Sakolb 
11862685Sakolb 		/*
11872685Sakolb 		 * Get mappings for a process unless it is a system process.
11882685Sakolb 		 */
11892685Sakolb 		if (!(Pstatus(Pr)->pr_flags & PR_ISSYS)) {
11902685Sakolb 			nstacks = psinfo.pr_nlwp * 2;
11912685Sakolb 			stacks = calloc(nstacks, sizeof (stacks[0]));
11922685Sakolb 			if (stacks != NULL) {
11932685Sakolb 				int n = 0;
11942685Sakolb 				(void) Plwp_iter(Pr, getstack, &n);
11952685Sakolb 				qsort(stacks, nstacks, sizeof (stacks[0]),
11962685Sakolb 				    cmpstacks);
11972685Sakolb 			}
11982685Sakolb 
11992685Sakolb 			if (Pgetauxval(Pr, AT_BASE) != -1L &&
12002685Sakolb 			    Prd_agent(Pr) == NULL) {
12012685Sakolb 				(void) fprintf(stderr,
12022685Sakolb 				    gettext("%s: warning: "
12032685Sakolb 					"librtld_db failed to initialize; "
12042685Sakolb 					"shared library information will not "
12052685Sakolb 					"be available\n"),
12062685Sakolb 				    progname);
12072685Sakolb 			}
12082685Sakolb 
12092685Sakolb 			/*
12102685Sakolb 			 * Create linked list of mappings for current process
12112685Sakolb 			 * In addition, add generic advice and raw advice
12122685Sakolb 			 * entries to merged_list.
12132685Sakolb 			 * e.g. if rawadv_list contains:
12142685Sakolb 			 *   [0x38000,0x3a000) = adv1
12152685Sakolb 			 *   [0x3a000,0x3c000) = adv2
12162685Sakolb 			 * and there is generic advice:
12172685Sakolb 			 *   heap = adv3
12182685Sakolb 			 * where heap corresponds to 0x38000, then merged_list
12192685Sakolb 			 * will contain:
12202685Sakolb 			 *   ... (include all other mappings from process)
12212685Sakolb 			 *   [0x38000,0x3c000) = adv3
12222685Sakolb 			 *   [0x38000,0x3a000) = adv1
12232685Sakolb 			 *   [0x3a000,0x3c000) = adv2
12242685Sakolb 			 *   ... (include all other mappings from process)
12252685Sakolb 			 */
12262685Sakolb 			assert(merged_list == NULL);
12272685Sakolb 			maplist_head = maplist_tail = NULL;
12282685Sakolb 			rc += Pmapping_iter(Pr, (proc_map_f *)create_maplist,
12292685Sakolb 			    NULL);
12302685Sakolb 
12312685Sakolb 			/*
12322685Sakolb 			 * Apply advice by iterating through merged list
12332685Sakolb 			 */
12342685Sakolb 			(void) apply_advice(&merged_list);
12352685Sakolb 
12362685Sakolb 			if (opt_verbose) {
12372685Sakolb 				assert(chopped_list == NULL);
12382685Sakolb 				/*
12392685Sakolb 				 * Create chopped_list from merged_list
12402685Sakolb 				 */
12412685Sakolb 				create_choplist(&chopped_list, merged_list);
12422685Sakolb 
12432685Sakolb 				/*
12442685Sakolb 				 * Iterate through maplist and output as
12452685Sakolb 				 * given by chopped_list
12462685Sakolb 				 */
12472685Sakolb 				print_advice(chopped_list, maplist_head);
12482685Sakolb 				delete_list(&chopped_list);
12492685Sakolb 			}
12502685Sakolb 
12512685Sakolb 			delete_list(&merged_list);
12522685Sakolb 
12532685Sakolb 			/*
12542685Sakolb 			 * Clear maplist
12552685Sakolb 			 */
12562685Sakolb 			pmapnode = maplist_head;
12572685Sakolb 			while (pmapnode) {
12582685Sakolb 				tempmapnode = pmapnode;
12592685Sakolb 				pmapnode = pmapnode->next;
12602685Sakolb 				free(tempmapnode);
12612685Sakolb 			}
12622685Sakolb 
12632685Sakolb 			if (stacks != NULL) {
12642685Sakolb 				free(stacks);
12652685Sakolb 				stacks = NULL;
12662685Sakolb 			}
12672685Sakolb 		}
12682685Sakolb 
12692685Sakolb 		Prelease(Pr, 0);
12702685Sakolb 	}
12712685Sakolb 
1272*7032Sakolb 	(void) proc_finistdio();
1273*7032Sakolb 
12742685Sakolb 	return (rc);
12752685Sakolb }
1276