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