/*
* 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
* 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.
*/
#include <stdio.h>
#include <stdarg.h>
#include <dtrace.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <strings.h>
#include <termio.h>
#include <signal.h>
#include <locale.h>
#include "statcommon.h"
#define INTRSTAT_CPUS_PER_LINE(w) \
(((w) - INTRSTAT_COLUMN_OFFS) / INTRSTAT_COLUMNS_PER_CPU)
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#endif
static int *g_present;
static int g_max_cpus;
static int g_header;
static int g_intr;
static const char *g_prog =
"interrupt-start"
"/arg0 != NULL/"
"{"
" self->ts = vtimestamp;"
"}"
""
"interrupt-complete"
"/self->ts/"
"{"
" this->devi = (struct dev_info *)arg0;"
" @counts[stringof(`devnamesp[this->devi->devi_major].dn_name),"
" this->devi->devi_instance] = count();"
" @times[stringof(`devnamesp[this->devi->devi_major].dn_name),"
" this->devi->devi_instance] = sum(vtimestamp - self->ts);"
" self->ts = 0;"
"}";
static void
usage(void)
{
"usage: intrstat [ -C psrset | -c cpulist ] [-x opt[=val]] "
"[-T d|u] [interval [ count]]\n");
}
static void
{
}
/*ARGSUSED*/
static void
{
g_intr++;
}
static void
status(void)
{}
static void
set_width(void)
{
return;
return;
/*
* If TIOCGWINSZ returned 0 for the columns, just return --
* thereby using the default value of g_cpus_per_line. (This
* happens, e.g., when running over a tip line.)
*/
return;
}
if (g_cpus_per_line < 1)
g_cpus_per_line = 1;
}
static void
{
int i, j;
char c[256];
if (!g_header)
return;
for (i = g_start, j = 0; i < g_max_cpus; i++) {
if (!g_present[i])
continue;
(void) sprintf(c, "cpu%d", i);
(void) printf(" %9s %%tim", c);
if (++j >= g_cpus_per_line)
break;
}
(void) printf("\n-------------+");
while (j--)
(void) printf("---------------");
(void) printf("\n");
g_header = 0;
}
/*ARGSUSED*/
static int
{
int i, j;
return (DTRACE_AGGWALK_NEXT);
}
/* LINTED - alignment */
/* LINTED - alignment */
/* LINTED - alignment */
if (!g_present[i])
continue;
if (j++ == 0) {
print_header();
(void) snprintf(c, sizeof (c), "%s#%d",
(void) printf("%12s |", c);
}
(void) printf(" %9lld %4.1f",
(unsigned long long)((double)n /
((double)g_interval / (double)NANOSEC)),
}
g_end = i;
return (DTRACE_AGGWALK_NEXT);
}
static void
{
fatal("cannot specify both a processor set and a processor\n");
}
}
static void
{
fatal("cannot specify both a processor set and processors\n");
do {
}
static void
{
if (pset < 0)
/*
* Only one processor set can be specified.
*/
fatal("at most one processor set may be specified\n");
/*
* One cannot select processors _and_ a processor set.
*/
for (i = 0; i < g_max_cpus; i++)
if (g_present[i])
break;
if (i != g_max_cpus)
fatal("cannot specify both a processor and a processor set\n");
if (g_pset_ncpus == 0)
for (i = 0; i < g_pset_ncpus; i++)
}
static void
check_pset(void)
{
return;
}
if (ncpus == 0)
if (ncpus == g_pset_ncpus) {
for (i = 0; i < g_pset_ncpus; i++) {
if (!g_present[g_pset_cpus[i]])
break;
}
/*
* If the number of CPUs hasn't changed, and every CPU
* in the processor set is also selected, we know that the
* processor set itself hasn't changed.
*/
if (i == g_pset_ncpus)
return;
}
/*
* If we're here, we have a new processor set. First, we need
* to zero out the present array.
*/
for (i = 0; i < g_pset_ncpus; i++)
}
int
{
long iter;
char *end, *p;
char c;
(void) textdomain(TEXT_DOMAIN);
set_width();
(void) sigemptyset(&set);
fatal("cannot create CLOCK_HIGHRES timer");
fatal("could not allocate g_present array\n");
if (g_pset_cpus == NULL)
fatal("could not allocate g_pset_cpus");
switch (c) {
case 'c': {
/*
* We allow CPUs to be specified as an optionally
* comma separated list of either CPU IDs or ranges
* of CPU IDs.
*/
while (s != NULL) {
*end = '\0';
fatal("invalid cpu '%s'\n", s);
}
if (*(s = end) != '\0') {
if (*s != '-')
fatal("invalid cpu '%s'\n", s);
if (*end != '\0' ||
fatal("invalid cpu '%s'\n", s);
select_cpus(id, p);
} else {
select_cpu(id);
}
}
break;
}
case 'C': {
if (*end != '\0' ||
break;
}
case 'T':
if (optarg) {
if (*optarg == 'u')
else if (*optarg == 'd')
else
usage();
} else {
usage();
}
break;
default:
usage();
}
}
fatal("cannot open dtrace library: %s\n",
}
fatal("invalid program");
fatal("failed to enable probes");
fatal("failed to set 'aggsize'");
fatal("failed to set 'aggrate'");
fatal("failed to set 'aggpercpu'");
optind = 1;
switch (c) {
case 'x':
*p++ = '\0';
break;
}
}
if (g_sleeptime <= 0)
fatal("interval must be greater than zero.\n");
indefinite = 0;
fatal("invalid count '%s'\n", s);
}
}
fatal("cannot set time on CLOCK_REALTIME timer");
for (i = 0; i < g_max_cpus && !g_present[i]; i++)
continue;
if (i == g_max_cpus) {
for (i = 0; i < g_max_cpus; i++)
}
fatal("dtrace_go()");
fatal("failed to get 'statusrate'");
fatal("cannot create status timer");
fatal("cannot set time on status timer");
}
(void) sigemptyset(&set);
while (indefinite || iter) {
(void) sigsuspend(&set);
fatal("dtrace_status()");
if (g_intr == 0)
continue;
iter--;
g_intr--;
check_pset();
if (dtrace_aggregate_snap(g_dtp) != 0)
fatal("failed to add to aggregate");
if (timestamp_fmt != NODATE)
do {
g_header = 1;
fatal("failed to sort aggregate");
break;
}
return (0);
}