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