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