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), DELTA(lcols_scan_time_complete) / 285 (NANOSEC / MILLISEC)); 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 } 438