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 2004 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 <fm/fmd_adm.h> 30 31 #include <strings.h> 32 #include <limits.h> 33 #include <stdlib.h> 34 #include <stdarg.h> 35 #include <stdio.h> 36 #include <errno.h> 37 #include <poll.h> 38 39 #define FMSTAT_EXIT_SUCCESS 0 40 #define FMSTAT_EXIT_ERROR 1 41 #define FMSTAT_EXIT_USAGE 2 42 43 static const struct stats { 44 fmd_stat_t loadtime; 45 fmd_stat_t snaptime; 46 fmd_stat_t dispatched; 47 fmd_stat_t dequeued; 48 fmd_stat_t prdequeued; 49 fmd_stat_t accepted; 50 fmd_stat_t memtotal; 51 fmd_stat_t buftotal; 52 fmd_stat_t caseopen; 53 fmd_stat_t casesolved; 54 fmd_stat_t wcnt; 55 fmd_stat_t wtime; 56 fmd_stat_t wlentime; 57 fmd_stat_t wlastupdate; 58 fmd_stat_t dtime; 59 fmd_stat_t dlastupdate; 60 } stats_template = { 61 { "fmd.loadtime", FMD_TYPE_TIME }, 62 { "fmd.snaptime", FMD_TYPE_TIME }, 63 { "fmd.dispatched", FMD_TYPE_UINT64 }, 64 { "fmd.dequeued", FMD_TYPE_UINT64 }, 65 { "fmd.prdequeued", FMD_TYPE_UINT64 }, 66 { "fmd.accepted", FMD_TYPE_UINT64 }, 67 { "fmd.memtotal", FMD_TYPE_SIZE }, 68 { "fmd.buftotal", FMD_TYPE_SIZE }, 69 { "fmd.caseopen", FMD_TYPE_UINT64 }, 70 { "fmd.casesolved", FMD_TYPE_UINT64 }, 71 { "fmd.wcnt", FMD_TYPE_UINT32 }, 72 { "fmd.wtime", FMD_TYPE_TIME }, 73 { "fmd.wlentime", FMD_TYPE_TIME }, 74 { "fmd.wlastupdate", FMD_TYPE_TIME }, 75 { "fmd.dtime", FMD_TYPE_TIME }, 76 { "fmd.dlastupdate", FMD_TYPE_TIME }, 77 }; 78 79 static const char *g_pname; 80 static fmd_adm_t *g_adm; 81 82 static struct modstats { 83 char *m_name; 84 struct modstats *m_next; 85 struct stats m_stbuf[2]; 86 int m_stidx; 87 } *g_mods; 88 89 static void 90 vwarn(const char *format, va_list ap) 91 { 92 int err = errno; 93 94 (void) fprintf(stderr, "%s: ", g_pname); 95 96 if (format != NULL) 97 (void) vfprintf(stderr, format, ap); 98 99 errno = err; /* restore errno for fmd_adm_errmsg() */ 100 101 if (format == NULL) 102 (void) fprintf(stderr, "%s\n", fmd_adm_errmsg(g_adm)); 103 else if (strchr(format, '\n') == NULL) 104 (void) fprintf(stderr, ": %s\n", fmd_adm_errmsg(g_adm)); 105 } 106 107 /*PRINTFLIKE1*/ 108 void 109 warn(const char *format, ...) 110 { 111 va_list ap; 112 113 va_start(ap, format); 114 vwarn(format, ap); 115 va_end(ap); 116 } 117 118 /*PRINTFLIKE1*/ 119 void 120 die(const char *format, ...) 121 { 122 va_list ap; 123 124 va_start(ap, format); 125 vwarn(format, ap); 126 va_end(ap); 127 128 fmd_adm_close(g_adm); 129 exit(FMSTAT_EXIT_ERROR); 130 } 131 132 static char * 133 time2str(char *buf, size_t len, uint64_t time) 134 { 135 static const struct unit { 136 const char *u_name; 137 hrtime_t u_mul; 138 } units[] = { 139 { "d", NANOSEC * (hrtime_t)(24 * 60 * 60) }, 140 { "h", NANOSEC * (hrtime_t)(60 * 60) }, 141 { "m", NANOSEC * (hrtime_t)60 }, 142 { "s", NANOSEC / SEC }, 143 { "ms", NANOSEC / MILLISEC }, 144 { "us", NANOSEC / MICROSEC }, 145 { "ns", NANOSEC / NANOSEC }, 146 }; 147 148 const struct unit *up; 149 150 for (up = units; time % up->u_mul != 0; up++) 151 continue; /* find largest unit of which 'time' is a multiple */ 152 153 (void) snprintf(buf, len, "%llu%s", time / up->u_mul, up->u_name); 154 return (buf); 155 } 156 157 static char * 158 size2str(char *buf, size_t len, uint64_t size) 159 { 160 static const char units[] = "bKMGTPE"; 161 const uint64_t scale = 1024; 162 const char *up = units; 163 uint64_t osize = 0; 164 165 /* 166 * Convert the input size to a round number of the appropriately 167 * scaled units (saved in 'size') and a remainder (saved in 'osize'). 168 */ 169 while (size >= scale && up < (units + sizeof (units) - 2)) { 170 up++; 171 osize = size; 172 size = (size + (scale / 2)) / scale; 173 } 174 175 /* 176 * Format the result using at most one decimal place and the unit 177 * depending upon the amount of remainder (same as df -h algorithm). 178 */ 179 if (osize != 0 && (osize / scale) < 10) 180 (void) snprintf(buf, len, "%.1f%c", (float)osize / scale, *up); 181 else if (size != 0) 182 (void) snprintf(buf, len, "%llu%c", size, *up); 183 else 184 (void) snprintf(buf, len, "0"); 185 186 return (buf); 187 } 188 189 static uint64_t 190 u64delta(uint64_t old, uint64_t new) 191 { 192 return (new >= old ? (new - old) : ((UINT64_MAX - old) + new + 1)); 193 } 194 195 /*ARGSUSED*/ 196 static int 197 stat_one_fmd(const fmd_adm_modinfo_t *ami, void *ignored) 198 { 199 static fmd_stat_t *t_beg = (fmd_stat_t *)(&stats_template + 0); 200 static fmd_stat_t *t_end = (fmd_stat_t *)(&stats_template + 1); 201 202 fmd_stat_t *tsp, *nsp, *sp; 203 struct stats *old, *new; 204 char memsz[8], bufsz[8]; 205 double elapsed, wait, avg_w, avg_d, svc, pct_b, pct_w; 206 uint64_t delta; 207 208 fmd_adm_stats_t ams; 209 struct modstats *mp; 210 char *name; 211 212 /* 213 * Take a snapshot of the statistics for this module and then set up 214 * 'old' and 'new' to point to the rotating persistent m_stbuf structs. 215 * If no matching module is found on our g_mods list, add a new module. 216 */ 217 if (fmd_adm_module_stats(g_adm, ami->ami_name, &ams) != 0) { 218 warn("failed to retrieve statistics for %s", ami->ami_name); 219 return (0); /* continue on to the next module */ 220 } 221 222 for (mp = g_mods; mp != NULL; mp = mp->m_next) { 223 if (strcmp(mp->m_name, ami->ami_name) == 0) 224 break; 225 } 226 227 if (mp == NULL) { 228 if ((mp = malloc(sizeof (struct modstats))) == NULL || 229 (name = strdup(ami->ami_name)) == NULL) { 230 warn("failed to allocate memory for %s", ami->ami_name); 231 (void) fmd_adm_stats_free(g_adm, &ams); 232 free(mp); 233 return (0); 234 } 235 236 bzero(mp, sizeof (struct modstats)); 237 mp->m_name = name; 238 mp->m_next = g_mods; 239 g_mods = mp; 240 } 241 242 old = &mp->m_stbuf[mp->m_stidx]; 243 mp->m_stidx = 1 - mp->m_stidx; 244 new = &mp->m_stbuf[mp->m_stidx]; 245 246 /* 247 * The statistics can come in any order; we compare each one to the 248 * template of statistics of interest, find the matching ones, and copy 249 * their values into the appropriate slot of the 'new' stats. 250 */ 251 for (nsp = ams.ams_buf; nsp < ams.ams_buf + ams.ams_len; nsp++) { 252 for (tsp = t_beg; tsp < t_end; tsp++) { 253 if (strcmp(tsp->fmds_name, nsp->fmds_name) != 0) 254 continue; /* continue until we match the name */ 255 256 if (tsp->fmds_type != nsp->fmds_type) { 257 warn("%s has unexpected type (%u != %u)\n", 258 tsp->fmds_name, tsp->fmds_type, 259 nsp->fmds_type); 260 } else { 261 sp = (fmd_stat_t *)new + (tsp - t_beg); 262 sp->fmds_value = nsp->fmds_value; 263 } 264 } 265 } 266 267 /* 268 * Compute the elapsed time by taking the delta between 'snaptime', or 269 * or between snaptime and loadtime if there is no previous snapshot. 270 * If delta is zero, set it to 1sec so we don't divide by zero later. 271 */ 272 delta = u64delta(old->snaptime.fmds_value.ui64 ? 273 old->snaptime.fmds_value.ui64 : old->loadtime.fmds_value.ui64, 274 new->snaptime.fmds_value.ui64); 275 276 elapsed = delta ? (double)delta : (double)NANOSEC; 277 278 /* 279 * Compute average wait queue len by taking the delta in the wait queue 280 * len * time products (wlentime stat) and dividing by the elapsed time. 281 */ 282 delta = u64delta(old->wlentime.fmds_value.ui64, 283 new->wlentime.fmds_value.ui64); 284 285 if (delta != 0) 286 wait = (double)delta / elapsed; 287 else 288 wait = 0.0; 289 290 /* 291 * Compute average wait time by taking the delta in the wait queue time 292 * (wtime) and dividing by the delta in the number of dispatches. 293 */ 294 delta = u64delta(old->dispatched.fmds_value.ui64, 295 new->dispatched.fmds_value.ui64); 296 297 if (delta != 0) { 298 avg_w = (double)u64delta(old->wtime.fmds_value.ui64, 299 new->wtime.fmds_value.ui64) / (double)delta; 300 } else 301 avg_w = 0.0; 302 303 /* 304 * Compute average dispatch time by taking the delta in the dispatch 305 * time (dtime) and dividing by the delta in the number of dequeues. 306 */ 307 delta = u64delta(old->dequeued.fmds_value.ui64, 308 new->dequeued.fmds_value.ui64); 309 310 if (delta != 0) { 311 avg_d = (double)u64delta(old->dtime.fmds_value.ui64, 312 new->dtime.fmds_value.ui64) / (double)delta; 313 } else 314 avg_d = 0.0; 315 316 /* 317 * Finally compute the average overall service time by adding together 318 * the average wait and dispatch times and converting to milliseconds. 319 */ 320 svc = ((avg_w + avg_d) * (double)MILLISEC) / (double)NANOSEC; 321 322 /* 323 * Compute the %wait and %busy times by taking the delta in wait and 324 * busy times, dividing by the elapsed time, and multiplying by 100. 325 */ 326 delta = u64delta(old->wtime.fmds_value.ui64, 327 new->wtime.fmds_value.ui64); 328 329 if (delta != 0) 330 pct_w = ((double)delta / elapsed) * 100.0; 331 else 332 pct_w = 0.0; 333 334 delta = u64delta(old->dtime.fmds_value.ui64, 335 new->dtime.fmds_value.ui64); 336 337 if (delta != 0) 338 pct_b = ((double)delta / elapsed) * 100.0; 339 else 340 pct_b = 0.0; 341 342 /* 343 * Print the formatted line of statistics for this module based on 344 * our calculations, and then free the statistics we sampled. 345 */ 346 (void) printf("%-18s %7llu %7llu %4.1f %6.1f %3.0f %3.0f " 347 "%5llu %5llu %6s %6s\n", ami->ami_name, 348 u64delta(old->prdequeued.fmds_value.ui64, 349 new->prdequeued.fmds_value.ui64), 350 u64delta(old->accepted.fmds_value.ui64, 351 new->accepted.fmds_value.ui64), 352 wait, svc, pct_w, pct_b, 353 new->caseopen.fmds_value.ui64, 354 new->casesolved.fmds_value.ui64, 355 size2str(memsz, sizeof (memsz), new->memtotal.fmds_value.ui64), 356 size2str(bufsz, sizeof (bufsz), new->buftotal.fmds_value.ui64)); 357 358 (void) fmd_adm_stats_free(g_adm, &ams); 359 return (0); 360 } 361 362 static void 363 stat_fmd(void) 364 { 365 (void) printf("%-18s %7s %7s %4s %6s %3s %3s %5s %5s %6s %6s\n", 366 "module", "ev_recv", "ev_acpt", "wait", "svc_t", "%w", "%b", 367 "open", "solve", "memsz", "bufsz"); 368 369 if (fmd_adm_module_iter(g_adm, stat_one_fmd, NULL) != 0) 370 die("failed to retrieve list of modules"); 371 } 372 373 static void 374 stat_mod(const char *name, int aflag, int zflag) 375 { 376 fmd_adm_stats_t ams; 377 fmd_stat_t *sp; 378 char buf[64]; 379 380 if (fmd_adm_stats_read(g_adm, name, &ams) != 0) { 381 die("failed to retrieve statistics for %s", 382 name ? name : "fmd(1M)"); 383 } 384 385 (void) printf("%20s %-16s %s\n", "NAME", "VALUE", "DESCRIPTION"); 386 387 for (sp = ams.ams_buf; sp < ams.ams_buf + ams.ams_len; sp++) { 388 if (aflag == 0 && strncmp(sp->fmds_name, "fmd.", 4) == 0) 389 continue; /* skip fmd-internal stats unless -a used */ 390 391 if (zflag) { 392 switch (sp->fmds_type) { 393 case FMD_TYPE_INT32: 394 case FMD_TYPE_UINT32: 395 if (sp->fmds_value.ui32 == 0) 396 continue; 397 break; 398 case FMD_TYPE_INT64: 399 case FMD_TYPE_UINT64: 400 case FMD_TYPE_TIME: 401 case FMD_TYPE_SIZE: 402 if (sp->fmds_value.ui64 == 0) 403 continue; 404 break; 405 case FMD_TYPE_STRING: 406 if (sp->fmds_value.str == NULL || 407 sp->fmds_value.str[0] == '\0') 408 continue; 409 break; 410 } 411 } 412 413 (void) printf("%20s ", sp->fmds_name); 414 415 switch (sp->fmds_type) { 416 case FMD_TYPE_BOOL: 417 (void) printf("%-16s", 418 sp->fmds_value.bool ? "true" : "false"); 419 break; 420 case FMD_TYPE_INT32: 421 (void) printf("%-16d", sp->fmds_value.i32); 422 break; 423 case FMD_TYPE_UINT32: 424 (void) printf("%-16u", sp->fmds_value.ui32); 425 break; 426 case FMD_TYPE_INT64: 427 (void) printf("%-16lld", sp->fmds_value.i64); 428 break; 429 case FMD_TYPE_UINT64: 430 (void) printf("%-16llu", sp->fmds_value.ui64); 431 break; 432 case FMD_TYPE_STRING: 433 (void) printf("%-16s", sp->fmds_value.str ? 434 sp->fmds_value.str : "<<null>>"); 435 break; 436 case FMD_TYPE_TIME: 437 (void) printf("%-16s", 438 time2str(buf, sizeof (buf), sp->fmds_value.ui64)); 439 break; 440 case FMD_TYPE_SIZE: 441 (void) printf("%-16s", 442 size2str(buf, sizeof (buf), sp->fmds_value.ui64)); 443 break; 444 default: 445 (void) snprintf(buf, sizeof (buf), 446 "<<type=%u>>\n", sp->fmds_type); 447 (void) printf("%-16s", buf); 448 } 449 450 (void) printf(" %s\n", sp->fmds_desc); 451 } 452 453 (void) fmd_adm_stats_free(g_adm, &ams); 454 } 455 456 /*ARGSUSED*/ 457 static int 458 stat_one_serd(const fmd_adm_serdinfo_t *asi, void *ignored) 459 { 460 char buf1[32], buf2[32], n[32]; 461 462 (void) snprintf(n, sizeof (n), ">%llu", asi->asi_n); 463 464 (void) printf("%-36s %3s %5s %3u %24s %s\n", 465 asi->asi_name, n, time2str(buf1, sizeof (buf1), asi->asi_t), 466 asi->asi_count, time2str(buf2, sizeof (buf2), asi->asi_delta), 467 (asi->asi_flags & FMD_ADM_SERD_FIRED) ? "fire" : "pend"); 468 469 return (0); 470 } 471 472 static void 473 stat_mod_serd(const char *name) 474 { 475 (void) printf("%-36s %3s %5s %3s %24s %4s\n", 476 "NAME", ">N", "T", "CNT", "DELTA", "STAT"); 477 478 if (fmd_adm_serd_iter(g_adm, name, stat_one_serd, NULL) != 0) 479 die("failed to retrieve serd engines for %s", name); 480 } 481 482 static int 483 getint(const char *name, const char *s) 484 { 485 long val; 486 char *p; 487 488 errno = 0; 489 val = strtol(s, &p, 10); 490 491 if (errno != 0 || p == s || *p != '\0' || val < 0 || val > INT_MAX) { 492 (void) fprintf(stderr, "%s: invalid %s argument -- %s\n", 493 g_pname, name, s); 494 exit(FMSTAT_EXIT_USAGE); 495 } 496 497 return ((int)val); 498 } 499 500 static uint32_t 501 getu32(const char *name, const char *s) 502 { 503 u_longlong_t val; 504 char *p; 505 506 errno = 0; 507 val = strtoull(s, &p, 0); 508 509 if (errno != 0 || p == s || *p != '\0' || val > UINT32_MAX) { 510 (void) fprintf(stderr, "%s: invalid %s argument -- %s\n", 511 g_pname, name, s); 512 exit(FMSTAT_EXIT_USAGE); 513 } 514 515 return ((uint32_t)val); 516 } 517 518 static int 519 usage(FILE *fp) 520 { 521 (void) fprintf(fp, "Usage: %s [-asz] [-m module] " 522 "[-P prog] [interval [count]]\n\n", g_pname); 523 524 (void) fprintf(fp, 525 "\t-a show all statistics, including those kept by fmd\n" 526 "\t-m show module-specific statistics\n" 527 "\t-P connect to alternate fmd program\n" 528 "\t-s show module-specific serd engines\n" 529 "\t-z suppress zero-valued statistics\n"); 530 531 return (FMSTAT_EXIT_USAGE); 532 } 533 534 int 535 main(int argc, char *argv[]) 536 { 537 int opt_a = 0, opt_s = 0, opt_z = 0; 538 const char *opt_m = NULL; 539 int msec = 0, iter = 1; 540 541 uint32_t program; 542 char *p; 543 int c; 544 545 if ((p = strrchr(argv[0], '/')) == NULL) 546 g_pname = argv[0]; 547 else 548 g_pname = p + 1; 549 550 if ((p = getenv("FMD_PROGRAM")) != NULL) 551 program = getu32("$FMD_PROGRAM", p); 552 else 553 program = FMD_ADM_PROGRAM; 554 555 while ((c = getopt(argc, argv, "am:P:sz")) != EOF) { 556 switch (c) { 557 case 'a': 558 opt_a++; 559 break; 560 case 'm': 561 opt_m = optarg; 562 break; 563 case 'P': 564 program = getu32("program", optarg); 565 break; 566 case 's': 567 opt_s++; 568 break; 569 case 'z': 570 opt_z++; 571 break; 572 default: 573 return (usage(stderr)); 574 } 575 } 576 577 if (optind < argc) { 578 msec = getint("interval", argv[optind++]) * MILLISEC; 579 iter = -1; 580 } 581 582 if (optind < argc) 583 iter = getint("count", argv[optind++]); 584 585 if (optind < argc) 586 return (usage(stderr)); 587 588 if (opt_m == NULL && opt_s != 0) { 589 (void) fprintf(stderr, 590 "%s: -s requires -m <module>\n", g_pname); 591 return (FMSTAT_EXIT_USAGE); 592 } 593 594 if ((g_adm = fmd_adm_open(NULL, program, FMD_ADM_VERSION)) == NULL) 595 die(NULL); /* fmd_adm_errmsg() has enough info */ 596 597 while (iter < 0 || iter-- > 0) { 598 if (opt_s) 599 stat_mod_serd(opt_m); 600 else if (opt_a || opt_m) 601 stat_mod(opt_m, opt_a, opt_z); 602 else 603 stat_fmd(); 604 605 if (iter != 0) { 606 (void) poll(NULL, 0, msec); 607 (void) putchar('\n'); 608 } 609 } 610 611 fmd_adm_close(g_adm); 612 return (FMSTAT_EXIT_SUCCESS); 613 } 614