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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 * 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 * 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 924 intr(int sig) 925 { 926 interrupt++; 927 } 928 929 int 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