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