xref: /onnv-gate/usr/src/cmd/ptools/pmadvise/pmadvise.c (revision 2685:80399c17fa47)
1*2685Sakolb /*
2*2685Sakolb  * CDDL HEADER START
3*2685Sakolb  *
4*2685Sakolb  * The contents of this file are subject to the terms of the
5*2685Sakolb  * Common Development and Distribution License (the "License").
6*2685Sakolb  * You may not use this file except in compliance with the License.
7*2685Sakolb  *
8*2685Sakolb  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*2685Sakolb  * or http://www.opensolaris.org/os/licensing.
10*2685Sakolb  * See the License for the specific language governing permissions
11*2685Sakolb  * and limitations under the License.
12*2685Sakolb  *
13*2685Sakolb  * When distributing Covered Code, include this CDDL HEADER in each
14*2685Sakolb  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*2685Sakolb  * If applicable, add the following below this CDDL HEADER, with the
16*2685Sakolb  * fields enclosed by brackets "[]" replaced with your own identifying
17*2685Sakolb  * information: Portions Copyright [yyyy] [name of copyright owner]
18*2685Sakolb  *
19*2685Sakolb  * CDDL HEADER END
20*2685Sakolb  */
21*2685Sakolb 
22*2685Sakolb /*
23*2685Sakolb  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*2685Sakolb  * Use is subject to license terms.
25*2685Sakolb  */
26*2685Sakolb 
27*2685Sakolb #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*2685Sakolb 
29*2685Sakolb /*
30*2685Sakolb  * pmadvise
31*2685Sakolb  *
32*2685Sakolb  * ptool wrapper for madvise(3C) to apply memory advice to running processes
33*2685Sakolb  *
34*2685Sakolb  * usage:	pmadvise -o option[,option] [-v] [-F] pid ...
35*2685Sakolb  *  (Give "advice" about a process's memory)
36*2685Sakolb  *  -o option[,option]: options are
37*2685Sakolb  *      private=<advice>
38*2685Sakolb  *      shared=<advice>
39*2685Sakolb  *      heap=<advice>
40*2685Sakolb  *      stack=<advice>
41*2685Sakolb  *      <segaddr>[:<length>]=<advice>
42*2685Sakolb  *     valid <advice> is one of:
43*2685Sakolb  *      normal, random, sequential, willneed, dontneed,
44*2685Sakolb  *      free, access_lwp, access_many, access_default
45*2685Sakolb  *  -v: verbose output
46*2685Sakolb  *  -F: force grabbing of the target process(es)
47*2685Sakolb  *  pid: process id list
48*2685Sakolb  *
49*2685Sakolb  *
50*2685Sakolb  * Advice passed to this tool are organized into various lists described here:
51*2685Sakolb  *  rawadv_list: includes all specific advice from command line (specific
52*2685Sakolb  *               advice being those given to a particular address range rather
53*2685Sakolb  *               than a type like "heap" or "stack".  In contrast, these
54*2685Sakolb  *               types are referred to as generic advice). Duplicates allowed.
55*2685Sakolb  *               List ordered by addr, then by size (largest size first).
56*2685Sakolb  *               Created once per run.
57*2685Sakolb  *  merged_list: includes all specific advice from the rawadv_list as well as
58*2685Sakolb  *               all generic advice.  This must be recreated for each process
59*2685Sakolb  *               as the generic advice will apply to different regions for
60*2685Sakolb  *               different processes. Duplicates allowed. List ordered by addr,
61*2685Sakolb  *               then by size (largest size first). Created once per pid.
62*2685Sakolb  *  chopped_list: used for verbose output only. This list parses the merged
63*2685Sakolb  *                list such that it eliminates any overlap and combines the
64*2685Sakolb  *                advice. Easiest to think of this visually: if you take all
65*2685Sakolb  *                the advice in the merged list and lay them down on a memory
66*2685Sakolb  *                range of the entire process (laying on top of each other when
67*2685Sakolb  *                necessary), then flatten them into one layer, combining advice
68*2685Sakolb  *                in the case of overlap, you get the chopped_list of advice.
69*2685Sakolb  *                Duplicate entries not allowed (since there is no overlap by
70*2685Sakolb  *                definition in this list).  List ordered by addr. Created once
71*2685Sakolb  *                per pid.
72*2685Sakolb  *
73*2685Sakolb  *                Example:
74*2685Sakolb  *                   merged_list:   |-----adv1----|---------adv3---------|
75*2685Sakolb  *                                       |--adv2--|--adv4--|-----adv5----|
76*2685Sakolb  *                                                  ||
77*2685Sakolb  *                                                  \/
78*2685Sakolb  *                   chopped_list:  |adv1|-adv1,2-|-adv3,4-|----adv3,5---|
79*2685Sakolb  *
80*2685Sakolb  *  maplist: list of memory mappings for a particular process. Used to create
81*2685Sakolb  *           generic advice entries for merged_list and for pmap like verbose
82*2685Sakolb  *           output. Created once per pid.
83*2685Sakolb  *
84*2685Sakolb  * Multiple lists are necessary because the actual advice applied given a set
85*2685Sakolb  * of generic and specific advice changes from process to process, so for each
86*2685Sakolb  * pid pmadvise is passed, it must create a new merged_list from which to apply
87*2685Sakolb  * advice (and a new chopped_list if verbose output is requested).
88*2685Sakolb  *
89*2685Sakolb  * Pseudo-code:
90*2685Sakolb  * I.	Input advice from command line
91*2685Sakolb  * II.	Create [raw advice list] of specific advice
92*2685Sakolb  * III.	Iterate through PIDs:
93*2685Sakolb  *	A.	Create [map list]
94*2685Sakolb  *	B.	Merge generic advice and [raw advice list] into [merged list]
95*2685Sakolb  *	C.	Apply advice from [merged list]; upon error:
96*2685Sakolb  *		i.	output madvise error message
97*2685Sakolb  *		ii.	remove element from [merged list]
98*2685Sakolb  *	D.	If verbose output:
99*2685Sakolb  *		i.	Create [chopped list] from [merged list]
100*2685Sakolb  *		ii.	Iterate through [map list]:
101*2685Sakolb  *			a.	output advice as given by [merged list]
102*2685Sakolb  *		iii.	Delete [chopped list]
103*2685Sakolb  *	E.	Delete [merged list]
104*2685Sakolb  *	F.	Delete [map list]
105*2685Sakolb  */
106*2685Sakolb 
107*2685Sakolb #include <stdio.h>
108*2685Sakolb #include <stdlib.h>
109*2685Sakolb #include <unistd.h>
110*2685Sakolb #include <ctype.h>
111*2685Sakolb #include <fcntl.h>
112*2685Sakolb #include <string.h>
113*2685Sakolb #include <dirent.h>
114*2685Sakolb #include <limits.h>
115*2685Sakolb #include <link.h>
116*2685Sakolb #include <libelf.h>
117*2685Sakolb #include <locale.h>
118*2685Sakolb #include <sys/types.h>
119*2685Sakolb #include <sys/mman.h>
120*2685Sakolb #include <sys/stat.h>
121*2685Sakolb #include <sys/mkdev.h>
122*2685Sakolb #include <assert.h>
123*2685Sakolb #include <libproc.h>
124*2685Sakolb #include <libgen.h>
125*2685Sakolb #include <signal.h>
126*2685Sakolb 
127*2685Sakolb #ifndef	TEXT_DOMAIN			/* should be defined by cc -D */
128*2685Sakolb #define	TEXT_DOMAIN	"SYS_TEST"	/* use this only if it wasn't */
129*2685Sakolb #endif
130*2685Sakolb 
131*2685Sakolb #define	KILOBYTE	1024
132*2685Sakolb 
133*2685Sakolb /*
134*2685Sakolb  * Round up the value to the nearest kilobyte
135*2685Sakolb  */
136*2685Sakolb #define	ROUNDUP_KB(x)	(((x) + (KILOBYTE - 1)) / KILOBYTE)
137*2685Sakolb 
138*2685Sakolb #define	NO_ADVICE		0
139*2685Sakolb 
140*2685Sakolb /*
141*2685Sakolb  * The following definitions are used as the third argument in insert_addr()
142*2685Sakolb  *   NODUPS = no duplicates are not allowed, thus if the addr being inserted
143*2685Sakolb  *   already exists in the list, return without inserting again.
144*2685Sakolb  *
145*2685Sakolb  *   YESDUPS = yes duplicates are allowed, thus always insert the addr
146*2685Sakolb  *   regardless of whether it already exists in the list or not.
147*2685Sakolb  */
148*2685Sakolb #define	NODUPS	1
149*2685Sakolb #define	YESDUPS	0
150*2685Sakolb 
151*2685Sakolb /*
152*2685Sakolb  * Advice that can be passed to madvise fit into three groups that each
153*2685Sakolb  * contain 3 mutually exclusive options.  These groups are defined below:
154*2685Sakolb  *   Group 1: normal, random, sequential
155*2685Sakolb  *   Group 2: willneed, dontneed, free
156*2685Sakolb  *   Group 3: default, accesslwp, accessmany
157*2685Sakolb  * Thus, advice that includes (at most) one from each group is valid.
158*2685Sakolb  *
159*2685Sakolb  * The following #define's are used as masks to determine which group(s) a
160*2685Sakolb  * particular advice fall under.
161*2685Sakolb  */
162*2685Sakolb 
163*2685Sakolb #define	GRP1_ADV	(1 << MADV_NORMAL | 1 << MADV_RANDOM | \
164*2685Sakolb 			1 << MADV_SEQUENTIAL)
165*2685Sakolb #define	GRP2_ADV	(1 << MADV_WILLNEED | 1 << MADV_DONTNEED | \
166*2685Sakolb 			1 << MADV_FREE)
167*2685Sakolb #define	GRP3_ADV	(1 << MADV_ACCESS_DEFAULT | 1 << MADV_ACCESS_LWP | \
168*2685Sakolb 			1 << MADV_ACCESS_MANY)
169*2685Sakolb 
170*2685Sakolb static	int	create_maplist(void *, const prmap_t *, const char *);
171*2685Sakolb static	int	pr_madvise(struct ps_prochandle *, caddr_t, size_t, int);
172*2685Sakolb 
173*2685Sakolb static	char	*mflags(uint_t);
174*2685Sakolb static	char	*advtostr(int);
175*2685Sakolb 
176*2685Sakolb static	int	addr_width, size_width;
177*2685Sakolb static	char	*progname;
178*2685Sakolb static	struct ps_prochandle *Pr;
179*2685Sakolb 
180*2685Sakolb typedef struct lwpstack {
181*2685Sakolb 	lwpid_t	lwps_lwpid;
182*2685Sakolb 	stack_t	lwps_stack;
183*2685Sakolb } lwpstack_t;
184*2685Sakolb 
185*2685Sakolb static	lwpstack_t *stacks;
186*2685Sakolb static	uint_t	nstacks;
187*2685Sakolb 
188*2685Sakolb /*
189*2685Sakolb  * Used to set the advice type var (at_map) when parsing the arguments to
190*2685Sakolb  * pmadvise.  Later, when creating the map list, at_map is used as a mask
191*2685Sakolb  * to determine if any generic advice applies to each memory mapping.
192*2685Sakolb  */
193*2685Sakolb enum	atype_enum {
194*2685Sakolb 	AT_PRIVM,
195*2685Sakolb 	AT_SHARED,
196*2685Sakolb 	AT_HEAP,
197*2685Sakolb 	AT_STACK,
198*2685Sakolb 	AT_SEG,
199*2685Sakolb 	AT_NTYPES
200*2685Sakolb };
201*2685Sakolb 
202*2685Sakolb static char	*suboptstr[] = {
203*2685Sakolb 	"private",
204*2685Sakolb 	"shared",
205*2685Sakolb 	"heap",
206*2685Sakolb 	"stack",
207*2685Sakolb 	NULL
208*2685Sakolb };
209*2685Sakolb 
210*2685Sakolb 
211*2685Sakolb int	generic_adv[] = {NO_ADVICE, NO_ADVICE, NO_ADVICE, NO_ADVICE};
212*2685Sakolb int	at_map = 0;
213*2685Sakolb 
214*2685Sakolb typedef struct saddr_struct {
215*2685Sakolb 	uintptr_t	addr;
216*2685Sakolb 	size_t		length;
217*2685Sakolb 	int		adv;
218*2685Sakolb 	struct saddr_struct	*next;
219*2685Sakolb } saddr_t;
220*2685Sakolb static int	apply_advice(saddr_t **);
221*2685Sakolb static void	set_advice(int *, int);
222*2685Sakolb static void	create_choplist(saddr_t **, saddr_t *);
223*2685Sakolb 
224*2685Sakolb /*
225*2685Sakolb  * The segment address advice from the command line
226*2685Sakolb  */
227*2685Sakolb saddr_t	*rawadv_list = NULL;
228*2685Sakolb /*
229*2685Sakolb  * The rawadv_list + list entries for the generic advice (if any).
230*2685Sakolb  * This must be recreated for each PID as the memory maps might be different.
231*2685Sakolb  */
232*2685Sakolb saddr_t *merged_list = NULL;
233*2685Sakolb /*
234*2685Sakolb  * The merged_list cut up so as to remove all overlap
235*2685Sakolb  * e.g. if merged_list contained two entries:
236*2685Sakolb  *
237*2685Sakolb  * [0x38000:0x3e000) = adv1
238*2685Sakolb  * [0x3a000:0x3c000) = adv2
239*2685Sakolb  *
240*2685Sakolb  * the chopped list will contain three entries:
241*2685Sakolb  *
242*2685Sakolb  * [0x38000:0x3a000) = adv1
243*2685Sakolb  * [0x3a000:0x3c000) = adv1,adv2
244*2685Sakolb  * [0x3c000:0x3e000) = adv1
245*2685Sakolb  *
246*2685Sakolb  */
247*2685Sakolb saddr_t *chopped_list = NULL;
248*2685Sakolb 
249*2685Sakolb typedef struct mapnode_struct {
250*2685Sakolb 	prmap_t			*pmp;
251*2685Sakolb 	char			label[PATH_MAX];
252*2685Sakolb 	int			mtypes;
253*2685Sakolb 	struct mapnode_struct	*next;
254*2685Sakolb } mapnode_t;
255*2685Sakolb 
256*2685Sakolb mapnode_t *maplist_head = NULL;
257*2685Sakolb mapnode_t *maplist_tail = NULL;
258*2685Sakolb static void	print_advice(saddr_t *, mapnode_t *);
259*2685Sakolb 
260*2685Sakolb int	opt_verbose;
261*2685Sakolb 
262*2685Sakolb static char	*advicestr[] = {
263*2685Sakolb 	"normal",
264*2685Sakolb 	"random",
265*2685Sakolb 	"sequential",
266*2685Sakolb 	"willneed",
267*2685Sakolb 	"dontneed",
268*2685Sakolb 	"free",
269*2685Sakolb 	"access_default",
270*2685Sakolb 	"access_lwp",
271*2685Sakolb 	"access_many"
272*2685Sakolb };
273*2685Sakolb 
274*2685Sakolb /*
275*2685Sakolb  * How many signals caught from terminal
276*2685Sakolb  * We bail out as soon as possible when interrupt is set
277*2685Sakolb  */
278*2685Sakolb static int	interrupt = 0;
279*2685Sakolb 
280*2685Sakolb /*
281*2685Sakolb  * Interrupt handler
282*2685Sakolb  */
283*2685Sakolb static void	intr(int);
284*2685Sakolb 
285*2685Sakolb /*
286*2685Sakolb  * Iterative function passed to Plwp_iter to
287*2685Sakolb  * get alt and main stacks for given lwp.
288*2685Sakolb  */
289*2685Sakolb static int
290*2685Sakolb getstack(void *data, const lwpstatus_t *lsp)
291*2685Sakolb {
292*2685Sakolb 	int *np = (int *)data;
293*2685Sakolb 
294*2685Sakolb 	if (Plwp_alt_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) {
295*2685Sakolb 		stacks[*np].lwps_stack.ss_flags |= SS_ONSTACK;
296*2685Sakolb 		stacks[*np].lwps_lwpid = lsp->pr_lwpid;
297*2685Sakolb 		(*np)++;
298*2685Sakolb 	}
299*2685Sakolb 
300*2685Sakolb 	if (Plwp_main_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) {
301*2685Sakolb 		stacks[*np].lwps_lwpid = lsp->pr_lwpid;
302*2685Sakolb 		(*np)++;
303*2685Sakolb 	}
304*2685Sakolb 
305*2685Sakolb 	return (0);
306*2685Sakolb }
307*2685Sakolb 
308*2685Sakolb /*
309*2685Sakolb  * We compare the high memory addresses since stacks are faulted in from
310*2685Sakolb  * high memory addresses to low memory addresses, and our prmap_t
311*2685Sakolb  * structures identify only the range of addresses that have been faulted
312*2685Sakolb  * in so far.
313*2685Sakolb  */
314*2685Sakolb static int
315*2685Sakolb cmpstacks(const void *ap, const void *bp)
316*2685Sakolb {
317*2685Sakolb 	const lwpstack_t *as = ap;
318*2685Sakolb 	const lwpstack_t *bs = bp;
319*2685Sakolb 	uintptr_t a = (uintptr_t)as->lwps_stack.ss_sp + as->lwps_stack.ss_size;
320*2685Sakolb 	uintptr_t b = (uintptr_t)bs->lwps_stack.ss_sp + bs->lwps_stack.ss_size;
321*2685Sakolb 
322*2685Sakolb 	if (a < b)
323*2685Sakolb 		return (1);
324*2685Sakolb 	if (a > b)
325*2685Sakolb 		return (-1);
326*2685Sakolb 	return (0);
327*2685Sakolb }
328*2685Sakolb 
329*2685Sakolb /*
330*2685Sakolb  * Prints usage and exits
331*2685Sakolb  */
332*2685Sakolb static void
333*2685Sakolb usage()
334*2685Sakolb {
335*2685Sakolb 	(void) fprintf(stderr,
336*2685Sakolb 	    gettext("usage:\t%s -o option[,option] [-v] [-F] pid ...\n"),
337*2685Sakolb 	    progname);
338*2685Sakolb 	(void) fprintf(stderr,
339*2685Sakolb 	    gettext("    (Give \"advice\" about a process's memory)\n"
340*2685Sakolb 		"    -o option[,option]: options are\n"
341*2685Sakolb 		"        private=<advice>\n"
342*2685Sakolb 		"        shared=<advice>\n"
343*2685Sakolb 		"        heap=<advice>\n"
344*2685Sakolb 		"        stack=<advice>\n"
345*2685Sakolb 		"        <segaddr>[:<length>]=<advice>\n"
346*2685Sakolb 		"       valid <advice> is one of:\n"
347*2685Sakolb 		"        normal, random, sequential, willneed, dontneed,\n"
348*2685Sakolb 		"        free, access_lwp, access_many, access_default\n"
349*2685Sakolb 		"    -v: verbose output\n"
350*2685Sakolb 		"    -F: force grabbing of the target process(es)\n"
351*2685Sakolb 		"    pid: process id list\n"));
352*2685Sakolb 	exit(2);
353*2685Sakolb }
354*2685Sakolb 
355*2685Sakolb /*
356*2685Sakolb  * Function to parse advice from options string
357*2685Sakolb  */
358*2685Sakolb static int
359*2685Sakolb get_advice(char *optarg)
360*2685Sakolb {
361*2685Sakolb 	/*
362*2685Sakolb 	 * Determine which advice is given, we use shifted values as
363*2685Sakolb 	 * multiple pieces of advice may apply for a particular region.
364*2685Sakolb 	 * (See comment above regarding GRP[1,2,3]_ADV definitions for
365*2685Sakolb 	 * breakdown of advice groups).
366*2685Sakolb 	 */
367*2685Sakolb 	if (strcmp(optarg, "access_default") == 0)
368*2685Sakolb 		return (1 << MADV_ACCESS_DEFAULT);
369*2685Sakolb 	else if (strcmp(optarg, "access_many") == 0)
370*2685Sakolb 		return (1 << MADV_ACCESS_MANY);
371*2685Sakolb 	else if (strcmp(optarg, "access_lwp") == 0)
372*2685Sakolb 		return (1 << MADV_ACCESS_LWP);
373*2685Sakolb 	else if (strcmp(optarg, "sequential") == 0)
374*2685Sakolb 		return (1 << MADV_SEQUENTIAL);
375*2685Sakolb 	else if (strcmp(optarg, "willneed") == 0)
376*2685Sakolb 		return (1 << MADV_WILLNEED);
377*2685Sakolb 	else if (strcmp(optarg, "dontneed") == 0)
378*2685Sakolb 		return (1 << MADV_DONTNEED);
379*2685Sakolb 	else if (strcmp(optarg, "random") == 0)
380*2685Sakolb 		return (1 << MADV_RANDOM);
381*2685Sakolb 	else if (strcmp(optarg, "normal") == 0)
382*2685Sakolb 		return (1 << MADV_NORMAL);
383*2685Sakolb 	else if (strcmp(optarg, "free") == 0)
384*2685Sakolb 		return (1 << MADV_FREE);
385*2685Sakolb 	else {
386*2685Sakolb 		(void) fprintf(stderr, gettext("%s: invalid advice: %s\n"),
387*2685Sakolb 		    progname, optarg);
388*2685Sakolb 		usage();
389*2685Sakolb 		return (-1);
390*2685Sakolb 	}
391*2685Sakolb }
392*2685Sakolb 
393*2685Sakolb /*
394*2685Sakolb  * Function to convert character size indicators into actual size
395*2685Sakolb  * (i.e., 123M => sz = 123 * 1024 * 1024)
396*2685Sakolb  */
397*2685Sakolb static size_t
398*2685Sakolb atosz(char *optarg, char **endptr)
399*2685Sakolb {
400*2685Sakolb 	size_t	sz = 0;
401*2685Sakolb 
402*2685Sakolb 	if (optarg == NULL || optarg[0] == '\0')
403*2685Sakolb 		return (0);
404*2685Sakolb 
405*2685Sakolb 	sz = strtoll(optarg, endptr, 0);
406*2685Sakolb 
407*2685Sakolb 	switch (**endptr) {
408*2685Sakolb 	case 'E':
409*2685Sakolb 	case 'e':
410*2685Sakolb 		sz *= KILOBYTE;
411*2685Sakolb 		/* FALLTHRU */
412*2685Sakolb 	case 'P':
413*2685Sakolb 	case 'p':
414*2685Sakolb 		sz *= KILOBYTE;
415*2685Sakolb 		/* FALLTHRU */
416*2685Sakolb 	case 'T':
417*2685Sakolb 	case 't':
418*2685Sakolb 		sz *= KILOBYTE;
419*2685Sakolb 		/* FALLTHRU */
420*2685Sakolb 	case 'G':
421*2685Sakolb 	case 'g':
422*2685Sakolb 		sz *= KILOBYTE;
423*2685Sakolb 		/* FALLTHRU */
424*2685Sakolb 	case 'M':
425*2685Sakolb 	case 'm':
426*2685Sakolb 		sz *= KILOBYTE;
427*2685Sakolb 		/* FALLTHRU */
428*2685Sakolb 	case 'K':
429*2685Sakolb 	case 'k':
430*2685Sakolb 		sz *= KILOBYTE;
431*2685Sakolb 		/* FALLTHRU */
432*2685Sakolb 	case 'B':
433*2685Sakolb 	case 'b':
434*2685Sakolb 		(*endptr)++;
435*2685Sakolb 		/* FALLTHRU */
436*2685Sakolb 	default:
437*2685Sakolb 		break;
438*2685Sakolb 	}
439*2685Sakolb 	return (sz);
440*2685Sakolb }
441*2685Sakolb 
442*2685Sakolb /*
443*2685Sakolb  * Inserts newaddr into list.  dups indicates whether we allow duplicate
444*2685Sakolb  * addr entries in the list (valid values are NODUPS and YESDUPS).
445*2685Sakolb  */
446*2685Sakolb static void
447*2685Sakolb insert_addr(saddr_t **list, saddr_t *newaddr, int dups)
448*2685Sakolb {
449*2685Sakolb 	saddr_t *prev = *list;
450*2685Sakolb 	saddr_t *psaddr;
451*2685Sakolb 
452*2685Sakolb 	if (*list == NULL) {
453*2685Sakolb 		newaddr->next = *list;
454*2685Sakolb 		*list = newaddr;
455*2685Sakolb 		return;
456*2685Sakolb 	}
457*2685Sakolb 
458*2685Sakolb 	for (psaddr = (*list)->next; psaddr != NULL; psaddr = psaddr->next) {
459*2685Sakolb 		if ((dups == NODUPS) && (psaddr->addr == newaddr->addr)) {
460*2685Sakolb 			free(newaddr);
461*2685Sakolb 			return;
462*2685Sakolb 		}
463*2685Sakolb 
464*2685Sakolb 		/*
465*2685Sakolb 		 * primary level of comparison is by address; smaller addr 1st
466*2685Sakolb 		 * secondary level of comparison is by length; bigger length 1st
467*2685Sakolb 		 */
468*2685Sakolb 		if ((psaddr->addr > newaddr->addr) ||
469*2685Sakolb 		    (psaddr->addr == newaddr->addr &&
470*2685Sakolb 		    psaddr->length < newaddr->length))
471*2685Sakolb 			break;
472*2685Sakolb 
473*2685Sakolb 		prev = psaddr;
474*2685Sakolb 	}
475*2685Sakolb 
476*2685Sakolb 	prev->next = newaddr;
477*2685Sakolb 	newaddr->next = psaddr;
478*2685Sakolb }
479*2685Sakolb 
480*2685Sakolb /*
481*2685Sakolb  * Deletes given element from list
482*2685Sakolb  */
483*2685Sakolb static void
484*2685Sakolb delete_addr(saddr_t **list, saddr_t *delme)
485*2685Sakolb {
486*2685Sakolb 	saddr_t	*prev = *list;
487*2685Sakolb 
488*2685Sakolb 	if (delme == *list) {
489*2685Sakolb 		*list = delme->next;
490*2685Sakolb 		free(delme);
491*2685Sakolb 		return;
492*2685Sakolb 	}
493*2685Sakolb 
494*2685Sakolb 	while (prev != NULL && prev->next != delme) {
495*2685Sakolb 		prev = prev->next;
496*2685Sakolb 	}
497*2685Sakolb 
498*2685Sakolb 	if (prev) {
499*2685Sakolb 		prev->next = delme->next;
500*2685Sakolb 		free(delme);
501*2685Sakolb 	}
502*2685Sakolb }
503*2685Sakolb 
504*2685Sakolb /*
505*2685Sakolb  * Delete entire list
506*2685Sakolb  */
507*2685Sakolb static void
508*2685Sakolb delete_list(saddr_t **list)
509*2685Sakolb {
510*2685Sakolb 	saddr_t *psaddr = *list;
511*2685Sakolb 
512*2685Sakolb 	while (psaddr != NULL) {
513*2685Sakolb 		saddr_t *temp = psaddr;
514*2685Sakolb 
515*2685Sakolb 		psaddr = psaddr->next;
516*2685Sakolb 		free(temp);
517*2685Sakolb 	}
518*2685Sakolb 	*list = NULL;
519*2685Sakolb }
520*2685Sakolb 
521*2685Sakolb static saddr_t *
522*2685Sakolb parse_suboptions(char *value)
523*2685Sakolb {
524*2685Sakolb 	char	*endptr;
525*2685Sakolb 	saddr_t *psaddr = malloc(sizeof (saddr_t));
526*2685Sakolb 
527*2685Sakolb 	/*
528*2685Sakolb 	 * This must (better) be a segment addr
529*2685Sakolb 	 */
530*2685Sakolb 	psaddr->addr =
531*2685Sakolb 	    strtoull(value, &endptr, 16);
532*2685Sakolb 
533*2685Sakolb 	/*
534*2685Sakolb 	 * Check to make sure strtoul worked correctly (a properly formatted
535*2685Sakolb 	 * string will terminate in a ':' (if size is given) or an '=' (if size
536*2685Sakolb 	 * is not specified). Also check to make sure a 0 addr wasn't returned
537*2685Sakolb 	 * indicating strtoll was unable to convert).
538*2685Sakolb 	 */
539*2685Sakolb 	if ((psaddr->addr == 0) || (*endptr != ':' && *endptr != '=')) {
540*2685Sakolb 		free(psaddr);
541*2685Sakolb 		(void) fprintf(stderr,
542*2685Sakolb 		    gettext("%s: invalid option %s\n"),
543*2685Sakolb 		    progname, value);
544*2685Sakolb 		usage();
545*2685Sakolb 	} else {
546*2685Sakolb 		/* init other fields */
547*2685Sakolb 		psaddr->length = 0;
548*2685Sakolb 		psaddr->adv = NO_ADVICE;
549*2685Sakolb 		psaddr->next = NULL;
550*2685Sakolb 
551*2685Sakolb 		/* skip past address */
552*2685Sakolb 		value = endptr;
553*2685Sakolb 
554*2685Sakolb 		/* check for length */
555*2685Sakolb 		if (*value == ':') {
556*2685Sakolb 			/* skip the ":" */
557*2685Sakolb 			value++;
558*2685Sakolb 			psaddr->length = atosz(value, &endptr);
559*2685Sakolb 		}
560*2685Sakolb 
561*2685Sakolb 		if (*endptr != '=') {
562*2685Sakolb 			(void) fprintf(stderr,
563*2685Sakolb 			    gettext("%s: invalid option %s\n"),
564*2685Sakolb 			    progname, value);
565*2685Sakolb 			/*
566*2685Sakolb 			 * if improperly formatted, free mem, print usage, and
567*2685Sakolb 			 * exit Note: usage ends with a call to exit()
568*2685Sakolb 			 */
569*2685Sakolb 			free(psaddr);
570*2685Sakolb 			usage();
571*2685Sakolb 		}
572*2685Sakolb 		/* skip the "=" */
573*2685Sakolb 		value = endptr + 1;
574*2685Sakolb 		at_map |= (1 << AT_SEG);
575*2685Sakolb 		psaddr->adv =
576*2685Sakolb 		    get_advice(value);
577*2685Sakolb 	}
578*2685Sakolb 
579*2685Sakolb 	return (psaddr);
580*2685Sakolb }
581*2685Sakolb 
582*2685Sakolb /*
583*2685Sakolb  * Create labels for non-anon, non-heap mappings
584*2685Sakolb  */
585*2685Sakolb static char *
586*2685Sakolb make_name(struct ps_prochandle *Pr, uintptr_t addr, const char *mapname,
587*2685Sakolb 	char *buf, size_t bufsz)
588*2685Sakolb {
589*2685Sakolb 	const pstatus_t *Psp = Pstatus(Pr);
590*2685Sakolb 	char fname[100];
591*2685Sakolb 	struct stat statb;
592*2685Sakolb 	int len;
593*2685Sakolb 
594*2685Sakolb 	if (strcmp(mapname, "a.out") == 0 &&
595*2685Sakolb 	    Pexecname(Pr, buf, bufsz) != NULL)
596*2685Sakolb 		return (buf);
597*2685Sakolb 
598*2685Sakolb 	if (Pobjname(Pr, addr, buf, bufsz) != NULL) {
599*2685Sakolb 		if ((len = resolvepath(buf, buf, bufsz)) > 0) {
600*2685Sakolb 			buf[len] = '\0';
601*2685Sakolb 			return (buf);
602*2685Sakolb 		}
603*2685Sakolb 	}
604*2685Sakolb 
605*2685Sakolb 	if (*mapname != '\0') {
606*2685Sakolb 		(void) snprintf(fname, sizeof (fname), "/proc/%d/object/%s",
607*2685Sakolb 			(int)Psp->pr_pid, mapname);
608*2685Sakolb 		if (stat(fname, &statb) == 0) {
609*2685Sakolb 			dev_t dev = statb.st_dev;
610*2685Sakolb 			ino_t ino = statb.st_ino;
611*2685Sakolb 			(void) snprintf(buf, bufsz, "dev:%lu,%lu ino:%lu",
612*2685Sakolb 				(ulong_t)major(dev), (ulong_t)minor(dev), ino);
613*2685Sakolb 			return (buf);
614*2685Sakolb 		}
615*2685Sakolb 	}
616*2685Sakolb 
617*2685Sakolb 	return (NULL);
618*2685Sakolb }
619*2685Sakolb 
620*2685Sakolb /*
621*2685Sakolb  * Create label for anon mappings
622*2685Sakolb  */
623*2685Sakolb static char *
624*2685Sakolb anon_name(char *name, const pstatus_t *Psp,
625*2685Sakolb     uintptr_t vaddr, size_t size, int mflags, int shmid, int *mtypes)
626*2685Sakolb {
627*2685Sakolb 	if (mflags & MA_ISM) {
628*2685Sakolb 		if (shmid == -1)
629*2685Sakolb 			(void) snprintf(name, PATH_MAX, "  [ %s shmid=null ]",
630*2685Sakolb 			    (mflags & MA_NORESERVE) ? "ism" : "dism");
631*2685Sakolb 		else
632*2685Sakolb 			(void) snprintf(name, PATH_MAX, "  [ %s shmid=0x%x ]",
633*2685Sakolb 			    (mflags & MA_NORESERVE) ? "ism" : "dism", shmid);
634*2685Sakolb 		*mtypes |= (1 << AT_SHARED);
635*2685Sakolb 	} else if (mflags & MA_SHM) {
636*2685Sakolb 		if (shmid == -1)
637*2685Sakolb 			(void) sprintf(name, "  [ shmid=null ]");
638*2685Sakolb 		else
639*2685Sakolb 			(void) sprintf(name, "  [ shmid=0x%x ]", shmid);
640*2685Sakolb 		*mtypes |= (1 << AT_SHARED);
641*2685Sakolb 
642*2685Sakolb 	} else if (vaddr + size > Psp->pr_stkbase &&
643*2685Sakolb 	    vaddr < Psp->pr_stkbase + Psp->pr_stksize) {
644*2685Sakolb 		(void) strcpy(name, "  [ stack ]");
645*2685Sakolb 		*mtypes |= (1 << AT_STACK);
646*2685Sakolb 
647*2685Sakolb 	} else if ((mflags & MA_ANON) &&
648*2685Sakolb 	    vaddr + size > Psp->pr_brkbase &&
649*2685Sakolb 	    vaddr < Psp->pr_brkbase + Psp->pr_brksize) {
650*2685Sakolb 		(void) strcpy(name, "  [ heap ]");
651*2685Sakolb 		*mtypes |= (1 << AT_HEAP);
652*2685Sakolb 
653*2685Sakolb 	} else {
654*2685Sakolb 		lwpstack_t key, *stk;
655*2685Sakolb 
656*2685Sakolb 		key.lwps_stack.ss_sp = (void *)vaddr;
657*2685Sakolb 		key.lwps_stack.ss_size = size;
658*2685Sakolb 		if (nstacks > 0 &&
659*2685Sakolb 		    (stk = bsearch(&key, stacks, nstacks, sizeof (stacks[0]),
660*2685Sakolb 		    cmpstacks)) != NULL) {
661*2685Sakolb 			(void) snprintf(name, PATH_MAX, "  [ %s tid=%d ]",
662*2685Sakolb 			    (stk->lwps_stack.ss_flags & SS_ONSTACK) ?
663*2685Sakolb 			    "altstack" : "stack",
664*2685Sakolb 			    stk->lwps_lwpid);
665*2685Sakolb 			*mtypes |= (1 << AT_STACK);
666*2685Sakolb 		} else {
667*2685Sakolb 			(void) strcpy(name, "  [ anon ]");
668*2685Sakolb 			*mtypes |= (1 << AT_PRIVM);
669*2685Sakolb 		}
670*2685Sakolb 	}
671*2685Sakolb 
672*2685Sakolb 	return (name);
673*2685Sakolb }
674*2685Sakolb 
675*2685Sakolb /*
676*2685Sakolb  * Create linked list of mappings for current process
677*2685Sakolb  * In addition, add generic advice and raw advice
678*2685Sakolb  * entries to merged_list.
679*2685Sakolb  */
680*2685Sakolb /* ARGSUSED */
681*2685Sakolb static int
682*2685Sakolb create_maplist(void *arg, const prmap_t *pmp, const char *object_name)
683*2685Sakolb {
684*2685Sakolb 	const 		pstatus_t *Psp = Pstatus(Pr);
685*2685Sakolb 	mapnode_t *newmap = malloc(sizeof (mapnode_t));
686*2685Sakolb 	saddr_t	*newaddr;
687*2685Sakolb 	saddr_t	*psaddr;
688*2685Sakolb 	char	*lname = NULL;
689*2685Sakolb 	int	i;
690*2685Sakolb 
691*2685Sakolb 	if (interrupt)
692*2685Sakolb 		return (0);
693*2685Sakolb 
694*2685Sakolb 	newmap->pmp = malloc(sizeof (prmap_t));
695*2685Sakolb 	newmap->label[0] = '\0';
696*2685Sakolb 	newmap->mtypes = 0;
697*2685Sakolb 	newmap->next = NULL;
698*2685Sakolb 	(void) memcpy(newmap->pmp, pmp, sizeof (prmap_t));
699*2685Sakolb 
700*2685Sakolb 	/*
701*2685Sakolb 	 * If the mapping is not anon or not part of the heap, make a name
702*2685Sakolb 	 * for it.  We don't want to report the heap as a.out's data.
703*2685Sakolb 	 */
704*2685Sakolb 	if (!(pmp->pr_mflags & MA_ANON) ||
705*2685Sakolb 	    (pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase ||
706*2685Sakolb 	    pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize)) {
707*2685Sakolb 		lname = make_name(Pr, pmp->pr_vaddr, pmp->pr_mapname,
708*2685Sakolb 		    newmap->label, sizeof (newmap->label));
709*2685Sakolb 		if (pmp->pr_mflags & MA_SHARED)
710*2685Sakolb 			newmap->mtypes |= 1 << AT_SHARED;
711*2685Sakolb 		else
712*2685Sakolb 			newmap->mtypes |= 1 << AT_PRIVM;
713*2685Sakolb 	}
714*2685Sakolb 
715*2685Sakolb 	if (lname == NULL && (pmp->pr_mflags & MA_ANON)) {
716*2685Sakolb 		lname = anon_name(newmap->label, Psp, pmp->pr_vaddr,
717*2685Sakolb 		    pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid,
718*2685Sakolb 		    &newmap->mtypes);
719*2685Sakolb 	}
720*2685Sakolb 
721*2685Sakolb 	/*
722*2685Sakolb 	 * Add raw advice that applies to this mapping to the merged_list
723*2685Sakolb 	 */
724*2685Sakolb 	psaddr = rawadv_list;
725*2685Sakolb 	/*
726*2685Sakolb 	 * Advance to point in rawadv_list that applies to this mapping
727*2685Sakolb 	 */
728*2685Sakolb 	while (psaddr && psaddr->addr < pmp->pr_vaddr)
729*2685Sakolb 		psaddr = psaddr->next;
730*2685Sakolb 	/*
731*2685Sakolb 	 * Copy over to merged_list, check to see if size needs to be filled in
732*2685Sakolb 	 */
733*2685Sakolb 	while (psaddr && psaddr->addr < (pmp->pr_vaddr + pmp->pr_size)) {
734*2685Sakolb 		newaddr = malloc(sizeof (saddr_t));
735*2685Sakolb 		(void) memcpy(newaddr, psaddr, sizeof (saddr_t));
736*2685Sakolb 		insert_addr(&merged_list, newaddr, YESDUPS);
737*2685Sakolb 		/*
738*2685Sakolb 		 * For raw advice that is given without size, try to default
739*2685Sakolb 		 * size to size of mapping (only allowed if raw adv addr is
740*2685Sakolb 		 * equal to beginning of mapping). Don't change the entry
741*2685Sakolb 		 * in rawadv_list, only in the merged_list as the mappings
742*2685Sakolb 		 * (and thus the default sizes) will be different for
743*2685Sakolb 		 * different processes.
744*2685Sakolb 		 */
745*2685Sakolb 		if ((pmp->pr_vaddr == psaddr->addr) && (psaddr->length == 0))
746*2685Sakolb 			newaddr->length = pmp->pr_size;
747*2685Sakolb 		psaddr = psaddr->next;
748*2685Sakolb 	}
749*2685Sakolb 
750*2685Sakolb 	/*
751*2685Sakolb 	 * Put mapping into merged list with no advice, then
752*2685Sakolb 	 * check to see if any generic advice applies.
753*2685Sakolb 	 */
754*2685Sakolb 	newaddr = malloc(sizeof (saddr_t));
755*2685Sakolb 	newaddr->addr = pmp->pr_vaddr;
756*2685Sakolb 	newaddr->length = pmp->pr_size;
757*2685Sakolb 	newaddr->adv = NO_ADVICE;
758*2685Sakolb 	insert_addr(&merged_list, newaddr, YESDUPS);
759*2685Sakolb 
760*2685Sakolb 	newmap->mtypes &= at_map;
761*2685Sakolb 	for (i = AT_STACK; i >= AT_PRIVM; i--) {
762*2685Sakolb 		if (newmap->mtypes & (1 << i)) {
763*2685Sakolb 			assert(generic_adv[i] != NO_ADVICE);
764*2685Sakolb 			newaddr->adv = generic_adv[i];
765*2685Sakolb 			break;
766*2685Sakolb 		}
767*2685Sakolb 	}
768*2685Sakolb 
769*2685Sakolb 	/*
770*2685Sakolb 	 * Add to linked list of mappings
771*2685Sakolb 	 */
772*2685Sakolb 	if (maplist_tail == NULL) {
773*2685Sakolb 		maplist_head = maplist_tail = newmap;
774*2685Sakolb 	} else {
775*2685Sakolb 		maplist_tail->next = newmap;
776*2685Sakolb 		maplist_tail = newmap;
777*2685Sakolb 	}
778*2685Sakolb 
779*2685Sakolb 
780*2685Sakolb 	return (0);
781*2685Sakolb }
782*2685Sakolb 
783*2685Sakolb /*
784*2685Sakolb  * Traverse advice list and apply all applicable advice to each region
785*2685Sakolb  */
786*2685Sakolb static int
787*2685Sakolb apply_advice(saddr_t **advicelist)
788*2685Sakolb {
789*2685Sakolb 	saddr_t	*psaddr = *advicelist;
790*2685Sakolb 	saddr_t	*next;
791*2685Sakolb 	int	i;
792*2685Sakolb 
793*2685Sakolb 
794*2685Sakolb 	while (!interrupt && psaddr != NULL) {
795*2685Sakolb 		/*
796*2685Sakolb 		 * Save next pointer since element may be removed before
797*2685Sakolb 		 * we get a chance to advance psaddr.
798*2685Sakolb 		 */
799*2685Sakolb 		next = psaddr->next;
800*2685Sakolb 
801*2685Sakolb 		/*
802*2685Sakolb 		 * Since mappings have been added to the merged list
803*2685Sakolb 		 * even if no generic advice was given for the map,
804*2685Sakolb 		 * check to make sure advice exists before bothering
805*2685Sakolb 		 * with the for loop.
806*2685Sakolb 		 */
807*2685Sakolb 		if (psaddr->adv != NO_ADVICE) {
808*2685Sakolb 			for (i = MADV_NORMAL; i <= MADV_ACCESS_MANY; i++) {
809*2685Sakolb 				if ((psaddr->adv & (1 << i)) &&
810*2685Sakolb 				    (pr_madvise(Pr, (caddr_t)psaddr->addr,
811*2685Sakolb 				    psaddr->length, i) < 0)) {
812*2685Sakolb 					/*
813*2685Sakolb 					 * madvise(3C) call failed trying to
814*2685Sakolb 					 * apply advice output error and remove
815*2685Sakolb 					 * from advice list
816*2685Sakolb 					 */
817*2685Sakolb 					(void) fprintf(stderr,
818*2685Sakolb 					    gettext("Error applying "
819*2685Sakolb 						"advice (%s) to memory range "
820*2685Sakolb 						"[%lx, %lx):\n"),
821*2685Sakolb 					    advicestr[i], (ulong_t)psaddr->addr,
822*2685Sakolb 					    (ulong_t)psaddr->addr +
823*2685Sakolb 					    psaddr->length);
824*2685Sakolb 					perror("madvise");
825*2685Sakolb 					/*
826*2685Sakolb 					 * Clear this advice from the advice
827*2685Sakolb 					 * mask. If no more advice is given
828*2685Sakolb 					 * for this element, remove element
829*2685Sakolb 					 * from list.
830*2685Sakolb 					 */
831*2685Sakolb 					psaddr->adv &= ~(1 << i);
832*2685Sakolb 					if (psaddr->adv == 0) {
833*2685Sakolb 						delete_addr(advicelist, psaddr);
834*2685Sakolb 						break;
835*2685Sakolb 					}
836*2685Sakolb 				}
837*2685Sakolb 			}
838*2685Sakolb 		}
839*2685Sakolb 		psaddr = next;
840*2685Sakolb 	}
841*2685Sakolb 	return (0);
842*2685Sakolb }
843*2685Sakolb 
844*2685Sakolb /*
845*2685Sakolb  * Set advice but keep mutual exclusive property of advice groupings
846*2685Sakolb  */
847*2685Sakolb static void
848*2685Sakolb set_advice(int *combined_adv, int new_adv) {
849*2685Sakolb 	/*
850*2685Sakolb 	 * Since advice falls in 3 groups of mutually exclusive options,
851*2685Sakolb 	 * clear previous value if new advice overwrites that group.
852*2685Sakolb 	 */
853*2685Sakolb 
854*2685Sakolb 	/*
855*2685Sakolb 	 * If this is the first advice to be applied, clear invalid value (-1)
856*2685Sakolb 	 */
857*2685Sakolb 	if (*combined_adv == -1)
858*2685Sakolb 		*combined_adv = 0;
859*2685Sakolb 
860*2685Sakolb 	if (new_adv & GRP1_ADV)
861*2685Sakolb 		*combined_adv &= ~GRP1_ADV;
862*2685Sakolb 	else if (new_adv & GRP2_ADV)
863*2685Sakolb 		*combined_adv &= ~GRP2_ADV;
864*2685Sakolb 	else
865*2685Sakolb 		*combined_adv &= ~GRP3_ADV;
866*2685Sakolb 
867*2685Sakolb 	*combined_adv |= new_adv;
868*2685Sakolb }
869*2685Sakolb 
870*2685Sakolb /*
871*2685Sakolb  * Create chopped list from merged list for use with verbose output
872*2685Sakolb  */
873*2685Sakolb static void
874*2685Sakolb create_choplist(saddr_t **choppedlist, saddr_t *mergedlist)
875*2685Sakolb {
876*2685Sakolb 	saddr_t	*mlptr, *clptr;
877*2685Sakolb 
878*2685Sakolb 	for (mlptr = mergedlist; mlptr != NULL; mlptr = mlptr->next) {
879*2685Sakolb 		clptr = malloc(sizeof (saddr_t));
880*2685Sakolb 		clptr->addr = mlptr->addr;
881*2685Sakolb 		clptr->length = 0;
882*2685Sakolb 		/*
883*2685Sakolb 		 * Initialize the adv to -1 as an indicator for invalid
884*2685Sakolb 		 * elements in the chopped list (created from gaps between
885*2685Sakolb 		 * memory maps).
886*2685Sakolb 		 */
887*2685Sakolb 		clptr->adv = -1;
888*2685Sakolb 		clptr->next = NULL;
889*2685Sakolb 		insert_addr(choppedlist, clptr, NODUPS);
890*2685Sakolb 
891*2685Sakolb 		clptr = malloc(sizeof (saddr_t));
892*2685Sakolb 		clptr->addr = mlptr->addr + mlptr->length;
893*2685Sakolb 		clptr->length = 0;
894*2685Sakolb 		/*
895*2685Sakolb 		 * Again, initialize to -1 as an indicatorfor invalid elements
896*2685Sakolb 		 */
897*2685Sakolb 		clptr->adv = -1;
898*2685Sakolb 		clptr->next = NULL;
899*2685Sakolb 		insert_addr(choppedlist, clptr, NODUPS);
900*2685Sakolb 	}
901*2685Sakolb 
902*2685Sakolb 	for (clptr = *choppedlist; clptr != NULL; clptr = clptr->next) {
903*2685Sakolb 		if (clptr->next)
904*2685Sakolb 			clptr->length = clptr->next->addr - clptr->addr;
905*2685Sakolb 		else {
906*2685Sakolb 			/*
907*2685Sakolb 			 * must be last element, now that we've calculated
908*2685Sakolb 			 * all segment lengths, we can remove this node
909*2685Sakolb 			 */
910*2685Sakolb 			delete_addr(choppedlist, clptr);
911*2685Sakolb 		}
912*2685Sakolb 	}
913*2685Sakolb 
914*2685Sakolb 	for (mlptr = mergedlist; mlptr != NULL; mlptr = mlptr->next) {
915*2685Sakolb 		for (clptr = *choppedlist; clptr != NULL; clptr = clptr->next) {
916*2685Sakolb 			if (mlptr->addr <= clptr->addr &&
917*2685Sakolb 			    mlptr->addr + mlptr->length >=
918*2685Sakolb 			    clptr->addr + clptr->length)
919*2685Sakolb 				/*
920*2685Sakolb 				 * set_advice() will take care of conflicting
921*2685Sakolb 				 * advice by taking only the last advice
922*2685Sakolb 				 * applied for each of the 3 groups of advice.
923*2685Sakolb 				 */
924*2685Sakolb 				set_advice(&clptr->adv, mlptr->adv);
925*2685Sakolb 			if (mlptr->addr + mlptr->length <
926*2685Sakolb 			    clptr->addr)
927*2685Sakolb 				break;
928*2685Sakolb 		}
929*2685Sakolb 	}
930*2685Sakolb }
931*2685Sakolb 
932*2685Sakolb /*
933*2685Sakolb  * Print advice in pmap style for verbose output
934*2685Sakolb  */
935*2685Sakolb static void
936*2685Sakolb print_advice(saddr_t *advlist, mapnode_t *maplist)
937*2685Sakolb {
938*2685Sakolb 	saddr_t		*psaddr = advlist;
939*2685Sakolb 	mapnode_t	*pmapnode;
940*2685Sakolb 	char		*advice;
941*2685Sakolb 
942*2685Sakolb 	pmapnode = maplist;
943*2685Sakolb 
944*2685Sakolb 	while (psaddr) {
945*2685Sakolb 		/*
946*2685Sakolb 		 * Using indicator flag from create_choppedlist, we know
947*2685Sakolb 		 * which entries in the chopped_list are gaps and should
948*2685Sakolb 		 * not be printed.
949*2685Sakolb 		 */
950*2685Sakolb 		if (psaddr->adv == -1) {
951*2685Sakolb 			psaddr = psaddr->next;
952*2685Sakolb 			continue;
953*2685Sakolb 		}
954*2685Sakolb 
955*2685Sakolb 		while (pmapnode && (pmapnode->pmp->pr_vaddr +
956*2685Sakolb 		    pmapnode->pmp->pr_size <= psaddr->addr))
957*2685Sakolb 			pmapnode = pmapnode->next;
958*2685Sakolb 
959*2685Sakolb 		advice = advtostr(psaddr->adv);
960*2685Sakolb 
961*2685Sakolb 		/*
962*2685Sakolb 		 * Print segment mapping and advice if there is any, or just a
963*2685Sakolb 		 * segment mapping.
964*2685Sakolb 		 */
965*2685Sakolb 		if (strlen(advice) > 0) {
966*2685Sakolb 			(void) printf("%.*lX %*uK %6s %s\t%s\n",
967*2685Sakolb 			    addr_width, (ulong_t)psaddr->addr, size_width - 1,
968*2685Sakolb 			    (int)ROUNDUP_KB(psaddr->length),
969*2685Sakolb 			    mflags(pmapnode->pmp->pr_mflags), pmapnode->label,
970*2685Sakolb 			    advice);
971*2685Sakolb 		} else {
972*2685Sakolb 			(void) printf("%.*lX %*uK %6s %s\n",
973*2685Sakolb 			    addr_width, (ulong_t)psaddr->addr, size_width - 1,
974*2685Sakolb 			    (int)ROUNDUP_KB(psaddr->length),
975*2685Sakolb 			    mflags(pmapnode->pmp->pr_mflags), pmapnode->label);
976*2685Sakolb 		}
977*2685Sakolb 		psaddr = psaddr->next;
978*2685Sakolb 
979*2685Sakolb 	}
980*2685Sakolb }
981*2685Sakolb 
982*2685Sakolb /*
983*2685Sakolb  * Call madvise(3c) in the context of the target process
984*2685Sakolb  */
985*2685Sakolb static int
986*2685Sakolb pr_madvise(struct ps_prochandle *Pr, caddr_t addr, size_t len, int advice)
987*2685Sakolb {
988*2685Sakolb 	return (pr_memcntl(Pr, addr, len, MC_ADVISE,
989*2685Sakolb 		    (caddr_t)(uintptr_t)advice, 0, 0));
990*2685Sakolb }
991*2685Sakolb 
992*2685Sakolb static char *
993*2685Sakolb mflags(uint_t arg)
994*2685Sakolb {
995*2685Sakolb 	static char code_buf[80];
996*2685Sakolb 
997*2685Sakolb 	/*
998*2685Sakolb 	 * rwxsR
999*2685Sakolb 	 *
1000*2685Sakolb 	 * r - segment is readable
1001*2685Sakolb 	 * w - segment is writable
1002*2685Sakolb 	 * x - segment is executable
1003*2685Sakolb 	 * s - segment is shared
1004*2685Sakolb 	 * R - segment is mapped MAP_NORESERVE
1005*2685Sakolb 	 *
1006*2685Sakolb 	 */
1007*2685Sakolb 	(void) snprintf(code_buf, sizeof (code_buf), "%c%c%c%c%c ",
1008*2685Sakolb 	    arg & MA_READ ? 'r' : '-',
1009*2685Sakolb 	    arg & MA_WRITE ? 'w' : '-',
1010*2685Sakolb 	    arg & MA_EXEC ? 'x' : '-',
1011*2685Sakolb 	    arg & MA_SHARED ? 's' : '-',
1012*2685Sakolb 	    arg & MA_NORESERVE ? 'R' : '-');
1013*2685Sakolb 
1014*2685Sakolb 	return (code_buf);
1015*2685Sakolb }
1016*2685Sakolb 
1017*2685Sakolb /*
1018*2685Sakolb  * Convert advice to a string containing a commented list of applicable advice
1019*2685Sakolb  */
1020*2685Sakolb static char *
1021*2685Sakolb advtostr(int adv)
1022*2685Sakolb {
1023*2685Sakolb 	static char buf[50];
1024*2685Sakolb 	int i;
1025*2685Sakolb 
1026*2685Sakolb 	*buf = '\0';
1027*2685Sakolb 
1028*2685Sakolb 	if (adv != NO_ADVICE) {
1029*2685Sakolb 		for (i = MADV_NORMAL; i <= MADV_ACCESS_MANY; i++) {
1030*2685Sakolb 			if (adv & (1 << i)) {
1031*2685Sakolb 				/*
1032*2685Sakolb 				 * check if it's the first advice entry
1033*2685Sakolb 				 */
1034*2685Sakolb 				if (*buf == '\0')
1035*2685Sakolb 					(void) snprintf(buf, sizeof (buf) - 1,
1036*2685Sakolb 					    "<= %s", advicestr[i]);
1037*2685Sakolb 				else
1038*2685Sakolb 					(void) snprintf(buf, sizeof (buf) - 1,
1039*2685Sakolb 					    "%s,%s", buf, advicestr[i]);
1040*2685Sakolb 			}
1041*2685Sakolb 		}
1042*2685Sakolb 	}
1043*2685Sakolb 
1044*2685Sakolb 	return (buf);
1045*2685Sakolb }
1046*2685Sakolb 
1047*2685Sakolb /*
1048*2685Sakolb  * Handler for catching signals from terminal
1049*2685Sakolb  */
1050*2685Sakolb /* ARGSUSED */
1051*2685Sakolb static void
1052*2685Sakolb intr(int sig)
1053*2685Sakolb {
1054*2685Sakolb 	interrupt++;
1055*2685Sakolb }
1056*2685Sakolb 
1057*2685Sakolb int
1058*2685Sakolb main(int argc, char **argv)
1059*2685Sakolb {
1060*2685Sakolb 	int Fflag = 0;
1061*2685Sakolb 	int rc = 0;
1062*2685Sakolb 	int opt, subopt;
1063*2685Sakolb 	int tmpadv;
1064*2685Sakolb 	char	*options, *value;
1065*2685Sakolb 	saddr_t	*psaddr;
1066*2685Sakolb 	mapnode_t *pmapnode, *tempmapnode;
1067*2685Sakolb 
1068*2685Sakolb 	(void) setlocale(LC_ALL, "");
1069*2685Sakolb 	(void) textdomain(TEXT_DOMAIN);
1070*2685Sakolb 
1071*2685Sakolb 	/*
1072*2685Sakolb 	 * Get name of program for error messages
1073*2685Sakolb 	 */
1074*2685Sakolb 	progname = basename(argv[0]);
1075*2685Sakolb 
1076*2685Sakolb 	/*
1077*2685Sakolb 	 * Not much to do when only name of program given
1078*2685Sakolb 	 */
1079*2685Sakolb 	if (argc == 1)
1080*2685Sakolb 		usage();
1081*2685Sakolb 
1082*2685Sakolb 	/*
1083*2685Sakolb 	 * Catch signals from terminal, so they can be handled asynchronously
1084*2685Sakolb 	 * when we're ready instead of when we're not (;-)
1085*2685Sakolb 	 */
1086*2685Sakolb 	if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
1087*2685Sakolb 		(void) sigset(SIGHUP, intr);
1088*2685Sakolb 	if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
1089*2685Sakolb 		(void) sigset(SIGINT, intr);
1090*2685Sakolb 	if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
1091*2685Sakolb 		(void) sigset(SIGQUIT, intr);
1092*2685Sakolb 	(void) sigset(SIGPIPE, intr);
1093*2685Sakolb 	(void) sigset(SIGTERM, intr);
1094*2685Sakolb 
1095*2685Sakolb 	/*
1096*2685Sakolb 	 * Parse options, record generic advice if any and create
1097*2685Sakolb 	 * rawadv_list from specific address advice.
1098*2685Sakolb 	 */
1099*2685Sakolb 
1100*2685Sakolb 	while ((opt = getopt(argc, argv, "Fo:v")) != EOF) {
1101*2685Sakolb 		switch (opt) {
1102*2685Sakolb 		case 'o':
1103*2685Sakolb 			options = optarg;
1104*2685Sakolb 			while (*options != '\0') {
1105*2685Sakolb 				subopt = getsubopt(&options, suboptstr,
1106*2685Sakolb 				    &value);
1107*2685Sakolb 				switch (subopt) {
1108*2685Sakolb 				case AT_PRIVM:
1109*2685Sakolb 				case AT_HEAP:
1110*2685Sakolb 				case AT_SHARED:
1111*2685Sakolb 				case AT_STACK:
1112*2685Sakolb 					at_map |= (1 << subopt);
1113*2685Sakolb 					tmpadv = get_advice(value);
1114*2685Sakolb 					set_advice(&generic_adv[subopt],
1115*2685Sakolb 					    tmpadv);
1116*2685Sakolb 					break;
1117*2685Sakolb 				default:
1118*2685Sakolb 					at_map |= (1 << AT_SEG);
1119*2685Sakolb 					psaddr = parse_suboptions(value);
1120*2685Sakolb 					if (psaddr == NULL) {
1121*2685Sakolb 						usage();
1122*2685Sakolb 					} else {
1123*2685Sakolb 						insert_addr(&rawadv_list,
1124*2685Sakolb 						    psaddr, YESDUPS);
1125*2685Sakolb 					}
1126*2685Sakolb 					break;
1127*2685Sakolb 				}
1128*2685Sakolb 			}
1129*2685Sakolb 			break;
1130*2685Sakolb 		case 'v':
1131*2685Sakolb 			opt_verbose = 1;
1132*2685Sakolb 			break;
1133*2685Sakolb 		case 'F':		/* force grabbing (no O_EXCL) */
1134*2685Sakolb 			Fflag = PGRAB_FORCE;
1135*2685Sakolb 			break;
1136*2685Sakolb 		default:
1137*2685Sakolb 			usage();
1138*2685Sakolb 			break;
1139*2685Sakolb 		}
1140*2685Sakolb 	}
1141*2685Sakolb 
1142*2685Sakolb 	argc -= optind;
1143*2685Sakolb 	argv += optind;
1144*2685Sakolb 
1145*2685Sakolb 	if (argc <= 0) {
1146*2685Sakolb 		usage();
1147*2685Sakolb 	}
1148*2685Sakolb 
1149*2685Sakolb 	/*
1150*2685Sakolb 	 * Iterate through all pid arguments, create new merged_list, maplist,
1151*2685Sakolb 	 * (and chopped_list if using verbose output) based on each process'
1152*2685Sakolb 	 * memory map.
1153*2685Sakolb 	 */
1154*2685Sakolb 
1155*2685Sakolb 	while (!interrupt && argc-- > 0) {
1156*2685Sakolb 		char *arg;
1157*2685Sakolb 		int gcode;
1158*2685Sakolb 		psinfo_t psinfo;
1159*2685Sakolb 
1160*2685Sakolb 		if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_PIDS,
1161*2685Sakolb 		    PGRAB_RETAIN | Fflag, &gcode)) == NULL) {
1162*2685Sakolb 			(void) fprintf(stderr,
1163*2685Sakolb 			    gettext("%s: cannot examine %s: %s\n"),
1164*2685Sakolb 			    progname, arg, Pgrab_error(gcode));
1165*2685Sakolb 			rc++;
1166*2685Sakolb 			continue;
1167*2685Sakolb 		}
1168*2685Sakolb 
1169*2685Sakolb 
1170*2685Sakolb 		addr_width =
1171*2685Sakolb 		    (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 16 : 8;
1172*2685Sakolb 		size_width =
1173*2685Sakolb 		    (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 11 : 8;
1174*2685Sakolb 		(void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t));
1175*2685Sakolb 
1176*2685Sakolb 		if (opt_verbose) {
1177*2685Sakolb 			proc_unctrl_psinfo(&psinfo);
1178*2685Sakolb 			(void) printf("%d:\t%.70s\n",
1179*2685Sakolb 			    (int)psinfo.pr_pid, psinfo.pr_psargs);
1180*2685Sakolb 		}
1181*2685Sakolb 
1182*2685Sakolb 		/*
1183*2685Sakolb 		 * Get mappings for a process unless it is a system process.
1184*2685Sakolb 		 */
1185*2685Sakolb 		if (!(Pstatus(Pr)->pr_flags & PR_ISSYS)) {
1186*2685Sakolb 			nstacks = psinfo.pr_nlwp * 2;
1187*2685Sakolb 			stacks = calloc(nstacks, sizeof (stacks[0]));
1188*2685Sakolb 			if (stacks != NULL) {
1189*2685Sakolb 				int n = 0;
1190*2685Sakolb 				(void) Plwp_iter(Pr, getstack, &n);
1191*2685Sakolb 				qsort(stacks, nstacks, sizeof (stacks[0]),
1192*2685Sakolb 				    cmpstacks);
1193*2685Sakolb 			}
1194*2685Sakolb 
1195*2685Sakolb 			if (Pgetauxval(Pr, AT_BASE) != -1L &&
1196*2685Sakolb 			    Prd_agent(Pr) == NULL) {
1197*2685Sakolb 				(void) fprintf(stderr,
1198*2685Sakolb 				    gettext("%s: warning: "
1199*2685Sakolb 					"librtld_db failed to initialize; "
1200*2685Sakolb 					"shared library information will not "
1201*2685Sakolb 					"be available\n"),
1202*2685Sakolb 				    progname);
1203*2685Sakolb 			}
1204*2685Sakolb 
1205*2685Sakolb 			/*
1206*2685Sakolb 			 * Create linked list of mappings for current process
1207*2685Sakolb 			 * In addition, add generic advice and raw advice
1208*2685Sakolb 			 * entries to merged_list.
1209*2685Sakolb 			 * e.g. if rawadv_list contains:
1210*2685Sakolb 			 *   [0x38000,0x3a000) = adv1
1211*2685Sakolb 			 *   [0x3a000,0x3c000) = adv2
1212*2685Sakolb 			 * and there is generic advice:
1213*2685Sakolb 			 *   heap = adv3
1214*2685Sakolb 			 * where heap corresponds to 0x38000, then merged_list
1215*2685Sakolb 			 * will contain:
1216*2685Sakolb 			 *   ... (include all other mappings from process)
1217*2685Sakolb 			 *   [0x38000,0x3c000) = adv3
1218*2685Sakolb 			 *   [0x38000,0x3a000) = adv1
1219*2685Sakolb 			 *   [0x3a000,0x3c000) = adv2
1220*2685Sakolb 			 *   ... (include all other mappings from process)
1221*2685Sakolb 			 */
1222*2685Sakolb 			assert(merged_list == NULL);
1223*2685Sakolb 			maplist_head = maplist_tail = NULL;
1224*2685Sakolb 			rc += Pmapping_iter(Pr, (proc_map_f *)create_maplist,
1225*2685Sakolb 			    NULL);
1226*2685Sakolb 
1227*2685Sakolb 			/*
1228*2685Sakolb 			 * Apply advice by iterating through merged list
1229*2685Sakolb 			 */
1230*2685Sakolb 			(void) apply_advice(&merged_list);
1231*2685Sakolb 
1232*2685Sakolb 			if (opt_verbose) {
1233*2685Sakolb 				assert(chopped_list == NULL);
1234*2685Sakolb 				/*
1235*2685Sakolb 				 * Create chopped_list from merged_list
1236*2685Sakolb 				 */
1237*2685Sakolb 				create_choplist(&chopped_list, merged_list);
1238*2685Sakolb 
1239*2685Sakolb 				/*
1240*2685Sakolb 				 * Iterate through maplist and output as
1241*2685Sakolb 				 * given by chopped_list
1242*2685Sakolb 				 */
1243*2685Sakolb 				print_advice(chopped_list, maplist_head);
1244*2685Sakolb 				delete_list(&chopped_list);
1245*2685Sakolb 			}
1246*2685Sakolb 
1247*2685Sakolb 			delete_list(&merged_list);
1248*2685Sakolb 
1249*2685Sakolb 			/*
1250*2685Sakolb 			 * Clear maplist
1251*2685Sakolb 			 */
1252*2685Sakolb 			pmapnode = maplist_head;
1253*2685Sakolb 			while (pmapnode) {
1254*2685Sakolb 				tempmapnode = pmapnode;
1255*2685Sakolb 				pmapnode = pmapnode->next;
1256*2685Sakolb 				free(tempmapnode);
1257*2685Sakolb 			}
1258*2685Sakolb 
1259*2685Sakolb 			if (stacks != NULL) {
1260*2685Sakolb 				free(stacks);
1261*2685Sakolb 				stacks = NULL;
1262*2685Sakolb 			}
1263*2685Sakolb 		}
1264*2685Sakolb 
1265*2685Sakolb 		Prelease(Pr, 0);
1266*2685Sakolb 	}
1267*2685Sakolb 
1268*2685Sakolb 	return (rc);
1269*2685Sakolb }
1270