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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <fm/fmd_adm.h>
38 #include "statcommon.h"
40 #define FMSTAT_EXIT_SUCCESS 0
41 #define FMSTAT_EXIT_ERROR 1
42 #define FMSTAT_EXIT_USAGE 2
44 static const struct stats
{
55 fmd_stat_t dispatched
;
57 fmd_stat_t prdequeued
;
62 fmd_stat_t casesolved
;
66 fmd_stat_t wlastupdate
;
68 fmd_stat_t dlastupdate
;
70 { "module", FMD_TYPE_STRING
},
71 { "authority", FMD_TYPE_STRING
},
72 { "state", FMD_TYPE_STRING
},
73 { "loadtime", FMD_TYPE_TIME
},
74 { "snaptime", FMD_TYPE_TIME
},
75 { "received", FMD_TYPE_UINT64
},
76 { "discarded", FMD_TYPE_UINT64
},
77 { "retried", FMD_TYPE_UINT64
},
78 { "replayed", FMD_TYPE_UINT64
},
79 { "lost", FMD_TYPE_UINT64
},
80 { "dispatched", FMD_TYPE_UINT64
},
81 { "dequeued", FMD_TYPE_UINT64
},
82 { "prdequeued", FMD_TYPE_UINT64
},
83 { "accepted", FMD_TYPE_UINT64
},
84 { "memtotal", FMD_TYPE_SIZE
},
85 { "buftotal", FMD_TYPE_SIZE
},
86 { "caseopen", FMD_TYPE_UINT64
},
87 { "casesolved", FMD_TYPE_UINT64
},
88 { "wcnt", FMD_TYPE_UINT32
},
89 { "wtime", FMD_TYPE_TIME
},
90 { "wlentime", FMD_TYPE_TIME
},
91 { "wlastupdate", FMD_TYPE_TIME
},
92 { "dtime", FMD_TYPE_TIME
},
93 { "dlastupdate", FMD_TYPE_TIME
},
96 static const char *g_pname
;
97 static fmd_adm_t
*g_adm
;
99 static struct modstats
{
101 struct modstats
*m_next
;
102 struct stats m_stbuf
[2];
113 static uint_t timestamp_fmt
= NODATE
;
115 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
116 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it isn't */
120 vwarn(const char *format
, va_list ap
)
124 (void) fprintf(stderr
, "%s: ", g_pname
);
127 (void) vfprintf(stderr
, format
, ap
);
129 errno
= err
; /* restore errno for fmd_adm_errmsg() */
132 (void) fprintf(stderr
, "%s\n", fmd_adm_errmsg(g_adm
));
133 else if (strchr(format
, '\n') == NULL
)
134 (void) fprintf(stderr
, ": %s\n", fmd_adm_errmsg(g_adm
));
139 warn(const char *format
, ...)
143 va_start(ap
, format
);
150 die(const char *format
, ...)
154 va_start(ap
, format
);
158 fmd_adm_close(g_adm
);
159 exit(FMSTAT_EXIT_ERROR
);
163 time2str(char *buf
, size_t len
, uint64_t time
)
165 static const struct unit
{
169 { "d", NANOSEC
* (hrtime_t
)(24 * 60 * 60) },
170 { "h", NANOSEC
* (hrtime_t
)(60 * 60) },
171 { "m", NANOSEC
* (hrtime_t
)60 },
172 { "s", NANOSEC
/ SEC
},
173 { "ms", NANOSEC
/ MILLISEC
},
174 { "us", NANOSEC
/ MICROSEC
},
175 { "ns", NANOSEC
/ NANOSEC
},
178 const struct unit
*up
;
180 for (up
= units
; time
% up
->u_mul
!= 0; up
++)
181 continue; /* find largest unit of which 'time' is a multiple */
183 (void) snprintf(buf
, len
, "%llu%s", time
/ up
->u_mul
, up
->u_name
);
188 size2str(char *buf
, size_t len
, uint64_t size
)
190 static const char units
[] = "bKMGTPE";
191 const uint64_t scale
= 1024;
192 const char *up
= units
;
196 * Convert the input size to a round number of the appropriately
197 * scaled units (saved in 'size') and a remainder (saved in 'osize').
199 while (size
>= scale
&& up
< (units
+ sizeof (units
) - 2)) {
202 size
= (size
+ (scale
/ 2)) / scale
;
206 * Format the result using at most one decimal place and the unit
207 * depending upon the amount of remainder (same as df -h algorithm).
209 if (osize
!= 0 && (osize
/ scale
) < 10)
210 (void) snprintf(buf
, len
, "%.1f%c", (float)osize
/ scale
, *up
);
212 (void) snprintf(buf
, len
, "%llu%c", size
, *up
);
214 (void) snprintf(buf
, len
, "0");
220 u64delta(uint64_t old
, uint64_t new)
222 return (new >= old
? (new - old
) : ((UINT64_MAX
- old
) + new + 1));
225 static struct modstats
*
226 modstat_create(const char *name
, id_t id
)
228 struct modstats
*mp
= malloc(sizeof (struct modstats
));
233 bzero(mp
, sizeof (struct modstats
));
235 if (name
!= NULL
&& (mp
->m_name
= strdup(name
)) == NULL
) {
247 * Given a statistics buffer containing event queue statistics, compute the
248 * common queue statistics for the given module and store the results in 'mp'.
249 * We set m_new and m_old for the caller, and store the compute values of
250 * m_svc, m_wait, m_pct_w, and m_pct_b there as well. The caller must not free
251 * 'ams' until after using the results as m_new may contain pointers to it.
254 modstat_compute(struct modstats
*mp
, fmd_adm_stats_t
*ams
)
256 static fmd_stat_t
*t_beg
= (fmd_stat_t
*)(&stats_template
+ 0);
257 static fmd_stat_t
*t_end
= (fmd_stat_t
*)(&stats_template
+ 1);
259 struct stats
*old
, *new;
260 fmd_stat_t
*tsp
, *nsp
, *sp
;
261 double elapsed
, avg_w
, avg_d
;
264 old
= mp
->m_old
= &mp
->m_stbuf
[mp
->m_stidx
];
265 mp
->m_stidx
= 1 - mp
->m_stidx
;
266 new = mp
->m_new
= &mp
->m_stbuf
[mp
->m_stidx
];
269 * The statistics can come in any order; we compare each one to the
270 * template of statistics of interest, find the matching ones, and copy
271 * their values into the appropriate slot of the 'new' stats.
273 for (nsp
= ams
->ams_buf
; nsp
< ams
->ams_buf
+ ams
->ams_len
; nsp
++) {
274 for (tsp
= t_beg
; tsp
< t_end
; tsp
++) {
275 const char *p
= strrchr(nsp
->fmds_name
, '.');
278 * The fmd queue stats can either be named fmd.<name>
279 * or fmd.xprt.%u.<name> depending on whether we're
280 * looking at the module queue or the transport queue.
281 * So we match using the patterns fmd.* and *.<name>
282 * and store only the value of <name> in stats_template.
284 if (p
== NULL
|| strcmp(p
+ 1, tsp
->fmds_name
) != 0 ||
285 strncmp(nsp
->fmds_name
, "fmd.", 4) != 0)
286 continue; /* continue until we match the stat */
288 if (tsp
->fmds_type
!= nsp
->fmds_type
) {
289 warn("%s has unexpected type (%u != %u)\n",
290 nsp
->fmds_name
, tsp
->fmds_type
,
293 sp
= (fmd_stat_t
*)new + (tsp
- t_beg
);
294 sp
->fmds_value
= nsp
->fmds_value
;
300 * Compute the elapsed time by taking the delta between 'snaptime', or
301 * or between snaptime and loadtime if there is no previous snapshot.
302 * If delta is zero, set it to 1sec so we don't divide by zero later.
304 delta
= u64delta(old
->snaptime
.fmds_value
.ui64
?
305 old
->snaptime
.fmds_value
.ui64
: old
->loadtime
.fmds_value
.ui64
,
306 new->snaptime
.fmds_value
.ui64
);
308 elapsed
= delta
? (double)delta
: (double)NANOSEC
;
311 * Compute average wait queue len by taking the delta in the wait queue
312 * len * time products (wlentime stat) and dividing by the elapsed time.
314 delta
= u64delta(old
->wlentime
.fmds_value
.ui64
,
315 new->wlentime
.fmds_value
.ui64
);
318 mp
->m_wait
= (double)delta
/ elapsed
;
323 * Compute average wait time by taking the delta in the wait queue time
324 * (wtime) and dividing by the delta in the number of dispatches.
326 delta
= u64delta(old
->dispatched
.fmds_value
.ui64
,
327 new->dispatched
.fmds_value
.ui64
);
330 avg_w
= (double)u64delta(old
->wtime
.fmds_value
.ui64
,
331 new->wtime
.fmds_value
.ui64
) / (double)delta
;
336 * Compute average dispatch time by taking the delta in the dispatch
337 * time (dtime) and dividing by the delta in the number of dequeues.
339 delta
= u64delta(old
->dequeued
.fmds_value
.ui64
,
340 new->dequeued
.fmds_value
.ui64
);
343 avg_d
= (double)u64delta(old
->dtime
.fmds_value
.ui64
,
344 new->dtime
.fmds_value
.ui64
) / (double)delta
;
349 * Finally compute the average overall service time by adding together
350 * the average wait and dispatch times and converting to milliseconds.
352 mp
->m_svc
= ((avg_w
+ avg_d
) * (double)MILLISEC
) / (double)NANOSEC
;
355 * Compute the %wait and %busy times by taking the delta in wait and
356 * busy times, dividing by the elapsed time, and multiplying by 100.
358 delta
= u64delta(old
->wtime
.fmds_value
.ui64
,
359 new->wtime
.fmds_value
.ui64
);
362 mp
->m_pct_w
= ((double)delta
/ elapsed
) * 100.0;
366 delta
= u64delta(old
->dtime
.fmds_value
.ui64
,
367 new->dtime
.fmds_value
.ui64
);
370 mp
->m_pct_b
= ((double)delta
/ elapsed
) * 100.0;
377 stat_one_xprt(id_t id
, void *ignored
)
382 if (fmd_adm_xprt_stats(g_adm
, id
, &ams
) != 0) {
383 warn("failed to retrieve statistics for transport %d", (int)id
);
384 return (0); /* continue on to the next transport */
387 for (mp
= g_mods
; mp
!= NULL
; mp
= mp
->m_next
) {
392 if (mp
== NULL
&& (mp
= modstat_create(NULL
, id
)) == NULL
) {
393 warn("failed to allocate memory for transport %d", (int)id
);
394 (void) fmd_adm_stats_free(g_adm
, &ams
);
398 modstat_compute(mp
, &ams
);
400 (void) printf("%3d %5s %7llu %7llu %7llu %7llu "
401 "%4.1f %6.1f %3.0f %3.0f %s\n", (int)id
,
402 mp
->m_new
->state
.fmds_value
.str
,
403 u64delta(mp
->m_old
->prdequeued
.fmds_value
.ui64
,
404 mp
->m_new
->prdequeued
.fmds_value
.ui64
),
405 u64delta(mp
->m_old
->received
.fmds_value
.ui64
,
406 mp
->m_new
->received
.fmds_value
.ui64
),
407 u64delta(mp
->m_old
->discarded
.fmds_value
.ui64
,
408 mp
->m_new
->discarded
.fmds_value
.ui64
),
409 u64delta(mp
->m_old
->lost
.fmds_value
.ui64
,
410 mp
->m_new
->lost
.fmds_value
.ui64
),
411 mp
->m_wait
, mp
->m_svc
, mp
->m_pct_w
, mp
->m_pct_b
,
412 mp
->m_new
->module
.fmds_value
.str
);
414 (void) fmd_adm_stats_free(g_adm
, &ams
);
421 (void) printf("%3s %5s %7s %7s %7s %7s %4s %6s %3s %3s %s\n",
422 "id", "state", "ev_send", "ev_recv", "ev_drop", "ev_lost",
423 "wait", "svc_t", "%w", "%b", "module");
425 if (fmd_adm_xprt_iter(g_adm
, stat_one_xprt
, NULL
) != 0)
426 die("failed to retrieve list of transports");
430 stat_one_xprt_auth(id_t id
, void *arg
)
432 const char *module
= arg
;
436 if (fmd_adm_xprt_stats(g_adm
, id
, &ams
) != 0) {
437 warn("failed to retrieve statistics for transport %d", (int)id
);
438 return (0); /* continue on to the next transport */
441 for (mp
= g_mods
; mp
!= NULL
; mp
= mp
->m_next
) {
446 if (mp
== NULL
&& (mp
= modstat_create(NULL
, id
)) == NULL
) {
447 warn("failed to allocate memory for transport %d", (int)id
);
448 (void) fmd_adm_stats_free(g_adm
, &ams
);
452 modstat_compute(mp
, &ams
);
454 if (module
== NULL
||
455 strcmp(module
, mp
->m_new
->module
.fmds_value
.str
) == 0) {
456 (void) printf("%3d %5s %-18s %s\n", (int)id
,
457 mp
->m_new
->state
.fmds_value
.str
,
458 mp
->m_new
->module
.fmds_value
.str
,
459 mp
->m_new
->authority
.fmds_value
.str
?
460 mp
->m_new
->authority
.fmds_value
.str
: "-");
463 (void) fmd_adm_stats_free(g_adm
, &ams
);
468 stat_xprt_auth(const char *module
)
470 (void) printf("%3s %5s %-18s %s\n",
471 "id", "state", "module", "authority");
473 if (fmd_adm_xprt_iter(g_adm
, stat_one_xprt_auth
, (void *)module
) != 0)
474 die("failed to retrieve list of transports");
479 stat_one_fmd(const fmd_adm_modinfo_t
*ami
, void *ignored
)
481 char memsz
[8], bufsz
[8];
485 if (fmd_adm_module_stats(g_adm
, ami
->ami_name
, &ams
) != 0) {
486 warn("failed to retrieve statistics for %s", ami
->ami_name
);
487 return (0); /* continue on to the next module */
490 for (mp
= g_mods
; mp
!= NULL
; mp
= mp
->m_next
) {
491 if (strcmp(mp
->m_name
, ami
->ami_name
) == 0)
495 if (mp
== NULL
&& (mp
= modstat_create(ami
->ami_name
, 0)) == NULL
) {
496 warn("failed to allocate memory for %s", ami
->ami_name
);
497 (void) fmd_adm_stats_free(g_adm
, &ams
);
501 modstat_compute(mp
, &ams
);
503 (void) printf("%-18s %7llu %7llu %4.1f %6.1f %3.0f %3.0f "
504 "%5llu %5llu %6s %6s\n", ami
->ami_name
,
505 u64delta(mp
->m_old
->prdequeued
.fmds_value
.ui64
,
506 mp
->m_new
->prdequeued
.fmds_value
.ui64
),
507 u64delta(mp
->m_old
->accepted
.fmds_value
.ui64
,
508 mp
->m_new
->accepted
.fmds_value
.ui64
),
509 mp
->m_wait
, mp
->m_svc
, mp
->m_pct_w
, mp
->m_pct_b
,
510 mp
->m_new
->caseopen
.fmds_value
.ui64
,
511 mp
->m_new
->casesolved
.fmds_value
.ui64
,
512 size2str(memsz
, sizeof (memsz
),
513 mp
->m_new
->memtotal
.fmds_value
.ui64
),
514 size2str(bufsz
, sizeof (bufsz
),
515 mp
->m_new
->buftotal
.fmds_value
.ui64
));
517 (void) fmd_adm_stats_free(g_adm
, &ams
);
524 (void) printf("%-18s %7s %7s %4s %6s %3s %3s %5s %5s %6s %6s\n",
525 "module", "ev_recv", "ev_acpt", "wait", "svc_t", "%w", "%b",
526 "open", "solve", "memsz", "bufsz");
528 if (fmd_adm_module_iter(g_adm
, stat_one_fmd
, NULL
) != 0)
529 die("failed to retrieve list of modules");
533 stat_mod(const char *name
, int aflag
, int zflag
)
539 if (fmd_adm_stats_read(g_adm
, name
, &ams
) != 0) {
540 die("failed to retrieve statistics for %s",
541 name
? name
: "fmd(1M)");
544 (void) printf("%20s %-16s %s\n", "NAME", "VALUE", "DESCRIPTION");
546 for (sp
= ams
.ams_buf
; sp
< ams
.ams_buf
+ ams
.ams_len
; sp
++) {
547 if (aflag
== 0 && strncmp(sp
->fmds_name
, "fmd.", 4) == 0)
548 continue; /* skip fmd-internal stats unless -a used */
551 switch (sp
->fmds_type
) {
553 case FMD_TYPE_UINT32
:
554 if (sp
->fmds_value
.ui32
== 0)
558 case FMD_TYPE_UINT64
:
561 if (sp
->fmds_value
.ui64
== 0)
564 case FMD_TYPE_STRING
:
565 if (sp
->fmds_value
.str
== NULL
||
566 sp
->fmds_value
.str
[0] == '\0')
572 (void) printf("%20s ", sp
->fmds_name
);
574 switch (sp
->fmds_type
) {
576 (void) printf("%-16s",
577 sp
->fmds_value
.b
? "true" : "false");
580 (void) printf("%-16d", sp
->fmds_value
.i32
);
582 case FMD_TYPE_UINT32
:
583 (void) printf("%-16u", sp
->fmds_value
.ui32
);
586 (void) printf("%-16lld", sp
->fmds_value
.i64
);
588 case FMD_TYPE_UINT64
:
589 (void) printf("%-16llu", sp
->fmds_value
.ui64
);
591 case FMD_TYPE_STRING
:
592 (void) printf("%-16s", sp
->fmds_value
.str
?
593 sp
->fmds_value
.str
: "<<null>>");
596 (void) printf("%-16s",
597 time2str(buf
, sizeof (buf
), sp
->fmds_value
.ui64
));
600 (void) printf("%-16s",
601 size2str(buf
, sizeof (buf
), sp
->fmds_value
.ui64
));
604 (void) snprintf(buf
, sizeof (buf
),
605 "<<type=%u>>\n", sp
->fmds_type
);
606 (void) printf("%-16s", buf
);
609 (void) printf(" %s\n", sp
->fmds_desc
);
612 (void) fmd_adm_stats_free(g_adm
, &ams
);
617 stat_one_serd(const fmd_adm_serdinfo_t
*asi
, void *ignored
)
619 char buf1
[32], buf2
[32], n
[32];
621 (void) snprintf(n
, sizeof (n
), ">%llu", asi
->asi_n
);
623 (void) printf("%-36s %3s %5s %3u %24s %s\n",
624 asi
->asi_name
, n
, time2str(buf1
, sizeof (buf1
), asi
->asi_t
),
625 asi
->asi_count
, time2str(buf2
, sizeof (buf2
), asi
->asi_delta
),
626 (asi
->asi_flags
& FMD_ADM_SERD_FIRED
) ? "fire" : "pend");
632 stat_mod_serd(const char *name
)
634 (void) printf("%-36s %3s %5s %3s %24s %4s\n",
635 "NAME", ">N", "T", "CNT", "DELTA", "STAT");
637 if (fmd_adm_serd_iter(g_adm
, name
, stat_one_serd
, NULL
) != 0)
638 die("failed to retrieve serd engines for %s", name
);
642 getint(const char *name
, const char *s
)
648 val
= strtol(s
, &p
, 10);
650 if (errno
!= 0 || p
== s
|| *p
!= '\0' || val
< 0 || val
> INT_MAX
) {
651 (void) fprintf(stderr
, "%s: invalid %s argument -- %s\n",
653 exit(FMSTAT_EXIT_USAGE
);
660 getu32(const char *name
, const char *s
)
666 val
= strtoull(s
, &p
, 0);
668 if (errno
!= 0 || p
== s
|| *p
!= '\0' || val
> UINT32_MAX
) {
669 (void) fprintf(stderr
, "%s: invalid %s argument -- %s\n",
671 exit(FMSTAT_EXIT_USAGE
);
674 return ((uint32_t)val
);
680 (void) fprintf(fp
, "Usage: %s [-astTz] [-m module] "
681 "[-P prog] [-d d|u] [interval [count]]\n\n", g_pname
);
684 "\t-a show all statistics, including those kept by fmd\n"
685 "\t-d display a timestamp in date (d) or unix time_t (u)\n"
686 "\t-m show module-specific statistics\n"
687 "\t-P connect to alternate fmd program\n"
688 "\t-s show module-specific serd engines\n"
689 "\t-t show transport-specific statistics\n"
690 "\t-T show transport modules and authorities\n"
691 "\t-z suppress zero-valued statistics\n");
693 return (FMSTAT_EXIT_USAGE
);
697 main(int argc
, char *argv
[])
699 int opt_a
= 0, opt_s
= 0, opt_t
= 0, opt_T
= 0, opt_z
= 0;
700 const char *opt_m
= NULL
;
701 int msec
= 0, iter
= 1;
707 if ((p
= strrchr(argv
[0], '/')) == NULL
)
712 if ((p
= getenv("FMD_PROGRAM")) != NULL
)
713 program
= getu32("$FMD_PROGRAM", p
);
715 program
= FMD_ADM_PROGRAM
;
717 (void) setlocale(LC_ALL
, "");
718 (void) textdomain(TEXT_DOMAIN
);
720 while ((c
= getopt(argc
, argv
, "ad:m:P:stTz")) != EOF
) {
728 timestamp_fmt
= UDATE
;
729 else if (*optarg
== 'd')
730 timestamp_fmt
= DDATE
;
732 return (usage(stderr
));
734 return (usage(stderr
));
741 program
= getu32("program", optarg
);
756 return (usage(stderr
));
761 msec
= getint("interval", argv
[optind
++]) * MILLISEC
;
766 iter
= getint("count", argv
[optind
++]);
769 return (usage(stderr
));
771 if (opt_t
!= 0 && (opt_m
!= NULL
|| opt_s
!= 0)) {
772 (void) fprintf(stderr
,
773 "%s: -t cannot be used with -m or -s\n", g_pname
);
774 return (FMSTAT_EXIT_USAGE
);
777 if (opt_t
!= 0 && opt_T
!= 0) {
778 (void) fprintf(stderr
,
779 "%s: -t and -T are mutually exclusive options\n", g_pname
);
780 return (FMSTAT_EXIT_USAGE
);
783 if (opt_m
== NULL
&& opt_s
!= 0) {
784 (void) fprintf(stderr
,
785 "%s: -s requires -m <module>\n", g_pname
);
786 return (FMSTAT_EXIT_USAGE
);
789 if ((g_adm
= fmd_adm_open(NULL
, program
, FMD_ADM_VERSION
)) == NULL
)
790 die(NULL
); /* fmd_adm_errmsg() has enough info */
792 while (iter
< 0 || iter
-- > 0) {
793 if (timestamp_fmt
!= NODATE
)
794 print_timestamp(timestamp_fmt
);
796 stat_mod_serd(opt_m
);
798 stat_xprt_auth(opt_m
);
799 else if (opt_a
|| opt_m
)
800 stat_mod(opt_m
, opt_a
, opt_z
);
807 (void) poll(NULL
, 0, msec
);
808 (void) putchar('\n');
812 fmd_adm_close(g_adm
);
813 return (FMSTAT_EXIT_SUCCESS
);