/*
* 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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>
#include <locale.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include <stddef.h>
#include <pool.h>
#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)
{
(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[])
{
char 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(
"<<State change>>\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());
}