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