changeset 13928:b13e6c26bd03

749 "/usr/bin/kstat" should be rewritten in C Reviewed by: Garrett D'Amore <garrett@damore.org> Reviewed by: Brendan Gregg <brendan.gregg@joyent.com> Approved by: Garrett D'Amore <garrett@damore.org>
author David H?ppner <0xffea@gmail.com>
date Wed, 09 Jan 2013 12:18:53 +0000
parents 4801c57f1ca7
children cb100869484a
files usr/src/cmd/Makefile usr/src/cmd/kstat/Makefile usr/src/cmd/kstat/kstat.pl usr/src/cmd/stat/Makefile usr/src/cmd/stat/kstat/Makefile usr/src/cmd/stat/kstat/kstat.c usr/src/cmd/stat/kstat/kstat.h usr/src/man/man1m/kstat.1m
diffstat 8 files changed, 1829 insertions(+), 306 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/Makefile	Fri Jan 25 10:08:50 2013 -0800
+++ b/usr/src/cmd/Makefile	Wed Jan 09 12:18:53 2013 +0000
@@ -23,6 +23,7 @@
 # Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
 # Copyright 2011 Joyent, Inc.  All rights reserved.
 # Copyright (c) 2012 by Delphix. All rights reserved.
+# Copyright (c) 2013 DEY Storage Systems, Inc. All rights reserved.
 
 include ../Makefile.master
 
@@ -221,7 +222,6 @@
 	killall		\
 	krb5		\
 	ksh		\
-	kstat		\
 	kvmstat		\
 	last		\
 	lastcomm	\
@@ -617,7 +617,6 @@
 	kbd		\
 	krb5		\
 	ksh		\
-	kstat		\
 	last		\
 	ldap		\
 	ldapcachemgr	\
--- a/usr/src/cmd/kstat/Makefile	Fri Jan 25 10:08:50 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-#
-# 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 2008 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-include ../Makefile.cmd
-
-PROG=	kstat
-
-.KEEP_STATE:
-
-all: $(PROG)
-
-install: all .WAIT $(ROOTPROG)
-
-_msg: $(PROG).po
-
-clean:
-
-$(ROOTBINPROG): $(PROG)
-	$(INS.file)
-
-lint:
-
-include ../Makefile.targ
--- a/usr/src/cmd/kstat/kstat.pl	Fri Jan 25 10:08:50 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,245 +0,0 @@
-#!/usr/perl5/bin/perl
-#
-# 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.
-#
-
-require 5.8.4;
-use strict;
-use warnings;
-use locale;
-use Getopt::Std;
-use POSIX qw(locale_h strftime);
-use I18N::Langinfo qw(langinfo D_T_FMT);
-use File::Basename;
-use Sun::Solaris::Utils qw(textdomain gettext gmatch);
-use Sun::Solaris::Kstat;
-
-#
-# Print an usage message and exit
-#
-
-sub usage(@)
-{
-	my (@msg) = @_;
-	print STDERR basename($0), ": @msg\n" if (@msg);
-	print STDERR gettext(
-	"Usage:\n" .
-	"kstat [ -qlp ] [ -T d|u ] [ -c class ]\n" .
-	"      [ -m module ] [ -i instance ] [ -n name ] [ -s statistic ]\n" .
-	"      [ interval [ count ] ]\n" .
-	"kstat [ -qlp ] [ -T d|u ] [ -c class ]\n" .
-	"      [ module:instance:name:statistic ... ]\n" .
-	"      [ interval [ count ] ]\n"
-	);
-	exit(2);
-}
-
-#
-# Print a fatal error message and exit
-#
-
-sub error(@)
-{
-	my (@msg) = @_;
-	print STDERR basename($0), ": @msg\n" if (@msg);
-	exit(1);
-}
-
-#
-# Generate an anonymous sub that can be used to filter the kstats we will
-# display.  The generated sub will take one parameter, the string to match
-# against.  There are three types of input catered for:
-#    1)  Empty string.  The returned sub will match anything
-#    2)  String surrounded by '/' characters.  This will be interpreted as a 
-#        perl RE.  If the RE is syntactically incorrect, an error will be
-#        reported.
-#    3) Any other string.  The returned sub will use gmatch(3GEN) to match
-#       against the passed string
-#
-
-sub gen_sub($)
-{
-	my ($pat) = @_;
-
-	# Anything undefined or empty will always match
-	if (! defined($pat) || $pat eq '') {
-		return (sub { 1; });
-
-	# Anything surrounded by '/' is a perl RE
-	} elsif ($pat =~ m!^/[^/]*/$!) {
-		my $sub;
-		if (! ($sub = eval "sub { return(\$_[0] =~ $pat); }" )) {
-			$@ =~ s/\s+at\s+.*\n$//;
-			usage($@);
-		}
-		return ($sub);
-
-	# Otherwise default to gmatch
-	} else {
-		return (sub { return(gmatch($_[0], $pat)); });
-	}
-}
-
-#
-# Main routine of the script
-#
-
-# Set message locale
-setlocale(LC_ALL, "");
-textdomain(TEXT_DOMAIN);
-
-# Process command options
-my (%opt, @matcher);
-getopts('?qlpT:m:i:n:s:c:', \%opt) || usage();
-usage() if exists($opt{'?'});
-
-# Validate -q and -l flags
-my $quiet = exists($opt{q}) ? 1 : 0;
-my $list = exists($opt{l}) ? 1 : 0;
-my $parseable = exists($opt{'p'}) || $list ? 1 : 0;
-usage(gettext("-q and -l are mutually exclusive")) if ($quiet && $list);
-
-# Get interval & count if specified
-my ($interval, $count) = (0, 1);
-if (@ARGV >= 2 && $ARGV[-2] =~ /^\d+$/ && $ARGV[-1] =~ /^\d+$/) {
-	$count = pop(@ARGV);
-	$interval = pop(@ARGV);
-	usage(gettext("Interval must be an integer >= 1")) if ($interval < 1);
-	usage(gettext("Count must be an integer >= 1")) if ($count < 1);
-} elsif (@ARGV >= 1 && $ARGV[-1] =~ /^\d+$/) {
-	$interval = pop(@ARGV);
-	$count = -1;
-	usage(gettext("Interval must be an integer >= 1")) if ($interval < 1);
-}
-
-# Get timestamp flag
-my $timestamp;
-my $timefmt;
-if ($timestamp = $opt{T}) {
-	if ($timestamp eq "d") {
-		$timefmt = langinfo(D_T_FMT) . "\n";
-		$timestamp = sub { print(strftime($timefmt, localtime())); };
-	} elsif ($timestamp eq "u") {
-		$timestamp = sub { print(time(), "\n"); };
-	} else {
-		usage(gettext("Invalid timestamp specifier"), $timestamp);
-	}
-}
-
-# Deal with -[mins] flags
-if (grep(/[mins]/, keys(%opt))) {
-	usage(gettext("module:instance:name:statistic and " .
-	    "-m -i -n -s are mutually exclusive")) if (@ARGV);
-	push(@ARGV, join(":", map(exists($opt{$_}) ? $opt{$_} : "",
-	    qw(m i n s))));
-}
-
-# Deal with class, if specified
-my $class = gen_sub(exists($opt{c}) ? $opt{c} : '');
-
-# If no selectors have been defined, add a dummy one to match everything
-push(@ARGV, ":::") if (! @ARGV);
-
-# Convert each remaining option into four anonymous subs
-foreach my $p (@ARGV) {
-	push(@matcher, [ map(gen_sub($_), (split(/:/, $p, 4))[0..3]) ]);
-}
-
-# Loop, printing the selected kstats as many times and as often as required
-my $ks = Sun::Solaris::Kstat->new(strip_strings => 1);
-my $matched = 0;
-
-# Format strings for displaying data
-my $fmt1 = "module: %-30.30s  instance: %-6d\n";
-my $fmt2 = "name:   %-30.30s  class:    %-.30s\n";
-my $fmt3 = "\t%-30s  %s\n";
-
-while ($count == -1 || $count-- > 0) {
-	&$timestamp() if ($timestamp);
-
-	foreach my $m (@matcher) {
-		my ($module, $instance, $name, $statistic) = @$m;
-
-		foreach my $m (sort(grep(&$module($_), keys(%$ks)))) {
-			my $mh = $ks->{$m};
-
-			foreach my $i (sort({ $a <=> $b }
-			    grep(&$instance($_), keys(%$mh)))) {
-				my $ih = $mh->{$i};
-
-				foreach my $n (sort(grep(&$name($_),
-				    keys(%$ih)))) {
-					my $nh = $ih->{$n};
-
-					# Prune any not in the required class
-					next if (! &$class($nh->{class}));
-
-					if ($quiet) {
-						$matched = grep(&$statistic($_),
-						    keys(%$nh)) ? 1 : 0;
-
-					} elsif ($parseable) {
-						foreach my $s
-						    (sort(grep(&$statistic($_),
-						    keys(%$nh)))) {
-							print("$m:$i:$n:$s");
-							print("\t$nh->{$s}")
-							    if (! $list);
-							print("\n");
-							$matched = 1;
-						}
-
-					# human-readable
-					} else {
-						if (my @stats =
-						    sort(grep(&$statistic($_),
-						    keys(%$nh)))) {
-							printf($fmt1, $m, $i);
-							printf($fmt2, $n,
-							$nh->{class});
-							foreach my $s
-							    (grep($_ ne "class",
-							    @stats)) {
-								printf($fmt3,
-								$s, $nh->{$s});
-							}
-							print("\n");
-							$matched = 1;
-						}
-					}
-				}
-			}
-		}
-	}
-	# Toggle line buffering off/on to flush output
-	$| = 1; $| = 0;
-
-	if ($interval && $count) {
-		sleep($interval);
-		$ks->update();
-		print("\n");
-	}
-}
-exit($matched ? 0 : 1);
--- a/usr/src/cmd/stat/Makefile	Fri Jan 25 10:08:50 2013 -0800
+++ b/usr/src/cmd/stat/Makefile	Wed Jan 09 12:18:53 2013 +0000
@@ -22,14 +22,12 @@
 # Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
 # cmd/stat/Makefile
 #
 
 include ../Makefile.cmd
 
-SUBDIRS=	iostat mpstat vmstat fsstat
+SUBDIRS=	iostat mpstat vmstat fsstat kstat
 
 all :=		TARGET = all
 install :=	TARGET = install
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/stat/kstat/Makefile	Wed Jan 09 12:18:53 2013 +0000
@@ -0,0 +1,61 @@
+#
+# 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.
+#
+
+PROG = kstat
+OBJS = kstat.o
+SRCS =$(OBJS:%.o=%.c) $(COMMON_SRCS)
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/stat/Makefile.stat
+
+LDLIBS += -lavl -lcmdutils -ldevinfo -lgen -lkstat
+CFLAGS += $(CCVERBOSE) -I${STATCOMMONDIR}
+CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-switch
+CERRWARN += -_gcc=-Wno-parentheses
+FILEMODE= 0555
+
+lint := LINTFLAGS = -muxs -I$(STATCOMMONDIR)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+$(PROG): $(OBJS) $(COMMON_OBJS)
+	$(LINK.c) -o $(PROG) $(OBJS) $(COMMON_OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+%.o : $(STATCOMMONDIR)/%.c
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
+
+clean:
+	-$(RM) $(OBJS) $(COMMON_OBJS)
+
+lint: lint_SRCS
+
+include $(SRC)/cmd/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/stat/kstat/kstat.c	Wed Jan 09 12:18:53 2013 +0000
@@ -0,0 +1,1470 @@
+/*
+ * 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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013 David Hoeppner. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ */
+
+/*
+ * Display kernel statistics
+ *
+ * This is a reimplementation of the perl kstat command originally found
+ * under usr/src/cmd/kstat/kstat.pl
+ *
+ * Incompatibilities:
+ *	- perl regular expressions replaced with extended REs bracketed by '/'
+ *	- options checking is stricter
+ *
+ * Flags added:
+ *	-C	similar to the -p option but value is separated by a colon
+ *	-h	display help
+ *	-j	json format
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <kstat.h>
+#include <langinfo.h>
+#include <libgen.h>
+#include <limits.h>
+#include <locale.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/list.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "kstat.h"
+#include "statcommon.h"
+
+char	*cmdname = "kstat";	/* Name of this command */
+int	caught_cont = 0;	/* Have caught a SIGCONT */
+
+static uint_t	g_timestamp_fmt = NODATE;
+
+/* Helper flag - header was printed already? */
+static boolean_t g_headerflg;
+
+/* Saved command line options */
+static boolean_t g_cflg = B_FALSE;
+static boolean_t g_jflg = B_FALSE;
+static boolean_t g_lflg = B_FALSE;
+static boolean_t g_pflg = B_FALSE;
+static boolean_t g_qflg = B_FALSE;
+static ks_pattern_t	g_ks_class = {"*", 0};
+
+/* Return zero if a selector did match */
+static int	g_matched = 1;
+
+/* Sorted list of kstat instances */
+static list_t	instances_list;
+static list_t	selector_list;
+
+int
+main(int argc, char **argv)
+{
+	ks_selector_t	*nselector;
+	ks_selector_t	*uselector;
+	kstat_ctl_t	*kc;
+	hrtime_t	start_n;
+	hrtime_t	period_n;
+	boolean_t	errflg = B_FALSE;
+	boolean_t	nselflg = B_FALSE;
+	boolean_t	uselflg = B_FALSE;
+	char		*q;
+	int		count = 1;
+	int		infinite_cycles = 0;
+	int		interval = 0;
+	int		n = 0;
+	int		c, m, tmp;
+
+	(void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"		/* Use this only if it wasn't */
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	/*
+	 * Create the selector list and a dummy default selector to match
+	 * everything. While we process the cmdline options we will add
+	 * selectors to this list.
+	 */
+	list_create(&selector_list, sizeof (ks_selector_t),
+	    offsetof(ks_selector_t, ks_next));
+
+	nselector = new_selector();
+
+	/*
+	 * Parse named command line arguments.
+	 */
+	while ((c = getopt(argc, argv, "h?CqjlpT:m:i:n:s:c:")) != EOF)
+		switch (c) {
+		case 'h':
+		case '?':
+			usage();
+			exit(0);
+			break;
+		case 'C':
+			g_pflg = g_cflg = B_TRUE;
+			break;
+		case 'q':
+			g_qflg = B_TRUE;
+			break;
+		case 'j':
+			g_jflg = B_TRUE;
+			break;
+		case 'l':
+			g_pflg = g_lflg = B_TRUE;
+			break;
+		case 'p':
+			g_pflg = B_TRUE;
+			break;
+		case 'T':
+			switch (*optarg) {
+			case 'd':
+				g_timestamp_fmt = DDATE;
+				break;
+			case 'u':
+				g_timestamp_fmt = UDATE;
+				break;
+			default:
+				errflg = B_TRUE;
+			}
+			break;
+		case 'm':
+			nselflg = B_TRUE;
+			nselector->ks_module.pstr =
+			    (char *)ks_safe_strdup(optarg);
+			break;
+		case 'i':
+			nselflg = B_TRUE;
+			nselector->ks_instance.pstr =
+			    (char *)ks_safe_strdup(optarg);
+			break;
+		case 'n':
+			nselflg = B_TRUE;
+			nselector->ks_name.pstr =
+			    (char *)ks_safe_strdup(optarg);
+			break;
+		case 's':
+			nselflg = B_TRUE;
+			nselector->ks_statistic.pstr =
+			    (char *)ks_safe_strdup(optarg);
+			break;
+		case 'c':
+			g_ks_class.pstr =
+			    (char *)ks_safe_strdup(optarg);
+			break;
+		default:
+			errflg = B_TRUE;
+			break;
+		}
+
+	if (g_qflg && (g_jflg || g_pflg)) {
+		(void) fprintf(stderr, gettext(
+		    "-q and -lpj are mutually exclusive\n"));
+		errflg = B_TRUE;
+	}
+
+	if (errflg) {
+		usage();
+		exit(2);
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	/*
+	 * Consume the rest of the command line. Parsing the
+	 * unnamed command line arguments.
+	 */
+	while (argc--) {
+		errno = 0;
+		tmp = strtoul(*argv, &q, 10);
+		if (tmp == ULONG_MAX && errno == ERANGE) {
+			if (n == 0) {
+				(void) fprintf(stderr, gettext(
+				    "Interval is too large\n"));
+			} else if (n == 1) {
+				(void) fprintf(stderr, gettext(
+				    "Count is too large\n"));
+			}
+			usage();
+			exit(2);
+		}
+
+		if (errno != 0 || *q != '\0') {
+			m = 0;
+			uselector = new_selector();
+			while ((q = (char *)strsep(argv, ":")) != NULL) {
+				m++;
+				if (m > 4) {
+					free(uselector);
+					usage();
+					exit(2);
+				}
+
+				if (*q != '\0') {
+					switch (m) {
+					case 1:
+						uselector->ks_module.pstr =
+						    (char *)ks_safe_strdup(q);
+						break;
+					case 2:
+						uselector->ks_instance.pstr =
+						    (char *)ks_safe_strdup(q);
+						break;
+					case 3:
+						uselector->ks_name.pstr =
+						    (char *)ks_safe_strdup(q);
+						break;
+					case 4:
+						uselector->ks_statistic.pstr =
+						    (char *)ks_safe_strdup(q);
+						break;
+					default:
+						assert(B_FALSE);
+					}
+				}
+			}
+
+			if (m < 4) {
+				free(uselector);
+				usage();
+				exit(2);
+			}
+
+			uselflg = B_TRUE;
+			list_insert_tail(&selector_list, uselector);
+		} else {
+			if (tmp < 1) {
+				if (n == 0) {
+					(void) fprintf(stderr, gettext(
+					    "Interval must be an "
+					    "integer >= 1"));
+				} else if (n == 1) {
+					(void) fprintf(stderr, gettext(
+					    "Count must be an integer >= 1"));
+				}
+				usage();
+				exit(2);
+			} else {
+				if (n == 0) {
+					interval = tmp;
+					count = -1;
+				} else if (n == 1) {
+					count = tmp;
+				} else {
+					usage();
+					exit(2);
+				}
+			}
+			n++;
+		}
+		argv++;
+	}
+
+	/*
+	 * Check if we founded a named selector on the cmdline.
+	 */
+	if (uselflg) {
+		if (nselflg) {
+			(void) fprintf(stderr, gettext(
+			    "module:instance:name:statistic and "
+			    "-m -i -n -s are mutually exclusive"));
+			usage();
+			exit(2);
+		} else {
+			free(nselector);
+		}
+	} else {
+		list_insert_tail(&selector_list, nselector);
+	}
+
+	assert(!list_is_empty(&selector_list));
+
+	list_create(&instances_list, sizeof (ks_instance_t),
+	    offsetof(ks_instance_t, ks_next));
+
+	while ((kc = kstat_open()) == NULL) {
+		if (errno == EAGAIN) {
+			(void) poll(NULL, 0, 200);
+		} else {
+			perror("kstat_open");
+			exit(3);
+		}
+	}
+
+	if (count > 1) {
+		if (signal(SIGCONT, cont_handler) == SIG_ERR) {
+			(void) fprintf(stderr, gettext(
+			    "signal failed"));
+			exit(3);
+		}
+	}
+
+	period_n = (hrtime_t)interval * NANOSEC;
+	start_n = gethrtime();
+
+	while (count == -1 || count-- > 0) {
+		ks_instances_read(kc);
+		ks_instances_print();
+
+		if (interval && count) {
+			ks_sleep_until(&start_n, period_n, infinite_cycles,
+			    &caught_cont);
+			(void) kstat_chain_update(kc);
+			(void) putchar('\n');
+		}
+	}
+
+	(void) kstat_close(kc);
+
+	return (g_matched);
+}
+
+/*
+ * Print usage.
+ */
+static void
+usage(void)
+{
+	(void) fprintf(stderr, gettext(
+	    "Usage:\n"
+	    "kstat [ -Cjlpq ] [ -T d|u ] [ -c class ]\n"
+	    "      [ -m module ] [ -i instance ] [ -n name ] [ -s statistic ]\n"
+	    "      [ interval [ count ] ]\n"
+	    "kstat [ -Cjlpq ] [ -T d|u ] [ -c class ]\n"
+	    "      [ module:instance:name:statistic ... ]\n"
+	    "      [ interval [ count ] ]\n"));
+}
+
+/*
+ * Sort compare function.
+ */
+static int
+compare_instances(ks_instance_t *l_arg, ks_instance_t *r_arg)
+{
+	int	rval;
+
+	rval = strcasecmp(l_arg->ks_module, r_arg->ks_module);
+	if (rval == 0) {
+		if (l_arg->ks_instance == r_arg->ks_instance) {
+			return (strcasecmp(l_arg->ks_name, r_arg->ks_name));
+		} else if (l_arg->ks_instance < r_arg->ks_instance) {
+			return (-1);
+		} else {
+			return (1);
+		}
+	} else {
+		return (rval);
+	}
+}
+
+static char *
+ks_safe_strdup(char *str)
+{
+	char	*ret;
+
+	if (str == NULL) {
+		return (NULL);
+	}
+
+	while ((ret = strdup(str)) == NULL) {
+		if (errno == EAGAIN) {
+			(void) poll(NULL, 0, 200);
+		} else {
+			perror("strdup");
+			exit(3);
+		}
+	}
+
+	return (ret);
+}
+
+static void
+ks_sleep_until(hrtime_t *wakeup, hrtime_t interval, int forever,
+    int *caught_cont)
+{
+	hrtime_t	now, pause, pause_left;
+	struct timespec	pause_tv;
+	int		status;
+
+	now = gethrtime();
+	pause = *wakeup + interval - now;
+
+	if (pause <= 0 || pause < (interval / 4)) {
+		if (forever || *caught_cont) {
+			*wakeup = now + interval;
+			pause = interval;
+		} else {
+			pause = interval / 2;
+			*wakeup += interval;
+		}
+	} else {
+		*wakeup += interval;
+	}
+
+	if (pause < 1000) {
+		return;
+	}
+
+	pause_left = pause;
+	do {
+		pause_tv.tv_sec = pause_left / NANOSEC;
+		pause_tv.tv_nsec = pause_left % NANOSEC;
+		status = nanosleep(&pause_tv, (struct timespec *)NULL);
+		if (status < 0) {
+			if (errno == EINTR) {
+				now = gethrtime();
+				pause_left = *wakeup - now;
+				if (pause_left < 1000) {
+					return;
+				}
+			} else {
+				perror("nanosleep");
+				exit(3);
+			}
+		}
+	} while (status != 0);
+}
+
+/*
+ * Inserts an instance in the per selector list.
+ */
+static void
+nvpair_insert(ks_instance_t *ksi, char *name, ks_value_t *value,
+    uchar_t data_type)
+{
+	ks_nvpair_t	*instance;
+	ks_nvpair_t	*tmp;
+
+	instance = (ks_nvpair_t *)malloc(sizeof (ks_nvpair_t));
+	if (instance == NULL) {
+		perror("malloc");
+		exit(3);
+	}
+
+	(void) strlcpy(instance->name, name, KSTAT_STRLEN);
+	(void) memcpy(&instance->value, value, sizeof (ks_value_t));
+	instance->data_type = data_type;
+
+	tmp = list_head(&ksi->ks_nvlist);
+	while (tmp != NULL && strcasecmp(instance->name, tmp->name) > 0)
+		tmp = list_next(&ksi->ks_nvlist, tmp);
+
+	list_insert_before(&ksi->ks_nvlist, tmp, instance);
+}
+
+/*
+ * Allocates a new all-matching selector.
+ */
+static ks_selector_t *
+new_selector(void)
+{
+	ks_selector_t	*selector;
+
+	selector = (ks_selector_t *)malloc(sizeof (ks_selector_t));
+	if (selector == NULL) {
+		perror("malloc");
+		exit(3);
+	}
+
+	list_link_init(&selector->ks_next);
+
+	selector->ks_module.pstr = "*";
+	selector->ks_instance.pstr = "*";
+	selector->ks_name.pstr = "*";
+	selector->ks_statistic.pstr = "*";
+
+	return (selector);
+}
+
+/*
+ * This function was taken from the perl kstat module code - please
+ * see for further comments there.
+ */
+static kstat_raw_reader_t
+lookup_raw_kstat_fn(char *module, char *name)
+{
+	char		key[KSTAT_STRLEN * 2];
+	register char 	*f, *t;
+	int		n = 0;
+
+	for (f = module, t = key; *f != '\0'; f++, t++) {
+		while (*f != '\0' && isdigit(*f))
+			f++;
+		*t = *f;
+	}
+	*t++ = ':';
+
+	for (f = name; *f != '\0'; f++, t++) {
+		while (*f != '\0' && isdigit(*f))
+			f++;
+		*t = *f;
+	}
+	*t = '\0';
+
+	while (ks_raw_lookup[n].fn != NULL) {
+		if (strncmp(ks_raw_lookup[n].name, key, strlen(key)) == 0)
+			return (ks_raw_lookup[n].fn);
+		n++;
+	}
+
+	return (0);
+}
+
+/*
+ * Match a string against a shell glob or extended regular expression.
+ */
+static boolean_t
+ks_match(const char *str, ks_pattern_t *pattern)
+{
+	int	regcode;
+	char	*regstr;
+	char	*errbuf;
+	size_t	bufsz;
+
+	if (pattern->pstr != NULL && gmatch(pattern->pstr, "/*/") != 0) {
+		/* All regex patterns are strdup'd copies */
+		regstr = pattern->pstr + 1;
+		*(strrchr(regstr, '/')) = '\0';
+
+		regcode = regcomp(&pattern->preg, regstr,
+		    REG_EXTENDED | REG_NOSUB);
+		if (regcode != 0) {
+			bufsz = regerror(regcode, NULL, NULL, 0);
+			if (bufsz != 0) {
+				errbuf = malloc(bufsz);
+				if (errbuf == NULL) {
+					perror("malloc");
+					exit(3);
+				}
+				(void) regerror(regcode, NULL, errbuf, bufsz);
+				(void) fprintf(stderr, "kstat: %s\n", errbuf);
+			}
+			usage();
+			exit(2);
+		}
+
+		pattern->pstr = NULL;
+	}
+
+	if (pattern->pstr == NULL) {
+		return (regexec(&pattern->preg, str, 0, NULL, 0) == 0);
+	}
+
+	return ((gmatch(str, pattern->pstr) != 0));
+}
+
+/*
+ * Iterate over all kernel statistics and save matches.
+ */
+static void
+ks_instances_read(kstat_ctl_t *kc)
+{
+	kstat_raw_reader_t save_raw = NULL;
+	kid_t		id;
+	ks_selector_t	*selector;
+	ks_instance_t	*ksi;
+	ks_instance_t	*tmp;
+	kstat_t		*kp;
+	boolean_t	skip;
+
+	for (kp = kc->kc_chain; kp != NULL; kp = kp->ks_next) {
+		/* Don't bother storing the kstat headers */
+		if (strncmp(kp->ks_name, "kstat_", 6) == 0) {
+			continue;
+		}
+
+		/* Don't bother storing raw stats we don't understand */
+		if (kp->ks_type == KSTAT_TYPE_RAW) {
+			save_raw = lookup_raw_kstat_fn(kp->ks_module,
+			    kp->ks_name);
+			if (save_raw == NULL) {
+#ifdef REPORT_UNKNOWN
+				(void) fprintf(stderr,
+				    "Unknown kstat type %s:%d:%s - "
+				    "%d of size %d\n", kp->ks_module,
+				    kp->ks_instance, kp->ks_name,
+				    kp->ks_ndata, kp->ks_data_size);
+#endif
+				continue;
+			}
+		}
+
+		/*
+		 * Iterate over the list of selectors and skip
+		 * instances we dont want. We filter for statistics
+		 * later, as we dont know them yet.
+		 */
+		skip = B_TRUE;
+		selector = list_head(&selector_list);
+		while (selector != NULL) {
+			if (ks_match(kp->ks_module, &selector->ks_module) ||
+			    ks_match(kp->ks_name, &selector->ks_name)) {
+				skip = B_FALSE;
+				break;
+			}
+			selector = list_next(&selector_list, selector);
+		}
+
+		if (skip) {
+			continue;
+		}
+
+		/*
+		 * Allocate a new instance and fill in the values
+		 * we know so far.
+		 */
+		ksi = (ks_instance_t *)malloc(sizeof (ks_instance_t));
+		if (ksi == NULL) {
+			perror("malloc");
+			exit(3);
+		}
+
+		list_link_init(&ksi->ks_next);
+
+		(void) strlcpy(ksi->ks_module, kp->ks_module, KSTAT_STRLEN);
+		(void) strlcpy(ksi->ks_name, kp->ks_name, KSTAT_STRLEN);
+		(void) strlcpy(ksi->ks_class, kp->ks_class, KSTAT_STRLEN);
+
+		ksi->ks_instance = kp->ks_instance;
+		ksi->ks_snaptime = kp->ks_snaptime;
+		ksi->ks_type = kp->ks_type;
+
+		list_create(&ksi->ks_nvlist, sizeof (ks_nvpair_t),
+		    offsetof(ks_nvpair_t, nv_next));
+
+		SAVE_HRTIME_X(ksi, "crtime", kp->ks_crtime);
+		SAVE_HRTIME_X(ksi, "snaptime", kp->ks_snaptime);
+		if (g_pflg) {
+			SAVE_STRING_X(ksi, "class", kp->ks_class);
+		}
+
+		/* Insert this instance into a sorted list */
+		tmp = list_head(&instances_list);
+		while (tmp != NULL && compare_instances(ksi, tmp) > 0)
+			tmp = list_next(&instances_list, tmp);
+
+		list_insert_before(&instances_list, tmp, ksi);
+
+		/* Read the actual statistics */
+		id = kstat_read(kc, kp, NULL);
+		if (id == -1) {
+#ifdef REPORT_UNKNOWN
+			perror("kstat_read");
+#endif
+			continue;
+		}
+
+		switch (kp->ks_type) {
+		case KSTAT_TYPE_RAW:
+			save_raw(kp, ksi);
+			break;
+		case KSTAT_TYPE_NAMED:
+			save_named(kp, ksi);
+			break;
+		case KSTAT_TYPE_INTR:
+			save_intr(kp, ksi);
+			break;
+		case KSTAT_TYPE_IO:
+			save_io(kp, ksi);
+			break;
+		case KSTAT_TYPE_TIMER:
+			save_timer(kp, ksi);
+			break;
+		default:
+			assert(B_FALSE); /* Invalid type */
+			break;
+		}
+	}
+}
+
+/*
+ * Print the value of a name-value pair.
+ */
+static void
+ks_value_print(ks_nvpair_t *nvpair)
+{
+	switch (nvpair->data_type) {
+	case KSTAT_DATA_CHAR:
+		(void) fprintf(stdout, "%s", nvpair->value.c);
+		break;
+	case KSTAT_DATA_INT32:
+		(void) fprintf(stdout, "%d", nvpair->value.i32);
+		break;
+	case KSTAT_DATA_UINT32:
+		(void) fprintf(stdout, "%u", nvpair->value.ui32);
+		break;
+	case KSTAT_DATA_INT64:
+		(void) fprintf(stdout, "%lld", nvpair->value.i64);
+		break;
+	case KSTAT_DATA_UINT64:
+		(void) fprintf(stdout, "%llu", nvpair->value.ui64);
+		break;
+	case KSTAT_DATA_STRING:
+		(void) fprintf(stdout, "%s", KSTAT_NAMED_STR_PTR(nvpair));
+		break;
+	case KSTAT_DATA_HRTIME:
+		if (nvpair->value.ui64 == 0)
+			(void) fprintf(stdout, "0");
+		else
+			(void) fprintf(stdout, "%.9f",
+			    nvpair->value.ui64 / 1000000000.0);
+		break;
+	default:
+		assert(B_FALSE);
+	}
+}
+
+/*
+ * Print a single instance.
+ */
+static void
+ks_instance_print(ks_instance_t *ksi, ks_nvpair_t *nvpair)
+{
+	if (g_headerflg) {
+		if (!g_pflg) {
+			(void) fprintf(stdout, DFLT_FMT,
+			    ksi->ks_module, ksi->ks_instance,
+			    ksi->ks_name, ksi->ks_class);
+		}
+		g_headerflg = B_FALSE;
+	}
+
+	if (g_pflg) {
+		(void) fprintf(stdout, KS_PFMT,
+		    ksi->ks_module, ksi->ks_instance,
+		    ksi->ks_name, nvpair->name);
+		if (!g_lflg) {
+			(void) putchar(g_cflg ? ':': '\t');
+			ks_value_print(nvpair);
+		}
+	} else {
+		(void) fprintf(stdout, KS_DFMT, nvpair->name);
+		ks_value_print(nvpair);
+	}
+
+	(void) putchar('\n');
+}
+
+/*
+ * Print a single instance in JSON format.
+ */
+static void
+ks_instance_print_json(ks_instance_t *ksi, ks_nvpair_t *nvpair)
+{
+	if (g_headerflg) {
+		(void) fprintf(stdout, JSON_FMT,
+		    ksi->ks_module, ksi->ks_instance,
+		    ksi->ks_name, ksi->ks_class,
+		    ksi->ks_type);
+
+		if (ksi->ks_snaptime == 0)
+			(void) fprintf(stdout, "\t\"snaptime\": 0,\n");
+		else
+			(void) fprintf(stdout, "\t\"snaptime\": %.9f,\n",
+			    ksi->ks_snaptime / 1000000000.0);
+
+		(void) fprintf(stdout, "\t\"data\": {\n");
+
+		g_headerflg = B_FALSE;
+	}
+
+	(void) fprintf(stdout, KS_JFMT, nvpair->name);
+	if (nvpair->data_type == KSTAT_DATA_STRING) {
+		(void) putchar('\"');
+		ks_value_print(nvpair);
+		(void) putchar('\"');
+	} else {
+		ks_value_print(nvpair);
+	}
+	if (nvpair != list_tail(&ksi->ks_nvlist))
+		(void) putchar(',');
+
+	(void) putchar('\n');
+}
+
+/*
+ * Print all instances.
+ */
+static void
+ks_instances_print(void)
+{
+	ks_selector_t	*selector;
+	ks_instance_t	*ksi, *ktmp;
+	ks_nvpair_t	*nvpair, *ntmp;
+	void		(*ks_print_fn)(ks_instance_t *, ks_nvpair_t *);
+	char		*ks_number;
+
+	if (g_timestamp_fmt != NODATE)
+		print_timestamp(g_timestamp_fmt);
+
+	if (g_jflg) {
+		ks_print_fn = &ks_instance_print_json;
+		(void) putchar('[');
+	} else {
+		ks_print_fn = &ks_instance_print;
+	}
+
+	/* Iterate over each selector */
+	selector = list_head(&selector_list);
+	while (selector != NULL) {
+
+		/* Iterate over each instance */
+		for (ksi = list_head(&instances_list); ksi != NULL;
+		    ksi = list_next(&instances_list, ksi)) {
+
+			(void) asprintf(&ks_number, "%d", ksi->ks_instance);
+			if (!(ks_match(ksi->ks_module, &selector->ks_module) &&
+			    ks_match(ksi->ks_name, &selector->ks_name) &&
+			    ks_match(ks_number, &selector->ks_instance) &&
+			    ks_match(ksi->ks_class, &g_ks_class))) {
+				free(ks_number);
+				continue;
+			}
+
+			free(ks_number);
+
+			/* Finally iterate over each statistic */
+			g_headerflg = B_TRUE;
+			for (nvpair = list_head(&ksi->ks_nvlist);
+			    nvpair != NULL;
+			    nvpair = list_next(&ksi->ks_nvlist, nvpair)) {
+				if (!ks_match(nvpair->name,
+				    &selector->ks_statistic))
+					continue;
+
+				g_matched = 0;
+				if (!g_qflg)
+					(*ks_print_fn)(ksi, nvpair);
+			}
+
+			if (!g_headerflg) {
+				if (g_jflg) {
+					(void) fprintf(stdout, "\t}\n}");
+					if (ksi != list_tail(&instances_list))
+						(void) putchar(',');
+				} else if (!g_pflg) {
+					(void) putchar('\n');
+				}
+			}
+		}
+
+		selector = list_next(&selector_list, selector);
+	}
+
+	if (g_jflg)
+		(void) fprintf(stdout, "]\n");
+
+	(void) fflush(stdout);
+
+	/* Free the instances list */
+	ksi = list_head(&instances_list);
+	while (ksi != NULL) {
+		nvpair = list_head(&ksi->ks_nvlist);
+		while (nvpair != NULL) {
+			ntmp = nvpair;
+			nvpair = list_next(&ksi->ks_nvlist, nvpair);
+			list_remove(&ksi->ks_nvlist, ntmp);
+			if (ntmp->data_type == KSTAT_DATA_STRING)
+				free(ntmp->value.str.addr.ptr);
+			free(ntmp);
+		}
+
+		ktmp = ksi;
+		ksi = list_next(&instances_list, ksi);
+		list_remove(&instances_list, ktmp);
+		list_destroy(&ktmp->ks_nvlist);
+		free(ktmp);
+	}
+}
+
+static void
+save_cpu_stat(kstat_t *kp, ks_instance_t *ksi)
+{
+	cpu_stat_t	*stat;
+	cpu_sysinfo_t	*sysinfo;
+	cpu_syswait_t	*syswait;
+	cpu_vminfo_t	*vminfo;
+
+	stat = (cpu_stat_t *)(kp->ks_data);
+	sysinfo = &stat->cpu_sysinfo;
+	syswait = &stat->cpu_syswait;
+	vminfo  = &stat->cpu_vminfo;
+
+	SAVE_UINT32_X(ksi, "idle", sysinfo->cpu[CPU_IDLE]);
+	SAVE_UINT32_X(ksi, "user", sysinfo->cpu[CPU_USER]);
+	SAVE_UINT32_X(ksi, "kernel", sysinfo->cpu[CPU_KERNEL]);
+	SAVE_UINT32_X(ksi, "wait", sysinfo->cpu[CPU_WAIT]);
+	SAVE_UINT32_X(ksi, "wait_io", sysinfo->cpu[W_IO]);
+	SAVE_UINT32_X(ksi, "wait_swap", sysinfo->cpu[W_SWAP]);
+	SAVE_UINT32_X(ksi, "wait_pio", sysinfo->cpu[W_PIO]);
+	SAVE_UINT32(ksi, sysinfo, bread);
+	SAVE_UINT32(ksi, sysinfo, bwrite);
+	SAVE_UINT32(ksi, sysinfo, lread);
+	SAVE_UINT32(ksi, sysinfo, lwrite);
+	SAVE_UINT32(ksi, sysinfo, phread);
+	SAVE_UINT32(ksi, sysinfo, phwrite);
+	SAVE_UINT32(ksi, sysinfo, pswitch);
+	SAVE_UINT32(ksi, sysinfo, trap);
+	SAVE_UINT32(ksi, sysinfo, intr);
+	SAVE_UINT32(ksi, sysinfo, syscall);
+	SAVE_UINT32(ksi, sysinfo, sysread);
+	SAVE_UINT32(ksi, sysinfo, syswrite);
+	SAVE_UINT32(ksi, sysinfo, sysfork);
+	SAVE_UINT32(ksi, sysinfo, sysvfork);
+	SAVE_UINT32(ksi, sysinfo, sysexec);
+	SAVE_UINT32(ksi, sysinfo, readch);
+	SAVE_UINT32(ksi, sysinfo, writech);
+	SAVE_UINT32(ksi, sysinfo, rcvint);
+	SAVE_UINT32(ksi, sysinfo, xmtint);
+	SAVE_UINT32(ksi, sysinfo, mdmint);
+	SAVE_UINT32(ksi, sysinfo, rawch);
+	SAVE_UINT32(ksi, sysinfo, canch);
+	SAVE_UINT32(ksi, sysinfo, outch);
+	SAVE_UINT32(ksi, sysinfo, msg);
+	SAVE_UINT32(ksi, sysinfo, sema);
+	SAVE_UINT32(ksi, sysinfo, namei);
+	SAVE_UINT32(ksi, sysinfo, ufsiget);
+	SAVE_UINT32(ksi, sysinfo, ufsdirblk);
+	SAVE_UINT32(ksi, sysinfo, ufsipage);
+	SAVE_UINT32(ksi, sysinfo, ufsinopage);
+	SAVE_UINT32(ksi, sysinfo, inodeovf);
+	SAVE_UINT32(ksi, sysinfo, fileovf);
+	SAVE_UINT32(ksi, sysinfo, procovf);
+	SAVE_UINT32(ksi, sysinfo, intrthread);
+	SAVE_UINT32(ksi, sysinfo, intrblk);
+	SAVE_UINT32(ksi, sysinfo, idlethread);
+	SAVE_UINT32(ksi, sysinfo, inv_swtch);
+	SAVE_UINT32(ksi, sysinfo, nthreads);
+	SAVE_UINT32(ksi, sysinfo, cpumigrate);
+	SAVE_UINT32(ksi, sysinfo, xcalls);
+	SAVE_UINT32(ksi, sysinfo, mutex_adenters);
+	SAVE_UINT32(ksi, sysinfo, rw_rdfails);
+	SAVE_UINT32(ksi, sysinfo, rw_wrfails);
+	SAVE_UINT32(ksi, sysinfo, modload);
+	SAVE_UINT32(ksi, sysinfo, modunload);
+	SAVE_UINT32(ksi, sysinfo, bawrite);
+#ifdef	STATISTICS	/* see header file */
+	SAVE_UINT32(ksi, sysinfo, rw_enters);
+	SAVE_UINT32(ksi, sysinfo, win_uo_cnt);
+	SAVE_UINT32(ksi, sysinfo, win_uu_cnt);
+	SAVE_UINT32(ksi, sysinfo, win_so_cnt);
+	SAVE_UINT32(ksi, sysinfo, win_su_cnt);
+	SAVE_UINT32(ksi, sysinfo, win_suo_cnt);
+#endif
+
+	SAVE_INT32(ksi, syswait, iowait);
+	SAVE_INT32(ksi, syswait, swap);
+	SAVE_INT32(ksi, syswait, physio);
+
+	SAVE_UINT32(ksi, vminfo, pgrec);
+	SAVE_UINT32(ksi, vminfo, pgfrec);
+	SAVE_UINT32(ksi, vminfo, pgin);
+	SAVE_UINT32(ksi, vminfo, pgpgin);
+	SAVE_UINT32(ksi, vminfo, pgout);
+	SAVE_UINT32(ksi, vminfo, pgpgout);
+	SAVE_UINT32(ksi, vminfo, swapin);
+	SAVE_UINT32(ksi, vminfo, pgswapin);
+	SAVE_UINT32(ksi, vminfo, swapout);
+	SAVE_UINT32(ksi, vminfo, pgswapout);
+	SAVE_UINT32(ksi, vminfo, zfod);
+	SAVE_UINT32(ksi, vminfo, dfree);
+	SAVE_UINT32(ksi, vminfo, scan);
+	SAVE_UINT32(ksi, vminfo, rev);
+	SAVE_UINT32(ksi, vminfo, hat_fault);
+	SAVE_UINT32(ksi, vminfo, as_fault);
+	SAVE_UINT32(ksi, vminfo, maj_fault);
+	SAVE_UINT32(ksi, vminfo, cow_fault);
+	SAVE_UINT32(ksi, vminfo, prot_fault);
+	SAVE_UINT32(ksi, vminfo, softlock);
+	SAVE_UINT32(ksi, vminfo, kernel_asflt);
+	SAVE_UINT32(ksi, vminfo, pgrrun);
+	SAVE_UINT32(ksi, vminfo, execpgin);
+	SAVE_UINT32(ksi, vminfo, execpgout);
+	SAVE_UINT32(ksi, vminfo, execfree);
+	SAVE_UINT32(ksi, vminfo, anonpgin);
+	SAVE_UINT32(ksi, vminfo, anonpgout);
+	SAVE_UINT32(ksi, vminfo, anonfree);
+	SAVE_UINT32(ksi, vminfo, fspgin);
+	SAVE_UINT32(ksi, vminfo, fspgout);
+	SAVE_UINT32(ksi, vminfo, fsfree);
+}
+
+static void
+save_var(kstat_t *kp, ks_instance_t *ksi)
+{
+	struct var	*var = (struct var *)(kp->ks_data);
+
+	assert(kp->ks_data_size == sizeof (struct var));
+
+	SAVE_INT32(ksi, var, v_buf);
+	SAVE_INT32(ksi, var, v_call);
+	SAVE_INT32(ksi, var, v_proc);
+	SAVE_INT32(ksi, var, v_maxupttl);
+	SAVE_INT32(ksi, var, v_nglobpris);
+	SAVE_INT32(ksi, var, v_maxsyspri);
+	SAVE_INT32(ksi, var, v_clist);
+	SAVE_INT32(ksi, var, v_maxup);
+	SAVE_INT32(ksi, var, v_hbuf);
+	SAVE_INT32(ksi, var, v_hmask);
+	SAVE_INT32(ksi, var, v_pbuf);
+	SAVE_INT32(ksi, var, v_sptmap);
+	SAVE_INT32(ksi, var, v_maxpmem);
+	SAVE_INT32(ksi, var, v_autoup);
+	SAVE_INT32(ksi, var, v_bufhwm);
+}
+
+static void
+save_ncstats(kstat_t *kp, ks_instance_t *ksi)
+{
+	struct ncstats	*ncstats = (struct ncstats *)(kp->ks_data);
+
+	assert(kp->ks_data_size == sizeof (struct ncstats));
+
+	SAVE_INT32(ksi, ncstats, hits);
+	SAVE_INT32(ksi, ncstats, misses);
+	SAVE_INT32(ksi, ncstats, enters);
+	SAVE_INT32(ksi, ncstats, dbl_enters);
+	SAVE_INT32(ksi, ncstats, long_enter);
+	SAVE_INT32(ksi, ncstats, long_look);
+	SAVE_INT32(ksi, ncstats, move_to_front);
+	SAVE_INT32(ksi, ncstats, purges);
+}
+
+static void
+save_sysinfo(kstat_t *kp, ks_instance_t *ksi)
+{
+	sysinfo_t	*sysinfo = (sysinfo_t *)(kp->ks_data);
+
+	assert(kp->ks_data_size == sizeof (sysinfo_t));
+
+	SAVE_UINT32(ksi, sysinfo, updates);
+	SAVE_UINT32(ksi, sysinfo, runque);
+	SAVE_UINT32(ksi, sysinfo, runocc);
+	SAVE_UINT32(ksi, sysinfo, swpque);
+	SAVE_UINT32(ksi, sysinfo, swpocc);
+	SAVE_UINT32(ksi, sysinfo, waiting);
+}
+
+static void
+save_vminfo(kstat_t *kp, ks_instance_t *ksi)
+{
+	vminfo_t	*vminfo = (vminfo_t *)(kp->ks_data);
+
+	assert(kp->ks_data_size == sizeof (vminfo_t));
+
+	SAVE_UINT64(ksi, vminfo, freemem);
+	SAVE_UINT64(ksi, vminfo, swap_resv);
+	SAVE_UINT64(ksi, vminfo, swap_alloc);
+	SAVE_UINT64(ksi, vminfo, swap_avail);
+	SAVE_UINT64(ksi, vminfo, swap_free);
+	SAVE_UINT64(ksi, vminfo, updates);
+}
+
+static void
+save_nfs(kstat_t *kp, ks_instance_t *ksi)
+{
+	struct mntinfo_kstat *mntinfo = (struct mntinfo_kstat *)(kp->ks_data);
+
+	assert(kp->ks_data_size == sizeof (struct mntinfo_kstat));
+
+	SAVE_STRING(ksi, mntinfo, mik_proto);
+	SAVE_UINT32(ksi, mntinfo, mik_vers);
+	SAVE_UINT32(ksi, mntinfo, mik_flags);
+	SAVE_UINT32(ksi, mntinfo, mik_secmod);
+	SAVE_UINT32(ksi, mntinfo, mik_curread);
+	SAVE_UINT32(ksi, mntinfo, mik_curwrite);
+	SAVE_INT32(ksi, mntinfo, mik_timeo);
+	SAVE_INT32(ksi, mntinfo, mik_retrans);
+	SAVE_UINT32(ksi, mntinfo, mik_acregmin);
+	SAVE_UINT32(ksi, mntinfo, mik_acregmax);
+	SAVE_UINT32(ksi, mntinfo, mik_acdirmin);
+	SAVE_UINT32(ksi, mntinfo, mik_acdirmax);
+	SAVE_UINT32_X(ksi, "lookup_srtt", mntinfo->mik_timers[0].srtt);
+	SAVE_UINT32_X(ksi, "lookup_deviate", mntinfo->mik_timers[0].deviate);
+	SAVE_UINT32_X(ksi, "lookup_rtxcur", mntinfo->mik_timers[0].rtxcur);
+	SAVE_UINT32_X(ksi, "read_srtt", mntinfo->mik_timers[1].srtt);
+	SAVE_UINT32_X(ksi, "read_deviate", mntinfo->mik_timers[1].deviate);
+	SAVE_UINT32_X(ksi, "read_rtxcur", mntinfo->mik_timers[1].rtxcur);
+	SAVE_UINT32_X(ksi, "write_srtt", mntinfo->mik_timers[2].srtt);
+	SAVE_UINT32_X(ksi, "write_deviate", mntinfo->mik_timers[2].deviate);
+	SAVE_UINT32_X(ksi, "write_rtxcur", mntinfo->mik_timers[2].rtxcur);
+	SAVE_UINT32(ksi, mntinfo, mik_noresponse);
+	SAVE_UINT32(ksi, mntinfo, mik_failover);
+	SAVE_UINT32(ksi, mntinfo, mik_remap);
+	SAVE_STRING(ksi, mntinfo, mik_curserver);
+}
+
+#ifdef __sparc
+static void
+save_sfmmu_global_stat(kstat_t *kp, ks_instance_t *ksi)
+{
+	struct sfmmu_global_stat *sfmmug =
+	    (struct sfmmu_global_stat *)(kp->ks_data);
+
+	assert(kp->ks_data_size == sizeof (struct sfmmu_global_stat));
+
+	SAVE_INT32(ksi, sfmmug, sf_tsb_exceptions);
+	SAVE_INT32(ksi, sfmmug, sf_tsb_raise_exception);
+	SAVE_INT32(ksi, sfmmug, sf_pagefaults);
+	SAVE_INT32(ksi, sfmmug, sf_uhash_searches);
+	SAVE_INT32(ksi, sfmmug, sf_uhash_links);
+	SAVE_INT32(ksi, sfmmug, sf_khash_searches);
+	SAVE_INT32(ksi, sfmmug, sf_khash_links);
+	SAVE_INT32(ksi, sfmmug, sf_swapout);
+	SAVE_INT32(ksi, sfmmug, sf_tsb_alloc);
+	SAVE_INT32(ksi, sfmmug, sf_tsb_allocfail);
+	SAVE_INT32(ksi, sfmmug, sf_tsb_sectsb_create);
+	SAVE_INT32(ksi, sfmmug, sf_scd_1sttsb_alloc);
+	SAVE_INT32(ksi, sfmmug, sf_scd_2ndtsb_alloc);
+	SAVE_INT32(ksi, sfmmug, sf_scd_1sttsb_allocfail);
+	SAVE_INT32(ksi, sfmmug, sf_scd_2ndtsb_allocfail);
+	SAVE_INT32(ksi, sfmmug, sf_tteload8k);
+	SAVE_INT32(ksi, sfmmug, sf_tteload64k);
+	SAVE_INT32(ksi, sfmmug, sf_tteload512k);
+	SAVE_INT32(ksi, sfmmug, sf_tteload4m);
+	SAVE_INT32(ksi, sfmmug, sf_tteload32m);
+	SAVE_INT32(ksi, sfmmug, sf_tteload256m);
+	SAVE_INT32(ksi, sfmmug, sf_tsb_load8k);
+	SAVE_INT32(ksi, sfmmug, sf_tsb_load4m);
+	SAVE_INT32(ksi, sfmmug, sf_hblk_hit);
+	SAVE_INT32(ksi, sfmmug, sf_hblk8_ncreate);
+	SAVE_INT32(ksi, sfmmug, sf_hblk8_nalloc);
+	SAVE_INT32(ksi, sfmmug, sf_hblk1_ncreate);
+	SAVE_INT32(ksi, sfmmug, sf_hblk1_nalloc);
+	SAVE_INT32(ksi, sfmmug, sf_hblk_slab_cnt);
+	SAVE_INT32(ksi, sfmmug, sf_hblk_reserve_cnt);
+	SAVE_INT32(ksi, sfmmug, sf_hblk_recurse_cnt);
+	SAVE_INT32(ksi, sfmmug, sf_hblk_reserve_hit);
+	SAVE_INT32(ksi, sfmmug, sf_get_free_success);
+	SAVE_INT32(ksi, sfmmug, sf_get_free_throttle);
+	SAVE_INT32(ksi, sfmmug, sf_get_free_fail);
+	SAVE_INT32(ksi, sfmmug, sf_put_free_success);
+	SAVE_INT32(ksi, sfmmug, sf_put_free_fail);
+	SAVE_INT32(ksi, sfmmug, sf_pgcolor_conflict);
+	SAVE_INT32(ksi, sfmmug, sf_uncache_conflict);
+	SAVE_INT32(ksi, sfmmug, sf_unload_conflict);
+	SAVE_INT32(ksi, sfmmug, sf_ism_uncache);
+	SAVE_INT32(ksi, sfmmug, sf_ism_recache);
+	SAVE_INT32(ksi, sfmmug, sf_recache);
+	SAVE_INT32(ksi, sfmmug, sf_steal_count);
+	SAVE_INT32(ksi, sfmmug, sf_pagesync);
+	SAVE_INT32(ksi, sfmmug, sf_clrwrt);
+	SAVE_INT32(ksi, sfmmug, sf_pagesync_invalid);
+	SAVE_INT32(ksi, sfmmug, sf_kernel_xcalls);
+	SAVE_INT32(ksi, sfmmug, sf_user_xcalls);
+	SAVE_INT32(ksi, sfmmug, sf_tsb_grow);
+	SAVE_INT32(ksi, sfmmug, sf_tsb_shrink);
+	SAVE_INT32(ksi, sfmmug, sf_tsb_resize_failures);
+	SAVE_INT32(ksi, sfmmug, sf_tsb_reloc);
+	SAVE_INT32(ksi, sfmmug, sf_user_vtop);
+	SAVE_INT32(ksi, sfmmug, sf_ctx_inv);
+	SAVE_INT32(ksi, sfmmug, sf_tlb_reprog_pgsz);
+	SAVE_INT32(ksi, sfmmug, sf_region_remap_demap);
+	SAVE_INT32(ksi, sfmmug, sf_create_scd);
+	SAVE_INT32(ksi, sfmmug, sf_join_scd);
+	SAVE_INT32(ksi, sfmmug, sf_leave_scd);
+	SAVE_INT32(ksi, sfmmug, sf_destroy_scd);
+}
+#endif
+
+#ifdef __sparc
+static void
+save_sfmmu_tsbsize_stat(kstat_t *kp, ks_instance_t *ksi)
+{
+	struct sfmmu_tsbsize_stat *sfmmut;
+
+	assert(kp->ks_data_size == sizeof (struct sfmmu_tsbsize_stat));
+	sfmmut = (struct sfmmu_tsbsize_stat *)(kp->ks_data);
+
+	SAVE_INT32(ksi, sfmmut, sf_tsbsz_8k);
+	SAVE_INT32(ksi, sfmmut, sf_tsbsz_16k);
+	SAVE_INT32(ksi, sfmmut, sf_tsbsz_32k);
+	SAVE_INT32(ksi, sfmmut, sf_tsbsz_64k);
+	SAVE_INT32(ksi, sfmmut, sf_tsbsz_128k);
+	SAVE_INT32(ksi, sfmmut, sf_tsbsz_256k);
+	SAVE_INT32(ksi, sfmmut, sf_tsbsz_512k);
+	SAVE_INT32(ksi, sfmmut, sf_tsbsz_1m);
+	SAVE_INT32(ksi, sfmmut, sf_tsbsz_2m);
+	SAVE_INT32(ksi, sfmmut, sf_tsbsz_4m);
+}
+#endif
+
+#ifdef __sparc
+static void
+save_simmstat(kstat_t *kp, ks_instance_t *ksi)
+{
+	uchar_t	*simmstat;
+	char	*simm_buf;
+	char	*list = NULL;
+	int	i;
+
+	assert(kp->ks_data_size == sizeof (uchar_t) * SIMM_COUNT);
+
+	for (i = 0, simmstat = (uchar_t *)(kp->ks_data); i < SIMM_COUNT - 1;
+	    i++, simmstat++) {
+		if (list == NULL) {
+			(void) asprintf(&simm_buf, "%d,", *simmstat);
+		} else {
+			(void) asprintf(&simm_buf, "%s%d,", list, *simmstat);
+			free(list);
+		}
+		list = simm_buf;
+	}
+
+	(void) asprintf(&simm_buf, "%s%d", list, *simmstat);
+	SAVE_STRING_X(ksi, "status", simm_buf);
+	free(list);
+	free(simm_buf);
+}
+#endif
+
+#ifdef __sparc
+/*
+ * Helper function for save_temperature().
+ */
+static char *
+short_array_to_string(short *shortp, int len)
+{
+	char	*list = NULL;
+	char	*list_buf;
+
+	for (; len > 1; len--, shortp++) {
+		if (list == NULL) {
+			(void) asprintf(&list_buf, "%d,", *shortp);
+		} else {
+			(void) asprintf(&list_buf, "%s%d,", list, *shortp);
+			free(list);
+		}
+		list = list_buf;
+	}
+
+	(void) asprintf(&list_buf, "%s%s", list, *shortp);
+	free(list);
+	return (list_buf);
+}
+
+static void
+save_temperature(kstat_t *kp, ks_instance_t *ksi)
+{
+	struct temp_stats *temps = (struct temp_stats *)(kp->ks_data);
+	char	*buf;
+	int	n = 1;
+
+	assert(kp->ks_data_size == sizeof (struct temp_stats));
+
+	SAVE_UINT32(ksi, temps, index);
+
+	buf = short_array_to_string(temps->l1, L1_SZ);
+	SAVE_STRING_X(ksi, "l1", buf);
+	free(buf);
+
+	buf = short_array_to_string(temps->l2, L2_SZ);
+	SAVE_STRING_X(ksi, "l2", buf);
+	free(buf);
+
+	buf = short_array_to_string(temps->l3, L3_SZ);
+	SAVE_STRING_X(ksi, "l3", buf);
+	free(buf);
+
+	buf = short_array_to_string(temps->l4, L4_SZ);
+	SAVE_STRING_X(ksi, "l4", buf);
+	free(buf);
+
+	buf = short_array_to_string(temps->l5, L5_SZ);
+	SAVE_STRING_X(ksi, "l5", buf);
+	free(buf);
+
+	SAVE_INT32(ksi, temps, max);
+	SAVE_INT32(ksi, temps, min);
+	SAVE_INT32(ksi, temps, state);
+	SAVE_INT32(ksi, temps, temp_cnt);
+	SAVE_INT32(ksi, temps, shutdown_cnt);
+	SAVE_INT32(ksi, temps, version);
+	SAVE_INT32(ksi, temps, trend);
+	SAVE_INT32(ksi, temps, override);
+}
+#endif
+
+#ifdef __sparc
+static void
+save_temp_over(kstat_t *kp, ks_instance_t *ksi)
+{
+	short	*sh = (short *)(kp->ks_data);
+	char	*value;
+
+	assert(kp->ks_data_size == sizeof (short));
+
+	(void) asprintf(&value, "%hu", *sh);
+	SAVE_STRING_X(ksi, "override", value);
+	free(value);
+}
+#endif
+
+#ifdef __sparc
+static void
+save_ps_shadow(kstat_t *kp, ks_instance_t *ksi)
+{
+	uchar_t	*uchar = (uchar_t *)(kp->ks_data);
+
+	assert(kp->ks_data_size == SYS_PS_COUNT);
+
+	SAVE_CHAR_X(ksi, "core_0", *uchar++);
+	SAVE_CHAR_X(ksi, "core_1", *uchar++);
+	SAVE_CHAR_X(ksi, "core_2", *uchar++);
+	SAVE_CHAR_X(ksi, "core_3", *uchar++);
+	SAVE_CHAR_X(ksi, "core_4", *uchar++);
+	SAVE_CHAR_X(ksi, "core_5", *uchar++);
+	SAVE_CHAR_X(ksi, "core_6", *uchar++);
+	SAVE_CHAR_X(ksi, "core_7", *uchar++);
+	SAVE_CHAR_X(ksi, "pps_0", *uchar++);
+	SAVE_CHAR_X(ksi, "clk_33", *uchar++);
+	SAVE_CHAR_X(ksi, "clk_50", *uchar++);
+	SAVE_CHAR_X(ksi, "v5_p", *uchar++);
+	SAVE_CHAR_X(ksi, "v12_p", *uchar++);
+	SAVE_CHAR_X(ksi, "v5_aux", *uchar++);
+	SAVE_CHAR_X(ksi, "v5_p_pch", *uchar++);
+	SAVE_CHAR_X(ksi, "v12_p_pch", *uchar++);
+	SAVE_CHAR_X(ksi, "v3_pch", *uchar++);
+	SAVE_CHAR_X(ksi, "v5_pch", *uchar++);
+	SAVE_CHAR_X(ksi, "p_fan", *uchar++);
+}
+#endif
+
+#ifdef __sparc
+static void
+save_fault_list(kstat_t *kp, ks_instance_t *ksi)
+{
+	struct ft_list *fault;
+	char	name[KSTAT_STRLEN + 7];
+	int	i;
+
+	for (i = 1, fault = (struct ft_list *)(kp->ks_data);
+	    i <= 999999 && i <= kp->ks_data_size / sizeof (struct ft_list);
+	    i++, fault++) {
+		(void) snprintf(name, sizeof (name), "unit_%d", i);
+		SAVE_INT32_X(ksi, name, fault->unit);
+		(void) snprintf(name, sizeof (name), "type_%d", i);
+		SAVE_INT32_X(ksi, name, fault->type);
+		(void) snprintf(name, sizeof (name), "fclass_%d", i);
+		SAVE_INT32_X(ksi, name, fault->fclass);
+		(void) snprintf(name, sizeof (name), "create_time_%d", i);
+		SAVE_HRTIME_X(ksi, name, fault->create_time);
+		(void) snprintf(name, sizeof (name), "msg_%d", i);
+		SAVE_STRING_X(ksi, name, faultp->msg);
+	}
+}
+#endif
+
+static void
+save_named(kstat_t *kp, ks_instance_t *ksi)
+{
+	kstat_named_t *knp;
+	int	n;
+
+	for (n = kp->ks_ndata, knp = KSTAT_NAMED_PTR(kp); n > 0; n--, knp++) {
+		switch (knp->data_type) {
+		case KSTAT_DATA_CHAR:
+			nvpair_insert(ksi, knp->name,
+			    (ks_value_t *)&knp->value, KSTAT_DATA_CHAR);
+			break;
+		case KSTAT_DATA_INT32:
+			nvpair_insert(ksi, knp->name,
+			    (ks_value_t *)&knp->value, KSTAT_DATA_INT32);
+			break;
+		case KSTAT_DATA_UINT32:
+			nvpair_insert(ksi, knp->name,
+			    (ks_value_t *)&knp->value, KSTAT_DATA_UINT32);
+			break;
+		case KSTAT_DATA_INT64:
+			nvpair_insert(ksi, knp->name,
+			    (ks_value_t *)&knp->value, KSTAT_DATA_INT64);
+			break;
+		case KSTAT_DATA_UINT64:
+			nvpair_insert(ksi, knp->name,
+			    (ks_value_t *)&knp->value, KSTAT_DATA_UINT64);
+			break;
+		case KSTAT_DATA_STRING:
+			SAVE_STRING_X(ksi, knp->name, KSTAT_NAMED_STR_PTR(knp));
+			break;
+		default:
+			assert(B_FALSE); /* Invalid data type */
+			break;
+		}
+	}
+}
+
+static void
+save_intr(kstat_t *kp, ks_instance_t *ksi)
+{
+	kstat_intr_t *intr = KSTAT_INTR_PTR(kp);
+	char	*intr_names[] = {"hard", "soft", "watchdog", "spurious",
+	    "multiple_service"};
+	int	n;
+
+	for (n = 0; n < KSTAT_NUM_INTRS; n++)
+		SAVE_UINT32_X(ksi, intr_names[n], intr->intrs[n]);
+}
+
+static void
+save_io(kstat_t *kp, ks_instance_t *ksi)
+{
+	kstat_io_t	*ksio = KSTAT_IO_PTR(kp);
+
+	SAVE_UINT64(ksi, ksio, nread);
+	SAVE_UINT64(ksi, ksio, nwritten);
+	SAVE_UINT32(ksi, ksio, reads);
+	SAVE_UINT32(ksi, ksio, writes);
+	SAVE_HRTIME(ksi, ksio, wtime);
+	SAVE_HRTIME(ksi, ksio, wlentime);
+	SAVE_HRTIME(ksi, ksio, wlastupdate);
+	SAVE_HRTIME(ksi, ksio, rtime);
+	SAVE_HRTIME(ksi, ksio, rlentime);
+	SAVE_HRTIME(ksi, ksio, rlastupdate);
+	SAVE_UINT32(ksi, ksio, wcnt);
+	SAVE_UINT32(ksi, ksio, rcnt);
+}
+
+static void
+save_timer(kstat_t *kp, ks_instance_t *ksi)
+{
+	kstat_timer_t	*ktimer = KSTAT_TIMER_PTR(kp);
+
+	SAVE_STRING(ksi, ktimer, name);
+	SAVE_UINT64(ksi, ktimer, num_events);
+	SAVE_HRTIME(ksi, ktimer, elapsed_time);
+	SAVE_HRTIME(ksi, ktimer, min_time);
+	SAVE_HRTIME(ksi, ktimer, max_time);
+	SAVE_HRTIME(ksi, ktimer, start_time);
+	SAVE_HRTIME(ksi, ktimer, stop_time);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/stat/kstat/kstat.h	Wed Jan 09 12:18:53 2013 +0000
@@ -0,0 +1,269 @@
+/*
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2013 David Hoeppner.  All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ */
+
+#ifndef _STAT_KSTAT_H
+#define	_STAT_KSTAT_H
+
+/*
+ * Structures needed by the kstat reader functions.
+ */
+#include <sys/var.h>
+#include <sys/utsname.h>
+#include <sys/sysinfo.h>
+#include <sys/flock.h>
+#include <sys/dnlc.h>
+#include <regex.h>
+#include <nfs/nfs.h>
+#include <nfs/nfs_clnt.h>
+
+#ifdef __sparc
+#include <vm/hat_sfmmu.h>
+#include <sys/simmstat.h>
+#include <sys/sysctrl.h>
+#include <sys/fhc.h>
+#endif
+
+#define	KSTAT_DATA_HRTIME	(KSTAT_DATA_STRING + 1)
+
+typedef union ks_value {
+	char		c[16];
+	int32_t		i32;
+	uint32_t	ui32;
+	struct {
+		union {
+			char	*ptr;
+			char	__pad[8];
+		} addr;
+		uint32_t	len;
+	} str;
+
+	int64_t		i64;
+	uint64_t	ui64;
+} ks_value_t;
+
+#define	SAVE_HRTIME(I, S, N)				\
+{							\
+	ks_value_t v;					\
+	v.ui64 = S->N;					\
+	nvpair_insert(I, #N, &v, KSTAT_DATA_UINT64);	\
+}
+
+#define	SAVE_INT32(I, S, N)				\
+{							\
+	ks_value_t v;					\
+	v.i32 = S->N;					\
+	nvpair_insert(I, #N, &v, KSTAT_DATA_INT32);	\
+}
+
+#define	SAVE_UINT32(I, S, N)				\
+{							\
+	ks_value_t v;					\
+	v.ui32 = S->N;					\
+	nvpair_insert(I, #N, &v, KSTAT_DATA_UINT32);	\
+}
+
+#define	SAVE_INT64(I, S, N)             		\
+{							\
+	ks_value_t v;					\
+	v.i64 = S->N;					\
+	nvpair_insert(I, #N, &v, KSTAT_DATA_INT64);	\
+}
+
+#define	SAVE_UINT64(I, S, N)				\
+{							\
+	ks_value_t v;					\
+	v.ui64 = S->N;					\
+	nvpair_insert(I, #N, &v, KSTAT_DATA_UINT64);	\
+}
+
+/*
+ * We dont want const "strings" because we free
+ * the instances later.
+ */
+#define	SAVE_STRING(I, S, N)				\
+{							\
+	ks_value_t v;					\
+	v.str.addr.ptr = safe_strdup(S->N);		\
+	v.str.len = strlen(S->N);			\
+	nvpair_insert(I, #N, &v, KSTAT_DATA_STRING);	\
+}
+
+#define	SAVE_HRTIME_X(I, N, V)				\
+{							\
+	ks_value_t v;					\
+	v.ui64 = V;					\
+	nvpair_insert(I, N, &v, KSTAT_DATA_HRTIME);	\
+}
+
+#define	SAVE_INT32_X(I, N, V)				\
+{							\
+	ks_value_t v;					\
+	v.i32 = V;					\
+	nvpair_insert(I, N, &v, KSTAT_DATA_INT32);	\
+}
+
+#define	SAVE_UINT32_X(I, N, V)				\
+{							\
+	ks_value_t v;					\
+	v.ui32 = V;					\
+	nvpair_insert(I, N, &v, KSTAT_DATA_UINT32);	\
+}
+
+#define	SAVE_UINT64_X(I, N, V)				\
+{							\
+	ks_value_t v;					\
+	v.ui64 = V;					\
+	nvpair_insert(I, N, &v, KSTAT_DATA_UINT64);	\
+}
+
+#define	SAVE_STRING_X(I, N, V)				\
+{							\
+	ks_value_t v;					\
+	v.str.addr.ptr = safe_strdup(V);		\
+	v.str.len = strlen(V);				\
+	nvpair_insert(I, N, &v, KSTAT_DATA_STRING);	\
+}
+
+#define	SAVE_CHAR_X(I, N, V)				\
+{							\
+	ks_value_t v;					\
+	asprintf(&v.str.addr.ptr, "%c", V);		\
+	v.str.len = 1;					\
+	nvpair_insert(I, N, &v, KSTAT_DATA_STRING);	\
+}
+
+#define	DFLT_FMT					\
+	"module: %-30.30s  instance: %-6d\n"		\
+	"name:   %-30.30s  class:    %-.30s\n"
+
+#define	JSON_FMT					\
+	"{\n\t\"module\": \"%s\",\n"			\
+	"\t\"instance\": %d,\n"				\
+	"\t\"name\": \"%s\",\n"				\
+	"\t\"class\": \"%s\",\n"			\
+	"\t\"type\": %d,\n"
+
+#define	KS_DFMT	"\t%-30s  "
+#define	KS_JFMT	"\t\t\"%s\": "
+#define	KS_PFMT	"%s:%d:%s:%s"
+
+typedef struct ks_instance {
+	list_node_t	ks_next;
+	char		ks_name[KSTAT_STRLEN];
+	char		ks_module[KSTAT_STRLEN];
+	char		ks_class[KSTAT_STRLEN];
+	int 		ks_instance;
+	uchar_t		ks_type;
+	hrtime_t	ks_snaptime;
+	list_t		ks_nvlist;
+} ks_instance_t;
+
+typedef struct ks_nvpair {
+	list_node_t	nv_next;
+	char		name[KSTAT_STRLEN];
+	uchar_t		data_type;
+	ks_value_t	value;
+} ks_nvpair_t;
+
+typedef struct ks_pattern {
+	char		*pstr;
+	regex_t		preg;
+} ks_pattern_t;
+
+typedef struct ks_selector {
+	list_node_t	ks_next;
+	ks_pattern_t	ks_module;
+	ks_pattern_t	ks_instance;
+	ks_pattern_t	ks_name;
+	ks_pattern_t	ks_statistic;
+} ks_selector_t;
+
+static void	usage(void);
+static int	compare_instances(ks_instance_t *, ks_instance_t *);
+static void	nvpair_insert(ks_instance_t *, char *, ks_value_t *, uchar_t);
+static boolean_t	ks_match(const char *, ks_pattern_t *);
+static ks_selector_t	*new_selector(void);
+static void	ks_instances_read(kstat_ctl_t *);
+static void	ks_value_print(ks_nvpair_t *);
+static void	ks_instance_print(ks_instance_t *, ks_nvpair_t *);
+static void	ks_instances_print(void);
+static char	*ks_safe_strdup(char *);
+static void	ks_sleep_until(hrtime_t *, hrtime_t, int, int *);
+
+/* Raw kstat readers */
+static void	save_cpu_stat(kstat_t *, ks_instance_t *);
+static void	save_var(kstat_t *, ks_instance_t *);
+static void	save_ncstats(kstat_t *, ks_instance_t *);
+static void	save_sysinfo(kstat_t *, ks_instance_t *);
+static void	save_vminfo(kstat_t *, ks_instance_t *);
+static void	save_nfs(kstat_t *, ks_instance_t *);
+#ifdef __sparc
+static void	save_sfmmu_global_stat(kstat_t *, ks_instance_t *);
+static void	save_sfmmu_tsbsize_stat(kstat_t *, ks_instance_t *);
+static void	save_simmstat(kstat_t *, ks_instance_t *);
+/* Helper function for save_temperature() */
+static char	*short_array_to_string(short *, int);
+static void	save_temperature(kstat_t *, ks_instance_t *);
+static void	save_temp_over(kstat_t *, ks_instance_t *);
+static void	save_ps_shadow(kstat_t *, ks_instance_t *);
+static void	save_fault_list(kstat_t *, ks_instance_t *);
+#endif
+
+/* Named kstat readers */
+static void	save_named(kstat_t *, ks_instance_t *);
+static void	save_intr(kstat_t *, ks_instance_t *);
+static void	save_io(kstat_t *, ks_instance_t *);
+static void	save_timer(kstat_t *, ks_instance_t *);
+
+/* Typedef for raw kstat reader functions */
+typedef void	(*kstat_raw_reader_t)(kstat_t *, ks_instance_t *);
+
+static struct {
+	kstat_raw_reader_t fn;
+	char *name;
+} ks_raw_lookup[] = {
+	/* Function name		kstat name		*/
+	{save_cpu_stat,			"cpu_stat:cpu_stat"},
+	{save_var,			"unix:var"},
+	{save_ncstats,			"unix:ncstats"},
+	{save_sysinfo,			"unix:sysinfo"},
+	{save_vminfo,			"unix:vminfo"},
+	{save_nfs,			"nfs:mntinfo"},
+#ifdef __sparc
+	{save_sfmmu_global_stat,	"unix:sfmmu_global_stat"},
+	{save_sfmmu_tsbsize_stat,	"unix:sfmmu_tsbsize_stat"},
+	{save_simmstat,			"unix:simm-status"},
+	{save_temperature,		"unix:temperature"},
+	{save_temp_over,		"unix:temperature override"},
+	{save_ps_shadow,		"unix:ps_shadow"},
+	{save_fault_list,		"unix:fault_list"},
+#endif
+	{NULL, NULL},
+};
+
+static kstat_raw_reader_t	lookup_raw_kstat_fn(char *, char *);
+
+#endif /* _STAT_KSTAT_H */
--- a/usr/src/man/man1m/kstat.1m	Fri Jan 25 10:08:50 2013 -0800
+++ b/usr/src/man/man1m/kstat.1m	Wed Jan 09 12:18:53 2013 +0000
@@ -3,20 +3,20 @@
 .\" 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]
-.TH KSTAT 1M "Mar 23, 2009"
+.TH KSTAT 1M "Jan 9, 2013"
 .SH NAME
 kstat \- display kernel statistics
 .SH SYNOPSIS
 .LP
 .nf
-\fBkstat\fR [\fB-lpq\fR] [\fB-T\fR u | d ] [\fB-c\fR \fIclass\fR] [\fB-m\fR \fImodule\fR]
+\fBkstat\fR [\fB-Cjlpq\fR] [\fB-T\fR u | d ] [\fB-c\fR \fIclass\fR] [\fB-m\fR \fImodule\fR]
      [\fB-i\fR \fIinstance\fR] [\fB-n\fR \fIname\fR] [\fB-s\fR \fIstatistic\fR]
      [interval [count]]
 .fi
 
 .LP
 .nf
-\fBkstat\fR [\fB-lpq\fR] [\fB-T\fR u | d ] [\fB-c\fR \fIclass\fR]
+\fBkstat\fR [\fB-Cjlpq\fR] [\fB-T\fR u | d ] [\fB-c\fR \fIclass\fR]
      [\fImodule\fR:\fIinstance\fR:\fIname\fR:\fIstatistic\fR]...
      [interval [count]]
 .fi
@@ -59,11 +59,20 @@
 .sp
 .LP
 The argument for the \fB-c\fR, \fB-i\fR, \fB-m\fR, \fB-n\fR, and \fB-s\fR
-options may be specified as a shell glob pattern, or a Perl regular expression
+options may be specified as a shell glob pattern, or a regular expression
 enclosed in '/' characters.
 .sp
 .ne 2
 .na
+\fB\fB-C\fR\fR
+.ad
+.RS 16n
+Displays output in parseable format with a colon as separator.
+.RE
+
+.sp
+.ne 2
+.na
 \fB\fB-c\fR \fIclass\fR\fR
 .ad
 .RS 16n
@@ -83,6 +92,15 @@
 .sp
 .ne 2
 .na
+\fB\fB-j\fR\fR
+.ad
+.RS 16n
+Displays output in JSON format.
+.RE
+
+.sp
+.ne 2
+.na
 \fB\fB-l\fR\fR
 .ad
 .RS 16n
@@ -161,7 +179,7 @@
 .RS 4n
 Alternate method of specifying module, instance, name, and statistic as
 described above. Each of the module, instance, name, or statistic specifiers
-may be a shell glob pattern or a Perl regular expression enclosed by '/'
+may be a shell glob pattern or a regular expression enclosed by '/'
 characters. It is possible to use both specifier types within a single operand.
 Leaving a specifier empty is equivalent to using the '*' glob pattern for that
 specifier.
@@ -202,7 +220,7 @@
 example$ \fBkstat -p -s 'avenrun*'\fR
 example$ \fBkstat -p 'unix:0:system_misc:avenrun*'\fR
 example$ \fBkstat -p ':::avenrun*'\fR
-example$ \fBkstat -p ':::/^avenrun_\ed+min$/'\fR
+example$ \fBkstat -p ':::/^avenrun_[0-9]+min$/'\fR
 
 unix:0:system_misc:avenrun_15min        3
 unix:0:system_misc:avenrun_1min 4
@@ -362,11 +380,11 @@
 .sp
 .LP
 \fBdate\fR(1), \fBsh\fR(1), \fBtime\fR(2), \fBgmatch\fR(3GEN),
-\fBkstat\fR(3KSTAT), \fBattributes\fR(5), \fBkstat\fR(7D), \fBsd\fR(7D),
-\fBkstat\fR(9S)
+\fBkstat\fR(3KSTAT), \fBattributes\fR(5), \fBregex\fR(5), \fBkstat\fR(7D),
+\fBsd\fR(7D), \fBkstat\fR(9S)
 .SH NOTES
 .sp
 .LP
-If the pattern argument contains glob or Perl RE metacharacters which are also
+If the pattern argument contains glob or RE metacharacters which are also
 shell metacharacters, it will be necessary to enclose the pattern with
 appropriate shell quotes.