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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <stdlib.h> 32 #include <fcntl.h> 33 #include <errno.h> 34 #include <stdio.h> 35 #include <unistd.h> 36 #include <string.h> 37 #include <strings.h> 38 #include <libintl.h> 39 #include <locale.h> 40 41 #include "rcapd.h" 42 #include "utils.h" 43 #include "rcapd_stat.h" 44 45 static char mode[RC_MODE_LEN]; 46 static rcapd_stat_hdr_t hdr; 47 static int global; 48 static int unformatted; 49 static time_t stat_mod = 0; 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 == id) 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] [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() 143 { 144 int fd; 145 int proc_fd; 146 char procfile[20]; 147 pid_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/%ld/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 col = col_find(report.lcol_id); 189 if (col == NULL) { 190 col = col_insert(report.lcol_id); 191 col->col_paged_eff_old = col->col_paged_eff = 192 report.lcol_stat.lcols_pg_eff; 193 col->col_paged_att_old = col->col_paged_att = 194 report.lcol_stat.lcols_pg_att; 195 col->col_count = 0; 196 } 197 (void) strncpy(col->col_name, report.lcol_name, LC_NAME_LEN); 198 col->col_vmsize = report.lcol_image_size; 199 col->col_rsssize = report.lcol_rss; 200 col->col_rsslimit = report.lcol_rss_cap; 201 col->col_fresh = 1; 202 if (report.lcol_stat.lcols_pg_eff > col->col_paged_eff_old) { 203 col->col_paged_eff = 204 report.lcol_stat.lcols_pg_eff - 205 col->col_paged_eff_old; 206 if (report.lcol_stat.lcols_scan_count > col->col_count) 207 col->col_paged_eff_avg = 208 col->col_paged_eff / 209 (report.lcol_stat.lcols_scan_count - 210 col->col_count); 211 } else { 212 col->col_paged_eff_avg = 0; 213 } 214 if (report.lcol_stat.lcols_pg_att > col->col_paged_att_old) { 215 col->col_paged_att = 216 report.lcol_stat.lcols_pg_att - 217 col->col_paged_att_old; 218 if (report.lcol_stat.lcols_scan_count > col->col_count) 219 col->col_paged_att_avg = 220 col->col_paged_att / 221 (report.lcol_stat.lcols_scan_count - 222 col->col_count); 223 } else { 224 col->col_paged_att_avg = 0; 225 } 226 col->col_paged_eff_old = report.lcol_stat.lcols_pg_eff; 227 col->col_paged_att_old = report.lcol_stat.lcols_pg_att; 228 col->col_nproc = 229 report.lcol_stat.lcols_proc_in - 230 report.lcol_stat.lcols_proc_out; 231 col->col_count = report.lcol_stat.lcols_scan_count; 232 col->col_src_stat = report.lcol_stat; 233 } 234 235 /* 236 * Remove stale data 237 */ 238 col = col_head; 239 while (col != NULL) { 240 col_next = col->col_next; 241 if (col->col_fresh == 0) 242 col_remove(col); 243 col = col_next; 244 } 245 (void) close(fd); 246 return (E_SUCCESS); 247 } 248 249 /* 250 * Print each collection's interval statistics. 251 */ 252 /*ARGSUSED*/ 253 static void 254 print_unformatted_stats(void) 255 { 256 col_t *col; 257 258 #define DELTA(field) \ 259 (col->col_src_stat.field - col->col_old_stat.field) 260 261 col = col_head; 262 while (col != NULL) { 263 if (bcmp(&col->col_src_stat, &col->col_old_stat, 264 sizeof (col->col_src_stat)) == 0) { 265 col = col->col_next; 266 continue; 267 } 268 (void) printf("%s %s status: succeeded/attempted (k): " 269 "%llu/%llu, ineffective/scans/unenforced/samplings: " 270 "%llu/%llu/%llu/%llu, RSS min/max (k): %llu/%llu, cap %llu " 271 "kB, processes/thpt: %llu/%llu, %llu scans over %lld ms\n", 272 mode, col->col_name, DELTA(lcols_pg_eff), 273 DELTA(lcols_pg_att), DELTA(lcols_scan_ineffective), 274 DELTA(lcols_scan), DELTA(lcols_unenforced_cap), 275 DELTA(lcols_rss_sample), col->col_src_stat.lcols_min_rss, 276 col->col_src_stat.lcols_max_rss, col->col_rsslimit, 277 (col->col_src_stat.lcols_proc_in - 278 col->col_old_stat.lcols_proc_out), DELTA(lcols_proc_out), 279 DELTA(lcols_scan_count), DELTA(lcols_scan_time_complete) / 280 (NANOSEC / MILLISEC)); 281 col->col_old_stat = col->col_src_stat; 282 283 col = col->col_next; 284 } 285 286 if (global) 287 (void) printf(gettext("physical memory utilization: %3u%% " 288 "cap enforcement threshold: %3u%%\n"), hdr.rs_pressure_cur, 289 hdr.rs_pressure_cap); 290 #undef DELTA 291 } 292 293 static void 294 print_stats() 295 { 296 col_t *col; 297 char size[6]; 298 char limit[6]; 299 char rss[6]; 300 char paged_att[6]; 301 char paged_eff[6]; 302 char paged_att_avg[6]; 303 char paged_eff_avg[6]; 304 static int count = 0; 305 306 /* 307 * Print a header once every 20 times if we're only displaying reports 308 * for one collection (10 times if -g is used). Print a header every 309 * interval otherwise. 310 */ 311 if (count == 0 || ncol != 1) 312 (void) printf("%6s %-15s %5s %5s %5s %5s %5s %5s %5s %5s\n", 313 "id", mode, "nproc", "vm", "rss", "cap", 314 "at", "avgat", "pg", "avgpg"); 315 if (++count >= 20 || (count >= 10 && global != 0) || ncol != 1) 316 count = 0; 317 318 for (col = col_head; col != NULL; col = col->col_next) { 319 format_size(size, col->col_vmsize, 6); 320 format_size(rss, col->col_rsssize, 6); 321 format_size(limit, col->col_rsslimit, 6); 322 format_size(paged_att, col->col_paged_att, 6); 323 format_size(paged_eff, col->col_paged_eff, 6); 324 format_size(paged_att_avg, col->col_paged_att_avg, 6); 325 format_size(paged_eff_avg, col->col_paged_eff_avg, 6); 326 (void) printf("%6lld %-15s %5lld %5s %5s %5s %5s %5s %5s %5s\n", 327 (long long)col->col_id, col->col_name, col->col_nproc, 328 size, rss, limit, 329 paged_att, paged_att_avg, 330 paged_eff, paged_eff_avg); 331 } 332 if (global) 333 (void) printf(gettext("physical memory utilization: %3u%% " 334 "cap enforcement threshold: %3u%%\n"), hdr.rs_pressure_cur, 335 hdr.rs_pressure_cap); 336 } 337 338 int 339 main(int argc, char *argv[]) 340 { 341 int interval = 5; 342 int count; 343 int always = 1; 344 int opt; 345 346 (void) setlocale(LC_ALL, ""); 347 (void) textdomain(TEXT_DOMAIN); 348 (void) setprogname("rcapstat"); 349 350 global = unformatted = 0; 351 while ((opt = getopt(argc, argv, "gu")) != (int)EOF) { 352 switch (opt) { 353 case 'g': 354 global = 1; 355 break; 356 case 'u': 357 unformatted = 1; 358 break; 359 default: 360 usage(); 361 } 362 } 363 364 if (argc > optind) 365 if ((interval = xatoi(argv[optind++])) <= 0) 366 die(gettext("invalid interval specified\n")); 367 if (argc > optind) { 368 if ((count = xatoi(argv[optind++])) <= 0) 369 die(gettext("invalid count specified\n")); 370 always = 0; 371 } 372 if (argc > optind) 373 usage(); 374 375 while (always || count-- > 0) { 376 if (read_stats() != E_SUCCESS) 377 return (E_ERROR); 378 if (!unformatted) { 379 print_stats(); 380 fflush(stdout); 381 if (count || always) 382 (void) sleep(interval); 383 } else { 384 struct stat st; 385 386 print_unformatted_stats(); 387 fflush(stdout); 388 while (stat(STAT_FILE_DEFAULT, &st) == 0 && 389 st.st_mtime == stat_mod) 390 usleep((useconds_t)(0.2 * MICROSEC)); 391 } 392 } 393 394 return (E_SUCCESS); 395 } 396