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