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