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