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