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