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