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