# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2025 Hans Rosenfeld # # cmd/pools/poolstat/Makefile PROG = poolstat OBJS = poolstat.o poolstat_utils.o sa_libpool.o sa_kstat.o SRCS = $(OBJS:%.o=%.c) $(COMMON_SRCS) POFILES = $(OBJS:.o=.po) CLEANFILES = $(OBJS) include ../../Makefile.cmd include $(SRC)/cmd/stat/Makefile.stat include ../Makefile.pools OBJS += $(COMMON_OBJS) CFLAGS += -I$(POOLSCOMMONDIR) $(EXTRA_CFLAGS) LDLIBS += -lpool -lkstat XGETFLAGS = -a CLOBBERFILES += $(POFILES) .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) %.o : $(POOLSCOMMONDIR)/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) install: all $(ROOTPROG) clean: $(RM) $(CLEANFILES) include ../../Makefile.targ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * poolstat - report active pool statistics */ #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "poolstat.h" #include "poolstat_utils.h" #include "statcommon.h" #ifndef TEXT_DOMAIN #define TEXT_DOMAIN "SYS_TEST" #endif #define addrof(s) ((char **)&(s)) /* verify if a field is printable in respect of the current option flags */ #define PRINTABLE(i) ((lf->plf_ffs[(i)].pff_prt & D_FIELD) || \ (lf->plf_ffs[(i)].pff_prt & X_FIELD)) typedef int (* formatter) (char *, int, int, poolstat_field_format_t *, char *); static uint_t timestamp_fmt = NODATE; /* available field formatters */ static int default_f(char *, int, int, poolstat_field_format_t *, char *); static int bigno_f(char *, int, int, poolstat_field_format_t *, char *); static int used_stat_f(char *, int, int, poolstat_field_format_t *, char *); static int header_f(char *, int, int, poolstat_field_format_t *, char *); /* statistics bags used to collect data from various provider */ static statistic_bag_t pool_sbag_s; static statistic_bag_t pset_sbag_s; static statistic_bag_t *pool_sbag = &pool_sbag_s; static statistic_bag_t *pset_sbag = &pset_sbag_s; /* formatter objects for pset, defined in a default printing sequence */ static poolstat_field_format_t pset_ffs[] = { /* prt flags,name,header,type,width,minwidth,offset,formatter */ { DX_FIELD, "id", "id", LL, 3, 1, addrof(pool_sbag), offsetof(statistic_bag_t, sb_sysid), (formatter)default_f }, { DX_FIELD, "pool", "pool", STR, 20, 14, addrof(pool_sbag), offsetof(statistic_bag_t, sb_name), (formatter)default_f }, { DX_FIELD, "type", "type", STR, 4, 5, addrof(pset_sbag), offsetof(statistic_bag_t, sb_type), (formatter)default_f }, { D_FIELD, "rid", "rid", LL, 3, 1, addrof(pset_sbag_s.bag), offsetof(pset_statistic_bag_t, pset_sb_sysid), (formatter)default_f }, { DX_FIELD, "rset", "rset", STR, 20, 14, addrof(pset_sbag), offsetof(statistic_bag_t, sb_name), (formatter)default_f }, { DX_FIELD, "min", "min", ULL, 4, 1, addrof(pset_sbag_s.bag), offsetof(pset_statistic_bag_t, pset_sb_min), (formatter)bigno_f }, { DX_FIELD, "max", "max", ULL, 4, 1, addrof(pset_sbag_s.bag), offsetof(pset_statistic_bag_t, pset_sb_max), (formatter)bigno_f }, { DX_FIELD, "size", "size", ULL, 4, 1, addrof(pset_sbag_s.bag), offsetof(pset_statistic_bag_t, pset_sb_size), (formatter)default_f }, { DX_FIELD, "used", "used", FL, 4, -1, addrof(pset_sbag_s.bag), offsetof(pset_statistic_bag_t, pset_sb_used), (formatter)used_stat_f }, { DX_FIELD, "load", "load", FL, 4, -1, addrof(pset_sbag_s.bag), offsetof(pset_statistic_bag_t, pset_sb_load), (formatter)default_f } }; /* formatter objects for pool, defined in a default printing sequence */ static poolstat_field_format_t pool_ffs[] = { /* prt flags,name,header,type,width,minwidth,offset,formatter */ { D_FIELD, "id", "id", LL, 3, 1, addrof(pool_sbag), offsetof(statistic_bag_t, sb_sysid), (formatter)default_f }, { D_FIELD, "pool", "pool", STR, 20, 13, addrof(pool_sbag), offsetof(statistic_bag_t, sb_name), (formatter)default_f }, { D_FIELD, "p_size", "size", ULL, 4, 1, addrof(pset_sbag_s.bag), offsetof(pset_statistic_bag_t, pset_sb_size), (formatter)default_f }, { D_FIELD, "p_used", "used", FL, 4, -1, addrof(pset_sbag_s.bag), offsetof(pset_statistic_bag_t, pset_sb_used), (formatter)default_f }, { D_FIELD, "p_load", "load", FL, 4, -1, addrof(pset_sbag_s.bag), offsetof(pset_statistic_bag_t, pset_sb_load), (formatter)default_f }, }; /* lists with formatter objects, one for each statistics field */ static poolstat_line_format_t pool_lf; /* formatting list in default mode */ static poolstat_line_format_t pset_lf; /* formatting list for psets */ /* name of pools to be shown */ static poolstat_list_element_t *pnames; /* * type of resources to be shown, currently we only have one type 'pset' * but, poolstat can be extended to handle new upcoming resource types. */ static poolstat_list_element_t *rtypes; /* a handle to the pool configuration */ static pool_conf_t *conf; /* option flags */ static int rflag; static int pflag; static int oflag; /* operands */ static int interval = 0; /* update interval */ static long count = 1; /* one run */ /* data structure handlers */ static poolstat_list_element_t * create_prt_sequence_list(char *, poolstat_line_format_t *); static poolstat_list_element_t * create_args_list(char *, poolstat_list_element_t *, const char *); /* statistics update function */ static void sa_update(statistic_bag_t *, int); /* statistics printing function */ static void prt_pool_stats(poolstat_list_element_t *); static void usage(void) __NORETURN; static void usage(void) { (void) fprintf(stderr, gettext( "Usage:\n" "poolstat [-p pool-list] [-r rset-list] [-T d|u] [interval [count]]\n" "poolstat [-p pool-list] [-o format -r rset-list] [-T d|u] [interval [count]]\n" " \'pool-list\' is a space-separated list of pool IDs or names\n" " \'rset-list\' is \'all\' or \'pset\'\n" " \'format\' for all resource types is one or more of:\n" "\tid pool type rid rset min max size used load\n")); (void) exit(E_USAGE); } static int Atoi(char *p, int *errp) { int i; char *q; errno = 0; i = strtol(p, &q, 10); if (errno != 0 || q == p || *q != '\0') *errp = -1; else *errp = 0; return (i); } int main(int argc, char *argv[]) { int c; int error = 0; (void) getpname(argv[0]); (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); /* pset_sbag_s is used to collect pset statistics */ pset_sbag_s.sb_type = PSET_TYPE_NAME; pset_sbag_s.bag = ZALLOC(sizeof (pset_statistic_bag_t)); pool_sbag_s.sb_type = POOL_TYPE_NAME; pset_lf.plf_ffs = pset_ffs; pset_lf.plf_ff_len = sizeof (pset_ffs) / sizeof (poolstat_field_format_t); pool_lf.plf_ffs = pool_ffs; pool_lf.plf_ff_len = sizeof (pool_ffs) / sizeof (poolstat_field_format_t); /* Don't let buffering interfere with piped output. */ (void) setvbuf(stdout, NULL, _IOLBF, 0); while ((c = getopt(argc, argv, ":p:r:o:T:")) != EOF) { switch (c) { case 'p': /* pool name specification */ pflag++; pnames = create_args_list(optarg, pnames, " \t"); break; case 'r': { /* resource type */ rflag++; rtypes = create_args_list(optarg, rtypes, " \t,"); break; } case 'o': { /* format specification */ oflag++; if (create_prt_sequence_list(optarg, &pset_lf) == NULL) usage(); break; } case 'T': if (optarg) { if (*optarg == 'u') timestamp_fmt = UDATE; else if (*optarg == 'd') timestamp_fmt = DDATE; else usage(); } else { usage(); } break; case ':': { (void) fprintf(stderr, gettext(ERR_OPTION_ARGS), optopt); usage(); /*NOTREACHED*/ } default: (void) fprintf(stderr, gettext(ERR_OPTION), optopt); usage(); /*NOTREACHED*/ } } /* get operands */ if (argc > optind) { if ((interval = Atoi(argv[optind++], &error)) < 1 || error != 0) usage(); count = -1; } if (argc > optind) { if ((count = Atoi(argv[optind++], &error)) < 1 || error != 0) usage(); } /* check for extra options/operands */ if (argc > optind) usage(); /* check options */ if (oflag && !rflag) usage(); /* global initializations */ if (!oflag) { /* create the default print sequences */ (void) create_prt_sequence_list(NULL, &pool_lf); (void) create_prt_sequence_list(NULL, &pset_lf); } if (rtypes == NULL || strcmp(rtypes->ple_obj, "all") == 0) { /* crate a default resource list */ FREE(rtypes); rtypes = create_args_list("pset", NULL, " \t,"); } if ((conf = pool_conf_alloc()) == NULL) die(gettext(ERR_NOMEM)); if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY) != PO_SUCCESS) die(gettext(ERR_OPEN_DYNAMIC), get_errstr()); /* initialize statistic adapters */ sa_libpool_init(conf); sa_kstat_init(NULL); /* collect and print out statistics */ while (count-- != 0) { sa_update(pool_sbag, SA_REFRESH); if (timestamp_fmt != NODATE) print_timestamp(timestamp_fmt); if (pool_sbag->sb_changed & POU_POOL) (void) printf( "<>\n"); prt_pool_stats(pnames); if (count != 0) { (void) sleep(interval); if (rflag) (void) printf("\n"); } } return (E_PO_SUCCESS); } /* * Take the arguments and create/append a string list to the 'le' list. */ static poolstat_list_element_t * create_args_list(char *arg, poolstat_list_element_t *le, const char *delim) { poolstat_list_element_t *head = le; while (arg != NULL && *arg != '\0') { char *name = arg; arg = strpbrk(arg, delim); if (arg != NULL) { *arg++ = '\0'; } if (le == NULL) { /* create first element */ NEW0(le); head = le; } else { /* find last and append */ while (le->ple_next != NULL) le = le->ple_next; NEW0(le->ple_next); le = le->ple_next; } le->ple_obj = (void *)name; } return (head); } /* * Take the arguments to the -o option, and create a format field list in order * specified by 'arg'. * If 'arg' is NULL a list in a default printing order is created. */ static poolstat_list_element_t * create_prt_sequence_list(char *arg, poolstat_line_format_t *lf) { /* * Create a default print sequence. It is the sequence defined * statically in the format list. At the same time mark the fields * printable according to the current option settings. */ if (arg == NULL) { int i; NEW0(lf->plf_prt_seq); lf->plf_ffs[0].pff_prt |= PRINTABLE(0) ? PABLE_FIELD : 0; lf->plf_last = lf->plf_prt_seq; lf->plf_last->ple_obj = &(lf->plf_ffs[0]); for (i = 1; i < lf->plf_ff_len; i++) { lf->plf_ffs[i].pff_prt |= PRINTABLE(i) ? PABLE_FIELD : 0; NEW0(lf->plf_last->ple_next); lf->plf_last = lf->plf_last->ple_next; lf->plf_last->ple_obj = &(lf->plf_ffs[i]); } return (lf->plf_prt_seq); } while (arg != NULL && *arg != '\0') { poolstat_field_format_t *ff; /* current format field */ int ffIdx; /* format field index */ char *name; /* name of field */ int n; /* no. of chars to strip */ n = strspn(arg, " ,\t\r\v\f\n"); arg += n; /* strip multiples separator */ name = arg; if (strlen(name) < 1) break; if ((arg = strpbrk(arg, " ,\t\r\v\f\n")) != NULL) *arg++ = '\0'; /* search for a named format field */ for (ffIdx = 0; ffIdx < lf->plf_ff_len; ffIdx++) { ff = lf->plf_ffs + ffIdx; if (strcmp(ff->pff_name, name) == 0) { ff->pff_prt |= PABLE_FIELD; break; } } /* if the name wasn't found */ if (ffIdx == lf->plf_ff_len) { (void) fprintf(stderr, gettext(ERR_UNSUPP_STAT_FIELD), name); usage(); } if (lf->plf_last == NULL) { /* create first print handle */ NEW0(lf->plf_prt_seq); lf->plf_last = lf->plf_prt_seq; } else { NEW0(lf->plf_last->ple_next); lf->plf_last = lf->plf_last->ple_next; } lf->plf_last->ple_obj = ff; /* refer to the format field */ } return (lf->plf_prt_seq); } /* update the statistic data by adapters */ static void sa_update(statistic_bag_t *sbag, int flags) { sa_libpool_update(sbag, flags); sa_kstat_update(sbag, flags); } /* * Format one statistic field and put it into the 'str' buffer. 'ff' contains * the field formatting parameters. Return the number of used bytes. */ static int default_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data) { int used; switch (ff->pff_type) { case LL: { int64_t v; v = *((int64_t *)(void *)(data + ff->pff_offset)); used = snprintf(str + pos, left, "%*.*lld", ff->pff_width, ff->pff_minwidth, v); } break; case ULL: { uint64_t v; v = *((uint64_t *)(void *)(data + ff->pff_offset)); used = snprintf(str + pos, left, "%*.*llu", ff->pff_width, ff->pff_minwidth, v); }; break; case FL: { int pw = 0; double v = *((double *)(void *)(data + ff->pff_offset)); if (v < 10) { pw = ff->pff_width - 2; } else if (v < 100) { pw = ff->pff_width - 3; } else if (v < 1000) { pw = ff->pff_width - 4; } if (pw < 0) pw = 0; used = snprintf(str + pos, left, "%*.*f", ff->pff_width, pw, v); }; break; case STR: { char *v; int sl; v = *((char **)(void *)(data + ff->pff_offset)); sl = strlen(v); /* truncate if it doesn't fit */ if (sl > ff->pff_width) { char *cp = v + ff->pff_width - 1; if (ff->pff_width < 4) die(gettext(ERR_STATS_FORMAT), ff->pff_header); *cp-- = 0; *cp-- = '.'; *cp-- = '.'; *cp-- = '.'; } used = snprintf(str + pos, left, "%-*s", ff->pff_width, v); } break; } return (used); } /* format big numbers */ static int bigno_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data) { uint64_t v; char tag; int pw = ff->pff_width - 4; double pv; int used; v = *((uint64_t *)(void *)(data + ff->pff_offset)); /* * the max value can be ULONG_MAX, which is formatted as: * E P T G M K * 18 446 744 073 709 551 615 * As a result ULONG_MAX is displayed as 18E */ pv = v; if (v < 1000) { pw = 0; } else if (v < KILO * 10) { pv = (double)v / KILO; tag = 'K'; } else if (v < KILO * 100) { pv = (double)v / KILO; tag = 'K'; pw -= 1; } else if (v < KILO * 1000) { pv = (double)v / KILO; tag = 'K'; pw -= 2; } else if (v < MEGA * 10) { pv = (double)v / MEGA; tag = 'M'; } else if (v < MEGA * 100) { pv = (double)v / MEGA; tag = 'M'; pw -= 1; } else if (v < MEGA * 1000) { pv = (double)v / MEGA; tag = 'M'; pw -= 2; } else if (v < GIGA * 10) { pv = (double)v / GIGA; tag = 'G'; } else if (v < GIGA * 100) { pv = (double)v / GIGA; tag = 'G'; pw -= 1; } else if (v < GIGA * 1000) { pv = (double)v / GIGA; tag = 'G'; pw -= 2; } else if (v < TERA * 10) { pv = (double)v / TERA; tag = 'T'; } else if (v < TERA * 100) { pv = (double)v / TERA; tag = 'T'; pw -= 1; } else if (v < TERA * 1000) { pv = (double)v / TERA; tag = 'T'; pw -= 2; } else if (v < PETA * 10) { pv = (double)v / PETA; tag = 'P'; } else if (v < PETA * 100) { pv = (double)v / PETA; tag = 'P'; pw -= 1; } else if (v < PETA * 1000) { pv = (double)v / PETA; tag = 'P'; pw -= 2; } else if (v < EXA * 10) { pv = (double)v / EXA; tag = 'E'; } else if (v < EXA * 100) { pv = (double)v / EXA; tag = 'E'; pw -= 1; } else { pv = (double)v / EXA; tag = 'E'; pw -= 2; } if (pw < 0) pw = 0; if (v < 1000) used = snprintf(str + pos, left, "%*.*f", ff->pff_width, pw, pv); else used = snprintf(str + pos, left, "%*.*f%c", ff->pff_width - 1, pw, pv, tag); return (used); } /* format usage statistic, if configuration has changed print '-'. */ static int used_stat_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data) { int pw = 0; double v = *((double *)(void *)(data + ff->pff_offset)); int used; if (pool_sbag->sb_changed & POU_POOL) { used = snprintf(str + pos, left, "%*c", ff->pff_width, '-'); } else { if (v < 10) { pw = ff->pff_width - 2; } else if (v < 100) { pw = ff->pff_width - 3; } else if (v < 1000) { pw = ff->pff_width - 4; } if (pw < 0) pw = 0; used = snprintf(str + pos, left, "%*.*f", ff->pff_width, pw, v); } return (used); } /* * Format one header field and put it into the 'str' buffer. */ /*ARGSUSED*/ static int header_f(char *str, int pos, int left, poolstat_field_format_t *ff, char *data) { int used = 0; if (ff->pff_type == STR) /* strings are left justified */ used = snprintf(str + pos, left, "%-*s", ff->pff_width, ff->pff_header); else used = snprintf(str + pos, left, "%*s", ff->pff_width, ff->pff_header); return (used); } /* * Print one statistic line according to the definitions in 'lf'. */ static void prt_stat_line(poolstat_line_format_t *lf) { poolstat_list_element_t *le; /* list element in the print sequence */ char *line; int pos = 0; /* position in the printed line */ int len = MAXLINE; /* the length of the line */ int left = len; /* chars left to use in the line */ line = ZALLOC(len); for (le = lf->plf_prt_seq; le; le = le->ple_next) { int used; poolstat_field_format_t *ff = (poolstat_field_format_t *)le->ple_obj; /* if the filed is marked to be printed */ if (ff->pff_prt & PABLE_FIELD) { if (((used = ff->pff_format(line, pos, left, ff, *ff->pff_data_ptr)) + 1) >= left) { /* if field doesn't fit allocate new space */ len += used + MAXLINE; left += used + MAXLINE; line = REALLOC(line, len); if (((used = ff->pff_format(line, pos, left, ff, *ff->pff_data_ptr)) + 1) >= left) die(gettext(ERR_STATS_FORMAT), line); } left -= used; pos += used; if (le->ple_next != NULL) { /* separate columns with a space */ line[pos++] = ' '; left--; } } } (void) printf("%s\n", line); FREE(line); } /* * Print a statistics header line for a given resource type. */ static void prt_stat_hd(const char *type) { poolstat_line_format_t *lf; /* line format */ poolstat_list_element_t *le; /* list element in the print sequence */ char *line; int pos = 0; /* position in the printed line */ int len = MAXLINE; /* the length of the line */ int left = len; /* chars left to use in the line */ if (strcmp(type, POOL_TYPE_NAME) == 0) { /* pool format needs an extra header */ (void) printf("%*s\n", 19 + 15, "pset"); lf = &pool_lf; } else if (strcmp(type, PSET_TYPE_NAME) == 0) { lf = &pset_lf; } else { die(gettext(ERR_UNSUPP_RTYPE), type); } line = ZALLOC(len); for (le = lf->plf_prt_seq; le; le = le->ple_next) { int used; /* used chars in line */ poolstat_field_format_t *ff = (poolstat_field_format_t *)le->ple_obj; /* if the filed is marked to be printed */ if (ff->pff_prt& PABLE_FIELD) { if (((used = header_f(line, pos, left, ff, NULL)) + 1) >= left) { /* if field doesn't fit allocate new space */ len += used + MAXLINE; left += used + MAXLINE; line = REALLOC(line, len); if (((used = header_f(line, pos, left, ff, NULL)) + 1) >= left) die(gettext(ERR_STATS_FORMAT), line); } left -= used; pos += used; if (le->ple_next != NULL) { /* separate columns with a space */ line[pos++] = ' '; left--; } } } /* only header line with non space characters should be printed */ pos = 0; while (*(line + pos) != '\n') { if (!isspace(*(line + pos))) { (void) printf("%s\n", line); break; } pos++; } FREE(line); } /* * Create a pool value instance and set its name to 'name'. */ static pool_value_t * create_pool_value(const char *name) { pool_value_t *pval; if ((pval = pool_value_alloc()) == NULL) { return (NULL); } if (pool_value_set_name(pval, name) != PO_SUCCESS) { pool_value_free(pval); return (NULL); } return (pval); } /* * Find all resources of type 'rtype'. * If 'pool_name' is defined find all resources bound to this pool. */ static pool_resource_t ** get_resources(const char *pool_name, const char *rtype, uint_t *nelem) { pool_resource_t **resources = NULL; pool_value_t *pvals[] = { NULL, NULL, NULL}; pool_value_t *pv_sys_id; pool_value_t *pv_name; char *name_prop; /* set name property */ if (strcmp(rtype, PSET_TYPE_NAME) == 0) { if ((pv_sys_id = create_pool_value(PSET_SYSID)) == NULL) goto on_error; name_prop = PSET_NAME; } else { die(gettext(ERR_UNSUPP_RTYPE), rtype); } if ((pvals[0] = create_pool_value("type")) == NULL) goto on_error; if ((pool_value_set_string(pvals[0], rtype)) == -1) goto on_error; if ((pv_name = create_pool_value(name_prop)) == NULL) goto on_error; if (pool_name != NULL) { /* collect resources associated to 'pool_name' */ pool_t *pool; if ((pool = pool_get_pool(conf, pool_name)) == NULL) die(gettext(ERR_STATS_POOL_N), pool_name); if ((resources = pool_query_pool_resources( conf, pool, nelem, pvals)) == NULL) goto on_error; } else { /* collect all resources */ if ((resources = pool_query_resources(conf, nelem, pvals)) == NULL) goto on_error; } if (pv_name != NULL) pool_value_free(pv_name); if (pv_sys_id != NULL) pool_value_free(pv_sys_id); if (pvals[0] != NULL) pool_value_free(pvals[0]); return (resources); on_error: die(gettext(ERR_STATS_RES), get_errstr()); /*NOTREACHED*/ } /* * Print statistics for all resources of type 'rtype' passed in 'resources'. */ static void prt_resource_stats_by_type(pool_resource_t **resources, const char *rtype) { int i; pool_elem_t *elem; pool_value_t *pv_name; char *name_prop; poolstat_line_format_t *lf; statistic_bag_t *sbag; if (strcmp(rtype, PSET_TYPE_NAME) == 0) { name_prop = PSET_NAME; lf = &pset_lf; sbag = pset_sbag; } else { die(gettext(ERR_UNSUPP_RTYPE), rtype); } if ((pv_name = create_pool_value(name_prop)) == NULL) goto on_error; /* collect and print statistics for the given resources */ for (i = 0; resources[i] != NULL; i++) { if ((elem = pool_resource_to_elem(conf, resources[i])) == NULL) goto on_error; if (pool_get_property(conf, elem, name_prop, pv_name) == -1) goto on_error; if (pool_value_get_string(pv_name, &sbag->sb_name) == -1) goto on_error; sa_update(sbag, 0); prt_stat_line(lf); } if (pv_name != NULL) pool_value_free(pv_name); return; on_error: die(gettext(ERR_STATS_RES), get_errstr()); } /* * Update statistics for all resources of type 'rtype' pased in 'resources'. */ static void update_resource_stats(pool_resource_t *resource, const char *rtype) { pool_elem_t *elem; pool_value_t *pv_name; char *name_prop; /* set name property */ statistic_bag_t *sbag; if (strcmp(rtype, PSET_TYPE_NAME) == 0) { name_prop = PSET_NAME; sbag = pset_sbag; } else { die(gettext(ERR_UNSUPP_RTYPE), rtype); } if ((pv_name = create_pool_value(name_prop)) == NULL) goto on_error; if ((elem = pool_resource_to_elem(conf, resource)) == NULL) goto on_error; if (pool_get_property(conf, elem, name_prop, pv_name) == -1) goto on_error; if (pool_value_get_string(pv_name, &sbag->sb_name) == -1) goto on_error; sa_update(sbag, 0); if (pv_name != NULL) pool_value_free(pv_name); return; on_error: die(gettext(ERR_STATS_RES), get_errstr()); } /* * For each pool in the configuration print statistics of associated resources. * If the pool name list 'pn' is defined, only print resources of pools * specified in the list. The list can specify the pool name or its system id. */ static void prt_pool_stats(poolstat_list_element_t *pn) { uint_t nelem; pool_elem_t *elem; int i; int error; pool_t **pools = NULL; pool_value_t *pvals[] = { NULL, NULL }; pool_value_t *pv_name = NULL; pool_value_t *pv_sys_id = NULL; statistic_bag_t *sbag = pool_sbag; poolstat_list_element_t *rtype; pool_resource_t **resources; if ((pv_sys_id = create_pool_value(POOL_SYSID)) == NULL) goto on_error; if ((pv_name = create_pool_value(POOL_NAME)) == NULL) goto on_error; if (pn == NULL) { /* collect all pools */ if ((pools = pool_query_pools(conf, &nelem, NULL)) == NULL) goto on_error; } else { /* * collect pools specified in the 'pn' list. * 'poolid' the pool identifier can be a pool name or sys_id. */ poolstat_list_element_t *poolid; for (poolid = pn, i = 1; poolid; poolid = poolid->ple_next) i++; pools = ZALLOC(sizeof (pool_t *) * (i + 1)); for (poolid = pn, i = 0; poolid; poolid = poolid->ple_next, i++) { pool_t **pool; int64_t sysid = Atoi(poolid->ple_obj, &error); if (error == 0) { /* the pool is identified by sys_id */ pool_value_set_int64(pv_sys_id, sysid); pvals[0] = pv_sys_id; pool = pool_query_pools(conf, &nelem, pvals); } else { if (pool_value_set_string(pv_name, poolid->ple_obj) == -1) die(gettext(ERR_NOMEM)); pvals[0] = pv_name; pool = pool_query_pools(conf, &nelem, pvals); } if (pool == NULL) die(gettext(ERR_STATS_POOL_N), poolid->ple_obj); pools[i] = pool[0]; FREE(pool); } } /* print statistic for all pools found */ if (!rflag) { /* print the common resource header */ prt_stat_hd(POOL_TYPE_NAME); /* print statistics for the resources bound to the pools */ for (i = 0; pools[i] != NULL; i++) { elem = pool_to_elem(conf, pools[i]); if (pool_get_property(conf, elem, POOL_NAME, pv_name) == -1) goto on_error; if (pool_value_get_string(pv_name, &sbag->sb_name) != 0) goto on_error; if (pool_get_property( conf, elem, "pool.sys_id", pv_sys_id) == -1) goto on_error; if (pool_value_get_int64( pv_sys_id, &sbag->sb_sysid) != 0) goto on_error; for (rtype = rtypes; rtype; rtype = rtype->ple_next) { resources = get_resources( sbag->sb_name, rtype->ple_obj, &nelem); update_resource_stats(*resources, rtype->ple_obj); FREE(resources); } prt_stat_line(&pool_lf); } } else { /* print statistic for all resource types defined in rtypes */ for (rtype = rtypes; rtype; rtype = rtype->ple_next) { prt_stat_hd(rtype->ple_obj); for (i = 0; pools[i] != NULL; i++) { elem = pool_to_elem(conf, pools[i]); if (pool_get_property( conf, elem, POOL_NAME, pv_name) == -1) goto on_error; if (pool_value_get_string( pv_name, &sbag->sb_name) != 0) goto on_error; if (pool_get_property( conf, elem, POOL_SYSID, pv_sys_id) == -1) goto on_error; if (pool_value_get_int64( pv_sys_id, &sbag->sb_sysid) != 0) goto on_error; resources = get_resources( sbag->sb_name, rtype->ple_obj, &nelem); if (resources == NULL) continue; update_resource_stats( *resources, rtype->ple_obj); prt_resource_stats_by_type(resources, rtype->ple_obj); FREE(resources); } } } FREE(pools); if (pv_name != NULL) pool_value_free(pv_name); if (pv_sys_id != NULL) pool_value_free(pv_sys_id); return; on_error: die(gettext(ERR_STATS_POOL), get_errstr()); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _POOLSTAT_H #define _POOLSTAT_H #include #ifdef __cplusplus extern "C" { #endif /* * The following are types and defines used to collect statistic data. * Different statistic providers can be used to collect the data. * Two functions build the interface to each provider: * 'provider'_init(), and 'provider'_update(). In the update function * a provider fills out the passed data structure with statistics data * it is responsible for. */ /* Error messages for poolstat */ #define ERR_LOAD_AVERAGE "cannot get load average: %s\n" #define ERR_BINDING "cannot get resource binding: %s\n" #define ERR_STATS_POOL_N "cannot get statistics for pool '%s'\n" #define ERR_STATS_RES_N "cannot get statistics for resource '%s': %s\n" #define ERR_STATS_POOL "cannot get pool statistics: %s\n" #define ERR_STATS_RES "cannot get resource statistics: %s\n" #define ERR_STATS_FORMAT "cannot format statistic line: %s\n" #define ERR_KSTAT_OPEN "kstat open failed: %s\n" #define ERR_KSTAT_DATA "cannot get kstat data: %s\n" #define ERR_KSTAT_DLOOKUP "kstat_data_lookup('%s', '%s') failed: %s\n" #define ERR_OPTION_ARGS "Option -%c requires an argument\n" #define ERR_OPTION "poolstat: illegal option -- %c\n" #define ERR_CONF_UPDATE "pool configuration update failed: %s\n" #define ERR_UNSUPP_STYPE "unsupported statistic type: %s\n" #define ERR_UNSUPP_RTYPE "unsupported resource type: %s\n" #define ERR_UNSUPP_STAT_FIELD "unsupported statistic field: %s\n" #define POOL_TYPE_NAME "pool" #define PSET_TYPE_NAME "pset" #define POOL_SYSID "pool.sys_id" #define PSET_SYSID "pset.sys_id" /* set types */ typedef enum { ST_PSET } st_t; /* update flag, forces refresh of statistic data */ #define SA_REFRESH 1 /* data bag used to collect statistics for a processor set */ typedef struct { int64_t pset_sb_sysid; uint64_t pset_sb_min; uint64_t pset_sb_max; uint64_t pset_sb_size; double pset_sb_used; double pset_sb_load; uint64_t pset_sb_changed; } pset_statistic_bag_t; /* wrapper for different statistic bags */ typedef struct { const char *sb_name; /* pool or resource name used as identifier */ int64_t sb_sysid; /* the sysid */ const char *sb_type; /* the type can be "pool", or "pset" */ uint64_t sb_changed; void* bag; } statistic_bag_t; /* shortcut to access a element in the pset statistic bag. */ #define PSETBAG_ELEM(p, e) (((pset_statistic_bag_t *)(p)->bag)->e) /* statistic adapters */ extern void sa_libpool_init(void *); extern void sa_libpool_update(statistic_bag_t *sbag, int flags); extern void sa_kstat_init(void *); extern void sa_kstat_update(statistic_bag_t *sbag, int flags); /* * The following types and defines are used to format statistic lines. * All formatting information for a particular line are grouped in 'lf_t' * structure. * Two data sequences are anchored there: an array with all possible formatting * directives for fields that can occur in a statistic line, and a list with * pointers to elements in this array. This list defines which fields and in * which order should be printed. * Formatting directives for one field are grouped in 'poolstat_field_format_t' * structure. Basically it contains a pointer to a formatting function and some * formatting parameters used by this function. */ /* the length of a statistic line */ #define MAXLINE 160 /* default print field */ #define D_FIELD 0x01 /* -x option print field */ #define X_FIELD 0x02 /* -o option print field */ #define O_FIELD 0x04 /* print field in default and extended mode */ #define DX_FIELD (D_FIELD | X_FIELD) /* marks a field as printable */ #define PABLE_FIELD 0x80 #define KILO 1000 #define MEGA ((uint64_t)(KILO * 1000)) #define GIGA ((uint64_t)(MEGA * 1000)) #define TERA ((uint64_t)(GIGA * 1000)) #define PETA ((uint64_t)(TERA * 1000)) #define EXA ((uint64_t)(PETA * 1000)) #define KBYTE 1024 #define MBYTE ((uint64_t)(KBYTE * 1024)) #define GBYTE ((uint64_t)(MBYTE * 1024)) #define TBYTE ((uint64_t)(GBYTE * 1024)) #define PBYTE ((uint64_t)(TBYTE * 1024)) #define EBYTE ((uint64_t)(PBYTE * 1024)) /* statistic data types */ typedef enum { ULL, LL, FL, STR } dt_t; /* poolstat_field_format_t contains information for one statistic field */ typedef struct poolstat_field_format { int pff_prt; /* printable flag */ const char *pff_name; /* name of the statistic */ const char *pff_header; const dt_t pff_type; /* the data type */ int pff_width; /* width, excluding whitespace */ const int pff_minwidth; char **pff_data_ptr; const size_t pff_offset; /* offset in a data block */ /* formatter */ int (* pff_format) (char *, int, int, struct poolstat_field_format *, char *); } poolstat_field_format_t; /* list element, used to link arbitrary objects in a list */ typedef struct _myself { void *ple_obj; struct _myself *ple_next; } poolstat_list_element_t; /* * poolstat_line_format_t contains formatting information for one * statistics line. */ typedef struct { /* pointer to an array with all format fields */ poolstat_field_format_t *plf_ffs; /* the lenght of format field array */ int plf_ff_len; /* the field's print sequence */ poolstat_list_element_t *plf_prt_seq; /* pointer to the last field in prt. sequence */ poolstat_list_element_t *plf_last; } poolstat_line_format_t; #ifdef __cplusplus } #endif #endif /* _POOLSTAT_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include "utils.h" #include "poolstat_utils.h" void * Realloc(void *ptr, size_t nbytes) { if ((ptr = realloc(ptr, nbytes))) return (ptr); die(gettext(ERR_NOMEM)); /*NOTREACHED*/ } void * Zalloc(size_t nbytes) { return (memset(Realloc(NULL, nbytes), 0, nbytes)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _POOLSTAT_UTILS_H #define _POOLSTAT_UTILS_H #include #ifdef __cplusplus extern "C" { #endif #define REALLOC(p, nbytes) Realloc((p), (nbytes)) #define ZALLOC(nbytes) Zalloc((nbytes)) #define FREE(p) ((void) (free((p)), (p) = 0)) #define NEW0(p) ((p) = ZALLOC((long)sizeof (*(p)))) extern void *Zalloc(size_t); extern void *Realloc(void *, size_t); extern void Free(void *); #ifdef __cplusplus } #endif #endif /* _POOLSTAT_UTILS_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * sa_kstat - kstat statistic adapter, collects statistic data provided * by kstat. */ #include #include #include #include #include #include "utils.h" #include #include "poolstat.h" #include "poolstat_utils.h" /* marks 'sdata_t' element as updated */ #define SD_UPDATED 1 /* Specified value for an invalid set. */ #define INVALID_SET -2 /* statistic data */ typedef struct sdata { kstat_t sd_oks; /* old kstat */ kstat_t sd_nks; /* new kstat */ void *sd_udata; /* user data */ uint_t sd_state; /* state of this data (UPDATED) */ struct sdata *sd_next; } sdata_t; /* pset user data */ typedef struct { psetid_t opset; /* old pset sysid */ psetid_t npset; /* new pset sysid */ } pset_ud_t; /* shortcuts to access set's id in 'pset_ud_t' */ #define SD_OPSET(p) (((pset_ud_t *)(p)->sd_udata)->opset) #define SD_NPSET(p) (((pset_ud_t *)(p)->sd_udata)->npset) static kstat_ctl_t *ks_ctl; /* libkstat handle */ static sdata_t *cpu_list; /* list with cpu statistics */ static sdata_t *update_sdata_list(sdata_t *, kstat_ctl_t *, char *, int, char *, int *); static void update_cpu_list(sdata_t *list); static void update_pset_stats(statistic_bag_t *, sdata_t *); /*ARGSUSED*/ void sa_kstat_init(void *unused) { if ((ks_ctl = kstat_open()) == NULL) die(gettext(ERR_KSTAT_OPEN), get_errstr()); } void sa_kstat_update(statistic_bag_t *sbag, int flags) { /* The SA_REFRESH flag forces the update of local data structures. */ if (flags & SA_REFRESH) { int ks_error = 0; if (kstat_chain_update(ks_ctl) == -1) die(gettext(ERR_KSTAT_DATA), get_errstr()); cpu_list = update_sdata_list(cpu_list, ks_ctl, "cpu", -1, "sys", &ks_error); if (ks_error) die(gettext(ERR_KSTAT_DATA), get_errstr()); /* update info about cpu binding to processor sets */ update_cpu_list(cpu_list); } if (strcmp(sbag->sb_type, PSET_TYPE_NAME) == 0) { update_pset_stats(sbag, cpu_list); } else if (strcmp(sbag->sb_type, POOL_TYPE_NAME) == 0) { return; } else { die(gettext(ERR_UNSUPP_STYPE), sbag->sb_type); } } static void * safe_kstat_data_lookup(kstat_t *ksp, char *name) { void *dp; if ((dp = kstat_data_lookup(ksp, name)) == NULL) die(gettext(ERR_KSTAT_DLOOKUP), ksp->ks_name, name, get_errstr()); return (dp); } /* * Find the delta over the interval between new_ksp and old_ksp. * If old_ksp->ks_data is NULL and 'oz' is set then pretend * that old_ksp is zero otherwise return 0. */ static uint64_t delta(kstat_t *new_ksp, kstat_t *old_ksp, char *name, int oz) { kstat_named_t *new_ksn; kstat_named_t *old_ksn; new_ksn = (kstat_named_t *)safe_kstat_data_lookup(new_ksp, name); if (old_ksp == NULL || old_ksp->ks_data == NULL) return ((oz == 1) ? new_ksn->value.ui64 : 0); old_ksn = (kstat_named_t *)safe_kstat_data_lookup(old_ksp, name); return (new_ksn->value.ui64 - old_ksn->value.ui64); } /* * Create a clone of the passed kstat_t structure 'kstat_t'. If * 'fr' flag is set free the old ks_data structure in 'dst'. */ static void kstat_clone(kstat_t *src, kstat_t *dst, int fr) { if (fr) FREE(dst->ks_data); *dst = *src; if (src->ks_data != NULL) { dst->ks_data = ZALLOC(src->ks_data_size); (void) memcpy(dst->ks_data, src->ks_data, src->ks_data_size); } else { dst->ks_data = NULL; dst->ks_data_size = 0; } } /* * Erase the data from 'src'. */ static void kstat_erase(kstat_t *src) { FREE(src->ks_data); (void) memset(src, 0, sizeof (*src)); } /* * Create a new statistic data object with its own copy of the passed * kstat. */ static sdata_t * sdata_new(kstat_t *ksp) { sdata_t *sdp; NEW0(sdp); kstat_clone(ksp, &sdp->sd_nks, 0); return (sdp); } static void sdata_free(sdata_t *sdp) { FREE(sdp->sd_oks.ks_data); FREE(sdp->sd_nks.ks_data); FREE(sdp->sd_udata); FREE(sdp); } /* * Create new or update an existing list of cpu statistics. For each * cpu two kstats are kept. One old kstat which contains the data from * the previous scan, and new with the current data. The old and the new * kstats *must* be for the same instance and have the same kid. * If 'instance' argument is set to -1 don't use it as a filter. */ static sdata_t * update_sdata_list(sdata_t *list, kstat_ctl_t *kc, char *module, int instance, char *name, int *errp) { kstat_t *ksp; sdata_t *sdp, *sdpp; /* kstat instance pointer/previous-pointer */ for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { if (strcmp(ksp->ks_module, module) == 0 && (name == NULL || strcmp(ksp->ks_name, name) == 0) && (instance == -1 || ksp->ks_instance == instance)) { if (kstat_read(kc, ksp, NULL) == -1) { *errp = -1; return (list); } /* * Find the kstat in the existing list: * If we find one for the same instance and with the * same ks_kid we'll save it as old_kstat. * If we find one for the same instance but with a * different ks_kid we'll removed it. */ for (sdpp = sdp = list; sdp; sdp = sdp->sd_next) { if (ksp->ks_instance == sdp->sd_nks.ks_instance) { if (ksp->ks_kid == sdp->sd_nks.ks_kid) { kstat_clone(&sdp->sd_nks, &sdp->sd_oks, 1); } else { kstat_erase(&sdp->sd_oks); } kstat_clone(ksp, &sdp->sd_nks, 1); sdp->sd_state |= SD_UPDATED; break; } sdpp = sdp; } /* add a new kstat instance */ if (!sdp) { /* first instance */ if (!list) { list = sdata_new(ksp); list->sd_state |= SD_UPDATED; } else { sdpp->sd_next = sdata_new(ksp); sdpp->sd_next->sd_state |= SD_UPDATED; } } } } /* remove untouched statistics */ sdp = list; sdpp = NULL; while (sdp != NULL) { if (sdp->sd_state & SD_UPDATED) { sdp->sd_state &= ~SD_UPDATED; sdpp = sdp; sdp = sdp->sd_next; } else { sdata_t *tmp; if (sdpp == NULL) list = sdp->sd_next; else sdpp->sd_next = sdp->sd_next; tmp = sdp->sd_next; sdata_free(sdp); sdp = tmp; } } *errp = 0; return (list); } /* * Update the pset assignment information for each cpu in the statistic * data list. */ static void update_cpu_list(sdata_t *list) { sdata_t *sdp; for (sdp = list; sdp; sdp = sdp->sd_next) { /* for new CPU create a new user data object */ if (sdp->sd_udata == NULL) { sdp->sd_udata = ZALLOC(sizeof (pset_ud_t)); /* * set its pset to invalid, so it will not be * used in statistics calculation. */ SD_NPSET(sdp) = INVALID_SET; } /* copy the pset assignment information to the previous stat */ SD_OPSET(sdp) = SD_NPSET(sdp); /* set the current assignment */ if (pset_assign(PS_QUERY, sdp->sd_nks.ks_instance, &(SD_NPSET(sdp))) == -1) SD_NPSET(sdp) = INVALID_SET; } } /* * Update statistic data for pset. Calculate the CPU usage in a pset. */ static void update_pset_stats(statistic_bag_t *sbag, sdata_t *list) { sdata_t *sdp; pset_statistic_bag_t *bag = (pset_statistic_bag_t *)sbag->bag; uint64_t allticks, ust, kst, ist, wst; ust = kst = ist = wst = 0; for (sdp = list; sdp; sdp = sdp->sd_next) { /* * only calculate for the asked pset id and if the cpu belongs * to the same set in the previous and in the current snapshot. * It means that the usage for CPUs that were rebound during * the sampling interval are not charged to any set. */ if ((SD_OPSET(sdp) == SD_NPSET(sdp)) && (SD_NPSET(sdp) == bag->pset_sb_sysid)) { ust += delta(&sdp->sd_nks, &sdp->sd_oks, "cpu_ticks_user", 0); kst += delta(&sdp->sd_nks, &sdp->sd_oks, "cpu_ticks_kernel", 0); ist += delta(&sdp->sd_nks, &sdp->sd_oks, "cpu_ticks_idle", 0); wst += delta(&sdp->sd_nks, &sdp->sd_oks, "cpu_ticks_wait", 0); } } if ((allticks = ust + kst + wst + ist) != 0) { bag->pset_sb_used = (double)(ust + kst) / allticks * bag->pset_sb_size; } else { bag->pset_sb_used = 0.0; } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * sa_libpool - libpool statistic adapter, collect statistic data provided * by libpool. */ #include #include #include #include #include "utils.h" #include "poolstat.h" typedef int (*prop_walk_cb_t) (pool_conf_t *, pool_elem_t *, const char *, pool_value_t *, void *); /* user data used in the property walk callback function. */ typedef struct { int ud_result; void* ud_bag; } userdata_cb_t; static pool_conf_t *conf; static const char *conf_loc; static void update_pset(statistic_bag_t *); /* * If not NULL use the passed 'configuration' to access the pool framework, * otherwise create and open a private access point. */ void sa_libpool_init(void *configuration) { if (configuration) { conf = configuration; } else { conf_loc = pool_dynamic_location(); if ((conf = pool_conf_alloc()) == NULL) die(gettext(ERR_NOMEM)); if (pool_conf_open(conf, conf_loc, PO_RDONLY | PO_UPDATE) != PO_SUCCESS) die(gettext(ERR_OPEN_STATIC), conf_loc, get_errstr()); } } /*ARGSUSED*/ void sa_libpool_update(statistic_bag_t *sbag, int flags) { static int changed; /* The SA_REFRESH flag forces the update of local data structures. */ if (flags & SA_REFRESH) { changed = 0; if (pool_conf_update(conf, &changed) != PO_SUCCESS) die(gettext(ERR_CONF_UPDATE), get_errstr()); sbag->sb_changed = changed; } if (strcmp(sbag->sb_type, PSET_TYPE_NAME) == 0) { if (changed & POU_PSET || changed & POU_CPU) ((pset_statistic_bag_t *)sbag->bag)->pset_sb_changed = changed; else ((pset_statistic_bag_t *)sbag->bag)->pset_sb_changed = 0; update_pset(sbag); } else if (strcmp(sbag->sb_type, POOL_TYPE_NAME) == 0) { return; } else { die(gettext(ERR_UNSUPP_STYPE), sbag->sb_type); } } /* * callback function to property walker, copies the property value from * the passed 'pvalue' to the corresponding field in the statistic data bag. */ /*ARGSUSED*/ static int populate_userdata_cb(pool_conf_t *unused1, pool_elem_t *unused2, const char *name, pool_value_t *pval, userdata_cb_t *ud) { pset_statistic_bag_t *bag = (pset_statistic_bag_t *)ud->ud_bag; ud->ud_result = 0; if (strcmp("pset.min", name) == 0) { ud->ud_result = pool_value_get_uint64(pval, &bag->pset_sb_min); } else if (strcmp("pset.max", name) == 0) { ud->ud_result = pool_value_get_uint64(pval, &bag->pset_sb_max); } else if (strcmp("pset.load", name) == 0) { uint64_t load; ud->ud_result = pool_value_get_uint64(pval, &load); bag->pset_sb_load = (double)load / 1000.0; } else if (strcmp("pset.size", name) == 0) { ud->ud_result = pool_value_get_uint64(pval, &bag->pset_sb_size); } else if (strcmp("pset.sys_id", name) == 0) { ud->ud_result = pool_value_get_int64(pval, &bag->pset_sb_sysid); } return (0); } /* * Update statistic data for the procssor set with the name 'sbag->name'. * Use 'sbag->bag' to store the data. */ static void update_pset(statistic_bag_t *sbag) { pool_resource_t *pset_reso; pool_elem_t *pset_elem; userdata_cb_t ud; ud.ud_bag = (void *) sbag->bag; if ((pset_reso = pool_get_resource(conf, PSET_TYPE_NAME, sbag->sb_name)) == NULL) die(gettext(ERR_STATS_RES_N), sbag->sb_name, get_errstr()); if ((pset_elem = pool_resource_to_elem(conf, pset_reso)) == NULL) die(gettext(ERR_STATS_RES_N), sbag->sb_name, get_errstr()); /* use the property walker to collect the resource properties */ if (pool_walk_properties(conf, pset_elem, &ud, (prop_walk_cb_t)populate_userdata_cb) == -1) die(gettext(ERR_STATS_RES_N), sbag->sb_name, get_errstr()); }