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 #include <stdio.h>
  28 #include <stdio_ext.h>
  29 #include <stdlib.h>
  30 #include <unistd.h>
  31 #include <ctype.h>
  32 #include <fcntl.h>
  33 #include <string.h>
  34 #include <dirent.h>
  35 #include <limits.h>
  36 #include <link.h>
  37 #include <libelf.h>
  38 #include <sys/types.h>
  39 #include <signal.h>
  40 #include <sys/stat.h>
  41 #include <sys/mkdev.h>
  42 #include <sys/mman.h>
  43 #include <sys/lgrp_user.h>
  44 #include <libproc.h>
  45 
  46 #include "pmap_common.h"
  47 
  48 #define KILOBYTE        1024
  49 #define MEGABYTE        (KILOBYTE * KILOBYTE)
  50 #define GIGABYTE        (KILOBYTE * KILOBYTE * KILOBYTE)
  51 
  52 /*
  53  * Round up the value to the nearest kilobyte
  54  */
  55 #define ROUNDUP_KB(x)   (((x) + (KILOBYTE - 1)) / KILOBYTE)
  56 
  57 /*
  58  * The alignment should be a power of 2.
  59  */
  60 #define P2ALIGN(x, align)               ((x) & -(align))
  61 
  62 #define INVALID_ADDRESS                 (uintptr_t)(-1)
  63 
  64 struct totals {
  65         ulong_t total_size;
  66         ulong_t total_swap;
  67         ulong_t total_rss;
  68         ulong_t total_anon;
  69         ulong_t total_locked;
  70 };
  71 
  72 /*
  73  * -L option requires per-page information. The information is presented in an
  74  * array of page_descr structures.
  75  */
  76 typedef struct page_descr {
  77         uintptr_t       pd_start;       /* start address of a page */
  78         size_t          pd_pagesize;    /* page size in bytes */
  79         lgrp_id_t       pd_lgrp;        /* lgroup of memory backing the page */
  80         int             pd_valid;       /* valid page description if non-zero */
  81 } page_descr_t;
  82 
  83 /*
  84  * Per-page information for a memory chunk.
  85  * The meminfo(2) system call accepts up to MAX_MEMINFO_CNT pages at once.
  86  * When we need to scan larger ranges we divide them in MAX_MEMINFO_CNT sized
  87  * chunks. The chunk information is stored in the memory_chunk structure.
  88  */
  89 typedef struct memory_chunk {
  90         page_descr_t    page_info[MAX_MEMINFO_CNT];
  91         uintptr_t       end_addr;
  92         uintptr_t       chunk_start;    /* Starting address */
  93         uintptr_t       chunk_end;      /* chunk_end is always <= end_addr */
  94         size_t          page_size;
  95         int             page_index;     /* Current page */
  96         int             page_count;     /* Number of pages */
  97 } memory_chunk_t;
  98 
  99 static volatile int interrupt;
 100 
 101 typedef int proc_xmap_f(void *, const prxmap_t *, const char *, int, int);
 102 
 103 static  int     xmapping_iter(struct ps_prochandle *, proc_xmap_f *, void *,
 104     int);
 105 static  int     rmapping_iter(struct ps_prochandle *, proc_map_f *, void *);
 106 
 107 static  int     look_map(void *, const prmap_t *, const char *);
 108 static  int     look_smap(void *, const prxmap_t *, const char *, int, int);
 109 static  int     look_xmap(void *, const prxmap_t *, const char *, int, int);
 110 static  int     look_xmap_nopgsz(void *, const prxmap_t *, const char *,
 111     int, int);
 112 
 113 static int gather_map(void *, const prmap_t *, const char *);
 114 static int gather_xmap(void *, const prxmap_t *, const char *, int, int);
 115 static int iter_map(proc_map_f *, void *);
 116 static int iter_xmap(proc_xmap_f *, void *);
 117 static int parse_addr_range(char *, uintptr_t *, uintptr_t *);
 118 static void mem_chunk_init(memory_chunk_t *, uintptr_t, size_t);
 119 
 120 static  int     perr(char *);
 121 static  void    printK(long, int);
 122 static  char    *mflags(uint_t);
 123 
 124 static size_t get_contiguous_region(memory_chunk_t *, uintptr_t,
 125     uintptr_t, size_t, lgrp_id_t *);
 126 static void     mem_chunk_get(memory_chunk_t *, uintptr_t);
 127 static lgrp_id_t addr_to_lgrp(memory_chunk_t *, uintptr_t, size_t *);
 128 static char     *lgrp2str(lgrp_id_t);
 129 
 130 static int      address_in_range(uintptr_t, uintptr_t, size_t);
 131 static size_t   adjust_addr_range(uintptr_t, uintptr_t, size_t,
 132     uintptr_t *, uintptr_t *);
 133 
 134 static  int     lflag = 0;
 135 static  int     Lflag = 0;
 136 static  int     aflag = 0;
 137 
 138 /*
 139  * The -A address range is represented as a pair of addresses
 140  * <start_addr, end_addr>. Either one of these may be unspecified (set to
 141  * INVALID_ADDRESS). If both are unspecified, no address range restrictions are
 142  * in place.
 143  */
 144 static  uintptr_t start_addr = INVALID_ADDRESS;
 145 static  uintptr_t end_addr = INVALID_ADDRESS;
 146 
 147 static  int     addr_width, size_width;
 148 static  char    *command;
 149 static  char    *procname;
 150 static  struct ps_prochandle *Pr;
 151 
 152 static void intr(int);
 153 
 154 typedef struct {
 155         prxmap_t        md_xmap;
 156         prmap_t         md_map;
 157         char            *md_objname;
 158         boolean_t       md_last;
 159         int             md_doswap;
 160 } mapdata_t;
 161 
 162 static  mapdata_t       *maps;
 163 static  int             map_count;
 164 static  int             map_alloc;
 165 
 166 static  lwpstack_t *stacks = NULL;
 167 static  uint_t  nstacks = 0;
 168 
 169 #define MAX_TRIES       5
 170 
 171 static int
 172 getstack(void *data, const lwpstatus_t *lsp)
 173 {
 174         int *np = (int *)data;
 175 
 176         if (Plwp_alt_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) {
 177                 stacks[*np].lwps_stack.ss_flags |= SS_ONSTACK;
 178                 stacks[*np].lwps_lwpid = lsp->pr_lwpid;
 179                 (*np)++;
 180         }
 181 
 182         if (Plwp_main_stack(Pr, lsp->pr_lwpid, &stacks[*np].lwps_stack) == 0) {
 183                 stacks[*np].lwps_lwpid = lsp->pr_lwpid;
 184                 (*np)++;
 185         }
 186 
 187         return (0);
 188 }
 189 
 190 int
 191 main(int argc, char **argv)
 192 {
 193         int rflag = 0, sflag = 0, xflag = 0, Fflag = 0;
 194         int errflg = 0, Sflag = 0;
 195         int rc = 0;
 196         int opt;
 197         const char *bar8 = "-------";
 198         const char *bar16 = "----------";
 199         const char *bar;
 200         struct rlimit rlim;
 201         struct stat64 statbuf;
 202         char buf[128];
 203         int mapfd;
 204         int prg_gflags = PGRAB_RDONLY;
 205         int prr_flags = 0;
 206         boolean_t use_agent_lwp = B_FALSE;
 207 
 208         if ((command = strrchr(argv[0], '/')) != NULL)
 209                 command++;
 210         else
 211                 command = argv[0];
 212 
 213         while ((opt = getopt(argc, argv, "arsxSlLFA:")) != EOF) {
 214                 switch (opt) {
 215                 case 'a':               /* include shared mappings in -[xS] */
 216                         aflag = 1;
 217                         break;
 218                 case 'r':               /* show reserved mappings */
 219                         rflag = 1;
 220                         break;
 221                 case 's':               /* show hardware page sizes */
 222                         sflag = 1;
 223                         break;
 224                 case 'S':               /* show swap reservations */
 225                         Sflag = 1;
 226                         break;
 227                 case 'x':               /* show extended mappings */
 228                         xflag = 1;
 229                         break;
 230                 case 'l':               /* show unresolved link map names */
 231                         lflag = 1;
 232                         break;
 233                 case 'L':               /* show lgroup information */
 234                         Lflag = 1;
 235                         use_agent_lwp = B_TRUE;
 236                         break;
 237                 case 'F':               /* force grabbing (no O_EXCL) */
 238                         Fflag = PGRAB_FORCE;
 239                         break;
 240                 case 'A':
 241                         if (parse_addr_range(optarg, &start_addr, &end_addr)
 242                             != 0)
 243                                 errflg++;
 244                         break;
 245                 default:
 246                         errflg = 1;
 247                         break;
 248                 }
 249         }
 250 
 251         argc -= optind;
 252         argv += optind;
 253 
 254         if ((Sflag && (xflag || rflag || sflag)) || (xflag && rflag) ||
 255             (aflag && (!xflag && !Sflag)) ||
 256             (Lflag && (xflag || Sflag))) {
 257                 errflg = 1;
 258         }
 259 
 260         if (errflg || argc <= 0) {
 261                 (void) fprintf(stderr,
 262                     "usage:\t%s [-rslF] [-A start[,end]] { pid | core } ...\n",
 263                     command);
 264                 (void) fprintf(stderr,
 265                     "\t\t(report process address maps)\n");
 266                 (void) fprintf(stderr,
 267                     "\t%s -L [-rslF] [-A start[,end]] pid ...\n", command);
 268                 (void) fprintf(stderr,
 269                     "\t\t(report process address maps lgroups mappings)\n");
 270                 (void) fprintf(stderr,
 271                     "\t%s -x [-aslF] [-A start[,end]] pid ...\n", command);
 272                 (void) fprintf(stderr,
 273                     "\t\t(show resident/anon/locked mapping details)\n");
 274                 (void) fprintf(stderr,
 275                     "\t%s -S [-alF] [-A start[,end]] { pid | core } ...\n",
 276                     command);
 277                 (void) fprintf(stderr,
 278                     "\t\t(show swap reservations)\n\n");
 279                 (void) fprintf(stderr,
 280                     "\t-a: include shared mappings in -[xS] summary\n");
 281                 (void) fprintf(stderr,
 282                     "\t-r: show reserved address maps\n");
 283                 (void) fprintf(stderr,
 284                     "\t-s: show hardware page sizes\n");
 285                 (void) fprintf(stderr,
 286                     "\t-l: show unresolved dynamic linker map names\n");
 287                 (void) fprintf(stderr,
 288                     "\t-F: force grabbing of the target process\n");
 289                 (void) fprintf(stderr,
 290                     "\t-L: show lgroup mappings\n");
 291                 (void) fprintf(stderr,
 292                     "\t-A start,end: limit output to the specified range\n");
 293                 return (2);
 294         }
 295 
 296         /*
 297          * Make sure we'll have enough file descriptors to handle a target
 298          * that has many many mappings.
 299          */
 300         if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
 301                 rlim.rlim_cur = rlim.rlim_max;
 302                 (void) setrlimit(RLIMIT_NOFILE, &rlim);
 303                 (void) enable_extended_FILE_stdio(-1, -1);
 304         }
 305 
 306         /*
 307          * The implementation of -L option creates an agent LWP in the target
 308          * process address space. The agent LWP issues meminfo(2) system calls
 309          * on behalf of the target process. If we are interrupted prematurely,
 310          * the target process remains in the stopped state with the agent still
 311          * attached to it. To prevent such situation we catch signals from
 312          * terminal and terminate gracefully.
 313          */
 314         if (use_agent_lwp) {
 315                 /*
 316                  * Buffer output to stdout, stderr while process is grabbed.
 317                  * Prevents infamous deadlocks due to pmap `pgrep xterm` and
 318                  * other variants.
 319                  */
 320                 (void) proc_initstdio();
 321 
 322                 prg_gflags = PGRAB_RETAIN | Fflag;
 323                 prr_flags = PRELEASE_RETAIN;
 324 
 325                 if (sigset(SIGHUP, SIG_IGN) == SIG_DFL)
 326                         (void) sigset(SIGHUP, intr);
 327                 if (sigset(SIGINT, SIG_IGN) == SIG_DFL)
 328                         (void) sigset(SIGINT, intr);
 329                 if (sigset(SIGQUIT, SIG_IGN) == SIG_DFL)
 330                         (void) sigset(SIGQUIT, intr);
 331                 (void) sigset(SIGPIPE, intr);
 332                 (void) sigset(SIGTERM, intr);
 333         }
 334 
 335         while (argc-- > 0) {
 336                 char *arg;
 337                 int gcode;
 338                 psinfo_t psinfo;
 339                 int tries = 0;
 340 
 341                 if (use_agent_lwp)
 342                         (void) proc_flushstdio();
 343 
 344                 if ((Pr = proc_arg_grab(arg = *argv++, PR_ARG_ANY,
 345                     prg_gflags, &gcode)) == NULL) {
 346                         (void) fprintf(stderr, "%s: cannot examine %s: %s\n",
 347                             command, arg, Pgrab_error(gcode));
 348                         rc++;
 349                         continue;
 350                 }
 351 
 352                 procname = arg;         /* for perr() */
 353 
 354                 addr_width = (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 16 : 8;
 355                 size_width = (Pstatus(Pr)->pr_dmodel == PR_MODEL_LP64) ? 11 : 8;
 356                 bar = addr_width == 8 ? bar8 : bar16;
 357                 (void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t));
 358                 proc_unctrl_psinfo(&psinfo);
 359 
 360                 if (Pstate(Pr) != PS_DEAD) {
 361                         (void) snprintf(buf, sizeof (buf),
 362                             "/proc/%d/map", (int)psinfo.pr_pid);
 363                         if ((mapfd = open(buf, O_RDONLY)) < 0) {
 364                                 (void) fprintf(stderr, "%s: cannot "
 365                                     "examine %s: lost control of "
 366                                     "process\n", command, arg);
 367                                 rc++;
 368                                 Prelease(Pr, prr_flags);
 369                                 continue;
 370                         }
 371                 } else {
 372                         mapfd = -1;
 373                 }
 374 
 375 again:
 376                 map_count = 0;
 377 
 378                 if (Pstate(Pr) == PS_DEAD) {
 379                         (void) printf("core '%s' of %d:\t%.70s\n",
 380                             arg, (int)psinfo.pr_pid, psinfo.pr_psargs);
 381 
 382                         if (rflag || sflag || xflag || Sflag || Lflag) {
 383                                 (void) printf("  -%c option is not compatible "
 384                                     "with core files\n", xflag ? 'x' :
 385                                     sflag ? 's' : rflag ? 'r' :
 386                                     Lflag ? 'L' : 'S');
 387                                 Prelease(Pr, prr_flags);
 388                                 rc++;
 389                                 continue;
 390                         }
 391 
 392                 } else {
 393                         (void) printf("%d:\t%.70s\n",
 394                             (int)psinfo.pr_pid, psinfo.pr_psargs);
 395                 }
 396 
 397                 if (!(Pstatus(Pr)->pr_flags & PR_ISSYS)) {
 398                         struct totals t;
 399 
 400                         /*
 401                          * Since we're grabbing the process readonly, we need
 402                          * to make sure the address space doesn't change during
 403                          * execution.
 404                          */
 405                         if (Pstate(Pr) != PS_DEAD) {
 406                                 if (tries++ == MAX_TRIES) {
 407                                         Prelease(Pr, prr_flags);
 408                                         (void) close(mapfd);
 409                                         (void) fprintf(stderr, "%s: cannot "
 410                                             "examine %s: address space is "
 411                                             "changing\n", command, arg);
 412                                         continue;
 413                                 }
 414 
 415                                 if (fstat64(mapfd, &statbuf) != 0) {
 416                                         Prelease(Pr, prr_flags);
 417                                         (void) close(mapfd);
 418                                         (void) fprintf(stderr, "%s: cannot "
 419                                             "examine %s: lost control of "
 420                                             "process\n", command, arg);
 421                                         continue;
 422                                 }
 423                         }
 424 
 425                         nstacks = psinfo.pr_nlwp * 2;
 426                         stacks = calloc(nstacks, sizeof (stacks[0]));
 427                         if (stacks != NULL) {
 428                                 int n = 0;
 429                                 (void) Plwp_iter(Pr, getstack, &n);
 430                                 qsort(stacks, nstacks, sizeof (stacks[0]),
 431                                     cmpstacks);
 432                         }
 433 
 434                         (void) memset(&t, 0, sizeof (t));
 435 
 436                         if (Pgetauxval(Pr, AT_BASE) != -1L &&
 437                             Prd_agent(Pr) == NULL) {
 438                                 (void) fprintf(stderr, "%s: warning: "
 439                                     "librtld_db failed to initialize; "
 440                                     "shared library information will not be "
 441                                     "available\n", command);
 442                         }
 443 
 444                         /*
 445                          * Gather data
 446                          */
 447                         if (xflag)
 448                                 rc += xmapping_iter(Pr, gather_xmap, NULL, 0);
 449                         else if (Sflag)
 450                                 rc += xmapping_iter(Pr, gather_xmap, NULL, 1);
 451                         else {
 452                                 if (rflag)
 453                                         rc += rmapping_iter(Pr, gather_map,
 454                                             NULL);
 455                                 else if (sflag)
 456                                         rc += xmapping_iter(Pr, gather_xmap,
 457                                             NULL, 0);
 458                                 else if (lflag)
 459                                         rc += Pmapping_iter(Pr,
 460                                             gather_map, NULL);
 461                                 else
 462                                         rc += Pmapping_iter_resolved(Pr,
 463                                             gather_map, NULL);
 464                         }
 465 
 466                         /*
 467                          * Ensure mappings are consistent.
 468                          */
 469                         if (Pstate(Pr) != PS_DEAD) {
 470                                 struct stat64 newbuf;
 471 
 472                                 if (fstat64(mapfd, &newbuf) != 0 ||
 473                                     memcmp(&newbuf.st_mtim, &statbuf.st_mtim,
 474                                     sizeof (newbuf.st_mtim)) != 0) {
 475                                         if (stacks != NULL) {
 476                                                 free(stacks);
 477                                                 stacks = NULL;
 478                                         }
 479                                         goto again;
 480                                 }
 481                         }
 482 
 483                         /*
 484                          * Display data.
 485                          */
 486                         if (xflag) {
 487                                 (void) printf("%*s%*s%*s%*s%*s "
 488                                     "%sMode   Mapped File\n",
 489                                     addr_width, "Address",
 490                                     size_width, "Kbytes",
 491                                     size_width, "RSS",
 492                                     size_width, "Anon",
 493                                     size_width, "Locked",
 494                                     sflag ? "Pgsz " : "");
 495 
 496                                 rc += iter_xmap(sflag ?  look_xmap :
 497                                     look_xmap_nopgsz, &t);
 498 
 499                                 (void) printf("%s%s %s %s %s %s\n",
 500                                     addr_width == 8 ? "-" : "------",
 501                                     bar, bar, bar, bar, bar);
 502 
 503                                 (void) printf("%stotal Kb", addr_width == 16 ?
 504                                     "        " : "");
 505 
 506                                 printK(t.total_size, size_width);
 507                                 printK(t.total_rss, size_width);
 508                                 printK(t.total_anon, size_width);
 509                                 printK(t.total_locked, size_width);
 510 
 511                                 (void) printf("\n");
 512 
 513                         } else if (Sflag) {
 514                                 (void) printf("%*s%*s%*s Mode"
 515                                     " Mapped File\n",
 516                                     addr_width, "Address",
 517                                     size_width, "Kbytes",
 518                                     size_width, "Swap");
 519 
 520                                 rc += iter_xmap(look_xmap_nopgsz, &t);
 521 
 522                                 (void) printf("%s%s %s %s\n",
 523                                     addr_width == 8 ? "-" : "------",
 524                                     bar, bar, bar);
 525 
 526                                 (void) printf("%stotal Kb", addr_width == 16 ?
 527                                     "        " : "");
 528 
 529                                 printK(t.total_size, size_width);
 530                                 printK(t.total_swap, size_width);
 531 
 532                                 (void) printf("\n");
 533 
 534                         } else {
 535 
 536                                 if (rflag) {
 537                                         rc += iter_map(look_map, &t);
 538                                 } else if (sflag) {
 539                                         if (Lflag) {
 540                                                 (void) printf("%*s %*s %4s"
 541                                                     " %-6s %s %s\n",
 542                                                     addr_width, "Address",
 543                                                     size_width,
 544                                                     "Bytes", "Pgsz", "Mode ",
 545                                                     "Lgrp", "Mapped File");
 546                                                 rc += iter_xmap(look_smap, &t);
 547                                         } else {
 548                                                 (void) printf("%*s %*s %4s"
 549                                                     " %-6s %s\n",
 550                                                     addr_width, "Address",
 551                                                     size_width,
 552                                                     "Bytes", "Pgsz", "Mode ",
 553                                                     "Mapped File");
 554                                                 rc += iter_xmap(look_smap, &t);
 555                                         }
 556                                 } else {
 557                                         rc += iter_map(look_map, &t);
 558                                 }
 559 
 560                                 (void) printf(" %stotal  %*luK\n",
 561                                     addr_width == 16 ?
 562                                     "        " : "",
 563                                     size_width, t.total_size);
 564                         }
 565 
 566                         if (stacks != NULL) {
 567                                 free(stacks);
 568                                 stacks = NULL;
 569                         }
 570 
 571                 }
 572 
 573                 Prelease(Pr, prr_flags);
 574                 if (mapfd != -1)
 575                         (void) close(mapfd);
 576         }
 577 
 578         if (use_agent_lwp)
 579                 (void) proc_finistdio();
 580 
 581         return (rc);
 582 }
 583 
 584 static int
 585 rmapping_iter(struct ps_prochandle *Pr, proc_map_f *func, void *cd)
 586 {
 587         char mapname[PATH_MAX];
 588         int mapfd, nmap, i, rc;
 589         struct stat st;
 590         prmap_t *prmapp, *pmp;
 591         ssize_t n;
 592 
 593         (void) snprintf(mapname, sizeof (mapname),
 594             "/proc/%d/rmap", (int)Pstatus(Pr)->pr_pid);
 595 
 596         if ((mapfd = open(mapname, O_RDONLY)) < 0 || fstat(mapfd, &st) != 0) {
 597                 if (mapfd >= 0)
 598                         (void) close(mapfd);
 599                 return (perr(mapname));
 600         }
 601 
 602         nmap = st.st_size / sizeof (prmap_t);
 603         prmapp = malloc((nmap + 1) * sizeof (prmap_t));
 604 
 605         if ((n = pread(mapfd, prmapp, (nmap + 1) * sizeof (prmap_t), 0L)) < 0) {
 606                 (void) close(mapfd);
 607                 free(prmapp);
 608                 return (perr("read rmap"));
 609         }
 610 
 611         (void) close(mapfd);
 612         nmap = n / sizeof (prmap_t);
 613 
 614         for (i = 0, pmp = prmapp; i < nmap; i++, pmp++) {
 615                 if ((rc = func(cd, pmp, NULL)) != 0) {
 616                         free(prmapp);
 617                         return (rc);
 618                 }
 619         }
 620 
 621         free(prmapp);
 622         return (0);
 623 }
 624 
 625 static int
 626 xmapping_iter(struct ps_prochandle *Pr, proc_xmap_f *func, void *cd, int doswap)
 627 {
 628         char mapname[PATH_MAX];
 629         int mapfd, nmap, i, rc;
 630         struct stat st;
 631         prxmap_t *prmapp, *pmp;
 632         ssize_t n;
 633 
 634         (void) snprintf(mapname, sizeof (mapname),
 635             "/proc/%d/xmap", (int)Pstatus(Pr)->pr_pid);
 636 
 637         if ((mapfd = open(mapname, O_RDONLY)) < 0 || fstat(mapfd, &st) != 0) {
 638                 if (mapfd >= 0)
 639                         (void) close(mapfd);
 640                 return (perr(mapname));
 641         }
 642 
 643         nmap = st.st_size / sizeof (prxmap_t);
 644         nmap *= 2;
 645 again:
 646         prmapp = malloc((nmap + 1) * sizeof (prxmap_t));
 647 
 648         if ((n = pread(mapfd, prmapp, (nmap + 1) * sizeof (prxmap_t), 0)) < 0) {
 649                 (void) close(mapfd);
 650                 free(prmapp);
 651                 return (perr("read xmap"));
 652         }
 653 
 654         if (nmap < n / sizeof (prxmap_t)) {
 655                 free(prmapp);
 656                 nmap *= 2;
 657                 goto again;
 658         }
 659 
 660         (void) close(mapfd);
 661         nmap = n / sizeof (prxmap_t);
 662 
 663         for (i = 0, pmp = prmapp; i < nmap; i++, pmp++) {
 664                 if ((rc = func(cd, pmp, NULL, i == nmap - 1, doswap)) != 0) {
 665                         free(prmapp);
 666                         return (rc);
 667                 }
 668         }
 669 
 670         /*
 671          * Mark the last element.
 672          */
 673         if (map_count > 0)
 674                 maps[map_count - 1].md_last = B_TRUE;
 675 
 676         free(prmapp);
 677         return (0);
 678 }
 679 
 680 /*ARGSUSED*/
 681 static int
 682 look_map(void *data, const prmap_t *pmp, const char *object_name)
 683 {
 684         struct totals *t = data;
 685         const pstatus_t *Psp = Pstatus(Pr);
 686         size_t size;
 687         char mname[PATH_MAX];
 688         char *lname = NULL;
 689         size_t  psz = pmp->pr_pagesize;
 690         uintptr_t vaddr = pmp->pr_vaddr;
 691         uintptr_t segment_end = vaddr + pmp->pr_size;
 692         lgrp_id_t lgrp;
 693         memory_chunk_t mchunk;
 694 
 695         /*
 696          * If the mapping is not anon or not part of the heap, make a name
 697          * for it.  We don't want to report the heap as a.out's data.
 698          */
 699         if (!(pmp->pr_mflags & MA_ANON) ||
 700             segment_end <= Psp->pr_brkbase ||
 701             pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) {
 702                 lname = make_name(Pr, lflag, pmp->pr_vaddr, pmp->pr_mapname,
 703                     mname, sizeof (mname));
 704         }
 705 
 706         if (lname == NULL &&
 707             ((pmp->pr_mflags & MA_ANON) || Pstate(Pr) == PS_DEAD)) {
 708                 lname = anon_name(mname, Psp, stacks, nstacks, pmp->pr_vaddr,
 709                     pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid, NULL);
 710         }
 711 
 712         /*
 713          * Adjust the address range if -A is specified.
 714          */
 715         size = adjust_addr_range(pmp->pr_vaddr, segment_end, psz,
 716             &vaddr, &segment_end);
 717 
 718         if (size == 0)
 719                 return (0);
 720 
 721         if (!Lflag) {
 722                 /*
 723                  * Display the whole mapping
 724                  */
 725                 size = ROUNDUP_KB(size);
 726 
 727                 (void) printf(lname ?
 728                     "%.*lX %*luK %-6s %s\n" :
 729                     "%.*lX %*luK %s\n",
 730                     addr_width, vaddr,
 731                     size_width - 1, size, mflags(pmp->pr_mflags), lname);
 732 
 733                 t->total_size += size;
 734                 return (0);
 735         }
 736 
 737         /*
 738          * We need to display lgroups backing physical memory, so we break the
 739          * segment into individual pages and coalesce pages with the same lgroup
 740          * into one "segment".
 741          */
 742 
 743         /*
 744          * Initialize address descriptions for the mapping.
 745          */
 746         mem_chunk_init(&mchunk, segment_end, psz);
 747         size = 0;
 748 
 749         /*
 750          * Walk mapping (page by page) and display contiguous ranges of memory
 751          * allocated to same lgroup.
 752          */
 753         do {
 754                 size_t          size_contig;
 755 
 756                 /*
 757                  * Get contiguous region of memory starting from vaddr allocated
 758                  * from the same lgroup.
 759                  */
 760                 size_contig = get_contiguous_region(&mchunk, vaddr,
 761                     segment_end, pmp->pr_pagesize, &lgrp);
 762 
 763                 (void) printf(lname ? "%.*lX %*luK %-6s%s %s\n" :
 764                     "%.*lX %*luK %s %s\n",
 765                     addr_width, vaddr,
 766                     size_width - 1, size_contig / KILOBYTE,
 767                     mflags(pmp->pr_mflags),
 768                     lgrp2str(lgrp), lname);
 769 
 770                 vaddr += size_contig;
 771                 size += size_contig;
 772         } while (vaddr < segment_end && !interrupt);
 773 
 774         /* Update the total size */
 775         t->total_size += ROUNDUP_KB(size);
 776         return (0);
 777 }
 778 
 779 static void
 780 printK(long value, int width)
 781 {
 782         if (value == 0)
 783                 (void) printf(width == 8 ? "       -" : "          -");
 784         else
 785                 (void) printf(" %*lu", width - 1, value);
 786 }
 787 
 788 static const char *
 789 pagesize(const prxmap_t *pmp)
 790 {
 791         int pagesize = pmp->pr_hatpagesize;
 792         static char buf[32];
 793 
 794         if (pagesize == 0) {
 795                 return ("-"); /* no underlying HAT mapping */
 796         }
 797 
 798         if (pagesize >= KILOBYTE && (pagesize % KILOBYTE) == 0) {
 799                 if ((pagesize % GIGABYTE) == 0)
 800                         (void) snprintf(buf, sizeof (buf), "%dG",
 801                             pagesize / GIGABYTE);
 802                 else if ((pagesize % MEGABYTE) == 0)
 803                         (void) snprintf(buf, sizeof (buf), "%dM",
 804                             pagesize / MEGABYTE);
 805                 else
 806                         (void) snprintf(buf, sizeof (buf), "%dK",
 807                             pagesize / KILOBYTE);
 808         } else
 809                 (void) snprintf(buf, sizeof (buf), "%db", pagesize);
 810 
 811         return (buf);
 812 }
 813 
 814 /*ARGSUSED*/
 815 static int
 816 look_smap(void *data,
 817         const prxmap_t *pmp,
 818         const char *object_name,
 819         int last, int doswap)
 820 {
 821         struct totals *t = data;
 822         const pstatus_t *Psp = Pstatus(Pr);
 823         size_t size;
 824         char mname[PATH_MAX];
 825         char *lname = NULL;
 826         const char *format;
 827         size_t  psz = pmp->pr_pagesize;
 828         uintptr_t vaddr = pmp->pr_vaddr;
 829         uintptr_t segment_end = vaddr + pmp->pr_size;
 830         lgrp_id_t lgrp;
 831         memory_chunk_t mchunk;
 832 
 833         /*
 834          * If the mapping is not anon or not part of the heap, make a name
 835          * for it.  We don't want to report the heap as a.out's data.
 836          */
 837         if (!(pmp->pr_mflags & MA_ANON) ||
 838             pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase ||
 839             pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) {
 840                 lname = make_name(Pr, lflag, pmp->pr_vaddr, pmp->pr_mapname,
 841                     mname, sizeof (mname));
 842         }
 843 
 844         if (lname == NULL &&
 845             ((pmp->pr_mflags & MA_ANON) || Pstate(Pr) == PS_DEAD)) {
 846                 lname = anon_name(mname, Psp, stacks, nstacks, pmp->pr_vaddr,
 847                     pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid, NULL);
 848         }
 849 
 850         /*
 851          * Adjust the address range if -A is specified.
 852          */
 853         size = adjust_addr_range(pmp->pr_vaddr, segment_end, psz,
 854             &vaddr, &segment_end);
 855 
 856         if (size == 0)
 857                 return (0);
 858 
 859         if (!Lflag) {
 860                 /*
 861                  * Display the whole mapping
 862                  */
 863                 if (lname != NULL)
 864                         format = "%.*lX %*luK %4s %-6s %s\n";
 865                 else
 866                         format = "%.*lX %*luK %4s %s\n";
 867 
 868                 size = ROUNDUP_KB(size);
 869 
 870                 (void) printf(format, addr_width, vaddr, size_width - 1, size,
 871                     pagesize(pmp), mflags(pmp->pr_mflags), lname);
 872 
 873                 t->total_size += size;
 874                 return (0);
 875         }
 876 
 877         if (lname != NULL)
 878                 format = "%.*lX %*luK %4s %-6s%s %s\n";
 879         else
 880                 format = "%.*lX %*luK %4s%s %s\n";
 881 
 882         /*
 883          * We need to display lgroups backing physical memory, so we break the
 884          * segment into individual pages and coalesce pages with the same lgroup
 885          * into one "segment".
 886          */
 887 
 888         /*
 889          * Initialize address descriptions for the mapping.
 890          */
 891         mem_chunk_init(&mchunk, segment_end, psz);
 892         size = 0;
 893 
 894         /*
 895          * Walk mapping (page by page) and display contiguous ranges of memory
 896          * allocated to same lgroup.
 897          */
 898         do {
 899                 size_t          size_contig;
 900 
 901                 /*
 902                  * Get contiguous region of memory starting from vaddr allocated
 903                  * from the same lgroup.
 904                  */
 905                 size_contig = get_contiguous_region(&mchunk, vaddr,
 906                     segment_end, pmp->pr_pagesize, &lgrp);
 907 
 908                 (void) printf(format, addr_width, vaddr,
 909                     size_width - 1, size_contig / KILOBYTE,
 910                     pagesize(pmp), mflags(pmp->pr_mflags),
 911                     lgrp2str(lgrp), lname);
 912 
 913                 vaddr += size_contig;
 914                 size += size_contig;
 915         } while (vaddr < segment_end && !interrupt);
 916 
 917         t->total_size += ROUNDUP_KB(size);
 918         return (0);
 919 }
 920 
 921 #define ANON(x) ((aflag || (((x)->pr_mflags & MA_SHARED) == 0)) ? \
 922             ((x)->pr_anon) : 0)
 923 
 924 /*ARGSUSED*/
 925 static int
 926 look_xmap(void *data,
 927         const prxmap_t *pmp,
 928         const char *object_name,
 929         int last, int doswap)
 930 {
 931         struct totals *t = data;
 932         const pstatus_t *Psp = Pstatus(Pr);
 933         char mname[PATH_MAX];
 934         char *lname = NULL;
 935         char *ln;
 936 
 937         /*
 938          * If the mapping is not anon or not part of the heap, make a name
 939          * for it.  We don't want to report the heap as a.out's data.
 940          */
 941         if (!(pmp->pr_mflags & MA_ANON) ||
 942             pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase ||
 943             pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) {
 944                 lname = make_name(Pr, lflag, pmp->pr_vaddr, pmp->pr_mapname,
 945                     mname, sizeof (mname));
 946         }
 947 
 948         if (lname != NULL) {
 949                 if ((ln = strrchr(lname, '/')) != NULL)
 950                         lname = ln + 1;
 951         } else if ((pmp->pr_mflags & MA_ANON) || Pstate(Pr) == PS_DEAD) {
 952                 lname = anon_name(mname, Psp, stacks, nstacks, pmp->pr_vaddr,
 953                     pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid, NULL);
 954         }
 955 
 956         (void) printf("%.*lX", addr_width, (ulong_t)pmp->pr_vaddr);
 957 
 958         printK(ROUNDUP_KB(pmp->pr_size), size_width);
 959         printK(pmp->pr_rss * (pmp->pr_pagesize / KILOBYTE), size_width);
 960         printK(ANON(pmp) * (pmp->pr_pagesize / KILOBYTE), size_width);
 961         printK(pmp->pr_locked * (pmp->pr_pagesize / KILOBYTE), size_width);
 962         (void) printf(lname ? " %4s %-6s %s\n" : " %4s %s\n",
 963             pagesize(pmp), mflags(pmp->pr_mflags), lname);
 964 
 965         t->total_size += ROUNDUP_KB(pmp->pr_size);
 966         t->total_rss += pmp->pr_rss * (pmp->pr_pagesize / KILOBYTE);
 967         t->total_anon += ANON(pmp) * (pmp->pr_pagesize / KILOBYTE);
 968         t->total_locked += (pmp->pr_locked * (pmp->pr_pagesize / KILOBYTE));
 969 
 970         return (0);
 971 }
 972 
 973 /*ARGSUSED*/
 974 static int
 975 look_xmap_nopgsz(void *data,
 976         const prxmap_t *pmp,
 977         const char *object_name,
 978         int last, int doswap)
 979 {
 980         struct totals *t = data;
 981         const pstatus_t *Psp = Pstatus(Pr);
 982         char mname[PATH_MAX];
 983         char *lname = NULL;
 984         char *ln;
 985         static uintptr_t prev_vaddr;
 986         static size_t prev_size;
 987         static offset_t prev_offset;
 988         static int prev_mflags;
 989         static char *prev_lname;
 990         static char prev_mname[PATH_MAX];
 991         static ulong_t prev_rss;
 992         static ulong_t prev_anon;
 993         static ulong_t prev_locked;
 994         static ulong_t prev_swap;
 995         int merged = 0;
 996         static int first = 1;
 997         ulong_t swap = 0;
 998         int kperpage;
 999 
1000         /*
1001          * Calculate swap reservations
1002          */
1003         if (pmp->pr_mflags & MA_SHARED) {
1004                 if (aflag && (pmp->pr_mflags & MA_NORESERVE) == 0) {
1005                         /* Swap reserved for entire non-ism SHM */
1006                         swap = pmp->pr_size / pmp->pr_pagesize;
1007                 }
1008         } else if (pmp->pr_mflags & MA_NORESERVE) {
1009                 /* Swap reserved on fault for each anon page */
1010                 swap = pmp->pr_anon;
1011         } else if (pmp->pr_mflags & MA_WRITE) {
1012                 /* Swap reserve for entire writable segment */
1013                 swap = pmp->pr_size / pmp->pr_pagesize;
1014         }
1015 
1016         /*
1017          * If the mapping is not anon or not part of the heap, make a name
1018          * for it.  We don't want to report the heap as a.out's data.
1019          */
1020         if (!(pmp->pr_mflags & MA_ANON) ||
1021             pmp->pr_vaddr + pmp->pr_size <= Psp->pr_brkbase ||
1022             pmp->pr_vaddr >= Psp->pr_brkbase + Psp->pr_brksize) {
1023                 lname = make_name(Pr, lflag, pmp->pr_vaddr, pmp->pr_mapname,
1024                     mname, sizeof (mname));
1025         }
1026 
1027         if (lname != NULL) {
1028                 if ((ln = strrchr(lname, '/')) != NULL)
1029                         lname = ln + 1;
1030         } else if ((pmp->pr_mflags & MA_ANON) || Pstate(Pr) == PS_DEAD) {
1031                 lname = anon_name(mname, Psp, stacks, nstacks, pmp->pr_vaddr,
1032                     pmp->pr_size, pmp->pr_mflags, pmp->pr_shmid, NULL);
1033         }
1034 
1035         kperpage = pmp->pr_pagesize / KILOBYTE;
1036 
1037         t->total_size += ROUNDUP_KB(pmp->pr_size);
1038         t->total_rss += pmp->pr_rss * kperpage;
1039         t->total_anon += ANON(pmp) * kperpage;
1040         t->total_locked += pmp->pr_locked * kperpage;
1041         t->total_swap += swap * kperpage;
1042 
1043         if (first == 1) {
1044                 first = 0;
1045                 prev_vaddr = pmp->pr_vaddr;
1046                 prev_size = pmp->pr_size;
1047                 prev_offset = pmp->pr_offset;
1048                 prev_mflags = pmp->pr_mflags;
1049                 if (lname == NULL) {
1050                         prev_lname = NULL;
1051                 } else {
1052                         (void) strcpy(prev_mname, lname);
1053                         prev_lname = prev_mname;
1054                 }
1055                 prev_rss = pmp->pr_rss * kperpage;
1056                 prev_anon = ANON(pmp) * kperpage;
1057                 prev_locked = pmp->pr_locked * kperpage;
1058                 prev_swap = swap * kperpage;
1059                 if (last == 0) {
1060                         return (0);
1061                 }
1062                 merged = 1;
1063         } else if (prev_vaddr + prev_size == pmp->pr_vaddr &&
1064             prev_mflags == pmp->pr_mflags &&
1065             ((prev_mflags & MA_ISM) ||
1066             prev_offset + prev_size == pmp->pr_offset) &&
1067             ((lname == NULL && prev_lname == NULL) ||
1068             (lname != NULL && prev_lname != NULL &&
1069             strcmp(lname, prev_lname) == 0))) {
1070                 prev_size += pmp->pr_size;
1071                 prev_rss += pmp->pr_rss * kperpage;
1072                 prev_anon += ANON(pmp) * kperpage;
1073                 prev_locked += pmp->pr_locked * kperpage;
1074                 prev_swap += swap * kperpage;
1075                 if (last == 0) {
1076                         return (0);
1077                 }
1078                 merged = 1;
1079         }
1080 
1081         (void) printf("%.*lX", addr_width, (ulong_t)prev_vaddr);
1082         printK(ROUNDUP_KB(prev_size), size_width);
1083 
1084         if (doswap)
1085                 printK(prev_swap, size_width);
1086         else {
1087                 printK(prev_rss, size_width);
1088                 printK(prev_anon, size_width);
1089                 printK(prev_locked, size_width);
1090         }
1091         (void) printf(prev_lname ? " %-6s %s\n" : "%s\n",
1092             mflags(prev_mflags), prev_lname);
1093 
1094         if (last == 0) {
1095                 prev_vaddr = pmp->pr_vaddr;
1096                 prev_size = pmp->pr_size;
1097                 prev_offset = pmp->pr_offset;
1098                 prev_mflags = pmp->pr_mflags;
1099                 if (lname == NULL) {
1100                         prev_lname = NULL;
1101                 } else {
1102                         (void) strcpy(prev_mname, lname);
1103                         prev_lname = prev_mname;
1104                 }
1105                 prev_rss = pmp->pr_rss * kperpage;
1106                 prev_anon = ANON(pmp) * kperpage;
1107                 prev_locked = pmp->pr_locked * kperpage;
1108                 prev_swap = swap * kperpage;
1109         } else if (merged == 0) {
1110                 (void) printf("%.*lX", addr_width, (ulong_t)pmp->pr_vaddr);
1111                 printK(ROUNDUP_KB(pmp->pr_size), size_width);
1112                 if (doswap)
1113                         printK(swap * kperpage, size_width);
1114                 else {
1115                         printK(pmp->pr_rss * kperpage, size_width);
1116                         printK(ANON(pmp) * kperpage, size_width);
1117                         printK(pmp->pr_locked * kperpage, size_width);
1118                 }
1119                 (void) printf(lname ? " %-6s %s\n" : " %s\n",
1120                     mflags(pmp->pr_mflags), lname);
1121         }
1122 
1123         if (last != 0)
1124                 first = 1;
1125 
1126         return (0);
1127 }
1128 
1129 static int
1130 perr(char *s)
1131 {
1132         if (s)
1133                 (void) fprintf(stderr, "%s: ", procname);
1134         else
1135                 s = procname;
1136         perror(s);
1137         return (1);
1138 }
1139 
1140 static char *
1141 mflags(uint_t arg)
1142 {
1143         static char code_buf[80];
1144         char *str = code_buf;
1145 
1146         /*
1147          * rwxsR
1148          *
1149          * r - segment is readable
1150          * w - segment is writable
1151          * x - segment is executable
1152          * s - segment is shared
1153          * R - segment is mapped MAP_NORESERVE
1154          *
1155          */
1156         (void) sprintf(str, "%c%c%c%c%c%c",
1157             arg & MA_READ ? 'r' : '-',
1158             arg & MA_WRITE ? 'w' : '-',
1159             arg & MA_EXEC ? 'x' : '-',
1160             arg & MA_SHARED ? 's' : '-',
1161             arg & MA_NORESERVE ? 'R' : '-',
1162             arg & MA_RESERVED1 ? '*' : ' ');
1163 
1164         return (str);
1165 }
1166 
1167 static mapdata_t *
1168 nextmap(void)
1169 {
1170         mapdata_t *newmaps;
1171         int next;
1172 
1173         if (map_count == map_alloc) {
1174                 if (map_alloc == 0)
1175                         next = 16;
1176                 else
1177                         next = map_alloc * 2;
1178 
1179                 newmaps = realloc(maps, next * sizeof (mapdata_t));
1180                 if (newmaps == NULL) {
1181                         (void) perr("failed to allocate maps");
1182                         exit(1);
1183                 }
1184                 (void) memset(newmaps + map_alloc, '\0',
1185                     (next - map_alloc) * sizeof (mapdata_t));
1186 
1187                 map_alloc = next;
1188                 maps = newmaps;
1189         }
1190 
1191         return (&maps[map_count++]);
1192 }
1193 
1194 /*ARGSUSED*/
1195 static int
1196 gather_map(void *ignored, const prmap_t *map, const char *objname)
1197 {
1198         mapdata_t *data;
1199 
1200         /* Skip mappings which are outside the range specified by -A */
1201         if (!address_in_range(map->pr_vaddr,
1202             map->pr_vaddr + map->pr_size, map->pr_pagesize))
1203                 return (0);
1204 
1205         data = nextmap();
1206         data->md_map = *map;
1207         if (data->md_objname != NULL)
1208                 free(data->md_objname);
1209         data->md_objname = objname ? strdup(objname) : NULL;
1210 
1211         return (0);
1212 }
1213 
1214 /*ARGSUSED*/
1215 static int
1216 gather_xmap(void *ignored, const prxmap_t *xmap, const char *objname,
1217     int last, int doswap)
1218 {
1219         mapdata_t *data;
1220 
1221         /* Skip mappings which are outside the range specified by -A */
1222         if (!address_in_range(xmap->pr_vaddr,
1223             xmap->pr_vaddr + xmap->pr_size, xmap->pr_pagesize))
1224                 return (0);
1225 
1226         data = nextmap();
1227         data->md_xmap = *xmap;
1228         if (data->md_objname != NULL)
1229                 free(data->md_objname);
1230         data->md_objname = objname ? strdup(objname) : NULL;
1231         data->md_last = last;
1232         data->md_doswap = doswap;
1233 
1234         return (0);
1235 }
1236 
1237 static int
1238 iter_map(proc_map_f *func, void *data)
1239 {
1240         int i;
1241         int ret;
1242 
1243         for (i = 0; i < map_count; i++) {
1244                 if (interrupt)
1245                         break;
1246                 if ((ret = func(data, &maps[i].md_map,
1247                     maps[i].md_objname)) != 0)
1248                         return (ret);
1249         }
1250 
1251         return (0);
1252 }
1253 
1254 static int
1255 iter_xmap(proc_xmap_f *func, void *data)
1256 {
1257         int i;
1258         int ret;
1259 
1260         for (i = 0; i < map_count; i++) {
1261                 if (interrupt)
1262                         break;
1263                 if ((ret = func(data, &maps[i].md_xmap, maps[i].md_objname,
1264                     maps[i].md_last, maps[i].md_doswap)) != 0)
1265                         return (ret);
1266         }
1267 
1268         return (0);
1269 }
1270 
1271 /*
1272  * Convert lgroup ID to string.
1273  * returns dash when lgroup ID is invalid.
1274  */
1275 static char *
1276 lgrp2str(lgrp_id_t lgrp)
1277 {
1278         static char lgrp_buf[20];
1279         char *str = lgrp_buf;
1280 
1281         (void) sprintf(str, lgrp == LGRP_NONE ? "   -" : "%4d", lgrp);
1282         return (str);
1283 }
1284 
1285 /*
1286  * Parse address range specification for -A option.
1287  * The address range may have the following forms:
1288  *
1289  * address
1290  *      start and end is set to address
1291  * address,
1292  *      start is set to address, end is set to INVALID_ADDRESS
1293  * ,address
1294  *      start is set to 0, end is set to address
1295  * address1,address2
1296  *      start is set to address1, end is set to address2
1297  *
1298  */
1299 static int
1300 parse_addr_range(char *input_str, uintptr_t *start, uintptr_t *end)
1301 {
1302         char *startp = input_str;
1303         char *endp = strchr(input_str, ',');
1304         ulong_t s = (ulong_t)INVALID_ADDRESS;
1305         ulong_t e = (ulong_t)INVALID_ADDRESS;
1306 
1307         if (endp != NULL) {
1308                 /*
1309                  * Comma is present. If there is nothing after comma, the end
1310                  * remains set at INVALID_ADDRESS. Otherwise it is set to the
1311                  * value after comma.
1312                  */
1313                 *endp = '\0';
1314                 endp++;
1315 
1316                 if ((*endp != '\0') && sscanf(endp, "%lx", &e) != 1)
1317                         return (1);
1318         }
1319 
1320         if (startp != NULL) {
1321                 /*
1322                  * Read the start address, if it is specified. If the address is
1323                  * missing, start will be set to INVALID_ADDRESS.
1324                  */
1325                 if ((*startp != '\0') && sscanf(startp, "%lx", &s) != 1)
1326                         return (1);
1327         }
1328 
1329         /* If there is no comma, end becomes equal to start */
1330         if (endp == NULL)
1331                 e = s;
1332 
1333         /*
1334          * ,end implies 0..end range
1335          */
1336         if (e != INVALID_ADDRESS && s == INVALID_ADDRESS)
1337                 s = 0;
1338 
1339         *start = (uintptr_t)s;
1340         *end = (uintptr_t)e;
1341 
1342         /* Return error if neither start nor end address were specified */
1343         return (! (s != INVALID_ADDRESS || e != INVALID_ADDRESS));
1344 }
1345 
1346 /*
1347  * Check whether any portion of [start, end] segment is within the
1348  * [start_addr, end_addr] range.
1349  *
1350  * Return values:
1351  *   0 - address is outside the range
1352  *   1 - address is within the range
1353  */
1354 static int
1355 address_in_range(uintptr_t start, uintptr_t end, size_t psz)
1356 {
1357         int rc = 1;
1358 
1359         /*
1360          *  Nothing to do if there is no address range specified with -A
1361          */
1362         if (start_addr != INVALID_ADDRESS || end_addr != INVALID_ADDRESS) {
1363                 /* The segment end is below the range start */
1364                 if ((start_addr != INVALID_ADDRESS) &&
1365                     (end < P2ALIGN(start_addr, psz)))
1366                         rc = 0;
1367 
1368                 /* The segment start is above the range end */
1369                 if ((end_addr != INVALID_ADDRESS) &&
1370                     (start > P2ALIGN(end_addr + psz, psz)))
1371                         rc = 0;
1372         }
1373         return (rc);
1374 }
1375 
1376 /*
1377  * Returns an intersection of the [start, end] interval and the range specified
1378  * by -A flag [start_addr, end_addr]. Unspecified parts of the address range
1379  * have value INVALID_ADDRESS.
1380  *
1381  * The start_addr address is rounded down to the beginning of page and end_addr
1382  * is rounded up to the end of page.
1383  *
1384  * Returns the size of the resulting interval or zero if the interval is empty
1385  * or invalid.
1386  */
1387 static size_t
1388 adjust_addr_range(uintptr_t start, uintptr_t end, size_t psz,
1389     uintptr_t *new_start, uintptr_t *new_end)
1390 {
1391         uintptr_t from;         /* start_addr rounded down */
1392         uintptr_t to;           /* end_addr rounded up */
1393 
1394         /*
1395          * Round down the lower address of the range to the beginning of page.
1396          */
1397         if (start_addr == INVALID_ADDRESS) {
1398                 /*
1399                  * No start_addr specified by -A, the lower part of the interval
1400                  * does not change.
1401                  */
1402                 *new_start = start;
1403         } else {
1404                 from = P2ALIGN(start_addr, psz);
1405                 /*
1406                  * If end address is outside the range, return an empty
1407                  * interval
1408                  */
1409                 if (end <  from) {
1410                         *new_start = *new_end = 0;
1411                         return (0);
1412                 }
1413                 /*
1414                  * The adjusted start address is the maximum of requested start
1415                  * and the aligned start_addr of the -A range.
1416                  */
1417                 *new_start = start < from ? from : start;
1418         }
1419 
1420         /*
1421          * Round up the higher address of the range to the end of page.
1422          */
1423         if (end_addr == INVALID_ADDRESS) {
1424                 /*
1425                  * No end_addr specified by -A, the upper part of the interval
1426                  * does not change.
1427                  */
1428                 *new_end = end;
1429         } else {
1430                 /*
1431                  * If only one address is specified and it is the beginning of a
1432                  * segment, get information about the whole segment. This
1433                  * function is called once per segment and the 'end' argument is
1434                  * always the end of a segment, so just use the 'end' value.
1435                  */
1436                 to = (end_addr == start_addr && start == start_addr) ?
1437                     end :
1438                     P2ALIGN(end_addr + psz, psz);
1439                 /*
1440                  * If start address is outside the range, return an empty
1441                  * interval
1442                  */
1443                 if (start > to) {
1444                         *new_start = *new_end = 0;
1445                         return (0);
1446                 }
1447                 /*
1448                  * The adjusted end address is the minimum of requested end
1449                  * and the aligned end_addr of the -A range.
1450                  */
1451                 *new_end = end > to ? to : end;
1452         }
1453 
1454         /*
1455          * Make sure that the resulting interval is legal.
1456          */
1457         if (*new_end < *new_start)
1458                         *new_start = *new_end = 0;
1459 
1460         /* Return the size of the interval */
1461         return (*new_end - *new_start);
1462 }
1463 
1464 /*
1465  * Initialize memory_info data structure with information about a new segment.
1466  */
1467 static void
1468 mem_chunk_init(memory_chunk_t *chunk, uintptr_t end, size_t psz)
1469 {
1470         chunk->end_addr = end;
1471         chunk->page_size = psz;
1472         chunk->page_index = 0;
1473         chunk->chunk_start = chunk->chunk_end = 0;
1474 }
1475 
1476 /*
1477  * Create a new chunk of addresses starting from vaddr.
1478  * Pass the whole chunk to pr_meminfo to collect lgroup and page size
1479  * information for each page in the chunk.
1480  */
1481 static void
1482 mem_chunk_get(memory_chunk_t *chunk, uintptr_t vaddr)
1483 {
1484         page_descr_t    *pdp = chunk->page_info;
1485         size_t          psz = chunk->page_size;
1486         uintptr_t       addr = vaddr;
1487         uint64_t        inaddr[MAX_MEMINFO_CNT];
1488         uint64_t        outdata[2 * MAX_MEMINFO_CNT];
1489         uint_t          info[2] = { MEMINFO_VLGRP, MEMINFO_VPAGESIZE };
1490         uint_t          validity[MAX_MEMINFO_CNT];
1491         uint64_t        *dataptr = inaddr;
1492         uint64_t        *outptr = outdata;
1493         uint_t          *valptr = validity;
1494         int             i, j, rc;
1495 
1496         chunk->chunk_start = vaddr;
1497         chunk->page_index = 0;       /* reset index for the new chunk */
1498 
1499         /*
1500          * Fill in MAX_MEMINFO_CNT wotrh of pages starting from vaddr. Also,
1501          * copy starting address of each page to inaddr array for pr_meminfo.
1502          */
1503         for (i = 0, pdp = chunk->page_info;
1504             (i < MAX_MEMINFO_CNT) && (addr <= chunk->end_addr);
1505             i++, pdp++, dataptr++, addr += psz) {
1506                 *dataptr = (uint64_t)addr;
1507                 pdp->pd_start = addr;
1508                 pdp->pd_lgrp = LGRP_NONE;
1509                 pdp->pd_valid = 0;
1510                 pdp->pd_pagesize = 0;
1511         }
1512 
1513         /* Mark the number of entries in the chunk and the last address */
1514         chunk->page_count = i;
1515         chunk->chunk_end = addr - psz;
1516 
1517         if (interrupt)
1518                 return;
1519 
1520         /* Call meminfo for all collected addresses */
1521         rc = pr_meminfo(Pr, inaddr, i, info, 2, outdata, validity);
1522         if (rc < 0) {
1523                 (void) perr("can not get memory information");
1524                 return;
1525         }
1526 
1527         /* Verify validity of each result and fill in the addrs array */
1528         pdp = chunk->page_info;
1529         for (j = 0; j < i; j++, pdp++, valptr++, outptr += 2) {
1530                 /* Skip invalid address pointers */
1531                 if ((*valptr & 1) == 0) {
1532                         continue;
1533                 }
1534 
1535                 /* Is lgroup information available? */
1536                 if ((*valptr & 2) != 0) {
1537                         pdp->pd_lgrp = (lgrp_id_t)*outptr;
1538                         pdp->pd_valid = 1;
1539                 }
1540 
1541                 /* Is page size informaion available? */
1542                 if ((*valptr & 4) != 0) {
1543                         pdp->pd_pagesize = *(outptr + 1);
1544                 }
1545         }
1546 }
1547 
1548 /*
1549  * Starting from address 'vaddr' find the region with pages allocated from the
1550  * same lgroup.
1551  *
1552  * Arguments:
1553  *      mchunk          Initialized memory chunk structure
1554  *      vaddr           Starting address of the region
1555  *      maxaddr         Upper bound of the region
1556  *      pagesize        Default page size to use
1557  *      ret_lgrp        On exit contains the lgroup ID of all pages in the
1558  *                      region.
1559  *
1560  * Returns:
1561  *      Size of the contiguous region in bytes
1562  *      The lgroup ID of all pages in the region in ret_lgrp argument.
1563  */
1564 static size_t
1565 get_contiguous_region(memory_chunk_t *mchunk, uintptr_t vaddr,
1566     uintptr_t maxaddr, size_t pagesize, lgrp_id_t *ret_lgrp)
1567 {
1568         size_t          size_contig = 0;
1569         lgrp_id_t       lgrp;           /* Lgroup of the region start */
1570         lgrp_id_t       curr_lgrp;      /* Lgroup of the current page */
1571         size_t          psz = pagesize; /* Pagesize to use */
1572 
1573         /* Set both lgroup IDs to the lgroup of the first page */
1574         curr_lgrp = lgrp = addr_to_lgrp(mchunk, vaddr, &psz);
1575 
1576         /*
1577          * Starting from vaddr, walk page by page until either the end
1578          * of the segment is reached or a page is allocated from a different
1579          * lgroup. Also stop if interrupted from keyboard.
1580          */
1581         while ((vaddr < maxaddr) && (curr_lgrp == lgrp) && !interrupt) {
1582                 /*
1583                  * Get lgroup ID and the page size of the current page.
1584                  */
1585                 curr_lgrp = addr_to_lgrp(mchunk, vaddr, &psz);
1586                 /* If there is no page size information, use the default */
1587                 if (psz == 0)
1588                         psz = pagesize;
1589 
1590                 if (curr_lgrp == lgrp) {
1591                         /*
1592                          * This page belongs to the contiguous region.
1593                          * Increase the region size and advance to the new page.
1594                          */
1595                         size_contig += psz;
1596                         vaddr += psz;
1597                 }
1598         }
1599 
1600         /* Return the region lgroup ID and the size */
1601         *ret_lgrp = lgrp;
1602         return (size_contig);
1603 }
1604 
1605 /*
1606  * Given a virtual address, return its lgroup and page size. If there is meminfo
1607  * information for an address, use it, otherwise shift the chunk window to the
1608  * vaddr and create a new chunk with known meminfo information.
1609  */
1610 static lgrp_id_t
1611 addr_to_lgrp(memory_chunk_t *chunk, uintptr_t vaddr, size_t *psz)
1612 {
1613         page_descr_t *pdp;
1614         lgrp_id_t lgrp = LGRP_NONE;
1615         int i;
1616 
1617         *psz = chunk->page_size;
1618 
1619         if (interrupt)
1620                 return (0);
1621 
1622         /*
1623          * Is there information about this address? If not, create a new chunk
1624          * starting from vaddr and apply pr_meminfo() to the whole chunk.
1625          */
1626         if (vaddr < chunk->chunk_start || vaddr > chunk->chunk_end) {
1627                 /*
1628                  * This address is outside the chunk, get the new chunk and
1629                  * collect meminfo information for it.
1630                  */
1631                 mem_chunk_get(chunk, vaddr);
1632         }
1633 
1634         /*
1635          * Find information about the address.
1636          */
1637         pdp = &chunk->page_info[chunk->page_index];
1638         for (i = chunk->page_index; i < chunk->page_count; i++, pdp++) {
1639                 if (pdp->pd_start == vaddr) {
1640                         if (pdp->pd_valid) {
1641                                 lgrp = pdp->pd_lgrp;
1642                                 /*
1643                                  * Override page size information if it is
1644                                  * present.
1645                                  */
1646                                 if (pdp->pd_pagesize > 0)
1647                                         *psz = pdp->pd_pagesize;
1648                         }
1649                         break;
1650                 }
1651         }
1652         /*
1653          * Remember where we ended - the next search will start here.
1654          * We can query for the lgrp for the same address again, so do not
1655          * advance index past the current value.
1656          */
1657         chunk->page_index = i;
1658 
1659         return (lgrp);
1660 }
1661 
1662 /* ARGSUSED */
1663 static void
1664 intr(int sig)
1665 {
1666         interrupt = 1;
1667 }