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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * poolstat - report active pool statistics 28 */ 29 #include <stdio.h> 30 #include <unistd.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 #include <locale.h> 34 #include <string.h> 35 #include <ctype.h> 36 #include <limits.h> 37 #include <errno.h> 38 39 #include <pool.h> 40 #include "utils.h" 41 #include "poolstat.h" 42 #include "poolstat_utils.h" 43 #include "statcommon.h" 44 45 #ifndef TEXT_DOMAIN 46 #define TEXT_DOMAIN "SYS_TEST" 47 #endif 48 49 /* calculate offset of a particular element in a structure */ 50 #define offsetof(s, m) ((size_t)(&(((s *)0)->m))) 51 #define addrof(s) ((char **)&(s)) 52 53 /* verify if a field is printable in respect of the current option flags */ 54 #define PRINTABLE(i) ((lf->plf_ffs[(i)].pff_prt & D_FIELD) || \ 55 (lf->plf_ffs[(i)].pff_prt & X_FIELD)) 56 57 typedef int (* formatter) (char *, int, int, poolstat_field_format_t *, char *); 58 59 static uint_t timestamp_fmt = NODATE; 60 61 /* available field formatters */ 62 static int default_f(char *, int, int, poolstat_field_format_t *, char *); 63 static int bigno_f(char *, int, int, poolstat_field_format_t *, char *); 64 static int used_stat_f(char *, int, int, poolstat_field_format_t *, char *); 65 static int header_f(char *, int, int, poolstat_field_format_t *, char *); 66 67 /* statistics bags used to collect data from various provider */ 68 static statistic_bag_t pool_sbag_s; 69 static statistic_bag_t pset_sbag_s; 70 static statistic_bag_t *pool_sbag = &pool_sbag_s; 71 static statistic_bag_t *pset_sbag = &pset_sbag_s; 72 73 /* formatter objects for pset, defined in a default printing sequence */ 74 static poolstat_field_format_t pset_ffs[] = { 75 /* prt flags,name,header,type,width,minwidth,offset,formatter */ 76 { DX_FIELD, "id", "id", LL, 3, 1, addrof(pool_sbag), 77 offsetof(statistic_bag_t, sb_sysid), 78 (formatter)default_f }, 79 { DX_FIELD, "pool", "pool", STR, 20, 14, addrof(pool_sbag), 80 offsetof(statistic_bag_t, sb_name), 81 (formatter)default_f }, 82 { DX_FIELD, "type", "type", STR, 4, 5, addrof(pset_sbag), 83 offsetof(statistic_bag_t, sb_type), 84 (formatter)default_f }, 85 { D_FIELD, "rid", "rid", LL, 3, 1, addrof(pset_sbag_s.bag), 86 offsetof(pset_statistic_bag_t, pset_sb_sysid), 87 (formatter)default_f }, 88 { DX_FIELD, "rset", "rset", STR, 20, 14, addrof(pset_sbag), 89 offsetof(statistic_bag_t, sb_name), 90 (formatter)default_f }, 91 { DX_FIELD, "min", "min", ULL, 4, 1, addrof(pset_sbag_s.bag), 92 offsetof(pset_statistic_bag_t, pset_sb_min), 93 (formatter)bigno_f }, 94 { DX_FIELD, "max", "max", ULL, 4, 1, addrof(pset_sbag_s.bag), 95 offsetof(pset_statistic_bag_t, pset_sb_max), 96 (formatter)bigno_f }, 97 { DX_FIELD, "size", "size", ULL, 4, 1, addrof(pset_sbag_s.bag), 98 offsetof(pset_statistic_bag_t, pset_sb_size), 99 (formatter)default_f }, 100 { DX_FIELD, "used", "used", FL, 4, -1, addrof(pset_sbag_s.bag), 101 offsetof(pset_statistic_bag_t, pset_sb_used), 102 (formatter)used_stat_f }, 103 { DX_FIELD, "load", "load", FL, 4, -1, addrof(pset_sbag_s.bag), 104 offsetof(pset_statistic_bag_t, pset_sb_load), 105 (formatter)default_f } 106 }; 107 108 /* formatter objects for pool, defined in a default printing sequence */ 109 static poolstat_field_format_t pool_ffs[] = { 110 /* prt flags,name,header,type,width,minwidth,offset,formatter */ 111 { D_FIELD, "id", "id", LL, 3, 1, addrof(pool_sbag), 112 offsetof(statistic_bag_t, sb_sysid), 113 (formatter)default_f }, 114 { D_FIELD, "pool", "pool", STR, 20, 13, addrof(pool_sbag), 115 offsetof(statistic_bag_t, sb_name), 116 (formatter)default_f }, 117 { D_FIELD, "p_size", "size", ULL, 4, 1, addrof(pset_sbag_s.bag), 118 offsetof(pset_statistic_bag_t, pset_sb_size), 119 (formatter)default_f }, 120 { D_FIELD, "p_used", "used", FL, 4, -1, addrof(pset_sbag_s.bag), 121 offsetof(pset_statistic_bag_t, pset_sb_used), 122 (formatter)default_f }, 123 { D_FIELD, "p_load", "load", FL, 4, -1, addrof(pset_sbag_s.bag), 124 offsetof(pset_statistic_bag_t, pset_sb_load), 125 (formatter)default_f }, 126 }; 127 128 /* lists with formatter objects, one for each statistics field */ 129 static poolstat_line_format_t pool_lf; /* formatting list in default mode */ 130 static poolstat_line_format_t pset_lf; /* formatting list for psets */ 131 132 /* name of pools to be shown */ 133 static poolstat_list_element_t *pnames; 134 /* 135 * type of resources to be shown, currently we only have one type 'pset' 136 * but, poolstat can be extended to handle new upcoming resource types. 137 */ 138 static poolstat_list_element_t *rtypes; 139 140 /* a handle to the pool configuration */ 141 static pool_conf_t *conf; 142 143 /* option flags */ 144 static int rflag; 145 static int pflag; 146 static int oflag; 147 148 /* operands */ 149 static int interval = 0; /* update interval */ 150 static long count = 1; /* one run */ 151 152 /* data structure handlers */ 153 static poolstat_list_element_t * 154 create_prt_sequence_list(char *, poolstat_line_format_t *); 155 static poolstat_list_element_t * 156 create_args_list(char *, poolstat_list_element_t *, const char *); 157 158 /* statistics update function */ 159 static void sa_update(statistic_bag_t *, int); 160 161 /* statistics printing function */ 162 static void prt_pool_stats(poolstat_list_element_t *); 163 164 static void 165 usage(void) 166 { 167 (void) fprintf(stderr, gettext( 168 "Usage:\n" 169 "poolstat [-p pool-list] [-r rset-list] [-T d|u] [interval [count]]\n" 170 "poolstat [-p pool-list] [-o format -r rset-list] [-T d|u] [interval [count]]\n" 171 " \'pool-list\' is a space-separated list of pool IDs or names\n" 172 " \'rset-list\' is \'all\' or \'pset\'\n" 173 " \'format\' for all resource types is one or more of:\n" 174 "\tid pool type rid rset min max size used load\n")); 175 (void) exit(E_USAGE); 176 } 177 178 static int 179 Atoi(char *p, int *errp) 180 { 181 int i; 182 char *q; 183 errno = 0; 184 i = strtol(p, &q, 10); 185 if (errno != 0 || q == p || *q != '\0') 186 *errp = -1; 187 else 188 *errp = 0; 189 return (i); 190 } 191 192 int 193 main(int argc, char *argv[]) 194 { 195 char c; 196 int error = 0; 197 198 (void) getpname(argv[0]); 199 (void) setlocale(LC_ALL, ""); 200 (void) textdomain(TEXT_DOMAIN); 201 202 /* pset_sbag_s is used to collect pset statistics */ 203 pset_sbag_s.sb_type = PSET_TYPE_NAME; 204 pset_sbag_s.bag = ZALLOC(sizeof (pset_statistic_bag_t)); 205 pool_sbag_s.sb_type = POOL_TYPE_NAME; 206 207 pset_lf.plf_ffs = pset_ffs; 208 pset_lf.plf_ff_len = sizeof (pset_ffs) / 209 sizeof (poolstat_field_format_t); 210 pool_lf.plf_ffs = pool_ffs; 211 pool_lf.plf_ff_len = sizeof (pool_ffs) / 212 sizeof (poolstat_field_format_t); 213 214 /* Don't let buffering interfere with piped output. */ 215 (void) setvbuf(stdout, NULL, _IOLBF, 0); 216 217 while ((c = getopt(argc, argv, ":p:r:o:T:")) != EOF) { 218 switch (c) { 219 case 'p': /* pool name specification */ 220 pflag++; 221 pnames = create_args_list(optarg, pnames, 222 " \t"); 223 break; 224 case 'r': { /* resource type */ 225 rflag++; 226 rtypes = create_args_list(optarg, rtypes, 227 " \t,"); 228 break; 229 } 230 case 'o': { /* format specification */ 231 oflag++; 232 if (create_prt_sequence_list(optarg, &pset_lf) == NULL) 233 usage(); 234 break; 235 } 236 case 'T': 237 if (optarg) { 238 if (*optarg == 'u') 239 timestamp_fmt = UDATE; 240 else if (*optarg == 'd') 241 timestamp_fmt = DDATE; 242 else 243 usage(); 244 } else { 245 usage(); 246 } 247 break; 248 case ':': { 249 (void) fprintf(stderr, 250 gettext(ERR_OPTION_ARGS), optopt); 251 usage(); 252 /*NOTREACHED*/ 253 } 254 default: 255 (void) fprintf(stderr, gettext(ERR_OPTION), optopt); 256 usage(); 257 /*NOTREACHED*/ 258 } 259 } 260 261 /* get operands */ 262 if (argc > optind) { 263 if ((interval = Atoi(argv[optind++], &error)) < 1 || error != 0) 264 usage(); 265 count = -1; 266 } 267 if (argc > optind) { 268 if ((count = Atoi(argv[optind++], &error)) < 1 || error != 0) 269 usage(); 270 } 271 /* check for extra options/operands */ 272 if (argc > optind) 273 usage(); 274 275 /* check options */ 276 if (oflag && !rflag) 277 usage(); 278 279 /* global initializations */ 280 if (!oflag) { 281 /* create the default print sequences */ 282 (void) create_prt_sequence_list(NULL, &pool_lf); 283 (void) create_prt_sequence_list(NULL, &pset_lf); 284 } 285 286 if (rtypes == NULL || strcmp(rtypes->ple_obj, "all") == 0) { 287 /* crate a default resource list */ 288 FREE(rtypes); 289 rtypes = create_args_list("pset", NULL, " \t,"); 290 } 291 292 if ((conf = pool_conf_alloc()) == NULL) 293 die(gettext(ERR_NOMEM)); 294 if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY) 295 != PO_SUCCESS) 296 die(gettext(ERR_OPEN_DYNAMIC), get_errstr()); 297 298 /* initialize statistic adapters */ 299 sa_libpool_init(conf); 300 sa_kstat_init(NULL); 301 302 /* collect and print out statistics */ 303 while (count-- != 0) { 304 sa_update(pool_sbag, SA_REFRESH); 305 if (timestamp_fmt != NODATE) 306 print_timestamp(timestamp_fmt); 307 if (pool_sbag->sb_changed & POU_POOL) 308 (void) printf( 309 "<<State change>>\n"); 310 prt_pool_stats(pnames); 311 if (count != 0) { 312 (void) sleep(interval); 313 if (rflag) 314 (void) printf("\n"); 315 } 316 } 317 318 return (E_PO_SUCCESS); 319 } 320 321 /* 322 * Take the arguments and create/append a string list to the 'le' list. 323 */ 324 static poolstat_list_element_t * 325 create_args_list(char *arg, poolstat_list_element_t *le, const char *delim) 326 { 327 poolstat_list_element_t *head = le; 328 329 while (arg != NULL && *arg != '\0') { 330 char *name = arg; 331 arg = strpbrk(arg, delim); 332 if (arg != NULL) { 333 *arg++ = '\0'; 334 } 335 if (le == NULL) { 336 /* create first element */ 337 NEW0(le); 338 head = le; 339 } else { 340 /* find last and append */ 341 while (le->ple_next != NULL) 342 le = le->ple_next; 343 NEW0(le->ple_next); 344 le = le->ple_next; 345 } 346 le->ple_obj = (void *)name; 347 } 348 349 return (head); 350 } 351 352 /* 353 * Take the arguments to the -o option, and create a format field list in order 354 * specified by 'arg'. 355 * If 'arg' is NULL a list in a default printing order is created. 356 */ 357 static poolstat_list_element_t * 358 create_prt_sequence_list(char *arg, poolstat_line_format_t *lf) 359 { 360 /* 361 * Create a default print sequence. It is the sequence defined 362 * statically in the format list. At the same time mark the fields 363 * printable according to the current option settings. 364 */ 365 if (arg == NULL) { 366 int i; 367 NEW0(lf->plf_prt_seq); 368 lf->plf_ffs[0].pff_prt |= PRINTABLE(0) ? PABLE_FIELD : 0; 369 lf->plf_last = lf->plf_prt_seq; 370 lf->plf_last->ple_obj = &(lf->plf_ffs[0]); 371 for (i = 1; i < lf->plf_ff_len; i++) { 372 lf->plf_ffs[i].pff_prt |= 373 PRINTABLE(i) ? PABLE_FIELD : 0; 374 NEW0(lf->plf_last->ple_next); 375 lf->plf_last = lf->plf_last->ple_next; 376 lf->plf_last->ple_obj = &(lf->plf_ffs[i]); 377 } 378 return (lf->plf_prt_seq); 379 } 380 381 while (arg != NULL && *arg != '\0') { 382 poolstat_field_format_t *ff; /* current format field */ 383 int ffIdx; /* format field index */ 384 char *name; /* name of field */ 385 int n; /* no. of chars to strip */ 386 387 n = strspn(arg, " ,\t\r\v\f\n"); 388 arg += n; /* strip multiples separator */ 389 name = arg; 390 391 if (strlen(name) < 1) 392 break; 393 394 if ((arg = strpbrk(arg, " ,\t\r\v\f\n")) != NULL) 395 *arg++ = '\0'; 396 397 /* search for a named format field */ 398 for (ffIdx = 0; ffIdx < lf->plf_ff_len; ffIdx++) { 399 ff = lf->plf_ffs + ffIdx; 400 if (strcmp(ff->pff_name, name) == 0) { 401 ff->pff_prt |= PABLE_FIELD; 402 break; 403 } 404 } 405 /* if the name wasn't found */ 406 if (ffIdx == lf->plf_ff_len) { 407 (void) fprintf(stderr, gettext(ERR_UNSUPP_STAT_FIELD), 408 name); 409 usage(); 410 } 411 if (lf->plf_last == NULL) { 412 /* create first print handle */ 413 NEW0(lf->plf_prt_seq); 414 lf->plf_last = lf->plf_prt_seq; 415 } else { 416 NEW0(lf->plf_last->ple_next); 417 lf->plf_last = lf->plf_last->ple_next; 418 } 419 lf->plf_last->ple_obj = ff; /* refer to the format field */ 420 } 421 422 return (lf->plf_prt_seq); 423 } 424 425 /* update the statistic data by adapters */ 426 static void 427 sa_update(statistic_bag_t *sbag, int flags) 428 { 429 sa_libpool_update(sbag, flags); 430 sa_kstat_update(sbag, flags); 431 } 432 433 /* 434 * Format one statistic field and put it into the 'str' buffer. 'ff' contains 435 * the field formatting parameters. Return the number of used bytes. 436 */ 437 static int 438 default_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data) 439 { 440 int used; 441 442 switch (ff->pff_type) { 443 case LL: { 444 int64_t v; 445 v = *((int64_t *)(void *)(data + ff->pff_offset)); 446 used = snprintf(str + pos, left, "%*.*lld", 447 ff->pff_width, ff->pff_minwidth, v); 448 } 449 break; 450 case ULL: { 451 uint64_t v; 452 v = *((uint64_t *)(void *)(data + ff->pff_offset)); 453 used = snprintf(str + pos, left, "%*.*llu", 454 ff->pff_width, ff->pff_minwidth, v); 455 }; 456 break; 457 case FL: { 458 int pw = 0; 459 double v = *((double *)(void *)(data + ff->pff_offset)); 460 if (v < 10) { 461 pw = ff->pff_width - 2; 462 } else if (v < 100) { 463 pw = ff->pff_width - 3; 464 } else if (v < 1000) { 465 pw = ff->pff_width - 4; 466 } 467 if (pw < 0) 468 pw = 0; 469 used = snprintf(str + pos, left, "%*.*f", 470 ff->pff_width, pw, v); 471 }; 472 break; 473 case STR: { 474 char *v; 475 int sl; 476 v = *((char **)(void *)(data + ff->pff_offset)); 477 sl = strlen(v); 478 /* truncate if it doesn't fit */ 479 if (sl > ff->pff_width) { 480 char *cp = v + ff->pff_width - 1; 481 if (ff->pff_width < 4) 482 die(gettext(ERR_STATS_FORMAT), 483 ff->pff_header); 484 *cp-- = 0; 485 *cp-- = '.'; 486 *cp-- = '.'; 487 *cp-- = '.'; 488 } 489 used = snprintf(str + pos, left, "%-*s", ff->pff_width, 490 v); 491 } 492 break; 493 } 494 495 return (used); 496 } 497 498 /* format big numbers */ 499 static int 500 bigno_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data) 501 { 502 uint64_t v; 503 char tag; 504 int pw = ff->pff_width - 4; 505 double pv; 506 int used; 507 508 v = *((uint64_t *)(void *)(data + ff->pff_offset)); 509 /* 510 * the max value can be ULONG_MAX, which is formatted as: 511 * E P T G M K 512 * 18 446 744 073 709 551 615 513 * As a result ULONG_MAX is displayed as 18E 514 */ 515 pv = v; 516 if (v < 1000) { 517 pw = 0; 518 } else if (v < KILO * 10) { 519 pv = (double)v / KILO; 520 tag = 'K'; 521 } else if (v < KILO * 100) { 522 pv = (double)v / KILO; 523 tag = 'K'; pw -= 1; 524 } else if (v < KILO * 1000) { 525 pv = (double)v / KILO; 526 tag = 'K'; pw -= 2; 527 } else if (v < MEGA * 10) { 528 pv = (double)v / MEGA; 529 tag = 'M'; 530 } else if (v < MEGA * 100) { 531 pv = (double)v / MEGA; 532 tag = 'M'; pw -= 1; 533 } else if (v < MEGA * 1000) { 534 pv = (double)v / MEGA; 535 tag = 'M'; pw -= 2; 536 } else if (v < GIGA * 10) { 537 pv = (double)v / GIGA; 538 tag = 'G'; 539 } else if (v < GIGA * 100) { 540 pv = (double)v / GIGA; 541 tag = 'G'; pw -= 1; 542 } else if (v < GIGA * 1000) { 543 pv = (double)v / GIGA; 544 tag = 'G'; pw -= 2; 545 } else if (v < TERA * 10) { 546 pv = (double)v / TERA; 547 tag = 'T'; 548 } else if (v < TERA * 100) { 549 pv = (double)v / TERA; 550 tag = 'T'; pw -= 1; 551 } else if (v < TERA * 1000) { 552 pv = (double)v / TERA; 553 tag = 'T'; pw -= 2; 554 } else if (v < PETA * 10) { 555 pv = (double)v / PETA; 556 tag = 'P'; 557 } else if (v < PETA * 100) { 558 pv = (double)v / PETA; 559 tag = 'P'; pw -= 1; 560 } else if (v < PETA * 1000) { 561 pv = (double)v / PETA; 562 tag = 'P'; pw -= 2; 563 } else if (v < EXA * 10) { 564 pv = (double)v / EXA; 565 tag = 'E'; 566 } else if (v < EXA * 100) { 567 pv = (double)v / EXA; 568 tag = 'E'; pw -= 1; 569 } else { 570 pv = (double)v / EXA; 571 tag = 'E'; pw -= 2; 572 } 573 if (pw < 0) 574 pw = 0; 575 if (v < 1000) 576 used = snprintf(str + pos, left, "%*.*f", 577 ff->pff_width, pw, pv); 578 else 579 used = snprintf(str + pos, left, "%*.*f%c", 580 ff->pff_width - 1, pw, pv, tag); 581 582 return (used); 583 } 584 585 /* format usage statistic, if configuration has changed print '-'. */ 586 static int 587 used_stat_f(char *str, int pos, int left, poolstat_field_format_t *ff, 588 char *data) 589 { 590 int pw = 0; 591 double v = *((double *)(void *)(data + ff->pff_offset)); 592 int used; 593 594 if (pool_sbag->sb_changed & POU_POOL) { 595 used = snprintf(str + pos, left, "%*c", ff->pff_width, '-'); 596 } else { 597 if (v < 10) { 598 pw = ff->pff_width - 2; 599 } else if (v < 100) { 600 pw = ff->pff_width - 3; 601 } else if (v < 1000) { 602 pw = ff->pff_width - 4; 603 } 604 if (pw < 0) 605 pw = 0; 606 used = snprintf(str + pos, left, "%*.*f", 607 ff->pff_width, pw, v); 608 } 609 return (used); 610 } 611 612 /* 613 * Format one header field and put it into the 'str' buffer. 614 */ 615 /*ARGSUSED*/ 616 static int 617 header_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data) 618 { 619 int used = 0; 620 621 if (ff->pff_type == STR) 622 /* strings are left justified */ 623 used = snprintf(str + pos, left, "%-*s", 624 ff->pff_width, ff->pff_header); 625 else 626 used = snprintf(str + pos, left, "%*s", 627 ff->pff_width, ff->pff_header); 628 return (used); 629 } 630 631 /* 632 * Print one statistic line according to the definitions in 'lf'. 633 */ 634 static void 635 prt_stat_line(poolstat_line_format_t *lf) 636 { 637 poolstat_list_element_t *le; /* list element in the print sequence */ 638 char *line; 639 int pos = 0; /* position in the printed line */ 640 int len = MAXLINE; /* the length of the line */ 641 int left = len; /* chars left to use in the line */ 642 643 line = ZALLOC(len); 644 for (le = lf->plf_prt_seq; le; le = le->ple_next) { 645 int used; 646 poolstat_field_format_t *ff = 647 (poolstat_field_format_t *)le->ple_obj; 648 /* if the filed is marked to be printed */ 649 if (ff->pff_prt & PABLE_FIELD) { 650 if (((used = ff->pff_format(line, pos, left, ff, 651 *ff->pff_data_ptr)) + 1) >= left) { 652 /* if field doesn't fit allocate new space */ 653 len += used + MAXLINE; 654 left += used + MAXLINE; 655 line = REALLOC(line, len); 656 if (((used = ff->pff_format(line, pos, left, ff, 657 *ff->pff_data_ptr)) + 1) >= left) 658 die(gettext(ERR_STATS_FORMAT), line); 659 } 660 left -= used; 661 pos += used; 662 if (le->ple_next != NULL) { 663 /* separate columns with a space */ 664 line[pos++] = ' '; 665 left--; 666 } 667 } 668 } 669 670 (void) printf("%s\n", line); 671 FREE(line); 672 } 673 674 /* 675 * Print a statistics header line for a given resource type. 676 */ 677 static void 678 prt_stat_hd(const char *type) 679 { 680 poolstat_line_format_t *lf; /* line format */ 681 poolstat_list_element_t *le; /* list element in the print sequence */ 682 char *line; 683 int pos = 0; /* position in the printed line */ 684 int len = MAXLINE; /* the length of the line */ 685 int left = len; /* chars left to use in the line */ 686 687 if (strcmp(type, POOL_TYPE_NAME) == 0) { 688 /* pool format needs an extra header */ 689 (void) printf("%*s\n", 19 + 15, "pset"); 690 lf = &pool_lf; 691 } else if (strcmp(type, PSET_TYPE_NAME) == 0) { 692 lf = &pset_lf; 693 } else { 694 die(gettext(ERR_UNSUPP_RTYPE), type); 695 } 696 line = ZALLOC(len); 697 for (le = lf->plf_prt_seq; le; le = le->ple_next) { 698 int used; /* used chars in line */ 699 poolstat_field_format_t *ff = 700 (poolstat_field_format_t *)le->ple_obj; 701 /* if the filed is marked to be printed */ 702 if (ff->pff_prt& PABLE_FIELD) { 703 if (((used = header_f(line, pos, left, ff, NULL)) + 1) 704 >= left) { 705 /* if field doesn't fit allocate new space */ 706 len += used + MAXLINE; 707 left += used + MAXLINE; 708 line = REALLOC(line, len); 709 if (((used = header_f(line, pos, left, ff, 710 NULL)) + 1) >= left) 711 die(gettext(ERR_STATS_FORMAT), line); 712 } 713 left -= used; 714 pos += used; 715 if (le->ple_next != NULL) { 716 /* separate columns with a space */ 717 line[pos++] = ' '; 718 left--; 719 } 720 } 721 } 722 723 /* only header line with non space characters should be printed */ 724 pos = 0; 725 while (*(line + pos) != '\n') { 726 if (!isspace(*(line + pos))) { 727 (void) printf("%s\n", line); 728 729 break; 730 } 731 pos++; 732 } 733 FREE(line); 734 } 735 736 /* 737 * Create a pool value instance and set its name to 'name'. 738 */ 739 static pool_value_t * 740 create_pool_value(const char *name) 741 { 742 pool_value_t *pval; 743 744 if ((pval = pool_value_alloc()) == NULL) { 745 return (NULL); 746 } 747 if (pool_value_set_name(pval, name) != PO_SUCCESS) { 748 pool_value_free(pval); 749 return (NULL); 750 } 751 752 return (pval); 753 } 754 755 /* 756 * Find all resources of type 'rtype'. 757 * If 'pool_name' is defined find all resources bound to this pool. 758 */ 759 static pool_resource_t ** 760 get_resources(const char *pool_name, const char *rtype, uint_t *nelem) 761 { 762 pool_resource_t **resources = NULL; 763 pool_value_t *pvals[] = { NULL, NULL, NULL}; 764 pool_value_t *pv_sys_id; 765 pool_value_t *pv_name; 766 char *name_prop; /* set name property */ 767 768 if (strcmp(rtype, PSET_TYPE_NAME) == 0) { 769 if ((pv_sys_id = create_pool_value(PSET_SYSID)) == NULL) 770 goto on_error; 771 name_prop = PSET_NAME; 772 } else { 773 die(gettext(ERR_UNSUPP_RTYPE), rtype); 774 } 775 776 if ((pvals[0] = create_pool_value("type")) == NULL) 777 goto on_error; 778 if ((pool_value_set_string(pvals[0], rtype)) == -1) 779 goto on_error; 780 781 if ((pv_name = create_pool_value(name_prop)) == NULL) 782 goto on_error; 783 784 if (pool_name != NULL) { 785 /* collect resources associated to 'pool_name' */ 786 pool_t *pool; 787 if ((pool = pool_get_pool(conf, pool_name)) == NULL) 788 die(gettext(ERR_STATS_POOL_N), pool_name); 789 if ((resources = pool_query_pool_resources( 790 conf, pool, nelem, pvals)) == NULL) 791 goto on_error; 792 } else { 793 /* collect all resources */ 794 if ((resources = 795 pool_query_resources(conf, nelem, pvals)) == NULL) 796 goto on_error; 797 } 798 799 if (pv_name != NULL) 800 pool_value_free(pv_name); 801 if (pv_sys_id != NULL) 802 pool_value_free(pv_sys_id); 803 if (pvals[0] != NULL) 804 pool_value_free(pvals[0]); 805 806 return (resources); 807 on_error: 808 die(gettext(ERR_STATS_RES), get_errstr()); 809 /*NOTREACHED*/ 810 } 811 812 /* 813 * Print statistics for all resources of type 'rtype' passed in 'resources'. 814 */ 815 static void 816 prt_resource_stats_by_type(pool_resource_t **resources, const char *rtype) 817 { 818 int i; 819 pool_elem_t *elem; 820 pool_value_t *pv_name; 821 char *name_prop; 822 823 poolstat_line_format_t *lf; 824 statistic_bag_t *sbag; 825 826 if (strcmp(rtype, PSET_TYPE_NAME) == 0) { 827 name_prop = PSET_NAME; 828 lf = &pset_lf; 829 sbag = pset_sbag; 830 } else { 831 die(gettext(ERR_UNSUPP_RTYPE), rtype); 832 } 833 834 if ((pv_name = create_pool_value(name_prop)) == NULL) 835 goto on_error; 836 837 /* collect and print statistics for the given resources */ 838 for (i = 0; resources[i] != NULL; i++) { 839 if ((elem = pool_resource_to_elem(conf, resources[i])) == NULL) 840 goto on_error; 841 if (pool_get_property(conf, elem, name_prop, pv_name) == -1) 842 goto on_error; 843 if (pool_value_get_string(pv_name, &sbag->sb_name) == -1) 844 goto on_error; 845 sa_update(sbag, 0); 846 847 prt_stat_line(lf); 848 } 849 850 if (pv_name != NULL) 851 pool_value_free(pv_name); 852 return; 853 on_error: 854 die(gettext(ERR_STATS_RES), get_errstr()); 855 } 856 857 /* 858 * Update statistics for all resources of type 'rtype' pased in 'resources'. 859 */ 860 static void 861 update_resource_stats(pool_resource_t *resource, const char *rtype) 862 { 863 pool_elem_t *elem; 864 pool_value_t *pv_name; 865 char *name_prop; /* set name property */ 866 867 statistic_bag_t *sbag; 868 869 if (strcmp(rtype, PSET_TYPE_NAME) == 0) { 870 name_prop = PSET_NAME; 871 sbag = pset_sbag; 872 } else { 873 die(gettext(ERR_UNSUPP_RTYPE), rtype); 874 } 875 876 if ((pv_name = create_pool_value(name_prop)) == NULL) 877 goto on_error; 878 879 if ((elem = pool_resource_to_elem(conf, resource)) == NULL) 880 goto on_error; 881 if (pool_get_property(conf, elem, name_prop, pv_name) == -1) 882 goto on_error; 883 if (pool_value_get_string(pv_name, &sbag->sb_name) == -1) 884 goto on_error; 885 sa_update(sbag, 0); 886 887 if (pv_name != NULL) 888 pool_value_free(pv_name); 889 return; 890 891 on_error: 892 die(gettext(ERR_STATS_RES), get_errstr()); 893 } 894 895 /* 896 * For each pool in the configuration print statistics of associated resources. 897 * If the pool name list 'pn' is defined, only print resources of pools 898 * specified in the list. The list can specify the pool name or its system id. 899 */ 900 static void 901 prt_pool_stats(poolstat_list_element_t *pn) 902 { 903 uint_t nelem; 904 pool_elem_t *elem; 905 int i; 906 int error; 907 pool_t **pools = NULL; 908 pool_value_t *pvals[] = { NULL, NULL }; 909 pool_value_t *pv_name = NULL; 910 pool_value_t *pv_sys_id = NULL; 911 statistic_bag_t *sbag = pool_sbag; 912 poolstat_list_element_t *rtype; 913 pool_resource_t **resources; 914 915 if ((pv_sys_id = create_pool_value(POOL_SYSID)) == NULL) 916 goto on_error; 917 if ((pv_name = create_pool_value(POOL_NAME)) == NULL) 918 goto on_error; 919 920 if (pn == NULL) { 921 /* collect all pools */ 922 if ((pools = pool_query_pools(conf, &nelem, NULL)) == NULL) 923 goto on_error; 924 } else { 925 /* 926 * collect pools specified in the 'pn' list. 927 * 'poolid' the pool identifier can be a pool name or sys_id. 928 */ 929 poolstat_list_element_t *poolid; 930 for (poolid = pn, i = 1; poolid; poolid = poolid->ple_next) 931 i++; 932 pools = ZALLOC(sizeof (pool_t *) * (i + 1)); 933 for (poolid = pn, i = 0; poolid; 934 poolid = poolid->ple_next, i++) { 935 pool_t **pool; 936 int64_t sysid = Atoi(poolid->ple_obj, &error); 937 if (error == 0) { 938 /* the pool is identified by sys_id */ 939 pool_value_set_int64(pv_sys_id, sysid); 940 pvals[0] = pv_sys_id; 941 pool = pool_query_pools(conf, &nelem, pvals); 942 } else { 943 if (pool_value_set_string(pv_name, 944 poolid->ple_obj) == -1) 945 die(gettext(ERR_NOMEM)); 946 pvals[0] = pv_name; 947 pool = pool_query_pools(conf, &nelem, pvals); 948 } 949 if (pool == NULL) 950 die(gettext(ERR_STATS_POOL_N), poolid->ple_obj); 951 pools[i] = pool[0]; 952 FREE(pool); 953 } 954 } 955 956 /* print statistic for all pools found */ 957 if (!rflag) { 958 /* print the common resource header */ 959 prt_stat_hd(POOL_TYPE_NAME); 960 961 /* print statistics for the resources bound to the pools */ 962 for (i = 0; pools[i] != NULL; i++) { 963 elem = pool_to_elem(conf, pools[i]); 964 if (pool_get_property(conf, elem, POOL_NAME, pv_name) 965 == -1) 966 goto on_error; 967 if (pool_value_get_string(pv_name, &sbag->sb_name) != 0) 968 goto on_error; 969 if (pool_get_property( 970 conf, elem, "pool.sys_id", pv_sys_id) == -1) 971 goto on_error; 972 if (pool_value_get_int64( 973 pv_sys_id, &sbag->sb_sysid) != 0) 974 goto on_error; 975 976 for (rtype = rtypes; rtype; rtype = rtype->ple_next) { 977 resources = get_resources( 978 sbag->sb_name, rtype->ple_obj, &nelem); 979 update_resource_stats(*resources, 980 rtype->ple_obj); 981 FREE(resources); 982 } 983 prt_stat_line(&pool_lf); 984 } 985 } else { 986 /* print statistic for all resource types defined in rtypes */ 987 for (rtype = rtypes; rtype; rtype = rtype->ple_next) { 988 prt_stat_hd(rtype->ple_obj); 989 for (i = 0; pools[i] != NULL; i++) { 990 elem = pool_to_elem(conf, pools[i]); 991 if (pool_get_property( 992 conf, elem, POOL_NAME, pv_name) == -1) 993 goto on_error; 994 if (pool_value_get_string( 995 pv_name, &sbag->sb_name) != 0) 996 goto on_error; 997 if (pool_get_property( 998 conf, elem, POOL_SYSID, pv_sys_id) == -1) 999 goto on_error; 1000 if (pool_value_get_int64( 1001 pv_sys_id, &sbag->sb_sysid) != 0) 1002 goto on_error; 1003 resources = get_resources( 1004 sbag->sb_name, rtype->ple_obj, &nelem); 1005 if (resources == NULL) 1006 continue; 1007 update_resource_stats( 1008 *resources, rtype->ple_obj); 1009 prt_resource_stats_by_type(resources, 1010 rtype->ple_obj); 1011 FREE(resources); 1012 } 1013 } 1014 } 1015 1016 FREE(pools); 1017 if (pv_name != NULL) 1018 pool_value_free(pv_name); 1019 if (pv_sys_id != NULL) 1020 pool_value_free(pv_sys_id); 1021 1022 return; 1023 on_error: 1024 die(gettext(ERR_STATS_POOL), get_errstr()); 1025 } 1026