changeset 10111:30fced71ad43

6825817 Integrate latencyTOP into OpenSolaris PSARC 2009/339 LatencyTOP for OpenSolaris
author Lejun Zhu <lejun.zhu@intel.com>
date Fri, 17 Jul 2009 09:57:00 -0700
parents ffe1648ab51a
children ca9cb52b0505
files usr/src/Makefile.lint usr/src/cmd/Makefile usr/src/cmd/latencytop/Makefile usr/src/cmd/latencytop/Makefile.com usr/src/cmd/latencytop/amd64/Makefile usr/src/cmd/latencytop/common/display.c usr/src/cmd/latencytop/common/dwrapper.c usr/src/cmd/latencytop/common/klog.c usr/src/cmd/latencytop/common/latencytop.c usr/src/cmd/latencytop/common/latencytop.d usr/src/cmd/latencytop/common/latencytop.h usr/src/cmd/latencytop/common/latencytop.trans usr/src/cmd/latencytop/common/stat.c usr/src/cmd/latencytop/common/table.c usr/src/cmd/latencytop/common/util.c usr/src/cmd/latencytop/i386/Makefile usr/src/cmd/latencytop/sparcv9/Makefile usr/src/pkgdefs/Makefile usr/src/pkgdefs/SUNWlatencytop/Makefile usr/src/pkgdefs/SUNWlatencytop/depend usr/src/pkgdefs/SUNWlatencytop/pkginfo.tmpl usr/src/pkgdefs/SUNWlatencytop/prototype_com usr/src/pkgdefs/SUNWlatencytop/prototype_i386 usr/src/pkgdefs/SUNWlatencytop/prototype_sparc
diffstat 24 files changed, 5499 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/Makefile.lint	Fri Jul 17 16:43:38 2009 +0100
+++ b/usr/src/Makefile.lint	Fri Jul 17 09:57:00 2009 -0700
@@ -231,6 +231,7 @@
 	cmd/pools \
 	cmd/power \
 	cmd/powertop \
+	cmd/latencytop \
 	cmd/ppgsz \
 	cmd/praudit \
 	cmd/prctl \
--- a/usr/src/cmd/Makefile	Fri Jul 17 16:43:38 2009 +0100
+++ b/usr/src/cmd/Makefile	Fri Jul 17 09:57:00 2009 -0700
@@ -227,6 +227,7 @@
 	kstat		\
 	last		\
 	lastcomm	\
+	latencytop	\
 	ldap		\
 	ldapcachemgr	\
 	lgrpinfo	\
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/Makefile	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,47 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+PROG = latencytop
+
+include ../Makefile.cmd
+
+$(64ONLY)SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all	:=	TARGET = all
+install	:=	TARGET = install
+clean	:=	TARGET = clean
+clobber	:=	TARGET = clobber
+lint	:=	TARGET = lint
+
+.KEEP_STATE:
+
+all install clean clobber lint:	$(SUBDIRS)
+
+$(SUBDIRS):	FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/Makefile.com	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,75 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+PROG = latencytop
+OBJS = latencytop.o display.o dwrapper.o klog.o stat.o table.o util.o conststr.o
+SRCS = $(OBJS:%.o=../common/%.c)
+
+include ../../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+CFLAGS64 += $(CCVERBOSE)
+
+CPPFLAGS += -DEMBED_CONFIGS -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
+C99MODE = $(C99_ENABLE)
+LDLIBS += -lcurses -ldtrace
+all install	:= LDLIBS += -lglib-2.0
+
+LINTFLAGS += -erroff=E_NAME_USED_NOT_DEF2
+
+LINTFLAGS64 += -erroff=E_NAME_USED_NOT_DEF2
+
+FILEMODE = 0555
+GROUP = bin
+
+CLEANFILES += $(OBJS)
+
+.KEEP_STATE:
+
+all: dumpcfg $(PROG)
+
+install:        dumpcfg $(SUBDIRS)
+	-$(RM) $(ROOTPROG)
+	-$(LN) $(ISAEXEC) $(ROOTPROG)
+
+$(PROG): $(OBJS)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+dumpcfg:
+	cd ../common; xxd -i latencytop.d >./conststr.c; xxd -i latencytop.trans >>./conststr.c; cd ..
+
+rmcfg:
+	rm -f ../common/conststr.c
+
+clean:	rmcfg
+	$(RM) $(CLEANFILES)
+
+lint:	dumpcfg lint_SRCS
+
+%.o: ../common/%.c
+	$(COMPILE.c) $<
+
+include ../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/amd64/Makefile	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,28 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+include ../Makefile.com
+include ../../Makefile.cmd.64
+
+install: all $(ROOTPROG64)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/display.c	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,998 @@
+/*
+ * 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) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <curses.h>
+#include <time.h>
+#include <wchar.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "latencytop.h"
+
+#define	LT_WINDOW_X			80
+#define	LT_WINDOW_Y			24
+
+#define	LT_COLOR_DEFAULT		1
+#define	LT_COLOR_HEADER			2
+
+/* Windows created by libcurses */
+static WINDOW	*titlebar = NULL;
+static WINDOW	*captionbar = NULL;
+static WINDOW	*sysglobal_window = NULL;
+static WINDOW	*taskbar = NULL;
+static WINDOW	*process_window = NULL;
+static WINDOW	*hintbar = NULL;
+/* Screen dimention */
+static int	screen_width = 1, screen_height = 1;
+/* Is display initialized, i.e. window pointers set up. */
+static int	display_initialized = FALSE;
+/* Is initscr() called */
+static int	curses_inited = FALSE;
+
+/* Changed by user key press */
+static pid_t selected_pid = INVALID_PID;
+static id_t selected_tid = INVALID_TID;
+static lt_sort_t sort_type = LT_SORT_TOTAL;
+static int thread_mode = FALSE;
+/* what kind of list are we showing now */
+static int current_list_type = LT_LIST_CAUSE;
+static int show_help = FALSE;
+
+/* Help functions that append/prepend blank to the string */
+#define	fill_space_right(a, b, c)	fill_space((a), (b), (c), TRUE)
+#define	fill_space_left(a, b, c)	fill_space((a), (b), (c), FALSE)
+
+static void
+fill_space(char *buffer, int len, int buffer_limit, int is_right)
+{
+	int i = 0;
+	int tofill;
+
+	if (len >= buffer_limit) {
+		len = buffer_limit - 1;
+	}
+
+	i = strlen(buffer);
+	if (i >= len) {
+		return;
+	}
+
+	tofill = len - i;
+
+	if (is_right) {
+		(void) memset(&buffer[i], ' ', tofill);
+		buffer[len] = 0;
+	} else	{
+		(void) memmove(&buffer[tofill], buffer, i+1);
+		(void) memset(buffer, ' ', tofill);
+	}
+}
+
+/* Formats a human readable string out of nanosecond value */
+static const char *
+get_time_string(double nanoseconds, char *buffer, int len, int fill_width)
+{
+	const double ONE_USEC = 1000.0;
+	const double ONE_MSEC = 1000000.0;
+	const double ONE_SEC  = 1000000000.0;
+
+	if (nanoseconds < (ONE_USEC - .5)) {
+		(void) snprintf(buffer, len, "%3.1f nsec", nanoseconds);
+	} else if (nanoseconds < (ONE_MSEC - .5 * ONE_USEC)) {
+		(void) snprintf(buffer, len,
+		    "%3.1f usec", nanoseconds / ONE_USEC);
+	} else if (nanoseconds < (ONE_SEC - .5 * ONE_MSEC)) {
+		(void) snprintf(buffer, len,
+		    "%3.1f msec", nanoseconds / ONE_MSEC);
+	} else if (nanoseconds < 999.5 * ONE_SEC) {
+		(void) snprintf(buffer, len,
+		    "%3.1f  sec", nanoseconds / ONE_SEC);
+	} else {
+		(void) snprintf(buffer, len,
+		    "%.0e sec", nanoseconds / ONE_SEC);
+	}
+	fill_space_left(buffer, fill_width, len);
+	return (buffer);
+}
+
+/*
+ * Print statistics in a window.
+ *	IN:	window - the global or process statistics window.
+ *		begin_line - where to start printing.
+ *		count - how many lines should we print.
+ *		list - a stat_list.
+ */
+#define	WIDTH_REASON_STRING	36
+#define	WIDTH_COUNT		12
+#define	WIDTH_SUM		12
+#define	WIDTH_MAX		12
+#define	WIDTH_PCT		8
+#define	BEGIN_COUNT		WIDTH_REASON_STRING
+#define	BEGIN_SUM		(BEGIN_COUNT + WIDTH_COUNT)
+#define	BEGIN_MAX		(BEGIN_SUM + WIDTH_SUM)
+#define	BEGIN_PCT		(BEGIN_MAX + WIDTH_MAX)
+
+static void
+print_statistics(WINDOW * window, int begin_line, int count, void *list)
+{
+	uint64_t total;
+	int i = 0;
+
+	if (!display_initialized) {
+		return;
+	}
+
+	total = lt_stat_list_get_gtotal(list);
+	if (total == 0) {
+		return;
+	}
+
+	while (i < count && lt_stat_list_has_item(list, i)) {
+		/*
+		 * We intentionally make tmp[] hold one character less
+		 * than WIDTH_REASON_STRING, so it will look nice on the
+		 * screen.
+		 */
+		char tmp[WIDTH_REASON_STRING];
+		const char *reason = lt_stat_list_get_reason(list, i);
+		uint64_t count = lt_stat_list_get_count(list, i);
+
+		if (count == 0) {
+			continue;
+		}
+
+		(void) snprintf(tmp, sizeof (tmp), "%s", reason);
+		(void) mvwprintw(window, i + begin_line, 0, "%s", tmp);
+		(void) snprintf(tmp, sizeof (tmp), "%d",
+		    lt_stat_list_get_count(list, i));
+		fill_space_left(tmp, WIDTH_COUNT, sizeof (tmp));
+		(void) mvwprintw(window, i + begin_line, BEGIN_COUNT,
+		    "%s", tmp);
+		(void) mvwprintw(window, i + begin_line, BEGIN_SUM,
+		    "%s", get_time_string(
+		    (double)lt_stat_list_get_sum(list, i) / count,
+		    tmp, sizeof (tmp), WIDTH_SUM));
+		(void) mvwprintw(window, i + begin_line, BEGIN_MAX,
+		    "%s", get_time_string(
+		    (double)lt_stat_list_get_max(list, i),
+		    tmp, sizeof (tmp), WIDTH_MAX));
+		if (LT_LIST_SPECIALS != current_list_type) {
+			(void) snprintf(tmp, sizeof (tmp), "%.1f %%",
+			    (double)lt_stat_list_get_sum(list, i)
+			    / total * 100.0);
+		} else	{
+			(void) snprintf(tmp, sizeof (tmp), "--- ");
+		}
+		fill_space_left(tmp, WIDTH_PCT, sizeof (tmp));
+
+		(void) mvwprintw(window, i + begin_line, BEGIN_PCT,
+		    "%s", tmp);
+		i++;
+	}
+}
+
+/*
+ * Print global statistics. Calls print_statistics().
+ */
+static void
+print_sysglobal(void)
+{
+	void *list;
+	char header[256];
+
+	if (!display_initialized) {
+		return;
+	}
+
+	(void) werase(sysglobal_window);
+
+	(void) wattron(sysglobal_window, A_REVERSE);
+	(void) snprintf(header, sizeof (header),
+	    "%s", lt_text("System wide latencies"));
+	fill_space_right(header, screen_width, sizeof (header));
+	(void) mvwprintw(sysglobal_window, 0, 0, "%s", header);
+	(void) wattroff(sysglobal_window, A_REVERSE);
+
+	list = lt_stat_list_create(current_list_type,
+	    LT_LEVEL_GLOBAL, 0, 0, 10, sort_type);
+	print_statistics(sysglobal_window, 1, 10, list);
+	lt_stat_list_free(list);
+
+	(void) wrefresh(sysglobal_window);
+}
+
+/*
+ * Prints current operation mode: process/thread, window 1/2/3.
+ */
+static void
+print_current_mode()
+{
+	char type;
+
+	if (!display_initialized) {
+		return;
+	}
+
+	switch (current_list_type) {
+	case LT_LIST_CAUSE:
+		type = 'C';
+		break;
+	case LT_LIST_SPECIALS:
+		type = 'S';
+		break;
+	case LT_LIST_SOBJ:
+		type = 'L';
+		break;
+	default:
+		type = '?';
+		break;
+	}
+
+	(void) mvwprintw(process_window, 0, screen_width - 2, "%c%c",
+	    type, thread_mode ? 'T' : 'P');
+}
+
+/*
+ * Print per-process statistics. Calls print_statistics().
+ * This one is used in per-process mode.
+ */
+static void
+print_process(unsigned int pid)
+{
+	void *list;
+	char header[256];
+	char tmp[30];
+
+	if (!display_initialized) {
+		return;
+	}
+
+	list = lt_stat_list_create(current_list_type, LT_LEVEL_PROCESS,
+	    pid, 0, 8, sort_type);
+
+	(void) werase(process_window);
+	(void) wattron(process_window, A_REVERSE);
+
+	(void) snprintf(header, sizeof (header), "Process %s (%i) ",
+	    lt_stat_proc_get_name(pid), pid);
+	fill_space_right(header, screen_width, sizeof (header));
+	(void) mvwprintw(process_window, 0, 0, "%s", header);
+
+	if (current_list_type != LT_LIST_SPECIALS) {
+		(void) mvwprintw(process_window, 0, 40,
+		    lt_text("Total: %s from %d threads"),
+		    get_time_string((double)lt_stat_list_get_gtotal(list),
+		    tmp, sizeof (tmp), 12),
+		    lt_stat_proc_get_nthreads(pid));
+	}
+	print_current_mode();
+
+	(void) wattroff(process_window, A_REVERSE);
+
+	print_statistics(process_window, 1, 8, list);
+	lt_stat_list_free(list);
+
+	(void) wrefresh(process_window);
+}
+
+/*
+ * List all processes in task bar.
+ * This one is used in per-process mode.
+ */
+static void
+print_taskbar_process(pid_t *pidlist, int pidlist_len, int pidlist_index)
+{
+	const int ITEM_WIDTH = 8;
+
+	int number_item;
+	int i;
+	int xpos = 0;
+
+	if (!display_initialized) {
+		return;
+	}
+
+	number_item = (screen_width / ITEM_WIDTH) - 1;
+	i = pidlist_index - (pidlist_index % number_item);
+
+	(void) werase(taskbar);
+	if (i != 0) {
+		(void) mvwprintw(taskbar, 0, xpos, "<-");
+	}
+	xpos = ITEM_WIDTH / 2;
+
+	while (xpos + ITEM_WIDTH <= screen_width && i < pidlist_len) {
+		char str[ITEM_WIDTH+1];
+		int slen;
+		const char *pname = lt_stat_proc_get_name(pidlist[i]);
+
+		if (pname && pname[0]) {
+			(void) snprintf(str, sizeof (str) - 1, "%s", pname);
+		} else	{
+			(void) snprintf(str, sizeof (str) - 1,
+			    "<%d>", pidlist[i]);
+		}
+
+		slen = strlen(str);
+		if (slen < ITEM_WIDTH) {
+			(void) memset(&str[slen], ' ', ITEM_WIDTH - slen);
+		}
+
+		str[sizeof (str) - 1] = 0;
+
+		if (i == pidlist_index) {
+			(void) wattron(taskbar, A_REVERSE);
+		}
+		(void) mvwprintw(taskbar, 0, xpos, "%s", str);
+		if (i == pidlist_index) {
+			(void) wattroff(taskbar, A_REVERSE);
+		}
+
+		xpos += ITEM_WIDTH;
+		i++;
+	}
+
+	if (i != pidlist_len) {
+		(void) mvwprintw(taskbar, 0, screen_width - 2, "->");
+	}
+	(void) wrefresh(taskbar);
+}
+
+/*
+ * List all processes in task bar.
+ * This one is used in per-thread mode.
+ */
+static void
+print_taskbar_thread(pid_t *pidlist, id_t *tidlist, int list_len,
+    int list_index)
+{
+	const int ITEM_WIDTH = 12;
+
+	int number_item;
+	int i;
+	int xpos = 0;
+	const char *pname = NULL;
+	pid_t last_pid = INVALID_PID;
+
+
+	if (!display_initialized) {
+		return;
+	}
+
+	number_item = (screen_width - 8) / ITEM_WIDTH;
+	i = list_index - (list_index % number_item);
+
+	(void) werase(taskbar);
+	if (i != 0) {
+		(void) mvwprintw(taskbar, 0, xpos, "<-");
+	}
+	xpos = 4;
+
+	while (xpos + ITEM_WIDTH <= screen_width && i < list_len) {
+		char str[ITEM_WIDTH+1];
+		int slen, tlen;
+
+		if (pidlist[i] != last_pid) {
+			pname = lt_stat_proc_get_name(pidlist[i]);
+			last_pid = pidlist[i];
+		}
+		/*
+		 * Calculate thread id length, leave enough space by print
+		 * shorter process name.
+		 */
+		tlen = snprintf(NULL, 0, "_%d", tidlist[i]);
+
+		if (pname && pname[0]) {
+			(void) snprintf(str, sizeof (str) - tlen - 1,
+			    "%s", pname);
+		} else	{
+			(void) snprintf(str, sizeof (str) - tlen - 1,
+			    "<%d>", pidlist[i]);
+		}
+		slen = strlen(str);
+
+		(void) snprintf(&str[slen], sizeof (str) - slen,
+		    "_%d", tidlist[i]);
+
+		slen += tlen;
+
+		if (slen < ITEM_WIDTH) {
+			(void) memset(&str[slen], ' ', ITEM_WIDTH - slen);
+		}
+		str[sizeof (str) - 1] = 0;
+
+		if (i == list_index) {
+			(void) wattron(taskbar, A_REVERSE);
+		}
+		(void) mvwprintw(taskbar, 0, xpos, "%s", str);
+		if (i == list_index) {
+			(void) wattroff(taskbar, A_REVERSE);
+		}
+
+		xpos += ITEM_WIDTH;
+		i++;
+	}
+
+	if (i != list_len) {
+		(void) mvwprintw(taskbar, 0, screen_width - 2, "->");
+	}
+	(void) wrefresh(taskbar);
+}
+
+/*
+ * Print statistics. Calls print_statistics().
+ * This one is used in per-thread mode.
+ */
+static void
+print_thread(pid_t pid, id_t tid)
+{
+	void *list;
+	char header[256];
+	char tmp[30];
+
+	if (!display_initialized) {
+		return;
+	}
+
+	list = lt_stat_list_create(current_list_type, LT_LEVEL_THREAD,
+	    pid, tid, 8, sort_type);
+
+	(void) werase(process_window);
+	(void) wattron(process_window, A_REVERSE);
+
+	(void) snprintf(header, sizeof (header),
+	    "Process %s (%i), LWP %d",
+	    lt_stat_proc_get_name(pid), pid, tid);
+	fill_space_right(header, screen_width, sizeof (header));
+	(void) mvwprintw(process_window, 0, 0, "%s", header);
+
+	if (current_list_type != LT_LIST_SPECIALS) {
+		(void) mvwprintw(process_window, 0, 40, lt_text("Total: %s"),
+		    get_time_string(
+		    (double)lt_stat_list_get_gtotal(list),
+		    tmp, sizeof (tmp), 12));
+	}
+	print_current_mode();
+
+	(void) wattroff(process_window, A_REVERSE);
+
+	print_statistics(process_window, 1, 8, list);
+	lt_stat_list_free(list);
+
+	(void) wrefresh(process_window);
+}
+
+/*
+ * Update hint string at the bottom line. The message to print is stored in
+ * hint. If hint is NULL, the function will pick a message from useful tips
+ * and display it.
+ */
+static void
+print_hint(const char *hint)
+{
+	const char *HINTS[] =    {
+		"Press '<' or '>' to switch between processes.",
+		"Press 'q' to exit.",
+		"Press 'r' to refresh immediately.",
+		"Press 't' to toggle Process/Thread display mode.",
+		"Press 'h' for help.",
+		"Use 'c', 'a', 'm', 'p' to change sort criteria."
+		"Use '1', '2', '3' to switch between windows."
+	};
+	const uint64_t update_interval = 5000; /* 5 seconds */
+
+	static int index = 0;
+	static uint64_t next_hint = 0;
+	uint64_t now = lt_millisecond();
+
+	if (!display_initialized) {
+		return;
+	}
+
+	if (hint == NULL) {
+		if (now < next_hint) {
+			return;
+		}
+		hint = HINTS[index];
+		index = (index + 1) % (sizeof (HINTS) / sizeof (HINTS[0]));
+		next_hint = now + update_interval;
+	} else	{
+		/*
+		 * To ensure important message
+		 * show at least 2 cycles.
+		 */
+		next_hint = now + update_interval * 2;
+	}
+
+	(void) werase(hintbar);
+	(void) mvwprintw(hintbar, 0, (screen_width - strlen(hint)) / 2,
+	    "%s", lt_text(hint));
+	(void) wrefresh(hintbar);
+}
+
+/*
+ * Get information from existing statistics, and create a PID list
+ * or PID/TID list based on current display mode.
+ */
+static void
+get_plist(pid_t **plist, id_t **tlist, int *list_len, int *list_index)
+{
+	if (!thread_mode) {
+		/* Per-process mode */
+		*list_len = lt_stat_proc_list_create(plist, NULL);
+
+		/* Search for previous selected PID */
+		for (*list_index = 0; *list_index < *list_len &&
+		    (*plist)[*list_index] != selected_pid;
+		    ++*list_index) {
+		}
+
+		if (*list_index >= *list_len) {
+			/*
+			 * The old selected pid is gone.
+			 * Select the first one
+			 */
+			*list_index = 0;
+		}
+	} else	{
+		/* Per-thread mode */
+		*list_len = lt_stat_proc_list_create(plist, tlist);
+
+		/* Search for previous selected PID & TID */
+		for (*list_index = 0; *list_index < *list_len;
+		    ++*list_index) {
+			if ((*plist)[*list_index] == selected_pid &&
+			    (*tlist)[*list_index] == selected_tid) {
+				break;
+			}
+		}
+
+		if (*list_index >= *list_len) {
+			/*
+			 * The old selected pid/tid is gone.
+			 * Select the first one in the pid
+			 */
+			for (*list_index = 0;
+			    *list_index < *list_len &&
+			    (*plist)[*list_index] != selected_pid;
+			    ++*list_index) {
+			}
+		}
+		if (*list_index >= *list_len) {
+			/*
+			 * The old selected pid is gone.
+			 * Select the first one
+			 */
+			*list_index = 0;
+		}
+	}
+}
+
+static void
+print_help(void)
+{
+	const char *HELP[] =    {
+		TITLE,
+		COPYRIGHT,
+		"",
+		"These single-character commands are available:",
+		"<       - Move to previous process/thread.",
+		">       - Move to next process/thread.",
+		"q       - Exit.",
+		"r       - Refresh.",
+		"t       - Toggle process/thread mode.",
+		"c       - Sort by count.",
+		"a       - Sort by average.",
+		"m       - Sort by maximum.",
+		"p       - Sort by percent.",
+		"1       - Show list by causes.",
+		"2       - Show list of special entries.",
+		"3       - Show list by synchronization objects.",
+		"h       - Show this help.",
+		"",
+		"Press any key to continue..."
+	};
+	int i;
+
+	if (!display_initialized) {
+		return;
+	}
+
+	for (i = 0; i < sizeof (HELP) / sizeof (HELP[0]); ++i) {
+		(void) mvwprintw(stdscr, i, 0, "%s", HELP[i]);
+	}
+	(void) refresh();
+}
+
+/*
+ * Print title on screen
+ */
+static void
+print_title(void)
+{
+	if (!display_initialized) {
+		return;
+	}
+
+	(void) wattrset(titlebar, COLOR_PAIR(LT_COLOR_HEADER));
+	(void) wbkgd(titlebar, COLOR_PAIR(LT_COLOR_HEADER));
+	(void) werase(titlebar);
+
+	(void) mvwprintw(titlebar, 0, (screen_width - strlen(TITLE)) / 2,
+	    "%s", TITLE);
+	(void) wrefresh(titlebar);
+
+	(void) werase(captionbar);
+	(void) mvwprintw(captionbar, 0, 0, "%s", lt_text(
+	    "               Cause                    "
+	    "Count      Average     Maximum   Percent"));
+	(void) wrefresh(captionbar);
+
+	(void) wattrset(hintbar, COLOR_PAIR(LT_COLOR_HEADER));
+	(void) wbkgd(hintbar, COLOR_PAIR(LT_COLOR_HEADER));
+}
+
+/*
+ * Signal handler on terminal resize
+ */
+/* ARGSUSED */
+static void
+on_resize(int sig)
+{
+	lt_gpipe_break("r");
+}
+
+/*
+ * Initialize display part. Screen will be cleared when this function returns.
+ */
+void
+lt_display_init(void)
+{
+	if (display_initialized) {
+		return;
+	}
+
+	/* Window resize signal */
+	(void) signal(SIGWINCH, on_resize);
+
+	/* Initialize curses lib. */
+	(void) initscr();
+	(void) start_color();
+	(void) keypad(stdscr, TRUE);
+	(void) nonl();
+	(void) cbreak();
+	(void) noecho();
+	(void) curs_set(0);
+
+	/* Set up color pairs */
+	(void) init_pair(LT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK);
+	(void) init_pair(LT_COLOR_HEADER, COLOR_BLACK, COLOR_WHITE);
+
+	curses_inited = TRUE;
+
+	getmaxyx(stdscr, screen_height, screen_width);
+	if (screen_width < LT_WINDOW_X || screen_height < LT_WINDOW_Y) {
+		(void) mvwprintw(stdscr, 0, 0, "Terminal size is too small.");
+		(void) mvwprintw(stdscr, 1, 0,
+		    "Please resize it to 80x24 or larger.");
+		(void) mvwprintw(stdscr, 2, 0, "Press q to quit.");
+		(void) refresh();
+		return;
+	}
+
+	/* Setup all windows on screen. */
+	titlebar = subwin(stdscr, 1, screen_width, 0, 0);
+	captionbar = subwin(stdscr, 1, screen_width, 1, 0);
+	sysglobal_window = subwin(stdscr, screen_height / 2 - 1,
+	    screen_width, 2, 0);
+	process_window = subwin(stdscr, screen_height / 2 - 3,
+	    screen_width, screen_height / 2 + 1, 0);
+	taskbar = subwin(stdscr, 1, screen_width, screen_height - 2, 0);
+	hintbar = subwin(stdscr, 1, screen_width, screen_height - 1, 0);
+	(void) werase(stdscr);
+	(void) refresh();
+
+	display_initialized = TRUE;
+
+	print_title();
+}
+
+/*
+ * The event loop. Display data on screen and handles key press. Will return
+ * after "duration" seconds, unless exit or refresh hotkey is pressed.
+ * Return 0 means main() should exit. 1 means to loop again.
+ */
+int
+lt_display_loop(int duration)
+{
+	uint64_t start;
+	int remaining;
+	struct timeval timeout;
+	fd_set read_fd;
+	int need_refresh = TRUE;
+	pid_t *plist = NULL;
+	id_t *tlist = NULL;
+	int list_len = 0;
+	int list_index = 0;
+	int retval = 1;
+	int next_snap;
+	int gpipe;
+
+	start = lt_millisecond();
+	gpipe = lt_gpipe_readfd();
+
+	if (!show_help) {
+		print_hint(NULL);
+		print_sysglobal();
+	}
+	get_plist(&plist, &tlist, &list_len, &list_index);
+
+	for (;;) {
+		if (list_len != 0 && need_refresh && !show_help) {
+			if (!thread_mode) {
+				print_taskbar_process(plist, list_len,
+				    list_index);
+				print_process(plist[list_index]);
+			} else	{
+				print_taskbar_thread(plist, tlist,
+				    list_len, list_index);
+				print_thread(plist[list_index],
+				    tlist[list_index]);
+			}
+		}
+		need_refresh = TRUE;	/* Usually we need refresh. */
+
+		remaining = duration - (int)(lt_millisecond() - start);
+		if (remaining <= 0) {
+			break;
+		}
+		/* Embedded dtrace snap action here. */
+		next_snap = lt_dtrace_work(0);
+		if (next_snap == 0) {
+			/*
+			 * Just did a snap, check again to get time for
+			 * next shot.
+			 */
+			next_snap = lt_dtrace_work(0);
+		}
+		if (next_snap > 0 && remaining > next_snap) {
+			remaining = next_snap;
+		}
+
+		timeout.tv_sec = remaining / 1000;
+		timeout.tv_usec = (remaining % 1000) * 1000;
+		FD_ZERO(&read_fd);
+		FD_SET(0, &read_fd);
+		FD_SET(gpipe, &read_fd);
+
+		/* Wait for keyboard input, or signal from gpipe */
+		if (select(gpipe + 1, &read_fd, NULL, NULL, &timeout) > 0) {
+			int k = 0;
+
+			if (FD_ISSET(gpipe, &read_fd)) {
+				/* data from pipe has priority */
+				char ch;    /* Need this for big-endian */
+				(void) read(gpipe, &ch, 1);
+				k = ch;
+			} else {
+				k = getch();
+			}
+
+			/*
+			 * We check if we need to update hint line whenever we
+			 * get chance.
+			 * NOTE: current implementation depends on
+			 * g_config.snap_interval, but it's OK because it
+			 * doesn't have to be precise.
+			 */
+			print_hint(NULL);
+			/*
+			 * If help is on, and a key press happens,
+			 * we need to clear the help and go on.
+			 */
+			if (show_help) {
+				(void) werase(stdscr);
+				(void) refresh();
+				print_title();
+				print_sysglobal();
+				show_help = FALSE;
+				/* Drop this key and continue */
+				continue;
+			}
+
+			switch (k) {
+			case 'Q':
+			case 'q':
+				retval = 0;
+				goto quit;
+			case 'R':
+			case 'r':
+				lt_display_deinit();
+				lt_display_init();
+				goto quit;
+			case 'H':
+			case 'h':
+				show_help = TRUE;
+				(void) werase(stdscr);
+				(void) refresh();
+				print_help();
+				break;
+			case ',':
+			case '<':
+			case KEY_LEFT:
+				--list_index;
+				if (list_index < 0) {
+					list_index = 0;
+				}
+				break;
+			case '.':
+			case '>':
+			case KEY_RIGHT:
+				++list_index;
+				if (list_index >= list_len) {
+					list_index = list_len - 1;
+				}
+				break;
+			case 'a':
+			case 'A':
+				sort_type = LT_SORT_AVG;
+				print_sysglobal();
+				break;
+			case 'p':
+			case 'P':
+				sort_type = LT_SORT_TOTAL;
+				print_sysglobal();
+				break;
+			case 'm':
+			case 'M':
+				sort_type = LT_SORT_MAX;
+				print_sysglobal();
+				break;
+			case 'c':
+			case 'C':
+				sort_type = LT_SORT_COUNT;
+				print_sysglobal();
+				break;
+			case 't':
+			case 'T':
+				if (plist != NULL) {
+					selected_pid = plist[list_index];
+				}
+				selected_tid = INVALID_TID;
+				thread_mode = !thread_mode;
+				get_plist(&plist, &tlist,
+				    &list_len, &list_index);
+				break;
+			case '1':
+			case '!':
+				current_list_type = LT_LIST_CAUSE;
+				print_sysglobal();
+				break;
+			case '2':
+			case '@':
+				if (g_config.low_overhead_mode) {
+					lt_display_error("Switching mode is "
+					    "not available for '-f low'.");
+				} else {
+					current_list_type = LT_LIST_SPECIALS;
+					print_sysglobal();
+				}
+				break;
+			case '3':
+			case '#':
+				if (g_config.trace_syncobj) {
+					current_list_type = LT_LIST_SOBJ;
+					print_sysglobal();
+				} else if (g_config.low_overhead_mode) {
+					lt_display_error("Switching mode is "
+					    "not available for '-f low'.");
+				} else {
+					lt_display_error("Tracing "
+					    "synchronization objects is "
+					    "disabled.");
+				}
+				break;
+			default:
+				/* Wake up for nothing, no need to refresh */
+				need_refresh = FALSE;
+				break;
+			}
+		} else	{
+			need_refresh = FALSE;
+		}
+	}
+
+quit:
+	if (plist != NULL) {
+		selected_pid = plist[list_index];
+	}
+	if (tlist != NULL) {
+		selected_tid = tlist[list_index];
+	}
+	lt_stat_proc_list_free(plist, tlist);
+
+	return (retval);
+}
+
+/*
+ * Close display part.
+ */
+void
+lt_display_deinit(void)
+{
+	if (curses_inited) {
+		(void) clear();
+		(void) refresh();
+		(void) endwin();
+	}
+
+	titlebar = NULL;
+	captionbar = NULL;
+	sysglobal_window = NULL;
+	taskbar = NULL;
+	process_window = NULL;
+	hintbar = NULL;
+	screen_width = 1;
+	screen_height = 1;
+
+	display_initialized = FALSE;
+	curses_inited = FALSE;
+}
+
+/*
+ * Print error message.
+ */
+/* ARGSUSED */
+void
+lt_display_error(const char *fmt, ...)
+{
+	va_list vl;
+	char tmp[81];
+	int l;
+
+	va_start(vl, fmt);
+	(void) vsnprintf(tmp, sizeof (tmp), fmt, vl);
+	va_end(vl);
+
+	l = strlen(tmp);
+	while (l > 0 && (tmp[l - 1] == '\n' || tmp[l - 1] == '\r')) {
+		tmp[l - 1] = 0;
+		--l;
+	}
+
+	if (!display_initialized) {
+		(void) printf("%s\n", tmp);
+	} else if (!show_help) {
+		print_hint(tmp);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/dwrapper.c	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,548 @@
+/*
+ * 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) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <dtrace.h>
+#include <string.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <limits.h>
+
+#include "latencytop.h"
+
+static dtrace_hdl_t *g_dtp = NULL;	/* The dtrace handle */
+static pid_t pid_self = -1;		/* PID of our own process */
+
+/*
+ * Checks if the process is latencytop itself or sched (if we are not tracing
+ * sched), we should ignore them.
+ */
+#define	SHOULD_IGNORE(pid)		\
+	((!g_config.trace_sched && 0 == (pid)) || pid_self == (pid))
+
+/*
+ * Get an integer value from dtrace record.
+ */
+static uint64_t
+rec_get_value(void *a, size_t b)
+{
+	uint64_t ret = 0;
+
+	switch (b) {
+	case sizeof (uint64_t):
+		ret = *((uint64_t *)(a));
+		break;
+	case sizeof (uint32_t):
+		ret = *((uint32_t *)(a));
+		break;
+	case sizeof (uint16_t):
+		ret = *((uint16_t *)(a));
+		break;
+	case sizeof (uint8_t):
+		ret = *((uint8_t *)(a));
+		break;
+	default:
+		break;
+	}
+
+	return (ret);
+}
+
+/*
+ * Callback to process each aggregation in the snapshot.
+ * This one processes lt_call_*, which contains on/off cpu activites.
+ */
+static int
+aggwalk_call(const dtrace_aggdata_t *data, lt_stat_type_t stat_type)
+{
+	const int REC_PID = 1;
+	const int REC_TID = 2;
+	const int REC_STACK = 3;
+	const int REC_AGG = 4;
+	const int NREC = 5;
+
+	dtrace_aggdesc_t *aggdesc = data->dtada_desc;
+	dtrace_syminfo_t dts;
+	GElf_Sym sym;
+	caddr_t addr;
+	pid_t pid;
+	id_t tid;
+	unsigned int stack_depth;
+	unsigned int pc_size;
+	uint64_t pc;
+	uint64_t agg_value;
+	char *ptr = NULL;
+	char *buffer = NULL;
+	int ptrsize;
+	unsigned int buffersize;
+
+	if (aggdesc->dtagd_nrecs < NREC) {
+		/* Not enough records */
+		goto err;
+	}
+
+	if (aggdesc->dtagd_rec[REC_PID].dtrd_action != DTRACEACT_DIFEXPR) {
+		/* Record is not PID, this is an error. */
+		goto err;
+	}
+	pid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_PID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_PID].dtrd_size);
+	if (SHOULD_IGNORE(pid)) {
+		goto done;
+	}
+
+	if (aggdesc->dtagd_rec[REC_TID].dtrd_action != DTRACEACT_DIFEXPR) {
+		/* Record is not TID, this is an error. */
+		goto err;
+	}
+	tid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_TID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_TID].dtrd_size);
+
+	if (aggdesc->dtagd_rec[REC_STACK].dtrd_action != DTRACEACT_STACK) {
+		/* Record is not stack(), this is an error. */
+		goto err;
+	}
+
+	/* Parse stack array from dtagd_rec */
+	stack_depth = aggdesc->dtagd_rec[REC_STACK].dtrd_arg;
+	pc_size = aggdesc->dtagd_rec[REC_STACK].dtrd_size / stack_depth;
+	addr = data->dtada_data + aggdesc->dtagd_rec[REC_STACK].dtrd_offset;
+	buffersize = (stack_depth * (2 * PATH_MAX + 2) + 1) * sizeof (char);
+	buffer = (char *)lt_malloc(buffersize);
+	ptr = buffer;
+	ptrsize = buffersize;
+
+	/* Print the stack */
+	while (stack_depth > 0) {
+		pc = rec_get_value(addr, pc_size);
+		if (pc == 0) {
+			break;
+		}
+		addr += pc_size;
+		if (dtrace_lookup_by_addr(g_dtp, pc, &sym, &dts) == 0) {
+			int len;
+			len = snprintf(ptr, ptrsize,
+			    "%s`%s ", dts.dts_object, dts.dts_name);
+			ptrsize -= len;
+			if (ptrsize <= 0) {
+				/*
+				 * Snprintf returns "desired" length, so
+				 * reaching here means our buffer is full.
+				 * Move ptr to last byte in the buffer and
+				 * break early.
+				 */
+				ptr = &buffer[buffersize-1];
+				break;
+			} else	{
+				ptr += len;
+			}
+		}
+	}
+
+	if (ptr != buffer) {
+		/*
+		 * We have printed something,
+		 * so it is safe to remove last ' '.
+		 */
+		*(ptr-1) = 0;
+	}
+
+	/* Parsing aggregation data */
+	if (!DTRACEACT_ISAGG(aggdesc->dtagd_rec[REC_AGG].dtrd_action)) {
+		/* Record is not aggregation, this is an error. */
+		goto err;
+	}
+	agg_value = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_AGG].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_AGG].dtrd_size);
+
+	lt_stat_update(pid, tid, buffer, stat_type, agg_value);
+
+done:
+	if (buffer != NULL) {
+		free(buffer);
+	}
+	return (0);
+
+err:
+	if (buffer != NULL) {
+		free(buffer);
+	}
+	return (-1);
+}
+
+/*
+ * Callback to process each aggregation in the snapshot.
+ * This one processes lt_named_*, which contains data such as lock spinning.
+ */
+static int
+aggwalk_named(const dtrace_aggdata_t *data, lt_stat_type_t stat_type)
+{
+	const int REC_PID = 1;
+	const int REC_TID = 2;
+	const int REC_TYPE = 3;
+	const int REC_AGG = 4;
+	const int NREC = 5;
+
+	dtrace_aggdesc_t *aggdesc = data->dtada_desc;
+	pid_t pid;
+	id_t tid;
+	uint64_t agg_value;
+	int cause_id;
+	char *type = NULL;
+
+	if (aggdesc->dtagd_nrecs < NREC) {
+		/* Not enough records */
+		return (-1);
+	}
+
+	if (aggdesc->dtagd_rec[REC_PID].dtrd_action != DTRACEACT_DIFEXPR) {
+		/* Record is not PID, this is an error. */
+		return (-1);
+	}
+	pid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_PID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_PID].dtrd_size);
+	if (SHOULD_IGNORE(pid)) {
+		return (0);
+	}
+	if (aggdesc->dtagd_rec[REC_TID].dtrd_action != DTRACEACT_DIFEXPR) {
+		/* Record is not TID, this is an error. */
+		return (-1);
+	}
+	tid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_TID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_TID].dtrd_size);
+
+	if (aggdesc->dtagd_rec[REC_TYPE].dtrd_action != DTRACEACT_DIFEXPR) {
+		/* Record is not type, this is an error. */
+		return (-1);
+	}
+	type = (char *)data->dtada_data
+	    + aggdesc->dtagd_rec[REC_TYPE].dtrd_offset;
+	cause_id = lt_table_lookup_named_cause(type, 1);
+
+	/* Parsing aggregation data */
+	if (!DTRACEACT_ISAGG(aggdesc->dtagd_rec[REC_AGG].dtrd_action)) {
+		/* Record is not aggregation, this is an error. */
+		return (-1);
+	}
+	agg_value = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_AGG].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_AGG].dtrd_size);
+
+	lt_stat_update_cause(pid, tid, cause_id, stat_type, agg_value);
+
+	return (0);
+
+}
+
+/*
+ * Callback to process each aggregation in the snapshot.
+ * This one processes lt_sync_*, which traces synchronization objects.
+ */
+static int
+aggwalk_sync(const dtrace_aggdata_t *data, lt_stat_type_t stat_type)
+{
+	const int REC_PID = 1;
+	const int REC_TID = 2;
+	const int REC_STYPE = 3;
+	const int REC_WCHAN = 4;
+	const int REC_AGG = 5;
+	const int NREC = 6;
+
+	dtrace_aggdesc_t *aggdesc = data->dtada_desc;
+	pid_t pid;
+	id_t tid;
+	uint64_t agg_value;
+	int stype;
+	unsigned long long wchan;
+
+	if (aggdesc->dtagd_nrecs < NREC) {
+		/* Not enough records */
+		return (-1);
+	}
+
+	if (aggdesc->dtagd_rec[REC_PID].dtrd_action != DTRACEACT_DIFEXPR) {
+		/* Record is not PID, this is an error. */
+		return (-1);
+	}
+	pid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_PID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_PID].dtrd_size);
+	if (SHOULD_IGNORE(pid)) {
+		return (0);
+	}
+
+	if (aggdesc->dtagd_rec[REC_TID].dtrd_action != DTRACEACT_DIFEXPR) {
+		/* Record is not TID, this is an error. */
+		return (-1);
+	}
+	tid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_TID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_TID].dtrd_size);
+
+	if (aggdesc->dtagd_rec[REC_STYPE].dtrd_action != DTRACEACT_DIFEXPR) {
+		/* Record is not stype, this is an error. */
+		return (-1);
+	}
+	stype = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_STYPE].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_STYPE].dtrd_size);
+
+	if (aggdesc->dtagd_rec[REC_WCHAN].dtrd_action != DTRACEACT_DIFEXPR) {
+		/* Record is not wchan, this is an error. */
+		return (-1);
+	}
+	wchan = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_WCHAN].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_WCHAN].dtrd_size);
+
+	/* Parsing aggregation data */
+	if (!DTRACEACT_ISAGG(aggdesc->dtagd_rec[REC_AGG].dtrd_action)) {
+		/* Record is not aggregation, this is an error. */
+		return (-1);
+	}
+	agg_value = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_AGG].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_AGG].dtrd_size);
+
+	lt_stat_update_sobj(pid, tid, stype, wchan, stat_type, agg_value);
+
+	return (0);
+}
+
+/*
+ * Callback to process each aggregation in the snapshot.
+ * This one dispatches to different aggwalk_*().
+ */
+/* ARGSUSED */
+static int
+aggwalk(const dtrace_aggdata_t *data, void *arg)
+{
+	char *tmp;
+	char buffer[32];
+	lt_stat_type_t stat_type = LT_STAT_COUNT;
+	int (*func)(const dtrace_aggdata_t *, lt_stat_type_t);
+
+	(void) strncpy(buffer, data->dtada_desc->dtagd_name, sizeof (buffer));
+	buffer[sizeof (buffer) - 1] = 0;
+
+	tmp = strtok(buffer, "_");
+	if (strcmp(tmp, "lt") != 0) {
+		goto done;
+	}
+
+	tmp = strtok(NULL, "_");
+	if (strcmp(tmp, "call") == 0) {
+		func = aggwalk_call;
+	} else if (strcmp(tmp, "named") == 0) {
+		func = aggwalk_named;
+	} else if (strcmp(tmp, "sync") == 0) {
+		func = aggwalk_sync;
+	} else {
+		goto done;
+	}
+
+	tmp = strtok(NULL, "_");
+	if (strcmp(tmp, "count") == 0) {
+		stat_type = LT_STAT_COUNT;
+	} else if (strcmp(tmp, "sum") == 0) {
+		stat_type = LT_STAT_SUM;
+	} else if (strcmp(tmp, "max") == 0) {
+		stat_type = LT_STAT_MAX;
+	} else {
+		goto done;
+	}
+
+	(void) func(data, stat_type);
+
+done:
+	/* We have our data, remove it from DTrace. */
+	return (DTRACE_AGGWALK_REMOVE);
+}
+
+/*
+ * Callback to handle DTrace drop data events.
+ */
+/*ARGSUSED*/
+static int
+drop_handler(const dtrace_dropdata_t *data, void *user)
+{
+	lt_display_error("Drop: %s\n", data->dtdda_msg);
+	/*
+	 * Pretend nothing happened. So our program can continue.
+	 */
+	return (DTRACE_HANDLE_OK);
+}
+
+/*
+ * DTrace initialization. The D script is running when this function returns.
+ */
+int
+lt_dtrace_init(void)
+{
+	dtrace_prog_t *prog;
+	dtrace_proginfo_t info;
+	int err;
+	FILE *fp_script = NULL;
+
+	pid_self = getpid();
+	/* Open dtrace, set up handler */
+	g_dtp = dtrace_open(DTRACE_VERSION, 0, &err);
+	if (g_dtp == NULL) {
+		lt_display_error("Cannot open dtrace library: %s\n",
+		    dtrace_errmsg(NULL, err));
+		return (-1);
+	}
+
+	if (dtrace_handle_drop(g_dtp, &drop_handler, NULL) == -1) {
+		lt_display_error("Cannot install DTrace handle: %s\n",
+		    dtrace_errmsg(NULL, err));
+		return (-1);
+	}
+
+	/* Load D script, set up macro and compile */
+#ifdef EMBED_CONFIGS
+	/* Create a temp file because libdtrace use cpp(1) on files only. */
+	fp_script = tmpfile();
+	if (fp_script == NULL) {
+		lt_display_error("Cannot create tmp file\n");
+		return (-1);
+	}
+	(void) fwrite(latencytop_d, latencytop_d_len, 1, fp_script);
+	(void) fseek(fp_script, 0, SEEK_SET);
+#else
+	fp_script = fopen(DEFAULT_D_SCRIPT_NAME, "r");
+	if (fp_script == NULL) {
+		lt_display_error("Cannot open script file %s\n",
+		    DEFAULT_D_SCRIPT_NAME);
+		return (-1);
+	}
+#endif	/* EMBED_CONFIGS */
+
+	if (g_config.enable_filter) {
+		(void) dtrace_setopt(g_dtp, "define", "ENABLE_FILTER");
+	}
+	if (g_config.trace_syncobj) {
+		(void) dtrace_setopt(g_dtp, "define", "ENABLE_SYNCOBJ");
+	}
+	if (g_config.trace_sched) {
+		(void) dtrace_setopt(g_dtp, "define", "ENABLE_SCHED");
+	}
+	if (g_config.low_overhead_mode) {
+		(void) dtrace_setopt(g_dtp, "define", "ENABLE_LOW_OVERHEAD");
+	}
+
+	prog = dtrace_program_fcompile(g_dtp, fp_script,
+	    DTRACE_C_CPP, 0, NULL);
+	(void) fclose(fp_script);
+	if (prog == NULL) {
+		lt_display_error("Failed to compile D script.\n");
+		return (dtrace_errno(g_dtp));
+	}
+
+	/* Execute the D script */
+	if (dtrace_program_exec(g_dtp, prog, &info) == -1) {
+		lt_display_error("Failed to enable probes.\n");
+		return (dtrace_errno(g_dtp));
+	}
+	if (dtrace_go(g_dtp) != 0) {
+		lt_display_error("Failed to run D script.\n");
+		return (dtrace_errno(g_dtp));
+	}
+	return (0);
+}
+
+/*
+ * Worker function to move aggregator data to user space.
+ * Needs to be called periodically to prevent running out of kernel memory.
+ */
+int
+lt_dtrace_work(int force)
+{
+	static uint64_t last_snap = 0;
+	uint64_t now = lt_millisecond();
+
+	if (!force && now - last_snap < g_config.snap_interval) {
+		return (last_snap + g_config.snap_interval - now);
+	}
+
+	if (dtrace_status(g_dtp) == -1) {
+		lt_display_error("Failed when getting status: %s\n",
+		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
+		return (-1);
+	}
+
+	if (dtrace_aggregate_snap(g_dtp) != 0) {
+		lt_display_error("Failed to snap aggregate: %s\n",
+		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
+		return (-1);
+	}
+
+	last_snap = now;
+	return (0);
+}
+
+/*
+ * Walk through aggregator and collect data to LatencyTOP.
+ * Different from lt_dtrace_work, this one moves data from libdtrace
+ * to latencytop.
+ * This needs to be called immediately before update UI.
+ */
+int
+lt_dtrace_collect(void)
+{
+	if (lt_dtrace_work(1) != 0) {
+		return (-1);
+	}
+
+	if (dtrace_aggregate_walk(g_dtp, aggwalk, NULL) != 0) {
+		lt_display_error("Failed to sort aggregate: %s\n",
+		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
+		return (-1);
+	}
+
+	/*
+	 * Probably no need to clear again, because we removed everything.
+	 * Paranoid.
+	 */
+	dtrace_aggregate_clear(g_dtp);
+
+	return (0);
+}
+
+/*
+ * Clean up and close DTrace.
+ */
+void
+lt_dtrace_deinit(void)
+{
+	(void) dtrace_stop(g_dtp);
+	dtrace_close(g_dtp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/klog.c	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,231 @@
+/*
+ * 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) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+#include <procfs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include "latencytop.h"
+
+static GHashTable *proc_table = NULL;    /* pid -> char * */
+static GHashTable *klog_table = NULL;    /* char * -> uint64_t total */
+static char klog_filename[PATH_MAX] = DEFAULT_KLOG_FILE;
+static int klog_level = LT_KLOG_LEVEL_NONE;
+
+static void
+print_proc(void *key, const char *args, FILE *fp)
+{
+	pid_t pid = LT_POINTER_TO_INT(key);
+	char tmp[16];
+
+	(void) snprintf(tmp, sizeof (tmp), "%ld,", (long)pid);
+	(void) fprintf(fp, "%-8s \"%s\"\n", tmp, args);
+}
+
+static void
+print_stat(const char *key, lt_stat_data_t *log, FILE *fp)
+{
+	(void) fprintf(fp, "%lld, %lld, %lld, %s\n",
+	    (long long)log->total,
+	    (long long)log->count,
+	    (long long)log->max,
+	    key);
+}
+
+/*
+ * Initialize kernel stack logging.
+ */
+void
+lt_klog_init(void)
+{
+	if (klog_table != NULL || proc_table != NULL) {
+		return;
+	}
+
+	klog_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+	    (GDestroyNotify)free, (GDestroyNotify)free);
+	lt_check_null(klog_table);
+
+	proc_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+	    NULL, (GDestroyNotify)free);
+	lt_check_null(proc_table);
+}
+
+/*
+ * Set log file path.
+ */
+int
+lt_klog_set_log_file(const char *filename)
+{
+	FILE *fp;
+	int file_exist;
+
+	if (strlen(filename) >= sizeof (klog_filename)) {
+		return (-1);
+	}
+
+	file_exist = lt_file_exist(filename);
+	/* Test if we can write to the file */
+	fp = fopen(filename, "a");
+	if (fp == NULL) {
+		return (-2);
+	}
+	(void) fclose(fp);
+	/* Don't leave empty file behind */
+	if (!file_exist) {
+		(void) unlink(filename);
+	}
+
+	(void) strncpy(klog_filename, filename,
+	    sizeof (klog_filename));
+
+	return (0);
+}
+
+/*
+ * Set log level.
+ */
+int
+lt_klog_set_log_level(int level)
+{
+	if (level < 0 || level > (int)LT_KLOG_LEVEL_ALL) {
+		return (-1);
+	}
+
+	klog_level = level;
+
+	return (0);
+}
+
+/*
+ * Write the log to file.
+ */
+void
+lt_klog_write(void)
+{
+	FILE *fp;
+	char buffer[32];
+
+	if (klog_level == LT_KLOG_LEVEL_NONE) {
+		return;
+	}
+
+	g_assert(klog_table != NULL && proc_table != NULL);
+
+	fp = fopen(klog_filename, "a");
+	if (fp == NULL) {
+		return;
+	}
+
+	lt_time_str(buffer, sizeof (buffer));
+
+	(void) fprintf(fp, "# Log generated %s by %s\n", buffer, TITLE);
+	(void) fprintf(fp, "# List of processes\n");
+	(void) fprintf(fp, "PID, CMD\n");
+	g_hash_table_foreach(proc_table,
+	    (GHFunc)print_proc, fp);
+
+	(void) fprintf(fp, "# Statistics\n");
+	(void) fprintf(fp, "TOTAL, COUNT, MAX, PID, KSTACK\n");
+	g_hash_table_foreach(klog_table,
+	    (GHFunc)print_stat, fp);
+
+	(void) fclose(fp);
+}
+
+/*
+ * Clean up function. This will cause all log in memory be written to the
+ * log file.
+ */
+void
+lt_klog_deinit(void)
+{
+	if (klog_table != NULL) {
+		g_hash_table_destroy(klog_table);
+		klog_table = NULL;
+	}
+
+	if (proc_table != NULL) {
+		g_hash_table_destroy(proc_table);
+		proc_table = NULL;
+	}
+}
+
+/*
+ * Log a stack and its statistics. Only "total" will be logged, others are
+ * internally discarded.
+ */
+/* ARGSUSED */
+void
+lt_klog_log(int level, pid_t pid, char *stack,
+	lt_stat_type_t type, uint64_t value)
+{
+	lt_stat_data_t *entry = NULL;
+	char *psargs;
+	char *str;
+	int str_len;
+
+	if ((level & klog_level) == 0) {
+		return;
+	}
+	g_assert(klog_table != NULL && proc_table != NULL);
+
+	psargs = (char *)g_hash_table_lookup(proc_table,
+	    LT_INT_TO_POINTER(pid));
+	if (psargs == NULL) {
+		psargs = lt_get_proc_field(pid, LT_FIELD_PSARGS);
+		if (psargs == NULL) {
+			psargs = lt_get_proc_field(pid, LT_FIELD_FNAME);
+		}
+
+		if (psargs == NULL) {
+			return;
+		}
+
+		g_hash_table_insert(proc_table,
+		    LT_INT_TO_POINTER(pid), psargs);
+	}
+
+	str_len = strlen(stack) + 20;
+	str = lt_malloc(str_len);
+	(void) snprintf(str, str_len, "%ld, \"%s\"", pid, stack);
+
+	entry = (lt_stat_data_t *)g_hash_table_lookup(klog_table, str);
+	if (entry == NULL) {
+		entry = (lt_stat_data_t *)lt_zalloc(sizeof (lt_stat_data_t));
+		g_hash_table_insert(klog_table, str, entry);
+	} else	{
+		free(str);
+	}
+
+	lt_update_stat_value(entry, type, value);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/latencytop.c	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,362 @@
+/*
+ * 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) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <unistd.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <libgen.h>
+#include <signal.h>
+#include "latencytop.h"
+
+#define	CMPOPT(a, b)	strncmp((a), (b), sizeof (b))
+
+lt_config_t g_config;
+
+/*
+ * Prints help for command line parameters.
+ */
+static void
+print_usage(const char *execname)
+{
+	char buffer[PATH_MAX];
+	(void) snprintf(buffer, sizeof (buffer), "%s", execname);
+	(void) fprintf(stderr, "\nUsage: %s [option(s)]\n", basename(buffer));
+	(void) fprintf(stderr, "Options:\n"
+	    "    -h, --help\n"
+	    "        Print this help.\n"
+	    "    -t, --interval TIME\n"
+	    "        Set refresh interval to TIME. "
+	    "Valid range [1...60] seconds, default = 5\n"
+	/*
+	 * Keep this option private, until we have chance to properly document
+	 * the format of translation rules.
+	 */
+#if 0
+	    "    -c, --config FILE\n"
+	    "        Use translation rules defined in FILE.\n"
+#endif
+	    "    -o, --output-log-file FILE\n"
+	    "        Output kernel log to FILE. Default = "
+	    DEFAULT_KLOG_FILE "\n"
+	    "    -k, --kernel-log-level LEVEL\n"
+	    "        Set kernel log level to LEVEL.\n"
+	    "        0(default) = None, 1 = Unmapped, 2 = Mapped, 3 = All.\n"
+	    "    -f, --feature [no]feature1,[no]feature2,...\n"
+	    "        Enable/disable features in LatencyTOP.\n"
+	    "        [no]filter:\n"
+	    "        Filter large interruptible latencies, e.g. sleep.\n"
+	    "        [no]sched:\n"
+	    "        Monitors sched (PID=0).\n"
+	    "        [no]sobj:\n"
+	    "        Monitors synchronization objects.\n"
+	    "        [no]low:\n"
+	    "        Lower overhead by sampling small latencies.\n"
+	    "    -l, --log-period TIME\n"
+	    "        Write and restart log every TIME seconds, TIME > 60s\n");
+}
+
+/*
+ * Properly shut down when latencytop receives SIGINT or SIGTERM.
+ */
+/* ARGSUSED */
+static void
+signal_handler(int sig)
+{
+	lt_gpipe_break("q");
+}
+
+/*
+ * Convert string to integer, return error if extra characters are found.
+ */
+static int
+to_int(const char *str, int *result)
+{
+	char *tail = NULL;
+	long ret;
+
+	if (str == NULL || result == NULL) {
+		return (-1);
+	}
+
+	ret = strtol(str, &tail, 10);
+
+	if (tail != NULL && *tail != '\0') {
+		return (-1);
+	}
+
+	*result = (int)ret;
+
+	return (0);
+}
+
+/*
+ * The main function.
+ */
+int
+main(int argc, char *argv[])
+{
+	const char *opt_string = "t:o:k:hf:l:c:";
+	struct option const longopts[] =
+	{
+		{"interval", required_argument, NULL, 't'},
+		{"output-log-file", required_argument, NULL, 'o'},
+		{"kernel-log-level", required_argument, NULL, 'k'},
+		{"help", no_argument, NULL, 'h'},
+		{"feature", required_argument, NULL, 'f'},
+		{"log-period", required_argument, NULL, 'l'},
+		{"config", required_argument, NULL, 'c'},
+		{NULL, 0, NULL, 0}
+	};
+
+	int optc;
+	int longind = 0;
+	int running = 1;
+	int unknown_option = FALSE;
+	int refresh_interval = 5;
+	int klog_level = 0;
+	int log_interval = 0;
+	long long last_logged = 0;
+	char *token = NULL;
+	int retval = 0;
+	int gpipe;
+	int err;
+	uint64_t collect_end;
+	uint64_t current_time;
+	uint64_t delta_time;
+
+	lt_gpipe_init();
+	(void) signal(SIGINT, signal_handler);
+	(void) signal(SIGTERM, signal_handler);
+
+	(void) printf("%s\n%s\n", TITLE, COPYRIGHT);
+
+	/* Default global settings */
+	g_config.enable_filter = 0;
+	g_config.trace_sched = 0;
+	g_config.trace_syncobj = 1;
+	g_config.low_overhead_mode = 0;
+	g_config.snap_interval = 1000;	/* DTrace snapshot every 1 sec */
+#ifdef EMBED_CONFIGS
+	g_config.config_name = NULL;
+#else
+	g_config.config_name = lt_strdup(DEFAULT_CONFIG_NAME);
+#endif
+
+	/* Parse command line arguments. */
+	while ((optc = getopt_long(argc, argv, opt_string,
+	    longopts, &longind)) != -1) {
+		switch (optc) {
+		case 'h':
+			print_usage(argv[0]);
+			goto end_none;
+		case 't':
+			if (to_int(optarg, &refresh_interval) != 0 ||
+			    refresh_interval < 1 || refresh_interval > 60) {
+				lt_display_error(
+				    "Invalid refresh interval: %s\n", optarg);
+				unknown_option = TRUE;
+			}
+			break;
+		case 'k':
+			if (to_int(optarg, &klog_level) != 0 ||
+			    lt_klog_set_log_level(klog_level) != 0) {
+				lt_display_error(
+				    "Invalid log level: %s\n", optarg);
+				unknown_option = TRUE;
+			}
+			break;
+		case 'o':
+			err = lt_klog_set_log_file(optarg);
+			if (err == 0) {
+				(void) printf("Writing to log file %s.\n",
+				    optarg);
+			} else if (err == -1) {
+				lt_display_error(
+				    "Log file name is too long: %s\n",
+				    optarg);
+				unknown_option = TRUE;
+			} else if (err == -2) {
+				lt_display_error(
+				    "Cannot write to log file: %s\n",
+				    optarg);
+				unknown_option = TRUE;
+			}
+			break;
+		case 'f':
+			for (token = strtok(optarg, ","); token != NULL;
+			    token = strtok(NULL, ",")) {
+				int v = TRUE;
+				if (strncmp(token, "no", 2) == 0) {
+					v = FALSE;
+					token = &token[2];
+				}
+				if (CMPOPT(token, "filter") == 0) {
+					g_config.enable_filter = v;
+				} else if (CMPOPT(token, "sched") == 0) {
+					g_config.trace_sched = v;
+				} else if (CMPOPT(token, "sobj") == 0) {
+					g_config.trace_syncobj = v;
+				} else if (CMPOPT(token, "low") == 0) {
+					g_config.low_overhead_mode = v;
+				} else {
+					lt_display_error(
+					    "Unknown feature: %s\n", token);
+					unknown_option = TRUE;
+				}
+			}
+			break;
+		case 'l':
+			if (to_int(optarg, &log_interval) != 0 ||
+			    log_interval < 60) {
+				lt_display_error(
+				    "Invalid refresh interval: %s\n", optarg);
+				unknown_option = TRUE;
+			}
+			break;
+		case 'c':
+			if (strlen(optarg) > PATH_MAX) {
+				lt_display_error(
+				    "Configuration name is too long.\n");
+				unknown_option = TRUE;
+			} else {
+				g_config.config_name = lt_strdup(optarg);
+			}
+			break;
+		default:
+			unknown_option = TRUE;
+			break;
+		}
+	}
+
+	/* Throw error for commands like: "latencytop 12345678" */
+	if (optind  < argc) {
+		int tmpind = optind;
+		(void) printf("Unknown option(s): ");
+		while (tmpind < argc) {
+			(void) printf("%s ", argv[tmpind++]);
+		}
+		(void) printf("\n");
+		unknown_option = TRUE;
+	}
+
+	if (unknown_option) {
+		print_usage(argv[0]);
+		retval = 1;
+		goto end_none;
+	}
+
+	/* Initialization */
+	lt_klog_init();
+	if (lt_table_init() != 0) {
+		lt_display_error("Unable to load configuration table.\n");
+		retval = 1;
+		goto end_notable;
+	}
+	if (lt_dtrace_init() != 0) {
+		lt_display_error("Unable to initialize dtrace.\n");
+		retval = 1;
+		goto end_nodtrace;
+	}
+
+	last_logged = lt_millisecond();
+
+	(void) printf("Collecting data for %d seconds...\n",
+	    refresh_interval);
+
+	gpipe = lt_gpipe_readfd();
+	collect_end = last_logged + refresh_interval * 1000;
+	for (;;) {
+		fd_set read_fd;
+		struct timeval timeout;
+		int tsleep = collect_end - lt_millisecond();
+
+		if (tsleep <= 0) {
+			break;
+		}
+
+		if (tsleep > g_config.snap_interval * 1000) {
+			tsleep = g_config.snap_interval * 1000;
+		}
+
+		timeout.tv_sec = tsleep / 1000;
+		timeout.tv_usec = (tsleep % 1000) * 1000;
+
+		FD_ZERO(&read_fd);
+		FD_SET(gpipe, &read_fd);
+		if (select(gpipe + 1, &read_fd, NULL, NULL, &timeout) > 0) {
+			goto end_ubreak;
+		}
+
+		(void) lt_dtrace_work(0);
+	}
+
+	lt_display_init();
+
+	do {
+		current_time = lt_millisecond();
+
+		lt_stat_clear_all();
+		(void) lt_dtrace_collect();
+
+		delta_time = current_time;
+		current_time = lt_millisecond();
+		delta_time = current_time - delta_time;
+
+		if (log_interval > 0 &&
+		    current_time - last_logged > log_interval * 1000) {
+			lt_klog_write();
+			last_logged = current_time;
+		}
+
+		running = lt_display_loop(refresh_interval * 1000 -
+		    delta_time);
+	} while (running != 0);
+
+	lt_klog_write();
+
+	/* Cleanup */
+	lt_display_deinit();
+
+end_ubreak:
+	lt_dtrace_deinit();
+	lt_stat_free_all();
+
+end_nodtrace:
+	lt_table_deinit();
+
+end_notable:
+	lt_klog_deinit();
+
+end_none:
+	lt_gpipe_deinit();
+	if (g_config.config_name != NULL) {
+		free(g_config.config_name);
+	}
+
+	return (retval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/latencytop.d	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,305 @@
+/*
+ * 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) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#pragma D option aggsize=8m
+#pragma D option bufsize=16m
+#pragma D option dynvarsize=16m
+#pragma D option aggrate=0
+#pragma D option stackframes=64
+
+#if defined(ENABLE_SCHED)
+#define TRACE_FILTER
+#define TRACE_FILTER_COND(a)	/ (a) /
+#else
+#define TRACE_FILTER	/ pid != 0 /
+#define TRACE_FILTER_COND(a)	/ pid != 0 && (a) /
+#endif
+
+#define FILTER_THRESHOLD	5000000
+/* From thread.h */
+#define T_WAKEABLE		2
+
+/*
+ * This array is used to store the timestamp when threads are enqueued
+ * to dispq.
+ * self-> is not accessible when enqueue happens.
+ */
+unsigned long long lt_timestamps[int, int];
+
+self unsigned int lt_is_block_wakeable;
+self unsigned long long lt_sleep_start;
+self unsigned long long lt_sleep_duration;
+self unsigned long long lt_sch_delay;
+self unsigned int lt_counter;		/* only used in low overhead */
+self unsigned long long lt_timestamp;	/* only used in low overhead */
+
+/*
+ * Make sure we leave nothing behind,
+ * otherwise memory will eventually run out.
+ */
+proc:::lwp-exit
+{
+	lt_timestamps[curpsinfo->pr_pid, curlwpsinfo->pr_lwpid] = 0;
+	self->lt_sleep_start = 0;
+	self->lt_is_block_wakeable = 0;
+	self->lt_counter = 0;
+	self->lt_timestamp = 0;
+}
+
+#if !defined(ENABLE_LOW_OVERHEAD)
+/*
+ * Log timestamp when a thread is off CPU.
+ */
+sched::resume:off-cpu
+TRACE_FILTER_COND(curlwpsinfo->pr_state == SSLEEP)
+{
+	self->lt_sleep_start = timestamp;
+	self->lt_is_block_wakeable = curthread->t_flag & T_WAKEABLE;
+	lt_timestamps[curpsinfo->pr_pid, curlwpsinfo->pr_lwpid] =
+	    self->lt_sleep_start;
+}
+
+/*
+ * Log timestamp when a thread is put on a dispatch queue and becomes runnable.
+ */
+sched:::enqueue
+/ lt_timestamps[args[1]->pr_pid, args[0]->pr_lwpid] != 0 /
+{
+	lt_timestamps[args[1]->pr_pid, args[0]->pr_lwpid] = timestamp;
+}
+
+/*
+ * Calculate latencies when the thread is actually on CPU.
+ * This is necessary to get the right stack().
+ */
+this unsigned long long end;
+this unsigned long long now;
+sched::resume:on-cpu
+/ self->lt_sleep_start != 0 /
+{
+	this->end = lt_timestamps[curpsinfo->pr_pid, curlwpsinfo->pr_lwpid];
+	this->now = timestamp;
+	lt_timestamps[curpsinfo->pr_pid, curlwpsinfo->pr_lwpid] = 0;
+	this->end = (this->end != 0 && this->end != self->lt_sleep_start)
+	    ? this->end : this->now;
+	self->lt_sch_delay = this->now - this->end;
+	self->lt_sleep_duration = this->end - self->lt_sleep_start;
+	self->lt_sleep_start = 0;
+}
+
+/*
+ * Filter: drop all "large" latencies when it is wakeable,
+ * trying to filter sleep() etc.
+ */
+#if defined(ENABLE_FILTER)
+sched::resume:on-cpu
+/ self->lt_sleep_duration > FILTER_THRESHOLD &&
+  self->lt_is_block_wakeable != 0 /
+{
+	self->lt_sch_delay = 0;
+	self->lt_sleep_duration = 0;
+	self->lt_is_block_wakeable = 0;
+}
+#endif /* defined(ENABLE_FILTER) */
+
+/*
+ * Write sleep time to the aggregation.
+ * lt_sleep_duration is from thread off cpu to it is enqueued again.
+ */
+sched::resume:on-cpu
+/ self->lt_sleep_duration != 0 /
+{
+	@lt_call_count[pid, tid, stack()] = count();
+	@lt_call_sum[pid, tid, stack()] = sum(self->lt_sleep_duration);
+	@lt_call_max[pid, tid, stack()] = max(self->lt_sleep_duration);
+	self->lt_is_block_wakeable = 0;	/* Clean the flag to avoid leak */
+	self->lt_sleep_duration = 0;
+}
+
+/*
+ * Write time spent in queue to the aggregation.
+ * lt_sch_delay: the interval between "thread runnable" and "thread on cpu".
+ */
+sched::resume:on-cpu
+/ self->lt_sch_delay != 0 /
+{
+	@lt_named_count[pid, tid, "Wait for available CPU"] = count();
+	@lt_named_sum[pid, tid, "Wait for available CPU"] =
+	    sum(self->lt_sch_delay);
+	@lt_named_max[pid, tid, "Wait for available CPU"] =
+	    max(self->lt_sch_delay);
+	self->lt_sch_delay = 0;
+}
+
+/*
+ * Probes that tracks lock spinning
+ */
+lockstat:::adaptive-spin
+TRACE_FILTER
+{
+	@lt_named_count[pid, tid, "Adapt. lock spin"] = count();
+	@lt_named_sum[pid, tid, "Adapt. lock spin"] = sum(arg1);
+	@lt_named_max[pid, tid, "Adapt. lock spin"] = max(arg1);
+}
+
+lockstat:::spin-spin
+TRACE_FILTER
+{
+	@lt_named_count[pid, tid, "Spinlock spin"] = count();
+	@lt_named_sum[pid, tid, "Spinlock spin"] = sum(arg1);
+	@lt_named_max[pid, tid, "Spinlock spin"] = max(arg1);
+}
+
+/*
+ * Probes that tracks lock blocking
+ */
+lockstat:::adaptive-block
+TRACE_FILTER
+{
+	@lt_named_count[pid, tid, "#Adapt. lock block"] = count();
+	@lt_named_sum[pid, tid, "#Adapt. lock block"] = sum(arg1);
+	@lt_named_max[pid, tid, "#Adapt. lock block"] = max(arg1);
+}
+
+lockstat:::rw-block
+TRACE_FILTER
+{
+	@lt_named_count[pid, tid, "#RW. lock block"] = count();
+	@lt_named_sum[pid, tid, "#RW. lock block"] = sum(arg1);
+	@lt_named_max[pid, tid, "#RW. lock block"] = max(arg1);
+}
+
+#if defined(ENABLE_SYNCOBJ)
+/*
+ * Probes that tracks synchronization objects.
+ */
+this int stype;
+this unsigned long long wchan;
+this unsigned long long wtime;
+sched:::wakeup
+/*
+ * Currently we are not able to track wakeup from sched, because all lwpid
+ * are zero for when we trace sched. That makes lt_timestamps not usable.
+ */
+/ args[1]->pr_pid != 0 &&
+  lt_timestamps[args[1]->pr_pid, args[0]->pr_lwpid] != 0 /
+{
+	this->stype = args[0]->pr_stype;
+	this->wchan = args[0]->pr_wchan;
+	/*
+	 * We can use lt_timestamps[] here, because
+	 * wakeup is always fired before enqueue.
+	 * After enqueue, lt_timestamps[] will be overwritten.
+	 */
+	this->wtime = timestamp -
+	    lt_timestamps[args[1]->pr_pid, args[0]->pr_lwpid];
+	@lt_sync_count[args[1]->pr_pid, args[0]->pr_lwpid, this->stype,
+	    this->wchan] = count();
+	@lt_sync_sum[args[1]->pr_pid, args[0]->pr_lwpid, this->stype,
+	    this->wchan] = sum(this->wtime);
+	@lt_sync_max[args[1]->pr_pid, args[0]->pr_lwpid, this->stype,
+	    this->wchan] = max(this->wtime);
+}
+#endif  /* defined(ENABLE_SYNCOBJ) */
+
+#else /* !defined(ENABLE_LOW_OVERHEAD) */
+
+/*
+ * This is the low overhead mode.
+ * In order to reduce the number of instructions executed during each
+ * off-cpu and on-cpu event, we do:
+ * 1. Use sampling, only update aggregations roughly 1/100 times (SAMPLE_TIMES).
+ * 2. Do not track anything other than needed for "main" window.
+ * 3. Use as few thread local variables as possible.
+ */
+
+#define SAMPLE_TIMES		100
+#define SAMPLE_THRESHOLD	50000000
+
+/*
+ * Log timestamp when a thread is off CPU.
+ */
+sched::resume:off-cpu
+TRACE_FILTER_COND(curlwpsinfo->pr_state == SSLEEP)
+{
+	self->lt_timestamp = timestamp;
+#if defined(ENABLE_FILTER)
+	self->lt_is_block_wakeable = curthread->t_flag & T_WAKEABLE;
+#endif /* defined(ENABLE_FILTER) */
+}
+
+/*
+ * Calculate latencies when the thread is actually on CPU.
+ */
+this int need_skip;
+sched::resume:on-cpu
+/ self->lt_timestamp != 0 /
+{
+	self->lt_timestamp = timestamp - self->lt_timestamp;
+
+#if defined(ENABLE_FILTER)
+	self->lt_timestamp =
+	    (self->lt_timestamp > FILTER_THRESHOLD &&
+	    self->lt_is_block_wakeable != 0) ? 0 : self->lt_timestamp;
+	self->lt_is_block_wakeable = 0;
+#endif /* defined(ENABLE_FILTER) */
+
+	this->need_skip = (self->lt_counter < (SAMPLE_TIMES - 1) &&
+	    self->lt_timestamp <= SAMPLE_THRESHOLD) ? 1 : 0;
+	self->lt_timestamp = this->need_skip ? 0 : self->lt_timestamp;
+	self->lt_counter += this->need_skip;
+}
+
+/*
+ * Log large ones first.
+ */
+sched::resume:on-cpu
+/ self->lt_timestamp > SAMPLE_THRESHOLD /
+{
+	@lt_call_count[pid, tid, stack()] = sum(1);
+	@lt_call_sum[pid, tid, stack()] = sum(self->lt_timestamp);
+	@lt_call_max[pid, tid, stack()] = max(self->lt_timestamp);
+
+	self->lt_timestamp = 0;
+}
+
+/*
+ * If we fall to this probe, this must be a small latency and counter
+ * reaches SAMPLE_TIMES.
+ */
+sched::resume:on-cpu
+/ self->lt_timestamp != 0 /
+{
+	/* Need +1 because lt_counter has not been updated in this cycle. */
+	@lt_call_count[pid, tid, stack()] = sum(self->lt_counter + 1);
+	@lt_call_sum[pid, tid, stack()] =
+	    sum((self->lt_counter + 1) * self->lt_timestamp);
+	@lt_call_max[pid, tid, stack()] = max(self->lt_timestamp);
+
+	self->lt_timestamp = 0;
+	self->lt_counter = 0;
+}
+
+#endif /* !defined(ENABLE_LOW_OVERHEAD) */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/latencytop.h	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,272 @@
+/*
+ * 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) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#ifndef _LATENCYTOP_H
+#define	_LATENCYTOP_H
+
+#include <sys/types.h>
+
+/*
+ * GLib header file, including TRUE and FALSE definitions
+ */
+#include <glib.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Lint seems to be confused by glib header file.
+ */
+#ifdef __lint
+#undef g_assert
+#define	g_assert(x)	((void)(x))
+#undef TRUE
+#define	TRUE		1
+#endif
+
+/*
+ * To avoid compiler warning, we define our own convertion.
+ */
+#define	LT_POINTER_TO_INT(a)	((int)(long)(a))
+#define	LT_INT_TO_POINTER(a)	((void *)(unsigned long)(a))
+
+#define	TITLE			"LatencyTOP for OpenSolaris, version 1.0"
+#define	COPYRIGHT		"Copyright (c) 2008-2009, Intel Corporation."
+#define	DEFAULT_KLOG_FILE	"/var/log/latencytop.log"
+
+#define	INVALID_PID		(~0)
+#define	INVALID_TID		(~0)
+#define	PID_SYS_GLOBAL		INVALID_PID
+#define	INVALID_CAUSE		0
+#define	HIGHER_PRIORITY(a, b)	((a) > (b))
+
+#ifdef EMBED_CONFIGS
+/*
+ * LatencyTOP configurations embedded in the binary.
+ * Array will be generated by /usr/bin/xxd.
+ */
+extern unsigned char latencytop_d[];
+extern unsigned int latencytop_d_len;
+extern unsigned char latencytop_trans[];
+extern unsigned int latencytop_trans_len;
+#else
+/*
+ * LatencyTOP configurations is externally. This is easy for debugging.
+ */
+#define	DEFAULT_CONFIG_NAME	"./latencytop.trans"
+#define	DEFAULT_D_SCRIPT_NAME   "./latencytop.d"
+#endif
+
+typedef enum {
+	LT_STAT_COUNT,
+	LT_STAT_MAX,
+	LT_STAT_SUM,
+} lt_stat_type_t;
+
+#define	LT_KLOG_LEVEL_NONE	0	/* Log nothing */
+#define	LT_KLOG_LEVEL_UNMAPPED	1	/* Log only stacks not mapped */
+#define	LT_KLOG_LEVEL_MAPPED	2	/* Log only stacks mapped */
+#define	LT_KLOG_LEVEL_ALL	3	/* Log all stacks, mapped or not */
+
+typedef enum {
+	LT_LEVEL_GLOBAL,	/* System wide statistics */
+	LT_LEVEL_PROCESS,	/* Per-process statistics */
+	LT_LEVEL_THREAD,	/* Per-thread statistics */
+} lt_stat_level_t;
+
+typedef enum {
+	LT_SORT_TOTAL,
+	LT_SORT_MAX,
+	LT_SORT_AVG,
+	LT_SORT_COUNT,
+} lt_sort_t;
+
+typedef enum {
+	LT_FIELD_FNAME,
+	LT_FIELD_PSARGS,
+} lt_field_t;
+
+typedef enum {
+	LT_LIST_CAUSE,		/* Lists latency by the causes (default) */
+	LT_LIST_SPECIALS,	/* Lists only "special" causes. */
+	LT_LIST_SOBJ		/* Lists synchronization objects. */
+} lt_list_type_t;
+
+/*
+ * Data entry which contains the statistics.
+ */
+typedef struct	{
+	uint64_t count;
+	uint64_t total;
+	uint64_t max;
+} lt_stat_data_t;
+
+/*
+ * Data entry stored along with its name.
+ */
+typedef struct {
+	enum	{
+		STAT_CAUSE,
+		STAT_SOBJ
+	} type;
+	const char *string;
+	lt_stat_data_t data;
+	union	{
+		struct	{
+			int id;
+			int flags;
+		} cause;
+		struct	{
+			int id;
+		} sobj;
+	} type_data;
+} lt_stat_entry_t;
+
+typedef struct {
+	int enable_filter;
+	int trace_sched;
+	int trace_syncobj;
+	int low_overhead_mode;
+	int snap_interval;
+	char *config_name;
+} lt_config_t;
+
+extern lt_config_t g_config;	/* The global settings */
+
+/*
+ * Causes can be disabled in the configuration file.
+ * Once disabled, DTrace script will still capture such causes,
+ * but they will not be counted in LatencyTOP.
+ */
+#define	CAUSE_FLAG_DISABLED		1
+/*
+ * Causes with this flag will not show and count as part of summary in
+ * "kstack window".
+ */
+#define	CAUSE_FLAG_HIDE_IN_SUMMARY	2
+/*
+ * This is generated from D script (named cause), which is "special".
+ */
+#define	CAUSE_FLAG_SPECIAL		4
+#define	CAUSE_ALL_FLAGS			0xffffffff
+
+/*
+ * These functions collect data using DTrace.
+ */
+extern int lt_dtrace_init(void);
+extern int lt_dtrace_work(int);
+extern int lt_dtrace_collect(void);
+extern void lt_dtrace_deinit(void);
+
+/*
+ * These functions maintain configuration, e.g. symbol to cause mapping.
+ */
+extern int lt_table_init(void);
+extern int lt_table_lookup_cause(const char *, int *, int *);
+extern const char *lt_table_get_cause_name(int);
+extern int lt_table_get_cause_flag(int, int);
+extern int lt_table_lookup_named_cause(char *, int);
+extern void lt_table_deinit(void);
+
+/*
+ * These functions update tatistic data of all causes collected from DTrace.
+ */
+extern void lt_stat_update(pid_t, id_t, char *, lt_stat_type_t, uint64_t);
+extern void lt_stat_update_cause(pid_t, id_t, int, lt_stat_type_t, uint64_t);
+extern void lt_stat_update_sobj(pid_t, id_t, int, unsigned long long,
+    lt_stat_type_t, uint64_t);
+extern void lt_stat_clear_all(void);
+extern void lt_stat_free_all(void);
+
+/*
+ * These functions produce lists for display part.
+ * Note: after a call to lt_stat_update_*, the old lists will be invalid.
+ */
+extern void *lt_stat_list_create(lt_list_type_t, lt_stat_level_t,
+    pid_t, id_t, int, lt_sort_t);
+extern int lt_stat_list_has_item(void *, int);
+extern const char *lt_stat_list_get_reason(void *, int);
+extern uint64_t lt_stat_list_get_max(void *, int);
+extern uint64_t lt_stat_list_get_sum(void *, int);
+extern uint64_t lt_stat_list_get_count(void *, int);
+extern uint64_t lt_stat_list_get_gtotal(void *);
+extern void lt_stat_list_free(void *);
+
+/*
+ * These functions produce process and thread list.
+ */
+extern int lt_stat_proc_list_create(pid_t **, id_t **);
+extern void lt_stat_proc_list_free(pid_t *, id_t *);
+extern const char *lt_stat_proc_get_name(pid_t);
+extern int lt_stat_proc_get_nthreads(pid_t);
+
+/*
+ * Console based display functions using ncurses.
+ */
+extern void lt_display_init(void);
+extern int lt_display_loop(int);
+extern void lt_display_error(const char *, ...);
+extern void lt_display_deinit(void);
+
+/*
+ * Write statistics to a log file.
+ * Useful for debug and offline analysis.
+ */
+extern void lt_klog_init(void);
+extern void lt_klog_deinit(void);
+extern int lt_klog_set_log_file(const char *);
+extern int lt_klog_set_log_level(int);
+extern void lt_klog_write(void);
+extern void lt_klog_log(int, pid_t, char *, lt_stat_type_t,
+    uint64_t);
+
+/*
+ * Utility functions.
+ */
+extern uint64_t lt_millisecond(void);
+extern const char *lt_text(const char *);
+extern void *lt_malloc(size_t);
+extern void *lt_zalloc(size_t);
+extern char *lt_strdup(const char *);
+extern void lt_check_null(void *);
+extern void lt_time_str(char *, int);
+extern char *lt_get_proc_field(pid_t, lt_field_t);
+extern void lt_update_stat_value(lt_stat_data_t *, lt_stat_type_t, uint64_t);
+extern int lt_sort_by_total_desc(lt_stat_entry_t *, lt_stat_entry_t *);
+extern int lt_sort_by_max_desc(lt_stat_entry_t *, lt_stat_entry_t *);
+extern int lt_sort_by_count_desc(lt_stat_entry_t *, lt_stat_entry_t *);
+extern int lt_sort_by_avg_desc(lt_stat_entry_t *, lt_stat_entry_t *);
+extern void lt_gpipe_init(void);
+extern void lt_gpipe_deinit(void);
+extern void lt_gpipe_break(const char *);
+extern int lt_gpipe_readfd(void);
+extern int lt_file_exist(const char *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LATENCYTOP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/latencytop.trans	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,425 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+# LatencyTOP 0.1 configuration
+#
+
+# Format:
+# <priority>    <module>`<function>     <Category>
+# Special command:
+# ; <command>  <option value>
+# <command>:
+# disable_category <category name> : do not count and display <category name>
+
+# Stream ops
+40      genunix`strdoioctl              Stream ioctl
+40      genunix`strclose                Stream close
+40      genunix`strread                 Stream read
+40      genunix`strwrite                Stream write
+
+# Door ops
+50      doorfs`door_call                Door call
+50      doorfs`door_return              Door release
+
+# sockfs
+50      sockfs`socktpi_close            Close socket
+50      sockfs`sotpi_connect            Create socket connection
+50      sockfs`socktpi_write            Write to socket
+50      sockfs`socktpi_read             Read from socket
+
+# SCSI
+40      sd`sd_ssc_send                  Execute USCSI command
+40      sd`sdread                       SCSI read
+40      sd`sdwrite                      SCSI write
+30      sd`sd_check_media               SCSI wait for device ready
+
+# UFS
+50      ufs`ufs_sync                    UFS sync
+50      ufs`ufs_fsync                   UFS sync
+50      ufs`ufs_remove                  UFS remove file
+50      ufs`ufs_create                  UFS create file
+40      ufs`ufs_getpage                 UFS getpage
+40      ufs`ufs_putpage                 UFS putpage
+40      ufs`ufs_iget_alloced            UFS get inode
+40      ufs`ufs_ialloc                  UFS create inode
+40      ufs`ldl_waito                   UFS log
+40      ufs`alloc                       UFS alloc block
+
+# ZFS
+50      zfs`zil_commit                  ZFS intent log commit
+40      zfs`zfs_fsync                   ZFS sync
+40      zfs`zfs_read                    ZFS read
+40      zfs`zfs_write                   ZFS write
+40      zfs`zfs_getattr                 ZFS get file attribute
+40      zfs`zfs_remove                  ZFS remove file
+40      zfs`zfs_create                  ZFS create file
+40      zfs`zfs_close                   ZFS close file
+40      zfs`zfs_open                    ZFS open file
+40      zfs`zfs_getsecattr              ZFS security check
+40      zfs`zfs_umount                  ZFS unmount
+30      zfs`zio_wait                    ZFS wait for I/O complete
+30      zfs`spa_export                  ZFS export storage pool
+30      zfs`spa_import                  ZFS import storage pool
+30      zfs`zio_execute                 ZFS execute IO
+30      zfs`spa_sync                    ZFS sync transaction group
+
+# Page fault
+60      unix`pagefault                  Page fault
+        
+# FIFO        
+50      fifofs`fifo_read                Read from pipe/FIFO
+50      fifofs`fifo_write               Write to pipe/FIFO
+
+# Module
+60      genunix`modload                 Loading kernel module
+
+# Devfs        
+50      devfs`dv_find                   Devfs lookup
+50      dev`sdev_lookup                 Devfs lookup
+
+# Misc.
+40      genunix`lookuppnat              File system directory operation
+
+#
+# FSFlush daemon
+#
+
+15      genunix`fsflush                 Sleep in daemon (fsflush)
+
+#
+# Pageout
+#
+
+15      genunix`pageout_scanner         Sleep in daemon (pageout)
+15      genunix`pageout                 Sleep in daemon (pageout)
+
+#
+# Syscalls
+# 
+# Syscalls have priority 10, this is the lowest priority defined as default.
+# This is to ensure a latency is traced to one of the syscalls if nothing 
+# else matches.
+#
+
+15      unix`trap                       Processor Trap
+15      genunix`post_syscall            Post Syscall
+5       unix`sys_syscall32              Syscall
+
+10      genunix`indir                   Syscall: indir
+10      genunix`rexit                   Syscall: exit
+10      genunix`forkall                 Syscall: forkall
+10      genunix`read                    Syscall: read
+10      genunix`read32                  Syscall: read
+10      genunix`write                   Syscall: write
+10      genunix`write32                 Syscall: write
+10      genunix`open                    Syscall: open
+10      genunix`open32                  Syscall: open
+10      genunix`close                   Syscall: close
+10      genunix`wait                    Syscall: wait
+10      genunix`creat                   Syscall: creat
+10      genunix`creat32                 Syscall: creat
+10      genunix`link                    Syscall: link
+10      genunix`unlink                  Syscall: unlink
+10      genunix`exec                    Syscall: exec
+10      genunix`chdir                   Syscall: chdir
+10      genunix`gtime                   Syscall: time
+10      genunix`mknod                   Syscall: mknod
+10      genunix`chmod                   Syscall: chmod
+10      genunix`chown                   Syscall: chown
+10      genunix`brk                     Syscall: brk
+10      genunix`stat                    Syscall: stat
+10      genunix`stat32                  Syscall: stat
+10      genunix`lseek64                 Syscall: lseek
+10      genunix`lseek32                 Syscall: lseek
+10      genunix`getpid                  Syscall: getpid
+10      genunix`mount                   Syscall: mount
+10      genunix`umount                  Syscall: umount
+10      genunix`setuid                  Syscall: setuid
+10      genunix`getuid                  Syscall: getuid
+10      genunix`stime                   Syscall: stime
+10      genunix`stime32                 Syscall: stime
+10      genunix`pcsample                Syscall: pcsample
+10      genunix`alarm                   Syscall: alarm
+10      genunix`fstat                   Syscall: fstat
+10      genunix`fstat32                 Syscall: fstat
+10      genunix`pause                   Syscall: pause
+10      genunix`utime                   Syscall: utime
+10      genunix`stty                    Syscall: stty
+10      genunix`gtty                    Syscall: gtty
+10      genunix`access                  Syscall: access
+10      genunix`nice                    Syscall: nice
+10      genunix`statfs32                Syscall: statfs
+10      genunix`syssync                 Syscall: sync
+10      genunix`kill                    Syscall: kill
+10      genunix`fstatfs32               Syscall: fstatfs
+10      genunix`setpgrp                 Syscall: setpgrp
+10      genunix`uucopystr               Syscall: uucopystr
+10      genunix`dup                     Syscall: dup
+10      genunix`times                   Syscall: times
+10      genunix`times32                 Syscall: times
+10      genunix`profil                  Syscall: prof
+10      genunix`setgid                  Syscall: setgid
+10      genunix`getgid                  Syscall: getgid
+10      genunix`ssig                    Syscall: sig
+10      unix`sysi86                     Syscall: sysi86
+10      genunix`ioctl                   Syscall: ioctl
+10      genunix`uadmin                  Syscall: uadmin
+10      genunix`utssys64                Syscall: utssys
+10      genunix`utssys32                Syscall: utssys
+10      genunix`fdsync                  Syscall: fdsync
+10      genunix`exece                   Syscall: exece
+10      genunix`umask                   Syscall: umask
+10      genunix`chroot                  Syscall: chroot
+10      genunix`fcntl                   Syscall: fcntl
+10      genunix`ulimit                  Syscall: ulimit
+10      genunix`ulimit32                Syscall: ulimit
+10      genunix`tasksys                 Syscall: tasksys
+10      genunix`getpagesizes            Syscall: getpagesizes
+10      genunix`getpagesizes32          Syscall: getpagesizes
+10      genunix`rctlsys                 Syscall: rctlsys
+10      genunix`sidsys                  Syscall: sidsys
+10      genunix`fsat64                  Syscall: fsat
+10      genunix`fsat32                  Syscall: fsat
+10      genunix`syslwp_park             Syscall: lwp_park
+10      genunix`sendfilev               Syscall: sendfilev
+10      genunix`rmdir                   Syscall: rmdir
+10      genunix`mkdir                   Syscall: mkdir
+10      genunix`getdents64              Syscall: getdents
+10      genunix`getdents32              Syscall: getdents
+10      genunix`privsys                 Syscall: privsys
+10      genunix`privsys32               Syscall: privsys
+10      genunix`ucredsys                Syscall: ucredsys
+10      genunix`ucredsys32              Syscall: ucredsys
+10      genunix`sysfs                   Syscall: sysfs
+10      genunix`getmsg                  Syscall: getmsg
+10      genunix`getmsg32                Syscall: getmsg
+10      genunix`putmsg                  Syscall: putmsg
+10      genunix`putmsg32                Syscall: putmsg
+10      genunix`poll                    Syscall: poll
+10      genunix`lstat                   Syscall: lstat
+10      genunix`lstat32                 Syscall: lstat
+10      genunix`symlink                 Syscall: symlink
+10      genunix`readlink                Syscall: readlink
+10      genunix`readlink32              Syscall: readlink
+10      genunix`setgroups               Syscall: setgroups
+10      genunix`getgroups               Syscall: getgroups
+10      genunix`fchmod                  Syscall: fchmod
+10      genunix`fchown                  Syscall: fchown
+10      genunix`sigprocmask             Syscall: sigprocmask
+10      genunix`sigsuspend              Syscall: sigsuspend
+10      genunix`sigaltstack             Syscall: sigaltstack
+10      genunix`sigaltstack32           Syscall: sigaltstack
+10      genunix`sigaction               Syscall: sigaction
+10      genunix`sigaction32             Syscall: sigaction
+10      genunix`sigpending              Syscall: sigpending
+10      genunix`getsetcontext           Syscall: getsetcontext
+10      genunix`getsetcontext32         Syscall: getsetcontext
+10      genunix`statvfs                 Syscall: statvfs
+10      genunix`statvfs32               Syscall: statvfs
+10      genunix`fstatvfs                Syscall: fstatvfs
+10      genunix`fstatvfs32              Syscall: fstatvfs
+10      genunix`getloadavg              Syscall: getloadavg
+10      genunix`waitsys                 Syscall: waitsys
+10      genunix`waitsys32               Syscall: waitsys
+10      genunix`sigsendsys              Syscall: sigsendset
+10      unix`hrtsys                     Syscall: hrtsys
+10      genunix`sigresend               Syscall: sigresend
+10      genunix`priocntlsys             Syscall: priocntlsys
+10      genunix`pathconf                Syscall: pathconf
+10      genunix`mincore                 Syscall: mincore
+10      genunix`smmap64                 Syscall: mmap
+10      genunix`smmap32                 Syscall: mmap
+10      genunix`mprotect                Syscall: mprotect
+10      genunix`munmap                  Syscall: munmap
+10      genunix`fpathconf               Syscall: fpathconf
+10      genunix`vfork                   Syscall: vfork
+10      genunix`fchdir                  Syscall: fchdir
+10      genunix`readv                   Syscall: readv
+10      genunix`readv32                 Syscall: readv
+10      genunix`writev                  Syscall: writev
+10      genunix`writev32                Syscall: writev
+10      genunix`xstat                   Syscall: xstat
+10      genunix`xstat32                 Syscall: xstat
+10      genunix`lxstat                  Syscall: lxstat
+10      genunix`lxstat32                Syscall: lxstat
+10      genunix`fxstat                  Syscall: fxstat
+10      genunix`fxstat32                Syscall: fxstat
+10      genunix`xmknod                  Syscall: xmknod
+10      genunix`setrlimit64             Syscall: setrlimit
+10      genunix`setrlimit32             Syscall: setrlimit
+10      genunix`getrlimit64             Syscall: getrlimit
+10      genunix`getrlimit32             Syscall: getrlimit
+10      genunix`lchown                  Syscall: lchown
+10      genunix`memcntl                 Syscall: memcntl
+10      genunix`getpmsg                 Syscall: getpmsg
+10      genunix`getpmsg32               Syscall: getpmsg
+10      genunix`putpmsg                 Syscall: putpmsg
+10      genunix`putpmsg32               Syscall: putpmsg
+10      genunix`rename                  Syscall: rename
+10      genunix`uname                   Syscall: uname
+10      genunix`setegid                 Syscall: setegid
+10      genunix`sysconfig               Syscall: sysconfig
+10      genunix`adjtime                 Syscall: adjtime
+10      genunix`systeminfo              Syscall: systeminfo
+10      genunix`seteuid                 Syscall: seteuid
+10      genunix`forksys                 Syscall: forksys
+10      genunix`fork1                   Syscall: fork1
+10      genunix`sigtimedwait            Syscall: sigtimedwait
+10      genunix`lwp_info                Syscall: lwp_info
+10      genunix`yield                   Syscall: yield
+10      genunix`lwp_sema_wait           Syscall: lwp_sema_wait
+10      genunix`lwp_sema_post           Syscall: lwp_sema_post
+10      genunix`lwp_sema_trywait        Syscall: lwp_sema_trywait
+10      genunix`lwp_detach              Syscall: lwp_detach
+10      genunix`corectl                 Syscall: corectl
+10      genunix`modctl                  Syscall: modctl
+10      genunix`fchroot                 Syscall: fchroot
+10      genunix`utimes                  Syscall: utimes
+10      genunix`vhangup                 Syscall: vhangup
+10      genunix`gettimeofday            Syscall: gettimeofday
+10      genunix`getitimer               Syscall: getitimer
+10      genunix`setitimer               Syscall: setitimer
+10      genunix`syslwp_create           Syscall: lwp_create
+10      genunix`syslwp_exit             Syscall: lwp_exit
+10      genunix`syslwp_suspend          Syscall: lwp_suspend
+10      genunix`syslwp_continue         Syscall: lwp_continue
+10      genunix`lwp_kill                Syscall: lwp_kill
+10      genunix`lwp_self                Syscall: lwp_self
+10      genunix`lwp_sigmask             Syscall: lwp_sigmask
+10      genunix`syslwp_private          Syscall: lwp_private
+10      genunix`lwp_wait                Syscall: lwp_wait
+10      genunix`lwp_mutex_wakeup        Syscall: lwp_mutex_wakeup
+10      genunix`lwp_mutex_lock          Syscall: lwp_mutex_lock
+10      genunix`lwp_cond_wait           Syscall: lwp_cond_wait
+10      genunix`lwp_cond_signal         Syscall: lwp_cond_signal
+10      genunix`lwp_cond_broadcast      Syscall: lwp_cond_broadcast
+10      genunix`pread                   Syscall: pread
+10      genunix`pread32                 Syscall: pread
+10      genunix`pwrite                  Syscall: pwrite
+10      genunix`pwrite32                Syscall: pwrite
+10      genunix`llseek32                Syscall: llseek
+10      genunix`brandsys                Syscall: brandsys
+10      genunix`lgrpsys                 Syscall: lgrpsys
+10      genunix`rusagesys               Syscall: rusagesys
+10      portfs`portfs                   Syscall: portfs
+10      portfs`portfs32                 Syscall: portfs
+10      genunix`pollsys                 Syscall: pollsys
+10      genunix`labelsys                Syscall: labelsys
+10      genunix`acl                     Syscall: acl
+10      genunix`auditsys                Syscall: auditsys
+10      genunix`processor_bind          Syscall: processor_bind
+10      genunix`processor_info          Syscall: processor_info
+10      genunix`p_online                Syscall: p_online
+10      genunix`sigqueue                Syscall: sigqueue
+10      genunix`sigqueue32              Syscall: sigqueue
+10      genunix`clock_gettime           Syscall: clock_gettime
+10      genunix`clock_settime           Syscall: clock_settime
+10      genunix`clock_getres            Syscall: clock_getres
+10      genunix`timer_create            Syscall: timer_create
+10      genunix`timer_delete            Syscall: timer_delete
+10      genunix`timer_settime           Syscall: timer_settime
+10      genunix`timer_gettime           Syscall: timer_gettime
+10      genunix`timer_getoverrun        Syscall: timer_getoverrun
+10      genunix`nanosleep               Syscall: nanosleep
+10      genunix`facl                    Syscall: facl
+10      doorfs`doorfs                   Syscall: door
+10      doorfs`doorfs32                 Syscall: door
+10      genunix`setreuid                Syscall: setreuid
+10      genunix`setregid                Syscall: setregid
+10      genunix`install_utrap           Syscall: install_utrap
+10      genunix`signotify               Syscall: signotify
+10      genunix`schedctl                Syscall: schedctl
+10      genunix`sparc_utrap_install     Syscall: sparc_utrap_install
+10      genunix`resolvepath             Syscall: resolvepath
+10      genunix`lwp_mutex_timedlock     Syscall: lwp_mutex_timedlock
+10      genunix`lwp_sema_timedwait      Syscall: lwp_sema_timedwait
+10      genunix`lwp_rwlock_sys          Syscall: lwp_rwlock_sys
+10      genunix`getdents64              Syscall: getdents64
+10      genunix`smmaplf32               Syscall: smmaplf32
+10      genunix`stat64                  Syscall: stat64
+10      genunix`stat64_32               Syscall: stat64
+10      genunix`lstat64                 Syscall: lstat64
+10      genunix`lstat64_32              Syscall: lstat64
+10      genunix`fstat64                 Syscall: fstat64
+10      genunix`fstat64_32              Syscall: fstat64
+10      genunix`statvfs64               Syscall: statvfs64
+10      genunix`statvfs64_32            Syscall: statvfs64
+10      genunix`fstatvfs64              Syscall: fstatvfs64
+10      genunix`fstatvfs64_32           Syscall: fstatvfs64
+10      genunix`setrlimit64             Syscall: setrlimit64
+10      genunix`getrlimit64             Syscall: getrlimit64
+10      genunix`pread64                 Syscall: pread64
+10      genunix`pwrite64                Syscall: pwrite64
+10      genunix`creat64                 Syscall: creat64
+10      genunix`open64                  Syscall: open64
+10      genunix`zone                    Syscall: zone
+10      genunix`getcwd                  Syscall: getcwd
+10      sockfs`so_socket                Syscall: so_socket
+10      sockfs`so_socketpair            Syscall: so_socketpair
+10      sockfs`bind                     Syscall: bind
+10      sockfs`listen                   Syscall: listen
+10      sockfs`accept                   Syscall: accept
+10      sockfs`connect                  Syscall: connect
+10      sockfs`shutdown                 Syscall: shutdown
+10      sockfs`recv                     Syscall: recv
+10      sockfs`recv32                   Syscall: recv
+10      sockfs`recvfrom                 Syscall: recvfrom
+10      sockfs`recvfrom32               Syscall: recvfrom
+10      sockfs`recvmsg                  Syscall: recvmsg
+10      sockfs`send                     Syscall: send
+10      sockfs`send32                   Syscall: send
+10      sockfs`sendmsg                  Syscall: sendmsg
+10      sockfs`sendto                   Syscall: sendto
+10      sockfs`sendto32                 Syscall: sendto
+10      sockfs`getpeername              Syscall: getpeername
+10      sockfs`getsockname              Syscall: getsockname
+10      sockfs`getsockopt               Syscall: getsockopt
+10      sockfs`setsockopt               Syscall: setsockopt
+10      sockfs`sockconfig               Syscall: sockconfig
+10      genunix`ntp_gettime             Syscall: ntp_gettime
+10      genunix`ntp_adjtime             Syscall: ntp_adjtime
+10      genunix`lwp_mutex_unlock        Syscall: lwp_mutex_unlock
+10      genunix`lwp_mutex_trylock       Syscall: lwp_mutex_trylock
+10      genunix`lwp_mutex_register      Syscall: lwp_mutex_register
+10      genunix`cladm                   Syscall: cladm
+10      genunix`uucopy                  Syscall: uucopy
+10      genunix`umount2                 Syscall: umount2
+
+# sched (pid = 0)
+5       genunix`taskq_d_thread          Sleep in daemon (sched)
+5       ufs`trans_roll                  Sleep in daemon (sched)
+5       zfs`txg_sync_thread             Sleep in daemon (sched)
+5       genunix`taskq_thread            Sleep in daemon (sched)
+5       zfs`l2arc_feed_thread           Sleep in daemon (sched)
+5       scsi`scsi_watch_thread          Sleep in daemon (sched)
+5       genunix`seg_pasync_thread       Sleep in daemon (sched)
+5       zfs`arc_reclaim_thread          Sleep in daemon (sched)
+5       zfs`txg_thread_wait             Sleep in daemon (sched) 
+5       ip`squeue_worker                Sleep in daemon (sched) 
+5       genunix`thread_reaper           Sleep in daemon (sched) 
+5       zfs`txg_quiesce_thread          Sleep in daemon (sched)
+5       ufs`ufs_thread_delete           Sleep in daemon (sched) 
+
+#; disable_cause  Sleep in daemon (fsflush)
+#; disable_cause  Sleep in daemon (pageout)
+#; disable_cause  Sleep in daemon (sched)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/stat.c	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,995 @@
+/*
+ * 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) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/stat.h>
+
+#include "latencytop.h"
+
+/* Statistics for each process/thread. */
+typedef struct _lt_stat_collection lt_stat_collection_t;
+typedef gboolean (*check_child_func_t) (gpointer key,
+    lt_stat_collection_t *stat, void *user);
+
+typedef struct {
+	lt_stat_entry_t summary;
+	/* cause_id -> stat entry */
+	GHashTable *sctable;
+} lt_datagroup_t;
+
+#define	NGROUPS			2
+#define	GROUP_CAUSE		0
+#define	GROUP_SOBJ		1
+
+/*
+ * A data collection (i.e. a "bucket"). E.g. system, process or thread.
+ * Collections are hierarchic, 1 sys -> many processes -> more threads.
+ */
+struct _lt_stat_collection {
+	lt_stat_level_t level;
+	unsigned int id;
+	char *name;
+	lt_datagroup_t groups[NGROUPS];
+	/*
+	 * The following fields: parent, children and check_child_func
+	 * maintain the tree structure.
+	 */
+	lt_stat_collection_t *parent;		/* Parent node */
+	GHashTable *children;	/* pid (or tid) -> lt_stat_collection_t */
+	check_child_func_t check_child_func;	/* Release dead children */
+};
+
+/* Internal data struct backs up a stat_list */
+typedef struct _lt_stat_list lt_stat_list_t;
+typedef void (*free_list_func_t)(lt_stat_list_t *);
+struct _lt_stat_list {
+	int entry_count;
+	lt_stat_entry_t **entries;
+	uint64_t gtotal;
+	free_list_func_t free_func;
+};
+
+/* The root collection: system level statistics */
+static lt_stat_collection_t *stat_system = NULL;
+
+/*
+ * The data structure which supports synchronization objects.
+ * We don't use normal "cause table" because this needs to be cleared
+ * every time we refresh, so that dead synchronization objects don't
+ * eat up memory little by little.
+ */
+typedef struct {
+	int sobj_type;
+	unsigned long long sobj_addr;
+} lt_sobj_id_t;
+typedef struct {
+	lt_sobj_id_t sobj_id;
+	int cause_id;
+	char string[32];	/* Enough to hold "%s: 0x%llX" */
+} lt_sobj_t;
+
+static GHashTable *sobj_table = NULL;
+static int sobj_table_len = 0;
+
+/*
+ * Hash synchronize object ID by returning lower 32bit of its address.
+ */
+static guint
+sobj_id_hash(lt_sobj_id_t *id)
+{
+	g_assert(id != NULL);
+	return (id->sobj_addr & 0xFFFFFFFF);
+}
+
+/*
+ * Test if two synchronization objects are the same.
+ */
+static gboolean
+sobj_id_equal(lt_sobj_id_t *a, lt_sobj_id_t *b)
+{
+	g_assert(a != NULL && b != NULL);
+	return (a->sobj_type == b->sobj_type && a->sobj_addr == b->sobj_addr);
+}
+
+/*
+ * Lookup the cause_id of an synchronization object.
+ * Note this cause_id is only unique in GROUP_SOBJ, and changes after refresh.
+ */
+static lt_sobj_t *
+lookup_sobj(lt_sobj_id_t *id)
+{
+	const char *stype_str[] = {
+		"None",
+		"Mutex",
+		"RWLock",
+		"CV",
+		"Sema",
+		"User",
+		"User_PI",
+		"Shuttle"
+	};
+	const int stype_str_len =
+	    sizeof (stype_str) / sizeof (stype_str[0]);
+	lt_sobj_t *ret = NULL;
+
+	g_assert(id != NULL);
+	if (id->sobj_type < 0 || id->sobj_type >= stype_str_len) {
+		return (NULL);
+	}
+
+	if (sobj_table != NULL) {
+		ret = (lt_sobj_t *)g_hash_table_lookup(sobj_table, id);
+	} else {
+		sobj_table = g_hash_table_new_full(
+		    (GHashFunc)sobj_id_hash, (GEqualFunc)sobj_id_equal,
+		    NULL, (GDestroyNotify)free);
+		lt_check_null(sobj_table);
+	}
+
+	if (ret == NULL) {
+		ret = (lt_sobj_t *)lt_zalloc(sizeof (lt_sobj_t));
+		ret->cause_id = ++sobj_table_len;
+		(void) snprintf(ret->string, sizeof (ret->string),
+		    "%s: 0x%llX", stype_str[id->sobj_type], id->sobj_addr);
+		ret->sobj_id.sobj_type = id->sobj_type;
+		ret->sobj_id.sobj_addr = id->sobj_addr;
+
+		g_hash_table_insert(sobj_table, &ret->sobj_id, ret);
+	}
+
+	return (ret);
+}
+
+/*
+ * Check if a process is alive by looking at /proc/pid
+ */
+/* ARGSUSED */
+static gboolean
+check_process(gpointer key, lt_stat_collection_t *stat, void *user)
+{
+	char name[PATH_MAX];
+
+	(void) snprintf(name, PATH_MAX, "/proc/%u", stat->id);
+	/* Don't remove (return FALSE) if file exists */
+	return (lt_file_exist(name) ? FALSE : TRUE);
+}
+
+/*
+ * Check if a thread is alive by looking at /proc/pid/lwp/tid
+ */
+/* ARGSUSED */
+static gboolean
+check_thread(gpointer key, lt_stat_collection_t *stat, void *user)
+{
+	char name[PATH_MAX];
+
+	g_assert(stat->parent != NULL);
+	g_assert(stat->parent->level == LT_LEVEL_PROCESS);
+
+	(void) snprintf(name, PATH_MAX, "/proc/%u/lwp/%u",
+	    stat->parent->id, stat->id);
+	/* Don't remove (return FALSE) if file exists */
+	return (lt_file_exist(name) ? FALSE : TRUE);
+}
+
+/*
+ * Helper function to free a stat node.
+ */
+static void
+free_stat(lt_stat_collection_t *stat)
+{
+	int i;
+
+	if (stat == NULL) {
+		return;
+	}
+
+	for (i = 0; i < NGROUPS; ++i) {
+		if (stat->groups[i].sctable != NULL) {
+			g_hash_table_destroy(stat->groups[i].sctable);
+		}
+	}
+
+	if (stat->children != NULL) {
+		g_hash_table_destroy(stat->children);
+	}
+
+	if (stat->name != NULL) {
+		free(stat->name);
+	}
+
+	free(stat);
+}
+
+/*
+ * Helper function zeroing a stat node.
+ */
+/* ARGSUSED */
+static void
+clear_stat(gpointer key, lt_stat_collection_t *stat, void *user)
+{
+	int i;
+
+	g_assert(stat != NULL);
+
+	for (i = 0; i < NGROUPS; ++i) {
+		if (stat->groups[i].sctable != NULL) {
+			g_hash_table_destroy(stat->groups[i].sctable);
+			stat->groups[i].sctable = NULL;
+		}
+
+		stat->groups[i].summary.data.count = 0;
+		stat->groups[i].summary.data.total = 0;
+		stat->groups[i].summary.data.max = 0;
+	}
+
+	if (stat->children != NULL) {
+		g_hash_table_foreach_remove(stat->children,
+		    (GHRFunc)stat->check_child_func, NULL);
+		g_hash_table_foreach(stat->children,
+		    (GHFunc)clear_stat, NULL);
+	}
+}
+
+/*
+ * Update a collection for the value given.
+ * Recursively update its parent until it reaches the root.
+ */
+static void
+update_stat_entry(lt_stat_collection_t *stat, int cause_id,
+		lt_stat_type_t type, uint64_t value,
+		const char *string, int group_to_use)
+{
+	lt_stat_entry_t *entry = NULL;
+	lt_datagroup_t *group;
+
+	if (group_to_use < 0 || group_to_use >= NGROUPS) {
+		return;
+	}
+	group = &(stat->groups[group_to_use]);
+
+	if (group->sctable != NULL) {
+		entry = (lt_stat_entry_t *)g_hash_table_lookup(
+		    group->sctable, LT_INT_TO_POINTER(cause_id));
+	} else   {
+		group->sctable = g_hash_table_new_full(
+		    g_direct_hash, g_direct_equal,
+		    NULL, (GDestroyNotify)free);
+		lt_check_null(group->sctable);
+	}
+
+	if (entry == NULL) {
+		entry = (lt_stat_entry_t *)lt_zalloc(sizeof (lt_stat_entry_t));
+		entry->string = string;
+
+		switch (group_to_use) {
+		case GROUP_CAUSE:
+			entry->type = STAT_CAUSE;
+			entry->type_data.cause.id = cause_id;
+			entry->type_data.cause.flags =
+			    lt_table_get_cause_flag(cause_id, CAUSE_ALL_FLAGS);
+			/* hide the first '#' */
+			if ((entry->type_data.cause.flags
+			    & CAUSE_FLAG_HIDE_IN_SUMMARY) != 0) {
+				++entry->string;
+			}
+			break;
+		case GROUP_SOBJ:
+			entry->type = STAT_SOBJ;
+			entry->type_data.sobj.id = cause_id;
+			break;
+		}
+
+		g_hash_table_insert(group->sctable, LT_INT_TO_POINTER(cause_id),
+		    entry);
+	}
+
+	lt_update_stat_value(&entry->data, type, value);
+
+	if (group_to_use == GROUP_SOBJ ||
+	    (entry->type_data.cause.flags & CAUSE_FLAG_HIDE_IN_SUMMARY) == 0) {
+		lt_update_stat_value(&group->summary.data, type, value);
+	}
+
+	if (stat->parent != NULL) {
+		update_stat_entry(stat->parent, cause_id, type, value,
+		    string, group_to_use);
+	}
+}
+
+/*
+ * Identify the cause from a stack trace.
+ * Returns the cause_id.
+ */
+static int
+find_cause(char *stack)
+{
+	int cause_temp;
+	int prio_temp;
+	int cause = INVALID_CAUSE;
+	int priority = 0;
+	int found = 0;
+
+	while (stack != NULL) {
+		char *sep;
+		sep = strchr(stack, ' ');
+		if (sep != NULL) {
+			*sep = 0;
+		}
+
+		found = lt_table_lookup_cause(stack, &cause_temp, &prio_temp);
+		if (found && (cause == INVALID_CAUSE ||
+		    HIGHER_PRIORITY(prio_temp, priority))) {
+			cause = cause_temp;
+			priority = prio_temp;
+		}
+
+		if (sep != NULL) {
+			*sep = ' ';
+			stack = sep + 1;
+		} else   {
+			stack = NULL;
+		}
+	}
+	return (cause);
+}
+
+/*
+ * Create a new collection, hook it to the parent.
+ */
+static lt_stat_collection_t *
+new_collection(lt_stat_level_t level, unsigned int id, char *name,
+    lt_stat_collection_t *parent, check_child_func_t check_child_func)
+{
+	int i;
+	lt_stat_collection_t *ret;
+
+	ret = (lt_stat_collection_t *)
+	    lt_zalloc(sizeof (lt_stat_collection_t));
+
+	ret->level = level;
+	ret->check_child_func = check_child_func;
+	ret->id = id;
+	ret->name = name;
+
+	for (i = 0; i < NGROUPS; ++i) {
+		ret->groups[i].summary.string = (const char *)name;
+	}
+
+	if (parent != NULL) {
+		ret->parent = parent;
+		if (parent->children == NULL) {
+			parent->children = g_hash_table_new_full(
+			    g_direct_hash, g_direct_equal,
+			    NULL, (GDestroyNotify)free_stat);
+			lt_check_null(parent->children);
+		}
+		g_hash_table_insert(parent->children,
+		    LT_INT_TO_POINTER((int)id), ret);
+	}
+
+	return (ret);
+}
+
+/*
+ * Finds the "leaf" collection, use given pid and tid.
+ */
+static lt_stat_collection_t *
+get_stat_c(pid_t pid, id_t tid)
+{
+	lt_stat_collection_t *stat_p = NULL;
+	lt_stat_collection_t *stat_t = NULL;
+
+	if (stat_system == NULL) {
+		stat_system = new_collection(LT_LEVEL_GLOBAL,
+		    PID_SYS_GLOBAL, lt_strdup("SYSTEM"), NULL, check_process);
+	} else if (stat_system->children != NULL) {
+		stat_p = (lt_stat_collection_t *)
+		    g_hash_table_lookup(stat_system->children,
+		    LT_INT_TO_POINTER(pid));
+	}
+
+	if (stat_p == NULL) {
+		char *fname;
+
+		fname = lt_get_proc_field(pid, LT_FIELD_FNAME);
+		if (fname == NULL) {
+			/*
+			 * we cannot get process execname,
+			 * process is probably already dead.
+			 */
+			return (NULL);
+		}
+
+		stat_p = new_collection(LT_LEVEL_PROCESS,
+		    (unsigned int)pid, fname, stat_system, check_thread);
+	} else if (stat_p->children != NULL) {
+		stat_t = (lt_stat_collection_t *)
+		    g_hash_table_lookup(stat_p->children,
+		    LT_INT_TO_POINTER(tid));
+	}
+
+	if (stat_t == NULL) {
+		const int tname_size = 16; /* Enough for "Thread %d" */
+		char *tname;
+
+		tname = (char *)lt_malloc(tname_size);
+		(void) snprintf(tname, tname_size, "Thread %d", tid);
+
+		stat_t = new_collection(LT_LEVEL_THREAD,
+		    (unsigned int)tid, tname, stat_p, NULL);
+	}
+
+	return (stat_t);
+}
+
+/*
+ * Update the statistics given cause_id directly. Value will be added to
+ * internal statistics.
+ */
+void
+lt_stat_update_cause(pid_t pid, id_t tid, int cause_id, lt_stat_type_t type,
+    uint64_t value)
+{
+	const char *string;
+	lt_stat_collection_t *stat_t = NULL;
+
+	if (cause_id < 0 || value == 0) {
+		return;
+	}
+
+	if (lt_table_get_cause_flag(cause_id, CAUSE_FLAG_DISABLED)) {
+		/* we don't care about this cause, ignore. */
+		return;
+	}
+
+	stat_t = get_stat_c(pid, tid);
+	if (stat_t == NULL) {
+		/* cannot get fname, process must be dead. */
+		return;
+	}
+
+	string = lt_table_get_cause_name(cause_id);
+
+	update_stat_entry(stat_t, cause_id, type, value, string, GROUP_CAUSE);
+}
+
+/*
+ * Update the statistics given the stack trace.
+ * Internally it will be mapped to a cause using lt_table_lookup_cause(),
+ * and call lt_stat_update_cause().
+ */
+void
+lt_stat_update(pid_t pid, id_t tid, char *stack, lt_stat_type_t type,
+    uint64_t value)
+{
+	int cause_id = INVALID_CAUSE;
+
+	if (value == 0) {
+		return;
+	}
+
+	cause_id = find_cause(stack);
+	if (cause_id == INVALID_CAUSE) {
+		cause_id = lt_table_lookup_named_cause(stack, 1);
+		lt_klog_log(LT_KLOG_LEVEL_UNMAPPED, pid, stack, type, value);
+	} else   {
+		lt_klog_log(LT_KLOG_LEVEL_MAPPED, pid, stack, type, value);
+	}
+
+	lt_stat_update_cause(pid, tid, cause_id, type, value);
+}
+
+/*
+ * Zero all statistics, but keep the data structure in memory
+ * to be filled with new data immediately after.
+ */
+void
+lt_stat_clear_all(void)
+{
+	if (stat_system != NULL) {
+		clear_stat(NULL, stat_system, NULL);
+	}
+
+	if (sobj_table != NULL) {
+		g_hash_table_destroy(sobj_table);
+		sobj_table = NULL;
+	}
+}
+
+/*
+ * Clean up function that frees all memory used by statistics.
+ */
+void
+lt_stat_free_all(void)
+{
+	if (stat_system != NULL) {
+		free_stat(stat_system);
+		stat_system = NULL;
+	}
+
+	if (sobj_table != NULL) {
+		g_hash_table_destroy(sobj_table);
+		sobj_table = NULL;
+	}
+}
+
+/*
+ * Get top N causes of latency for a process. Returns handle to a stat_list.
+ * Use pid = PID_SYS_GLOBAL to get global top list.
+ * Call lt_stat_list_free after use.
+ */
+void *
+lt_stat_list_create(lt_list_type_t list_type, lt_stat_level_t level,
+    pid_t pid, id_t tid, int count, lt_sort_t sort_by)
+{
+	GCompareFunc func;
+	GList *list, *walk;
+	lt_stat_collection_t *stat_c = NULL;
+	lt_stat_list_t *ret;
+	lt_datagroup_t *group;
+
+	if (level == LT_LEVEL_GLOBAL) {
+		/* Use global entry */
+		stat_c = stat_system;
+	} else if (stat_system != NULL && stat_system->children != NULL) {
+		/* Find process entry first */
+		stat_c = (lt_stat_collection_t *)g_hash_table_lookup(
+		    stat_system->children, LT_INT_TO_POINTER(pid));
+
+		if (level == LT_LEVEL_THREAD) {
+			/*
+			 * If we request thread entry, find it based on
+			 * process entry.
+			 */
+			if (stat_c != NULL && stat_c->children != NULL) {
+				stat_c = (lt_stat_collection_t *)
+				    g_hash_table_lookup(stat_c->children,
+				    LT_INT_TO_POINTER(tid));
+			} else	{
+				/*
+				 * Couldn't find thread entry, set it to NULL
+				 * so we will return empty list later.
+				 */
+				stat_c = NULL;
+			}
+		}
+	}
+
+	ret = (lt_stat_list_t *)lt_zalloc(sizeof (lt_stat_list_t));
+	ret->entries = (lt_stat_entry_t **)
+	    lt_zalloc(count * sizeof (lt_stat_entry_t *));
+
+	if (stat_c == NULL) {
+		/* empty list */
+		return (ret);
+	}
+
+	if (list_type == LT_LIST_SOBJ) {
+		group = &(stat_c->groups[GROUP_SOBJ]);
+	} else {
+		group = &(stat_c->groups[GROUP_CAUSE]);
+	}
+
+	if (group->sctable == NULL) {
+		/* empty list */
+		return (ret);
+	}
+
+	ret->gtotal = group->summary.data.total;
+
+	list = g_hash_table_get_values(group->sctable);
+
+	switch (sort_by) {
+	case LT_SORT_TOTAL:
+		func = (GCompareFunc)lt_sort_by_total_desc;
+		break;
+	case LT_SORT_MAX:
+		func = (GCompareFunc)lt_sort_by_max_desc;
+		break;
+	case LT_SORT_AVG:
+		func = (GCompareFunc)lt_sort_by_avg_desc;
+		break;
+	case LT_SORT_COUNT:
+		func = (GCompareFunc)lt_sort_by_count_desc;
+		break;
+	}
+	list = g_list_sort(list, func);
+
+	for (walk = list;
+	    walk != NULL && count > 0;
+	    walk = g_list_next(walk), --count) {
+		lt_stat_entry_t *data = (lt_stat_entry_t *)walk->data;
+
+		if (list_type == LT_LIST_CAUSE &&
+		    data->type == STAT_CAUSE &&
+		    (data->type_data.cause.flags & CAUSE_FLAG_HIDE_IN_SUMMARY)
+		    != 0) {
+			continue;
+		}
+		if (list_type == LT_LIST_SPECIALS &&
+		    data->type == STAT_CAUSE &&
+		    (data->type_data.cause.flags & CAUSE_FLAG_SPECIAL)
+		    == 0) {
+			continue;
+		}
+		if (data->data.count == 0) {
+			break;
+		}
+		ret->entries[ret->entry_count++] = data;
+	}
+
+	g_list_free(list);
+
+	return (ret);
+}
+
+/*
+ * Free memory allocated by lt_stat_list_create().
+ */
+void
+lt_stat_list_free(void *ptr)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL) {
+		return;
+	}
+
+	if (list->free_func != NULL) {
+		list->free_func(list);
+	}
+
+	if (list->entries != NULL) {
+		free(list->entries);
+	}
+
+	free(list);
+}
+
+/*
+ * Check if the list has item number i.
+ */
+int
+lt_stat_list_has_item(void *ptr, int i)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL || i < 0 || i >= list->entry_count ||
+	    list->entries[i] == NULL) {
+		return (0);
+	}
+	return (1);
+}
+
+/*
+ * Get the display name of item number i in the list.
+ */
+const char *
+lt_stat_list_get_reason(void *ptr, int i)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+	if (list == NULL || i < 0 || i >= list->entry_count ||
+	    list->entries[i] == NULL) {
+		return (NULL);
+	}
+
+	g_assert(list->entries[i]->string != NULL);
+
+	return (list->entries[i]->string);
+}
+
+/*
+ * Get the max. of item number i in the list.
+ */
+uint64_t
+lt_stat_list_get_max(void *ptr, int i)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL || i < 0 || i >= list->entry_count ||
+	    list->entries[i] == NULL) {
+		return (0);
+	}
+
+	return (list->entries[i]->data.max);
+}
+
+/*
+ * Get the total of item number i in the list.
+ */
+uint64_t
+lt_stat_list_get_sum(void *ptr, int i)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL || i < 0 || i >= list->entry_count ||
+	    list->entries[i] == NULL) {
+		return (0);
+	}
+
+	return (list->entries[i]->data.total);
+}
+
+/*
+ * Get the count of item number i in the list.
+ */
+uint64_t
+lt_stat_list_get_count(void *ptr, int i)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL || i < 0 || i >= list->entry_count ||
+	    list->entries[i] == NULL) {
+		return (0);
+	}
+
+	return (list->entries[i]->data.count);
+}
+
+/*
+ * Get grand total of all latencies in the pid where the list is drawn.
+ */
+uint64_t
+lt_stat_list_get_gtotal(void *ptr)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL) {
+		return (0);
+	}
+	return (list->gtotal);
+}
+
+/*
+ * ============================================================================
+ * Process and thread list.
+ * They share a lot of static variables as stat part does,
+ * so put them in the same file.
+ */
+
+/*
+ * Helper function, sort by PID/TID ascend.
+ */
+static int
+sort_id(lt_stat_collection_t *a, lt_stat_collection_t *b)
+{
+	return ((int)(a->id - b->id));
+}
+
+/*
+ * Get current list of processes. Call lt_stat_proc_list_free after use.
+ */
+static int
+plist_create(pid_t ** list)
+{
+	GList *pid_list, *walk;
+	int ret, count;
+
+	ret = g_hash_table_size(stat_system->children);
+	*list = (pid_t *)lt_malloc(sizeof (pid_t) * ret);
+
+	pid_list = g_hash_table_get_values(stat_system->children);
+	pid_list = g_list_sort(pid_list, (GCompareFunc)sort_id);
+
+	for (walk = pid_list, count = 0;
+	    walk != NULL && count < ret;
+	    walk = g_list_next(walk), ++count) {
+		(*list)[count] = (int)
+		    ((lt_stat_collection_t *)(walk->data))->id;
+	}
+
+	g_list_free(pid_list);
+
+	return (ret);
+}
+
+/*
+ * Count how many threads are found so far in a process.
+ * Only thread caused SSLEEP will be found.
+ */
+/* ARGSUSED */
+static void
+count_threads(gpointer key, lt_stat_collection_t *stat_c, int *ret)
+{
+	g_assert(ret != NULL);
+
+	if (stat_c->children != NULL) {
+		*ret += g_hash_table_size(stat_c->children);
+	}
+}
+
+/*
+ * Get current list of processes+threads.
+ * Call lt_stat_proc_list_free after use.
+ */
+static int
+tlist_create(pid_t ** plist, id_t ** tlist)
+{
+	GList *pid_list, *walk_p;
+	GList *tid_list, *walk_t;
+	int ret = 0;
+	int count = 0;
+
+	g_hash_table_foreach(stat_system->children,
+	    (GHFunc)count_threads, &ret);
+
+	*plist = (pid_t *)lt_malloc(sizeof (pid_t) * ret);
+	*tlist = (id_t *)lt_malloc(sizeof (id_t) * ret);
+
+	pid_list = g_hash_table_get_values(stat_system->children);
+	pid_list = g_list_sort(pid_list, (GCompareFunc)sort_id);
+
+	for (walk_p = pid_list; walk_p != NULL;
+	    walk_p = g_list_next(walk_p)) {
+		lt_stat_collection_t *stat_p =
+		    (lt_stat_collection_t *)walk_p->data;
+
+		if (stat_p->children == NULL) {
+			continue;
+		}
+
+		tid_list = g_hash_table_get_values(stat_p->children);
+		tid_list = g_list_sort(tid_list, (GCompareFunc)sort_id);
+
+		for (walk_t = tid_list; walk_t != NULL;
+		    walk_t = g_list_next(walk_t)) {
+			lt_stat_collection_t *stat_t =
+			    (lt_stat_collection_t *)walk_t->data;
+
+			(*plist)[count] = (int)stat_p->id;
+			(*tlist)[count] = (int)stat_t->id;
+
+			++count;
+		}
+		g_list_free(tid_list);
+	}
+
+	g_list_free(pid_list);
+	g_assert(count == ret);
+
+	return (ret);
+}
+
+/*
+ * List processes that have been processed in LatencyTOP.
+ */
+int
+lt_stat_proc_list_create(pid_t ** plist, id_t ** tlist)
+{
+	if (plist == NULL) {
+		return (-1);
+	}
+
+	if (stat_system == NULL || stat_system->children == NULL) {
+		*plist = NULL;
+
+		if (tlist != NULL) {
+			*tlist = NULL;
+		}
+
+		return (0);
+	}
+
+	if (tlist == NULL) {
+		return (plist_create(plist));
+	} else	{
+		return (tlist_create(plist, tlist));
+	}
+}
+
+/*
+ * Free memory allocated by lt_stat_proc_list_create().
+ */
+void
+lt_stat_proc_list_free(pid_t *plist, id_t *tlist)
+{
+	if (plist != NULL) {
+		free(plist);
+	}
+
+	if (tlist != NULL) {
+		free(tlist);
+	}
+}
+
+/*
+ * Get execname of a PID.
+ */
+const char *
+lt_stat_proc_get_name(pid_t pid)
+{
+	lt_stat_collection_t *stat_p = NULL;
+
+	if (stat_system == NULL || stat_system->children == NULL) {
+		return (NULL);
+	}
+
+	stat_p = (lt_stat_collection_t *)
+	    g_hash_table_lookup(stat_system->children, LT_INT_TO_POINTER(pid));
+
+	if (stat_p != NULL) {
+		return (stat_p->name);
+	} else   {
+		return (NULL);
+	}
+}
+
+/*
+ * Get number of threads.
+ */
+int
+lt_stat_proc_get_nthreads(pid_t pid)
+{
+	lt_stat_collection_t *stat_p = NULL;
+
+	if (stat_system == NULL || stat_system->children == NULL) {
+		return (0);
+	}
+
+	stat_p = (lt_stat_collection_t *)
+	    g_hash_table_lookup(stat_system->children, LT_INT_TO_POINTER(pid));
+
+	if (stat_p != NULL) {
+		return (g_hash_table_size(stat_p->children));
+	} else   {
+		return (0);
+	}
+}
+
+/*
+ * Update the statistics for synchronization objects.
+ */
+void
+lt_stat_update_sobj(pid_t pid, id_t tid, int stype,
+    unsigned long long wchan,
+    lt_stat_type_t type, uint64_t value)
+{
+	lt_sobj_id_t id;
+	lt_sobj_t *sobj;
+	int cause_id;
+	lt_stat_collection_t *stat_t = NULL;
+
+	stat_t = get_stat_c(pid, tid);
+	if (stat_t == NULL) {
+		return;
+	}
+
+	id.sobj_type = stype;
+	id.sobj_addr = wchan;
+	sobj = lookup_sobj(&id);
+	if (sobj == NULL) {
+		return;
+	}
+
+	cause_id = sobj->cause_id;
+
+	update_stat_entry(stat_t, cause_id, type, value,
+	    sobj->string, GROUP_SOBJ);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/table.c	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,560 @@
+/*
+ * 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) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "latencytop.h"
+
+/*
+ * Structure that holds detail of a cause.
+ */
+typedef struct {
+	int cause_id;
+	int flags;
+	char *name;
+} lt_cause_t;
+
+/*
+ * Structure that represents a matched cause.
+ */
+typedef struct  {
+	int priority;
+	int cause_id;
+} lt_match_t;
+
+/* All lt_cause_t that are created. */
+static GPtrArray *causes_array = NULL;
+static int causes_array_len = 0;
+/*
+ * This hash table maps a symbol to a cause entry.
+ * key type is "char *" and value type is "lt_match_t *".
+ */
+static GHashTable *symbol_lookup_table = NULL;
+/*
+ * This hash table maps a cause name to an cause id.
+ * Note only cause names that are found in D script is put in this table.
+ * key type is "char *" and value type is "int" (which is cause_id).
+ */
+static GHashTable *named_causes = NULL;
+
+/*
+ * Help function to free one lt_cause_t structure.
+ */
+/* ARGSUSED */
+static void
+free_cause(lt_cause_t *cause, void *user)
+{
+	g_assert(cause != NULL && cause->name != NULL);
+
+	free(cause->name);
+	free(cause);
+}
+
+/*
+ * Add a cause.
+ * Note this function takes ownership of char *name.
+ */
+static lt_cause_t *
+new_cause(char *name, int flags)
+{
+	lt_cause_t *entry;
+
+	g_assert(name != NULL);
+
+	entry = (lt_cause_t *)lt_malloc(sizeof (lt_cause_t));
+	entry->flags = flags;
+	entry->name = name;
+	entry->cause_id = causes_array_len;
+
+	g_ptr_array_add(causes_array, entry);
+	++causes_array_len;
+
+	return (entry);
+}
+
+/*
+ * Set a cause to "disabled" state.
+ */
+static void
+disable_cause(char *cause_str, GHashTable *cause_table)
+{
+	lt_cause_t *cause;
+
+	cause = (lt_cause_t *)g_hash_table_lookup(cause_table, cause_str);
+	if (cause != NULL) {
+		cause->flags |= CAUSE_FLAG_DISABLED;
+	}
+}
+
+/*
+ * Helper functions that reads a line from a char * array.
+ */
+static int
+read_line_from_mem(const char *mem, int mem_len, char *line, int line_len,
+    int *index)
+{
+	g_assert(mem != NULL && line != NULL && index != NULL);
+
+	if (line_len <= 0 || mem_len <= 0) {
+		return (0);
+	}
+	if (*index >= mem_len) {
+		return (0);
+	}
+
+	while (line_len > 1 && *index < mem_len) {
+		*line = mem[(*index)++];
+		--line_len;
+		++line;
+		if (*(line-1) == '\r' || *(line-1) == '\n') {
+			break;
+		}
+	}
+	*line = 0;
+
+	return (1);
+}
+
+/*
+ * The main loop that parses the translation rules one line at a time,
+ * and construct latencytop lookup data structure from it.
+ */
+static int
+parse_config(const char *work, int work_len)
+{
+	char line[256];
+	int len;
+	char *begin, *end, *tmp;
+	int priority = 0;
+	char *match;
+	char *match_dup;
+	char *cause_str;
+	lt_cause_t *cause;
+	lt_match_t *match_entry;
+	int current = 0;
+	GHashTable *cause_lookup;
+	GSequence *cmd_disable;
+
+	cause_lookup = g_hash_table_new(g_str_hash, g_str_equal);
+	lt_check_null(cause_lookup);
+
+	cmd_disable = g_sequence_new((GDestroyNotify)free);
+	lt_check_null(cmd_disable);
+
+	while (read_line_from_mem(work, work_len, line, sizeof (line),
+	    &current)) {
+		len = strlen(line);
+		if (line[len-1] != '\n' && line[len-1] != '\r' &&
+		    current < work_len) {
+			lt_display_error("Configuration line too long.\n");
+			goto err;
+		}
+
+		begin = line;
+		while (isspace(*begin)) {
+			++begin;
+		}
+		if (*begin == '\0') {
+			/* empty line, ignore */
+			continue;
+		}
+
+		/* Delete trailing spaces. */
+		end = begin + strlen(begin) - 1;
+		while (isspace(*end)) {
+			--end;
+		}
+		end[1] = 0;
+
+		if (*begin == '#') {
+			continue;
+		} else if (*begin == ';') {
+			char old_chr = 0;
+			/* special command */
+			/* ; disable_cause  FSFlush Daemon  */
+			/* ^ */
+			++begin;
+
+			while (isspace(*begin)) {
+				++begin;
+			}
+			/* ; disable_cause  FSFlush Daemon  */
+			/*   ^ */
+			if (*begin == '\0') {
+				continue;
+			}
+
+			for (tmp = begin;
+			    *tmp != '\0' && !isspace(*tmp);
+			    ++tmp) {
+			}
+			old_chr = *tmp;
+			*tmp = 0;
+
+			if (strcmp("disable_cause", begin) == 0) {
+				if (old_chr == '\0') {
+					/* Must have an argument */
+					lt_display_error(
+					    "Invalid command format: %s\n",
+					    begin);
+					goto err;
+				}
+
+				begin = tmp+1;
+				while (isspace(*begin)) {
+					++begin;
+				}
+
+				g_sequence_append(cmd_disable,
+				    lt_strdup(begin));
+			} else   {
+				*tmp = old_chr;
+				lt_display_error(
+				    "Unknown command: %s\n", begin);
+				goto err;
+			}
+			continue;
+		}
+
+		g_assert(*begin != '#' && *begin != ';');
+
+		/* 10	genunix`indir			Syscall indir */
+		/* ^ */
+		priority = strtol(begin, &tmp, 10);
+		if (tmp == begin || priority == 0) {
+			lt_display_error(
+			    "Invalid configuration line: %s\n", line);
+			goto err;
+		}
+		begin = tmp;
+
+		/* 10	genunix`indir			Syscall indir */
+		/*   ^ */
+		while (isspace(*begin)) {
+			++begin;
+		}
+		if (*begin == 0) {
+			lt_display_error(
+			    "Invalid configuration line: %s\n", line);
+			goto err;
+		}
+
+		/* 10	genunix`indir			Syscall indir */
+		/* -----^ */
+		for (tmp = begin;
+		    *tmp != '\0' && !isspace(*tmp);
+		    ++tmp) {
+		}
+		if (*tmp == '\0') {
+			lt_display_error(
+			    "Invalid configuration line: %s\n", line);
+			goto err;
+		}
+		*tmp = 0;
+		match = begin;
+
+		/* Check if we have mapped this function before. */
+		match_entry = (lt_match_t *)
+		    g_hash_table_lookup(symbol_lookup_table, match);
+		if (match_entry != NULL &&
+		    HIGHER_PRIORITY(match_entry->priority, priority)) {
+			/* We already have a higher entry. Ignore this. */
+			continue;
+		}
+
+		begin = tmp+1;
+
+		/* 10	genunix`indir			Syscall indir */
+		/* -------------------------------------^ */
+		while (isspace(*begin)) {
+			++begin;
+		}
+		if (*begin == 0) {
+			lt_display_error(
+			    "Invalid configuration line: %s\n", line);
+			goto err;
+		}
+		cause_str = begin;
+
+		/* Check if we have mapped this cause before. */
+		cause = (lt_cause_t *)
+		    g_hash_table_lookup(cause_lookup, cause_str);
+		if (cause == NULL) {
+			char *cause_dup = lt_strdup(cause_str);
+			cause = new_cause(cause_dup, 0);
+			g_hash_table_insert(cause_lookup, cause_dup, cause);
+		}
+
+		match_entry = (lt_match_t *)lt_malloc(sizeof (lt_match_t));
+		g_assert(NULL != match_entry);
+		match_entry->priority = priority;
+		match_entry->cause_id = cause->cause_id;
+		match_dup = lt_strdup(match);
+
+		g_hash_table_insert(symbol_lookup_table, match_dup,
+		    match_entry);
+	}
+
+	g_sequence_foreach(cmd_disable, (GFunc)disable_cause, cause_lookup);
+	g_sequence_free(cmd_disable);
+	g_hash_table_destroy(cause_lookup);
+
+	return (0);
+
+err:
+	g_sequence_free(cmd_disable);
+	g_hash_table_destroy(cause_lookup);
+
+	return (-1);
+}
+
+/*
+ * Init function, called when latencytop starts.
+ * It loads the translation rules from a file.
+ * A configuration file defines some causes and symbols matching these causes.
+ */
+int
+lt_table_init(void)
+{
+	char *config_loaded = NULL;
+	int config_loaded_len = 0;
+	const char *work = NULL;
+	int work_len = 0;
+	lt_cause_t *cause;
+
+#ifdef EMBED_CONFIGS
+	work = (char *)latencytop_trans;
+	work_len = latencytop_trans_len;
+#endif
+
+	if (g_config.config_name != NULL) {
+		FILE *fp;
+
+		fp = fopen(g_config.config_name, "r");
+		if (NULL == fp) {
+			lt_display_error(
+			    "Unable to open configuration file.\n");
+			return (-1);
+		}
+
+		(void) fseek(fp, 0, SEEK_END);
+		config_loaded_len = (int)ftell(fp);
+		config_loaded = (char *)lt_malloc(config_loaded_len);
+		(void) fseek(fp, 0, SEEK_SET);
+
+		if (fread(config_loaded, config_loaded_len, 1, fp) == 0) {
+			lt_display_error(
+			    "Unable to read configuration file.\n");
+			(void) fclose(fp);
+			free(config_loaded);
+			return (-1);
+		}
+
+		(void) fclose(fp);
+		(void) printf("Loaded configuration from %s\n",
+		    g_config.config_name);
+
+		work = config_loaded;
+		work_len = config_loaded_len;
+	}
+
+	g_assert(work != NULL && work_len != 0);
+
+	lt_table_deinit();
+	causes_array = g_ptr_array_new();
+	lt_check_null(causes_array);
+
+	/* 0 is not used, to keep a place for bugs etc. */
+	cause = new_cause(lt_strdup("Nothing"), CAUSE_FLAG_DISABLED);
+	g_assert(cause->cause_id == INVALID_CAUSE);
+
+	symbol_lookup_table = g_hash_table_new_full(
+	    g_str_hash, g_str_equal,
+	    (GDestroyNotify)free, (GDestroyNotify)free);
+	lt_check_null(symbol_lookup_table);
+
+	if (parse_config(work, work_len) != 0) {
+		return (-1);
+	}
+
+	if (config_loaded != NULL) {
+		free(config_loaded);
+	}
+
+	return (0);
+}
+
+/*
+ * Some causes, such as "lock spinning", does not have stack trace.
+ * Instead, their names are explicitly specified in DTrace script.
+ * This function will resolve such causes, and dynamically add them
+ * to the global tables when first met (lazy initialization).
+ * auto_create: set to TRUE will create the entry if it is not found.
+ * Returns cause_id of the cause.
+ */
+int
+lt_table_lookup_named_cause(char *name, int auto_create)
+{
+	int cause_id = INVALID_CAUSE;
+
+	if (named_causes == NULL) {
+		named_causes = g_hash_table_new_full(
+		    g_str_hash, g_str_equal, (GDestroyNotify)free, NULL);
+		lt_check_null(named_causes);
+	} else   {
+		cause_id = LT_POINTER_TO_INT(g_hash_table_lookup(
+		    named_causes, name));
+	}
+
+	if (cause_id == INVALID_CAUSE && auto_create) {
+		int flags = CAUSE_FLAG_SPECIAL;
+		lt_cause_t *cause;
+
+		if (name[0] == '#') {
+			flags |= CAUSE_FLAG_HIDE_IN_SUMMARY;
+		}
+
+		cause = new_cause(lt_strdup(name), flags);
+		if (cause == NULL) {
+			return (INVALID_CAUSE);
+		}
+		cause_id = cause->cause_id;
+
+		g_hash_table_insert(named_causes, lt_strdup(name),
+		    LT_INT_TO_POINTER(cause_id));
+	}
+
+	return (cause_id);
+}
+
+/*
+ * Try to map a symbol on stack to a known cause.
+ * module_func has the format "module_name`function_name".
+ * cause_id and priority will be set if a cause is found.
+ * Returns 1 if found, 0 if not found.
+ */
+int
+lt_table_lookup_cause(const char *module_func, int *cause_id, int *priority)
+{
+	lt_match_t *match;
+
+	g_assert(module_func != NULL && cause_id != NULL && priority != NULL);
+
+	if (symbol_lookup_table == NULL) {
+		return (0);
+	}
+
+	match = (lt_match_t *)
+	    g_hash_table_lookup(symbol_lookup_table, module_func);
+	if (match == NULL) {
+		char *func = strchr(module_func, '`');
+
+		if (func != NULL) {
+			match = (lt_match_t *)
+			    g_hash_table_lookup(symbol_lookup_table, func);
+		}
+	}
+
+	if (match == NULL) {
+		return (0);
+	} else   {
+		*cause_id = match->cause_id;
+		*priority = match->priority;
+		return (1);
+	}
+}
+
+/*
+ * Get the display name of a cause. Cause_id must be valid,
+ * which is usually return from lt_table_lookup_cause() or
+ * lt_table_lookup_named_cause().
+ */
+const char *
+lt_table_get_cause_name(int cause_id)
+{
+	lt_cause_t *cause;
+
+	if (cause_id < 0 || cause_id >= causes_array_len) {
+		return (NULL);
+	}
+
+	cause = (lt_cause_t *)g_ptr_array_index(causes_array, cause_id);
+	if (cause == NULL) {
+		return (NULL);
+	} else {
+		return (cause->name);
+	}
+}
+
+/*
+ * Check a cause's flag, e.g. if it has CAUSE_FLAG_DISABLED.
+ * Use CAUSE_ALL_FLAGS to get all flags at once.
+ */
+int
+lt_table_get_cause_flag(int cause_id, int flag)
+{
+	lt_cause_t *cause;
+
+	if (cause_id < 0 || cause_id >= causes_array_len) {
+		return (0);
+	}
+	cause = (lt_cause_t *)g_ptr_array_index(causes_array, cause_id);
+
+	if (cause == NULL) {
+		return (0);
+	} else {
+		return (cause->flags & flag);
+	}
+}
+
+/*
+ * Clean up function.
+ * Free the resource used for symbol table. E.g. symbols, causes.
+ */
+void
+lt_table_deinit(void)
+{
+	if (symbol_lookup_table != NULL) {
+		g_hash_table_destroy(symbol_lookup_table);
+		symbol_lookup_table = NULL;
+	}
+
+	if (named_causes != NULL) {
+		g_hash_table_destroy(named_causes);
+		named_causes = NULL;
+	}
+
+	if (causes_array != NULL) {
+		g_ptr_array_foreach(causes_array, (GFunc)free_cause, NULL);
+		g_ptr_array_free(causes_array, TRUE);
+		causes_array = NULL;
+	}
+
+	causes_array_len = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/util.c	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,323 @@
+/*
+ * 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) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <unistd.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <procfs.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "latencytop.h"
+
+/* pipe that breaks the event loop (and exit early) */
+static int signal_pipe[2];
+
+/*
+ * Get current system time in milliseconds (1e-3).
+ */
+uint64_t
+lt_millisecond(void)
+{
+	struct timeval p;
+	(void) gettimeofday(&p, NULL);
+	return ((uint64_t)p.tv_sec * 1000 + p.tv_usec / 1000);
+}
+
+/*
+ * Wrapper of gettext().
+ */
+const char *
+lt_text(const char *text)
+{
+	if (text == NULL) {
+		return ("");
+	}
+
+	return (gettext(text));
+}
+
+/*
+ * Checks if OOM happens by comparing pointers with NULL in various places.
+ */
+void
+lt_check_null(void *p)
+{
+	if (p == NULL) {
+		(void) printf("Out of memory!\n");
+		g_assert(0);
+		exit(2);
+	}
+}
+
+/*
+ * Safe malloc.
+ */
+void *
+lt_malloc(size_t size)
+{
+	void *ret = malloc(size);
+
+	lt_check_null(ret);
+
+	return (ret);
+}
+
+/*
+ * Safe alloc with memory cleared.
+ * Named it "zalloc" because its signature is different from
+ * calloc() in stdlib.
+ */
+void *
+lt_zalloc(size_t size)
+{
+	void *ret = lt_malloc(size);
+	(void) memset(ret, 0, size);
+	return (ret);
+}
+
+/*
+ * Safe strdup.
+ */
+char *
+lt_strdup(const char *str)
+{
+	char *ret = strdup(str);
+
+	lt_check_null(ret);
+
+	return (ret);
+}
+
+/*
+ * Get string for current time, e.g. YYYY-MM-DD
+ */
+void
+lt_time_str(char *buffer, int len)
+{
+	struct tm tms;
+	time_t t;
+	int i;
+
+	(void) time(&t);
+	(void) gmtime_r(&t, &tms);
+	(void) asctime_r(&tms, buffer, len);
+
+	for (i = strlen(buffer)-1; i > 0; --i) {
+		if (isspace(buffer[i])) {
+			buffer[i] = 0;
+		} else	{
+			break;
+		}
+	}
+}
+
+/*
+ * Retrieves process exeutable name etc. from /proc.
+ */
+char *
+lt_get_proc_field(pid_t pid, lt_field_t field)
+{
+	char name[PATH_MAX];
+	int fd;
+	int ret;
+	psinfo_t psinfo;
+
+	(void) snprintf(name, PATH_MAX, "/proc/%d/psinfo", (int)pid);
+	fd = open(name, O_RDONLY);
+	if (fd == -1) {
+		return (NULL);
+	}
+
+	ret = read(fd, (char *)&psinfo, sizeof (psinfo_t));
+	(void) close(fd);
+	if (ret < 0) {
+		return (NULL);
+	}
+
+	switch (field) {
+	case LT_FIELD_FNAME:
+		return (lt_strdup(psinfo.pr_fname));
+	case LT_FIELD_PSARGS:
+		return (lt_strdup(psinfo.pr_psargs));
+	}
+	return (NULL);
+}
+
+/*
+ * Help function to update the data structure.
+ */
+void
+lt_update_stat_value(lt_stat_data_t *entry,
+    lt_stat_type_t type, uint64_t value)
+{
+	switch (type) {
+	case LT_STAT_COUNT:
+		entry->count += value;
+		break;
+	case LT_STAT_SUM:
+		entry->total += value;
+		break;
+	case LT_STAT_MAX:
+		if (value > entry->max) {
+			entry->max = value;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ * Help function to sort by total.
+ */
+int
+lt_sort_by_total_desc(lt_stat_entry_t *a, lt_stat_entry_t *b)
+{
+	g_assert(a != NULL && b != NULL);
+	/*
+	 * ->data.total is int64, so we can't simply return
+	 * b->data.total - a->data.total
+	 */
+	if (b->data.total > a->data.total) {
+		return (1);
+	} else if (b->data.total < a->data.total) {
+		return (-1);
+	} else	{
+		return (0);
+	}
+}
+
+/*
+ * Help function to sort by max.
+ */
+int
+lt_sort_by_max_desc(lt_stat_entry_t *a, lt_stat_entry_t *b)
+{
+	g_assert(a != NULL && b != NULL);
+
+	if (b->data.max > a->data.max) {
+		return (1);
+	} else if (b->data.max < a->data.max) {
+		return (-1);
+	} else	{
+		return (0);
+	}
+}
+
+/*
+ * Help function to sort by count.
+ */
+int
+lt_sort_by_count_desc(lt_stat_entry_t *a, lt_stat_entry_t *b)
+{
+	g_assert(a != NULL && b != NULL);
+
+	if (b->data.count > a->data.count) {
+		return (1);
+	} else if (b->data.count < a->data.count) {
+		return (-1);
+	} else	{
+		return (0);
+	}
+}
+
+/*
+ * Help function to sort by average.
+ */
+int
+lt_sort_by_avg_desc(lt_stat_entry_t *a, lt_stat_entry_t *b)
+{
+	double avg_a, avg_b;
+
+	g_assert(a != NULL && b != NULL);
+
+	avg_a = (double)a->data.total / a->data.count;
+	avg_b = (double)b->data.total / b->data.count;
+
+	if (avg_b > avg_a) {
+		return (1);
+	} else if (avg_b < avg_a) {
+		return (-1);
+	} else	{
+		return (0);
+	}
+}
+
+/*
+ * Create pipe for signal handler and wakeup.
+ */
+void
+lt_gpipe_init(void)
+{
+	(void) pipe(signal_pipe);
+}
+
+/*
+ * Release pipe used in signal handler.
+ */
+void
+lt_gpipe_deinit(void)
+{
+	(void) close(signal_pipe[0]);
+	(void) close(signal_pipe[1]);
+}
+
+/*
+ * Break from main loop early.
+ */
+void
+lt_gpipe_break(const char *ch)
+{
+	(void) write(signal_pipe[1], ch, 1);
+}
+
+/*
+ * Returns fd# used to detect "break main loop".
+ */
+int
+lt_gpipe_readfd(void)
+{
+	return (signal_pipe[0]);
+}
+
+/*
+ * Check if a file exists.
+ */
+int
+lt_file_exist(const char *name)
+{
+	struct stat64 st;
+
+	if (stat64(name, &st) == 0) {
+		return (1);
+	} else	{
+		return (0);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/i386/Makefile	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,27 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTPROG32)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/sparcv9/Makefile	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,31 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+include ../../Makefile.cmd.64
+
+install: all $(ROOTPROG64)
--- a/usr/src/pkgdefs/Makefile	Fri Jul 17 16:43:38 2009 +0100
+++ b/usr/src/pkgdefs/Makefile	Fri Jul 17 09:57:00 2009 -0700
@@ -316,6 +316,7 @@
 	SUNWixgbe \
 	SUNWkrbr \
 	SUNWkrbu \
+	SUNWlatencytop \
 	SUNWldskint \
 	SUNWlibsasl \
 	SUNWllc \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/Makefile	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,34 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(FILES) depend
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/depend	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,31 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+P SUNWcsu	Core Solaris, (Usr)
+P SUNWcslr	Core Solaris Libraries (Root)
+P SUNWdtrc	DTrace Clients 
+P SUNWdtrp	DTrace Providers
+P SUNWGlib	GLIB - Library of useful routines for C programming
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/pkginfo.tmpl	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,50 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWlatencytop"
+NAME="LatencyTOP tool"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+CATEGORY="system"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+DESC="LatencyTOP tool"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="usr"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+MAXINST="1000"
+CLASSES="none"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="false"
+SUNW_PKG_THISZONE="false"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/prototype_com	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,48 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+#
+# source locations relative to the prototype file
+#
+# SUNWlatencytop
+#
+d none usr 0755 root sys
+d none usr/bin 0755 root bin
+l none usr/bin/latencytop=../../usr/lib/isaexec
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/prototype_i386	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,54 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are I386 specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWlatencytop
+#
+d none usr/bin/i86 755 root bin
+f none usr/bin/i86/latencytop 555 root bin
+d none usr/bin/amd64 755 root bin
+f none usr/bin/amd64/latencytop 555 root bin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/prototype_sparc	Fri Jul 17 09:57:00 2009 -0700
@@ -0,0 +1,52 @@
+#
+# 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) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWlatencytop
+#
+d none usr/bin/sparcv9 755 root bin
+f none usr/bin/sparcv9/latencytop 555 root bin