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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/stat.h>
  28 #include <stdlib.h>
  29 #include <fcntl.h>
  30 #include <errno.h>
  31 #include <stdio.h>
  32 #include <unistd.h>
  33 #include <string.h>
  34 #include <strings.h>
  35 #include <libintl.h>
  36 #include <locale.h>
  37 
  38 #include "rcapd.h"
  39 #include "utils.h"
  40 #include "rcapd_stat.h"
  41 #include "statcommon.h"
  42 
  43 static char mode[RC_MODE_LEN];
  44 static rcapd_stat_hdr_t hdr;
  45 static int global;
  46 static int unformatted;
  47 static time_t stat_mod = 0;
  48 
  49 static uint_t timestamp_fmt = NODATE;
  50 
  51 typedef struct col {
  52         rcid_t          col_id;
  53         char            col_name[LC_NAME_LEN];
  54         uint64_t        col_nproc;
  55         uint64_t        col_vmsize;
  56         uint64_t        col_rsssize;
  57         uint64_t        col_rsslimit;
  58         uint64_t        col_paged_eff;
  59         uint64_t        col_paged_eff_old;
  60         uint64_t        col_paged_eff_avg;
  61         uint64_t        col_paged_att;
  62         uint64_t        col_paged_att_old;
  63         uint64_t        col_paged_att_avg;
  64         uint64_t        col_count;
  65         int             col_fresh;
  66         struct col      *col_next;
  67         struct col      *col_prev;
  68         lcollection_stat_t      col_src_stat;
  69         lcollection_stat_t      col_old_stat;
  70 } col_t;
  71 
  72 static col_t *col_head;
  73 static int ncol;
  74 
  75 static col_t *
  76 col_find(rcid_t id)
  77 {
  78         col_t *col;
  79         for (col = col_head; col != NULL; col = col->col_next)
  80                 if (col->col_id.rcid_type == id.rcid_type &&
  81                     col->col_id.rcid_val == id.rcid_val)
  82                         return (col);
  83         return (NULL);
  84 }
  85 
  86 static col_t *
  87 col_insert(rcid_t id)
  88 {
  89         col_t *new_col;
  90 
  91         new_col = malloc(sizeof (col_t));
  92         if (new_col == NULL) {
  93                 (void) fprintf(stderr, gettext("rcapstat: malloc() failed\n"));
  94                 exit(E_ERROR);
  95         }
  96         (void) memset(new_col, 0, sizeof (col_t));
  97         new_col->col_next = col_head;
  98         new_col->col_id = id;
  99         if (col_head != NULL)
 100                 col_head->col_prev = new_col;
 101         col_head = new_col;
 102         ncol++;
 103         return (new_col);
 104 }
 105 
 106 static void
 107 col_remove(col_t *col)
 108 {
 109         if (col->col_prev != NULL)
 110                 col->col_prev->col_next = col->col_next;
 111         if (col->col_next != NULL)
 112                 col->col_next->col_prev = col->col_prev;
 113         if (col_head == col)
 114                 col_head = col->col_next;
 115         ncol--;
 116         free(col);
 117 }
 118 
 119 static void
 120 usage()
 121 {
 122         (void) fprintf(stderr,
 123             gettext("usage: rcapstat [-g] [-p | -z] [-T d|u] "
 124             "[interval [count]]\n"));
 125         exit(E_USAGE);
 126 }
 127 
 128 static void
 129 format_size(char *str, uint64_t size, int length)
 130 {
 131         char tag = 'K';
 132         if (size >= 10000) {
 133                 size = (size + 512) / 1024;
 134                 tag = 'M';
 135                 if (size >= 10000) {
 136                         size = (size + 512) / 1024;
 137                         tag = 'G';
 138                 }
 139         }
 140         (void) snprintf(str, length, "%4lld%c", size, tag);
 141 }
 142 
 143 static int
 144 read_stats(rcid_type_t stat_type)
 145 {
 146         int fd;
 147         int proc_fd;
 148         char procfile[20];
 149         uint64_t pid;
 150         col_t *col, *col_next;
 151         lcollection_report_t report;
 152         struct stat st;
 153 
 154         if ((fd = open(STAT_FILE_DEFAULT, O_RDONLY)) < 0) {
 155                 warn(gettext("rcapd is not active\n"));
 156                 return (E_ERROR);
 157         }
 158 
 159         if (fstat(fd, &st) == 0)
 160                 stat_mod = st.st_mtime;
 161 
 162         if (read(fd, &hdr, sizeof (hdr)) != sizeof (hdr)) {
 163                 (void) fprintf(stderr,
 164                     gettext("rcapstat: can't read stat file header: %s\n"),
 165                     strerror(errno));
 166                 (void) close(fd);
 167                 return (E_ERROR);
 168         }
 169 
 170         /*
 171          * Check if rcapd is running
 172          */
 173         pid = hdr.rs_pid;
 174         (void) snprintf(procfile, 20, "/proc/%lld/psinfo", pid);
 175         if ((proc_fd = open(procfile, O_RDONLY)) < 0) {
 176                 warn(gettext("rcapd is not active\n"));
 177                 (void) close(fd);
 178                 return (E_ERROR);
 179         }
 180         (void) close(proc_fd);
 181 
 182         (void) strncpy(mode, hdr.rs_mode, RC_MODE_LEN);
 183         for (col = col_head; col != NULL; col = col->col_next) {
 184                 col->col_fresh = 0;
 185                 col->col_paged_eff = 0;
 186                 col->col_paged_att = 0;
 187         }
 188 
 189         while (read(fd, &report, sizeof (report)) == sizeof (report)) {
 190                 if (report.lcol_id.rcid_type != stat_type)
 191                         continue;
 192 
 193                 col = col_find(report.lcol_id);
 194                 if (col == NULL) {
 195                         col = col_insert(report.lcol_id);
 196                         col->col_paged_eff_old = col->col_paged_eff =
 197                             report.lcol_stat.lcols_pg_eff;
 198                         col->col_paged_att_old = col->col_paged_att =
 199                             report.lcol_stat.lcols_pg_att;
 200                         col->col_count = 0;
 201                 }
 202                 (void) strncpy(col->col_name, report.lcol_name, LC_NAME_LEN);
 203                 col->col_vmsize = report.lcol_image_size;
 204                 col->col_rsssize = report.lcol_rss;
 205                 col->col_rsslimit = report.lcol_rss_cap;
 206                 col->col_fresh = 1;
 207                 if (report.lcol_stat.lcols_pg_eff > col->col_paged_eff_old) {
 208                         col->col_paged_eff =
 209                             report.lcol_stat.lcols_pg_eff -
 210                             col->col_paged_eff_old;
 211                         if (report.lcol_stat.lcols_scan_count > col->col_count)
 212                                 col->col_paged_eff_avg =
 213                                     col->col_paged_eff /
 214                                     (report.lcol_stat.lcols_scan_count -
 215                                     col->col_count);
 216                 } else {
 217                         col->col_paged_eff_avg = 0;
 218                 }
 219                 if (report.lcol_stat.lcols_pg_att > col->col_paged_att_old) {
 220                         col->col_paged_att =
 221                             report.lcol_stat.lcols_pg_att -
 222                             col->col_paged_att_old;
 223                         if (report.lcol_stat.lcols_scan_count > col->col_count)
 224                                 col->col_paged_att_avg =
 225                                     col->col_paged_att /
 226                                     (report.lcol_stat.lcols_scan_count -
 227                                     col->col_count);
 228                 } else {
 229                         col->col_paged_att_avg = 0;
 230                 }
 231                 col->col_paged_eff_old = report.lcol_stat.lcols_pg_eff;
 232                 col->col_paged_att_old = report.lcol_stat.lcols_pg_att;
 233                 col->col_nproc =
 234                     report.lcol_stat.lcols_proc_in -
 235                     report.lcol_stat.lcols_proc_out;
 236                 col->col_count = report.lcol_stat.lcols_scan_count;
 237                 col->col_src_stat = report.lcol_stat;
 238         }
 239 
 240         /*
 241          * Remove stale data
 242          */
 243         col = col_head;
 244         while (col != NULL) {
 245                 col_next = col->col_next;
 246                 if (col->col_fresh == 0)
 247                         col_remove(col);
 248                 col = col_next;
 249         }
 250         (void) close(fd);
 251         return (E_SUCCESS);
 252 }
 253 
 254 /*
 255  * Print each collection's interval statistics.
 256  */
 257 /*ARGSUSED*/
 258 static void
 259 print_unformatted_stats(void)
 260 {
 261         col_t *col;
 262 
 263 #define DELTA(field) \
 264         (col->col_src_stat.field - col->col_old_stat.field)
 265 
 266         col = col_head;
 267         while (col != NULL) {
 268                 if (bcmp(&col->col_src_stat, &col->col_old_stat,
 269                     sizeof (col->col_src_stat)) == 0) {
 270                         col = col->col_next;
 271                         continue;
 272                 }
 273                 (void) printf("%s %s status: succeeded/attempted (k): "
 274                     "%llu/%llu, ineffective/scans/unenforced/samplings:  "
 275                     "%llu/%llu/%llu/%llu, RSS min/max (k): %llu/%llu, cap %llu "
 276                     "kB, processes/thpt: %llu/%llu, %llu scans over %lld ms\n",
 277                     mode, col->col_name, DELTA(lcols_pg_eff),
 278                     DELTA(lcols_pg_att), DELTA(lcols_scan_ineffective),
 279                     DELTA(lcols_scan), DELTA(lcols_unenforced_cap),
 280                     DELTA(lcols_rss_sample), col->col_src_stat.lcols_min_rss,
 281                     col->col_src_stat.lcols_max_rss, col->col_rsslimit,
 282                     (col->col_src_stat.lcols_proc_in -
 283                     col->col_old_stat.lcols_proc_out), DELTA(lcols_proc_out),
 284                     DELTA(lcols_scan_count),
 285                     NSEC2MSEC(DELTA(lcols_scan_time_complete)));
 286                 col->col_old_stat = col->col_src_stat;
 287 
 288                 col = col->col_next;
 289         }
 290 
 291         if (global)
 292                 (void) printf(gettext("physical memory utilization: %3u%%   "
 293                     "cap enforcement threshold: %3u%%\n"), hdr.rs_pressure_cur,
 294                     hdr.rs_pressure_cap);
 295 #undef DELTA
 296 }
 297 
 298 static void
 299 print_stats(rcid_type_t stat_type)
 300 {
 301         col_t *col;
 302         char size[6];
 303         char limit[6];
 304         char rss[6];
 305         char nproc[6];
 306         char paged_att[6];
 307         char paged_eff[6];
 308         char paged_att_avg[6];
 309         char paged_eff_avg[6];
 310         static int count = 0;
 311 
 312         /*
 313          * Print a header once every 20 times if we're only displaying reports
 314          * for one collection (10 times if -g is used).  Print a header every
 315          * interval otherwise.
 316          */
 317         if (count == 0 || ncol != 1)
 318                 (void) printf("%6s %-15s %5s %5s %5s %5s %5s %5s %5s %5s\n",
 319                     "id", (stat_type == RCIDT_PROJECT ?  "project" : "zone"),
 320                     "nproc", "vm", "rss", "cap",
 321                     "at", "avgat", "pg", "avgpg");
 322         if (++count >= 20 || (count >= 10 && global != 0) || ncol != 1)
 323                 count = 0;
 324 
 325         for (col = col_head; col != NULL; col = col->col_next) {
 326                 if (col->col_id.rcid_type != stat_type)
 327                         continue;
 328 
 329                 if (col->col_paged_att == 0)
 330                         (void) strlcpy(nproc, "-", sizeof (nproc));
 331                 else
 332                         (void) snprintf(nproc, sizeof (nproc), "%lld",
 333                             col->col_nproc);
 334                 format_size(size, col->col_vmsize, 6);
 335                 format_size(rss, col->col_rsssize, 6);
 336                 format_size(limit, col->col_rsslimit, 6);
 337                 format_size(paged_att, col->col_paged_att, 6);
 338                 format_size(paged_eff, col->col_paged_eff, 6);
 339                 format_size(paged_att_avg, col->col_paged_att_avg, 6);
 340                 format_size(paged_eff_avg, col->col_paged_eff_avg, 6);
 341                 (void) printf("%6lld %-15s %5s %5s %5s %5s %5s %5s %5s %5s\n",
 342                     col->col_id.rcid_val, col->col_name,
 343                     nproc,
 344                     size, rss, limit,
 345                     paged_att, paged_att_avg,
 346                     paged_eff, paged_eff_avg);
 347         }
 348         if (global)
 349                 (void) printf(gettext("physical memory utilization: %3u%%   "
 350                     "cap enforcement threshold: %3u%%\n"), hdr.rs_pressure_cur,
 351                     hdr.rs_pressure_cap);
 352 }
 353 
 354 int
 355 main(int argc, char *argv[])
 356 {
 357         int interval = 5;
 358         int count;
 359         int always = 1;
 360         int opt;
 361         int projects = 0;
 362         int zones = 0;
 363         /* project reporting is the default if no option is specified */
 364         rcid_type_t stat_type = RCIDT_PROJECT;
 365 
 366         (void) setlocale(LC_ALL, "");
 367         (void) textdomain(TEXT_DOMAIN);
 368         (void) setpname("rcapstat");
 369 
 370         global = unformatted = 0;
 371         while ((opt = getopt(argc, argv, "gpuzT:")) != (int)EOF) {
 372                 switch (opt) {
 373                 case 'g':
 374                         global = 1;
 375                         break;
 376                 case 'p':
 377                         projects = 1;
 378                         stat_type = RCIDT_PROJECT;
 379                         break;
 380                 case 'u':
 381                         unformatted = 1;
 382                         break;
 383                 case 'z':
 384                         stat_type = RCIDT_ZONE;
 385                         zones = 1;
 386                         break;
 387                 case 'T':
 388                         if (optarg) {
 389                                 if (*optarg == 'u')
 390                                         timestamp_fmt = UDATE;
 391                                 else if (*optarg == 'd')
 392                                         timestamp_fmt = DDATE;
 393                                 else
 394                                         usage();
 395                         } else {
 396                                 usage();
 397                         }
 398                         break;
 399                 default:
 400                         usage();
 401                 }
 402         }
 403 
 404         if (argc > optind)
 405                 if ((interval = xatoi(argv[optind++])) <= 0)
 406                         die(gettext("invalid interval specified\n"));
 407         if (argc > optind) {
 408                 if ((count = xatoi(argv[optind++])) <= 0)
 409                         die(gettext("invalid count specified\n"));
 410                 always = 0;
 411         }
 412         if (argc > optind || (projects > 0 && zones > 0))
 413                 usage();
 414 
 415         while (always || count-- > 0) {
 416                 if (read_stats(stat_type) != E_SUCCESS)
 417                         return (E_ERROR);
 418                 if (timestamp_fmt != NODATE)
 419                         print_timestamp(timestamp_fmt);
 420                 if (!unformatted) {
 421                         print_stats(stat_type);
 422                         (void) fflush(stdout);
 423                         if (count || always)
 424                                 (void) sleep(interval);
 425                 } else {
 426                         struct stat st;
 427 
 428                         print_unformatted_stats();
 429                         (void) fflush(stdout);
 430                         while (stat(STAT_FILE_DEFAULT, &st) == 0 &&
 431                             st.st_mtime == stat_mod)
 432                                 (void) usleep((useconds_t)(0.2 * MICROSEC));
 433                 }
 434         }
 435 
 436         return (E_SUCCESS);
 437 }