summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/Makefile.lint1
-rw-r--r--usr/src/cmd/Makefile1
-rw-r--r--usr/src/cmd/powertop/Makefile55
-rw-r--r--usr/src/cmd/powertop/Makefile.com56
-rw-r--r--usr/src/cmd/powertop/amd64/Makefile28
-rw-r--r--usr/src/cmd/powertop/battery.c223
-rw-r--r--usr/src/cmd/powertop/cpufreq.c425
-rw-r--r--usr/src/cmd/powertop/cpuidle.c216
-rw-r--r--usr/src/cmd/powertop/display.c401
-rw-r--r--usr/src/cmd/powertop/events.c321
-rw-r--r--usr/src/cmd/powertop/i386/Makefile27
-rw-r--r--usr/src/cmd/powertop/powertop.c335
-rw-r--r--usr/src/cmd/powertop/powertop.h277
-rw-r--r--usr/src/cmd/powertop/sparcv9/Makefile28
-rw-r--r--usr/src/cmd/powertop/suggestions.c181
-rw-r--r--usr/src/cmd/powertop/util.c179
-rw-r--r--usr/src/pkgdefs/Makefile1
-rw-r--r--usr/src/pkgdefs/SUNWpowertop/Makefile37
-rw-r--r--usr/src/pkgdefs/SUNWpowertop/pkginfo.tmpl50
-rw-r--r--usr/src/pkgdefs/SUNWpowertop/prototype_com46
-rw-r--r--usr/src/pkgdefs/SUNWpowertop/prototype_i38652
-rw-r--r--usr/src/pkgdefs/SUNWpowertop/prototype_sparc50
22 files changed, 2990 insertions, 0 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index bd8cf275e2..018eec7115 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -214,6 +214,7 @@ COMMON_SUBDIRS = \
cmd/plockstat \
cmd/pools \
cmd/power \
+ cmd/powertop \
cmd/ppgsz \
cmd/praudit \
cmd/prctl \
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 153cc86f1b..c9b15faf8d 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -293,6 +293,7 @@ COMMON_SUBDIRS= \
policykit \
pools \
power \
+ powertop \
ppgsz \
pg \
plockstat \
diff --git a/usr/src/cmd/powertop/Makefile b/usr/src/cmd/powertop/Makefile
new file mode 100644
index 0000000000..65860399af
--- /dev/null
+++ b/usr/src/cmd/powertop/Makefile
@@ -0,0 +1,55 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/powertop/Makefile
+#
+
+PROG = powertop
+
+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: $(SUBDIRS)
+
+clean clobber lint: $(SUBDIRS)
+
+install: $(SUBDIRS)
+ -$(RM) $(ROOTPROG)
+ -$(LN) $(ISAEXEC) $(ROOTPROG)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/powertop/Makefile.com b/usr/src/cmd/powertop/Makefile.com
new file mode 100644
index 0000000000..8408a3b528
--- /dev/null
+++ b/usr/src/cmd/powertop/Makefile.com
@@ -0,0 +1,56 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG = powertop
+OBJS = $(PROG).o display.o battery.o cpufreq.o cpuidle.o events.o util.o suggestions.o
+SRCS = $(OBJS:%.o=../%.c)
+
+include ../../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+CFLAGS64 += $(CCVERBOSE)
+
+LDLIBS += -lcurses -ldtrace -lkstat
+
+FILEMODE = 0555
+GROUP = bin
+
+CLEANFILES += $(OBJS)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+clean:
+ $(RM) $(CLEANFILES)
+
+lint: lint_SRCS
+
+%.o: ../%.c
+ $(COMPILE.c) $<
+
+include ../../Makefile.targ
diff --git a/usr/src/cmd/powertop/amd64/Makefile b/usr/src/cmd/powertop/amd64/Makefile
new file mode 100644
index 0000000000..d99d30ca4c
--- /dev/null
+++ b/usr/src/cmd/powertop/amd64/Makefile
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+include ../../Makefile.cmd.64
+
+install: all $(ROOTPROG64)
diff --git a/usr/src/cmd/powertop/battery.c b/usr/src/cmd/powertop/battery.c
new file mode 100644
index 0000000000..66d899bb66
--- /dev/null
+++ b/usr/src/cmd/powertop/battery.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2008, Intel Corporation
+ * Copyright 2008, Sun Microsystems, Inc
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ * Eric C Saxe <eric.saxe@sun.com>
+ * Aubrey Li <aubrey.li@intel.com>
+ */
+
+/*
+ * GPL Disclaimer
+ *
+ * For the avoidance of doubt, except that if any license choice other
+ * than GPL or LGPL is available it will apply instead, Sun elects to
+ * use only the General Public License version 2 (GPLv2) at this time
+ * for any software where a choice of GPL license versions is made
+ * available with the language indicating that GPLv2 or any later
+ * version may be used, or where a choice of which version of the GPL
+ * is applied is otherwise unspecified.
+ */
+
+#include <string.h>
+#include <kstat.h>
+#include <errno.h>
+#include "powertop.h"
+
+typedef struct battery_state {
+ uint32_t exist;
+ uint32_t power_unit;
+ uint32_t bst_state;
+ double present_rate;
+ double remain_cap;
+ double last_cap;
+} battery_state_t;
+
+battery_state_t battery;
+
+static int battery_stat_snapshot(void);
+
+#define mW2W(value) ((value) / 1000)
+
+void
+print_battery(void)
+{
+ int err;
+
+ (void) memset(&battery, 0, sizeof (battery_state_t));
+
+ /*
+ * The return value of battery_stat_snapsho() can be used for
+ * debug or to show/hide the acpi power line. We currently don't
+ * make the distinction of a system that runs only on AC and one
+ * that runs on battery but has no kstat battery info.
+ *
+ * We still display the estimate power usage for systems
+ * running on AC with a fully charged battery because some
+ * batteries may still consume power.
+ *
+ * If battery_mod_lookup() didn't find a kstat battery module, don't
+ * bother trying to take the snapshot
+ */
+ if (kstat_batt_idx > 0) {
+ if ((err = battery_stat_snapshot()) < 0)
+ pt_error("%s : battery kstat not found %d\n", __FILE__,
+ err);
+ }
+
+ show_acpi_power_line(battery.exist, battery.present_rate,
+ battery.remain_cap, battery.last_cap, battery.bst_state);
+}
+
+static int
+battery_stat_snapshot(void)
+{
+ kstat_ctl_t *kc;
+ kstat_t *ksp;
+ kstat_named_t *knp;
+
+ kc = kstat_open();
+
+ /*
+ * power unit:
+ * 0 - Capacity information is reported in [mWh] and
+ * charge/discharge rate information in [mW]
+ * 1 - Capacity information is reported in [mAh] and
+ * charge/discharge rate information in [mA].
+ */
+ ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0,
+ "battery BIF0");
+
+ if (ksp == NULL) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ (void) kstat_read(kc, ksp, NULL);
+ knp = kstat_data_lookup(ksp, "bif_unit");
+
+ if (knp == NULL) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ battery.power_unit = knp->value.ui32;
+
+ /*
+ * Present rate:
+ * the power or current being supplied or accepted
+ * through the battery's terminal
+ */
+ ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0,
+ "battery BST0");
+
+ if (ksp == NULL) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ (void) kstat_read(kc, ksp, NULL);
+ knp = kstat_data_lookup(ksp, "bst_rate");
+
+ if (knp == NULL) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ if (knp->value.ui32 == 0xFFFFFFFF)
+ battery.present_rate = 0;
+ else {
+ battery.exist = 1;
+ battery.present_rate = mW2W((double)(knp->value.ui32));
+ }
+
+ /*
+ * Last Full charge capacity:
+ * Predicted battery capacity when fully charged.
+ */
+ ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0,
+ "battery BIF0");
+
+ if (ksp == NULL) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ (void) kstat_read(kc, ksp, NULL);
+ knp = kstat_data_lookup(ksp, "bif_last_cap");
+
+ if (knp == NULL) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ battery.last_cap = mW2W((double)(knp->value.ui32));
+
+ /*
+ * Remaining capacity:
+ * the estimated remaining battery capacity
+ */
+ ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0,
+ "battery BST0");
+
+ if (ksp == NULL) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ (void) kstat_read(kc, ksp, NULL);
+ knp = kstat_data_lookup(ksp, "bst_rem_cap");
+
+ if (knp == NULL) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ battery.remain_cap = mW2W((double)(knp->value.ui32));
+
+ /*
+ * Battery State:
+ * Bit0 - 1 : discharging
+ * Bit1 - 1 : charging
+ * Bit2 - 1 : critical energy state
+ */
+ ksp = kstat_lookup(kc, kstat_batt_mod[kstat_batt_idx], 0,
+ "battery BST0");
+
+ if (ksp == NULL) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ (void) kstat_read(kc, ksp, NULL);
+ knp = kstat_data_lookup(ksp, "bst_state");
+
+ if (knp == NULL) {
+ (void) kstat_close(kc);
+ return (-1);
+ }
+
+ battery.bst_state = knp->value.ui32;
+
+ (void) kstat_close(kc);
+
+ return (0);
+}
diff --git a/usr/src/cmd/powertop/cpufreq.c b/usr/src/cmd/powertop/cpufreq.c
new file mode 100644
index 0000000000..18bd393665
--- /dev/null
+++ b/usr/src/cmd/powertop/cpufreq.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright 2008, Intel Corporation
+ * Copyright 2008, Sun Microsystems, Inc
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ * Eric C Saxe <eric.saxe@sun.com>
+ * Aubrey Li <aubrey.li@intel.com>
+ */
+
+/*
+ * GPL Disclaimer
+ *
+ * For the avoidance of doubt, except that if any license choice other
+ * than GPL or LGPL is available it will apply instead, Sun elects to
+ * use only the General Public License version 2 (GPLv2) at this time
+ * for any software where a choice of GPL license versions is made
+ * available with the language indicating that GPLv2 or any later
+ * version may be used, or where a choice of which version of the GPL
+ * is applied is otherwise unspecified.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <dtrace.h>
+#include <kstat.h>
+#include <errno.h>
+#include "powertop.h"
+
+#define HZ2MHZ(speed) ((speed) / 1000000)
+
+static uint64_t max_cpufreq = 0;
+static dtrace_hdl_t *g_dtp;
+
+/*
+ * Enabling PM through /etc/power.conf
+ * See suggest_p_state()
+ */
+static char default_conf[] = "/etc/power.conf";
+static char default_pmconf[] = "/usr/sbin/pmconfig";
+static char cpupm_enable[] = " echo cpupm enable >> /etc/power.conf";
+static char cpupm_treshold[] = " echo cpu-threshold 1s >> /etc/power.conf";
+
+/*
+ * Buffer containing DTrace program to track CPU frequency transitions
+ */
+static const char *pt_cpufreq_dtrace_prog =
+""
+"hrtime_t last[int];"
+""
+"BEGIN"
+"{"
+" begin = timestamp;"
+"}"
+""
+":::cpu-change-speed"
+"/last[((cpudrv_devstate_t *)arg0)->cpu_id] != 0/"
+"{"
+" this->cpu = ((cpudrv_devstate_t *)arg0)->cpu_id;"
+" this->oldspeed = ((cpudrv_pm_t *)arg1)->cur_spd->speed;"
+" @times[this->cpu, this->oldspeed] = sum(timestamp - last[this->cpu]);"
+" last[this->cpu] = timestamp;"
+"}"
+":::cpu-change-speed"
+"/last[((cpudrv_devstate_t *)arg0)->cpu_id] == 0/"
+"{"
+" this->cpu = ((cpudrv_devstate_t *)arg0)->cpu_id;"
+" this->oldspeed = ((cpudrv_pm_t *)arg1)->cur_spd->speed;"
+" @times[this->cpu, this->oldspeed] = sum(timestamp - begin);"
+" last[this->cpu] = timestamp;"
+"}";
+
+static int pt_cpufreq_snapshot(void);
+static int pt_cpufreq_dtrace_walk(const dtrace_aggdata_t *, void *);
+
+/*
+ * Perform setup necessary to enumerate and track CPU speed changes
+ */
+int
+pt_cpufreq_stat_prepare(void)
+{
+ dtrace_prog_t *prog;
+ dtrace_proginfo_t info;
+ dtrace_optval_t statustime;
+
+ kstat_ctl_t *kc;
+ kstat_t *ksp;
+ kstat_named_t *knp;
+
+ pstate_info_t *state;
+ char *s, *token;
+ int err;
+
+ state = pstate_info;
+ cpu_power_states = calloc((size_t)g_ncpus, sizeof (cpu_power_info_t));
+
+ /*
+ * Enumerate the CPU frequencies
+ */
+ if ((kc = kstat_open()) == NULL)
+ return (errno);
+
+ ksp = kstat_lookup(kc, "cpu_info", cpu_table[0], NULL);
+
+ if (ksp == NULL)
+ return (errno);
+
+ (void) kstat_read(kc, ksp, NULL);
+
+ knp = kstat_data_lookup(ksp, "supported_frequencies_Hz");
+ s = knp->value.str.addr.ptr;
+
+ npstates = 0;
+
+ for (token = strtok(s, ":"), s = NULL;
+ NULL != token && npstates < NSTATES;
+ token = strtok(NULL, ":")) {
+
+ state->speed = HZ2MHZ(atoll(token));
+
+ if (state->speed > max_cpufreq)
+ max_cpufreq = state->speed;
+
+ state->total_time = (uint64_t)0;
+
+ npstates++;
+ state++;
+ }
+
+ if (token != NULL)
+ pt_error("%s : exceeded NSTATES\n", __FILE__);
+
+ (void) kstat_close(kc);
+
+ /*
+ * Return if speed transition is not supported
+ */
+ if (npstates < 2)
+ return (-1);
+
+ /*
+ * Setup DTrace to look for CPU frequency changes
+ */
+ if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) {
+ pt_error("%s : cannot open dtrace library: %s\n", __FILE__,
+ dtrace_errmsg(NULL, err));
+ return (-2);
+ }
+ if ((prog = dtrace_program_strcompile(g_dtp, pt_cpufreq_dtrace_prog,
+ DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) {
+ pt_error("%s : cpu-change-speed probe unavailable\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ if (dtrace_program_exec(g_dtp, prog, &info) == -1) {
+ pt_error("%s : failed to enable speed probe\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ if (dtrace_setopt(g_dtp, "aggsize", "128k") == -1) {
+ pt_error("%s : failed to set speed 'aggsize'\n", __FILE__);
+ }
+ if (dtrace_setopt(g_dtp, "aggrate", "0") == -1) {
+ pt_error("%s : failed to set speed 'aggrate'\n", __FILE__);
+ }
+ if (dtrace_setopt(g_dtp, "aggpercpu", 0) == -1) {
+ pt_error("%s : failed to set speed 'aggpercpu'\n", __FILE__);
+ }
+ if (dtrace_go(g_dtp) != 0) {
+ pt_error("%s : failed to start speed observation", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ if (dtrace_getopt(g_dtp, "statusrate", &statustime) == -1) {
+ pt_error("%s : failed to get speed 'statusrate'\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+
+ return (0);
+}
+
+/*
+ * The DTrace probes have already been enabled, and are tracking
+ * CPU speed transitions. Take a snapshot of the aggregations, and
+ * look for any CPUs that have made a speed transition over the last
+ * sampling interval. Note that the aggregations may be empty if no
+ * speed transitions took place over the last interval. In that case,
+ * notate that we have already accounted for the time, so that when
+ * we do encounter a speed transition in a future sampling interval
+ * we can subtract that time back out.
+ */
+int
+pt_cpufreq_stat_collect(double interval)
+{
+ int cpu, i, ret;
+ uint64_t speed;
+ hrtime_t duration;
+ cpu_power_info_t *cpu_pow;
+
+ /*
+ * Zero out the interval time reported by DTrace for
+ * this interval
+ */
+ for (i = 0; i < npstates; i++)
+ pstate_info[i].total_time = 0;
+
+ for (i = 0; i < g_ncpus; i++)
+ cpu_power_states[i].dtrace_time = 0;
+
+ if (dtrace_status(g_dtp) == -1)
+ return (-1);
+
+ if (dtrace_aggregate_snap(g_dtp) != 0)
+ pt_error("%s : failed to add to stats aggregation", __FILE__);
+
+ if (dtrace_aggregate_walk_keyvarsorted(g_dtp, pt_cpufreq_dtrace_walk,
+ NULL) != 0)
+ pt_error("%s : failed to sort stats aggregation", __FILE__);
+
+ dtrace_aggregate_clear(g_dtp);
+
+ if ((ret = pt_cpufreq_snapshot()) != 0) {
+ pt_error("%s : failed to add to stats aggregation", __FILE__);
+ return (ret);
+ }
+
+ for (cpu = 0; cpu < g_ncpus; cpu++) {
+ cpu_pow = &cpu_power_states[cpu];
+
+ speed = cpu_pow->current_pstate;
+
+ duration = (hrtime_t)((interval * NANOSEC)) -
+ cpu_pow->dtrace_time;
+
+ for (i = 0; i < npstates; i++) {
+ if (pstate_info[i].speed == speed) {
+ pstate_info[i].total_time += duration;
+ cpu_pow->time_accounted += duration;
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Take a snapshot of each CPU's speed by looking through the cpu_info kstats.
+ */
+static int
+pt_cpufreq_snapshot(void)
+{
+ kstat_ctl_t *kc;
+ kstat_t *ksp;
+ kstat_named_t *knp;
+ int cpu;
+ cpu_power_info_t *state;
+
+ if ((kc = kstat_open()) == NULL)
+ return (errno);
+
+ for (cpu = 0; cpu < g_ncpus; cpu++) {
+ ksp = kstat_lookup(kc, "cpu_info", cpu_table[cpu], NULL);
+ if (ksp == NULL) {
+ pt_error("%s : couldn't find cpu_info kstat for CPU "
+ "%d\n", __FILE__, cpu);
+ (void) kstat_close(kc);
+ return (1);
+ }
+
+ if (kstat_read(kc, ksp, NULL) == -1) {
+ pt_error("%s : couldn't read cpu_info kstat for "
+ "CPU %d\n", __FILE__, cpu);
+ (void) kstat_close(kc);
+ return (2);
+ }
+
+ knp = kstat_data_lookup(ksp, "current_clock_Hz");
+ if (knp == NULL) {
+ pt_error("%s : couldn't find current_clock_Hz "
+ "kstat for CPU %d\n", __FILE__, cpu);
+ (void) kstat_close(kc);
+ return (3);
+ }
+
+ state = &cpu_power_states[cpu];
+ state->current_pstate = HZ2MHZ(knp->value.ui64);
+ }
+
+ if (kstat_close(kc) != 0)
+ pt_error("%s : couldn't close kstat\n", __FILE__);
+
+ return (0);
+}
+
+/*
+ * DTrace aggregation walker that sorts through a snapshot of the
+ * aggregation data collected during firings of the cpu-change-speed
+ * probe.
+ */
+/*ARGSUSED*/
+static int
+pt_cpufreq_dtrace_walk(const dtrace_aggdata_t *data, void *arg)
+{
+ dtrace_aggdesc_t *aggdesc = data->dtada_desc;
+ dtrace_recdesc_t *cpu_rec, *speed_rec;
+ cpu_power_info_t *cpu_pow;
+ int32_t cpu;
+ uint64_t speed;
+ hrtime_t dt_state_time = 0;
+ int i;
+
+ if (strcmp(aggdesc->dtagd_name, "times") == 0) {
+ cpu_rec = &aggdesc->dtagd_rec[1];
+ speed_rec = &aggdesc->dtagd_rec[2];
+
+ for (i = 0; i < g_ncpus; i++) {
+ /* LINTED - alignment */
+ dt_state_time += *((hrtime_t *)(data->dtada_percpu[i]));
+ }
+
+ /* LINTED - alignment */
+ cpu = *(int32_t *)(data->dtada_data + cpu_rec->dtrd_offset);
+ /* LINTED - alignment */
+ speed = *(uint64_t *)(data->dtada_data +
+ speed_rec->dtrd_offset);
+
+ if (speed == 0) {
+ speed = max_cpufreq;
+ }
+
+ /*
+ * We have an aggregation record for "cpu" being at "speed"
+ * for an interval of "n" nanoseconds. The reported interval
+ * may exceed the powertop sampling interval, since we only
+ * notice during potentially infrequent firings of the
+ * "speed change" DTrace probe. In this case powertop would
+ * have already accounted for the portions of the interval
+ * that happened during prior powertop sampings, so subtract
+ * out time already accounted.
+ */
+ cpu_pow = &cpu_power_states[cpu];
+
+ for (i = 0; i < npstates; i++) {
+ if (pstate_info[i].speed == speed) {
+ if (cpu_pow->time_accounted > 0) {
+ if (dt_state_time == 0)
+ continue;
+ if (dt_state_time >
+ cpu_pow->time_accounted) {
+ dt_state_time -=
+ cpu_pow->time_accounted;
+ cpu_pow->time_accounted = 0;
+ }
+ }
+ pstate_info[i].total_time += dt_state_time;
+ cpu_pow->dtrace_time += dt_state_time;
+ }
+ }
+ }
+ return (DTRACE_AGGWALK_NEXT);
+}
+
+/*
+ * Used as a suggestion, sets PM in /etc/power.conf and
+ * a 1sec threshold, then calls /usr/sbin/pmconfig
+ */
+void
+enable_p_state(void)
+{
+ (void) system(cpupm_enable);
+ (void) system(cpupm_treshold);
+ (void) system(default_pmconf);
+}
+
+/*
+ * Checks if PM is enabled in /etc/power.conf, enabling if not
+ */
+void
+suggest_p_state(void)
+{
+ char line[1024];
+ FILE *file;
+
+ /*
+ * Return if speed transition is not supported
+ */
+ if (npstates < 2)
+ return;
+
+ file = fopen(default_conf, "r");
+
+ if (!file)
+ return;
+
+ (void) memset(line, 0, 1024);
+
+ while (fgets(line, 1023, file)) {
+ if (strstr(line, "cpupm")) {
+ if (strstr(line, "enable")) {
+ (void) fclose(file);
+ return;
+ }
+ }
+ }
+
+ add_suggestion("Suggestion: enable CPU power management by "
+ "pressing the P key", 40, 'P', "P - Enable p-state",
+ enable_p_state);
+
+ (void) fclose(file);
+}
diff --git a/usr/src/cmd/powertop/cpuidle.c b/usr/src/cmd/powertop/cpuidle.c
new file mode 100644
index 0000000000..7682ae0425
--- /dev/null
+++ b/usr/src/cmd/powertop/cpuidle.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2008, Intel Corporation
+ * Copyright 2008, Sun Microsystems, Inc
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ * Eric C Saxe <eric.saxe@sun.com>
+ * Aubrey Li <aubrey.li@intel.com>
+ */
+
+/*
+ * GPL Disclaimer
+ *
+ * For the avoidance of doubt, except that if any license choice other
+ * than GPL or LGPL is available it will apply instead, Sun elects to
+ * use only the General Public License version 2 (GPLv2) at this time
+ * for any software where a choice of GPL license versions is made
+ * available with the language indicating that GPLv2 or any later
+ * version may be used, or where a choice of which version of the GPL
+ * is applied is otherwise unspecified.
+ */
+
+#include <string.h>
+#include <dtrace.h>
+#include "powertop.h"
+
+static dtrace_hdl_t *g_dtp;
+
+/*
+ * Buffer containing DTrace program to track CPU idle state transitions
+ */
+static const char *pt_cpuidle_dtrace_prog =
+":::idle-state-transition"
+"/arg0 != 0/"
+"{"
+" self->start = timestamp;"
+" self->state = arg0;"
+"}"
+""
+":::idle-state-transition"
+"/arg0 == 0 && self->start/"
+"{"
+" @number[self->state] = count();"
+" @times[self->state] = sum((timestamp - self->start)/1000000);"
+" self->start = 0;"
+" self->state = 0;"
+"}";
+
+static int pt_cpuidle_dtrace_walk(const dtrace_aggdata_t *, void *);
+
+/*
+ * Perform setup necessary to track CPU idle state transitions
+ */
+int
+pt_cpuidle_stat_prepare(void)
+{
+ dtrace_prog_t *prog;
+ dtrace_proginfo_t info;
+ dtrace_optval_t statustime;
+ int err;
+
+ if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) {
+ pt_error("%s : cannot open dtrace library: %s\n", __FILE__,
+ dtrace_errmsg(NULL, err));
+ return (-1);
+ }
+ if ((prog = dtrace_program_strcompile(g_dtp, pt_cpuidle_dtrace_prog,
+ DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) {
+ pt_error("%s : C-State DTrace probes unavailable\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ if (dtrace_program_exec(g_dtp, prog, &info) == -1) {
+ pt_error("%s : failed to enable C State probes\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ if (dtrace_setopt(g_dtp, "aggsize", "128k") == -1) {
+ pt_error("%s : failed to set C-state 'aggsize'\n", __FILE__);
+ }
+ if (dtrace_setopt(g_dtp, "aggrate", "0") == -1) {
+ pt_error("%s : failed to set C-state'aggrate'\n", __FILE__);
+ }
+ if (dtrace_setopt(g_dtp, "aggpercpu", 0) == -1) {
+ pt_error("%s : failed to set C-state 'aggpercpu'\n", __FILE__);
+ }
+ if (dtrace_go(g_dtp) != 0) {
+ pt_error("%s : failed to start C-state observation", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ if (dtrace_getopt(g_dtp, "statusrate", &statustime) == -1) {
+ pt_error("%s : failed to get C-state 'statusrate'\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ return (0);
+}
+
+/*
+ * The DTrace probes have been enabled, and are tracking CPU idle state
+ * transitions. Take a snapshot of the aggregations, and invoke the aggregation
+ * walker to process any records. The walker does most of the accounting work
+ * chalking up time spent into the cstate_info structure.
+ */
+int
+pt_cpuidle_stat_collect(double interval)
+{
+ int i;
+ hrtime_t t = 0;
+
+ /*
+ * Zero out the interval time reported by DTrace for
+ * this interval
+ */
+ for (i = 0; i < NSTATES; i++) {
+ cstate_info[i].total_time = 0;
+ cstate_info[i].events = 0;
+ }
+
+ /*
+ * Assume that all the time spent in this interval will
+ * be the default "0" state. The DTrace walker will reallocate
+ * time out of the default bucket as it processes aggregation
+ * records for time spent in other states.
+ */
+ cstate_info[0].total_time = (long)(interval * g_ncpus * 1000);
+
+ if (dtrace_status(g_dtp) == -1)
+ return (-1);
+
+ if (dtrace_aggregate_snap(g_dtp) != 0)
+ pt_error("%s : failed to add to aggregation", __FILE__);
+
+ if (dtrace_aggregate_walk_keyvarsorted(g_dtp, pt_cpuidle_dtrace_walk,
+ NULL) != 0)
+ pt_error("%s : failed to sort aggregation", __FILE__);
+
+ dtrace_aggregate_clear(g_dtp);
+
+ /*
+ * Populate cstate_info with the correct amount of time spent
+ * in each C state and update the number of C states in max_cstate
+ */
+ total_c_time = 0;
+ for (i = 0; i < NSTATES; i++) {
+ if (cstate_info[i].total_time > 0) {
+ total_c_time += cstate_info[i].total_time;
+ if (i > max_cstate)
+ max_cstate = i;
+ if (cstate_info[i].last_time > t) {
+ t = cstate_info[i].last_time;
+ longest_cstate = i;
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * DTrace aggregation walker that sorts through a snapshot of data records
+ * collected during firings of the idle-state-transition probe.
+ *
+ * XXX A way of querying the current idle state for a CPU is needed in addition
+ * to logic similar to that in cpufreq.c
+ */
+/*ARGSUSED*/
+static int
+pt_cpuidle_dtrace_walk(const dtrace_aggdata_t *data, void *arg)
+{
+ dtrace_aggdesc_t *aggdesc = data->dtada_desc;
+ dtrace_recdesc_t *rec;
+ uint64_t n = 0;
+ int32_t state;
+ int i;
+
+ rec = &aggdesc->dtagd_rec[1];
+ /* LINTED - alignment */
+ state = *(int32_t *)(data->dtada_data + rec->dtrd_offset);
+
+ if (strcmp(aggdesc->dtagd_name, "number") == 0) {
+ for (i = 0; i < g_ncpus; i++) {
+ /* LINTED - alignment */
+ n += *((uint64_t *)(data->dtada_percpu[i]));
+ }
+ total_events += n;
+ cstate_info[state].events += n;
+ }
+ else
+ if (strcmp(aggdesc->dtagd_name, "times") == 0) {
+ for (i = 0; i < g_ncpus; i++) {
+ /* LINTED - alignment */
+ n += *((uint64_t *)(data->dtada_percpu[i]));
+ }
+ cstate_info[state].last_time = n;
+ cstate_info[state].total_time += n;
+ if (cstate_info[0].total_time >= n)
+ cstate_info[0].total_time -= n;
+ }
+
+ return (DTRACE_AGGWALK_NEXT);
+}
diff --git a/usr/src/cmd/powertop/display.c b/usr/src/cmd/powertop/display.c
new file mode 100644
index 0000000000..322cd1c613
--- /dev/null
+++ b/usr/src/cmd/powertop/display.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright 2008, Intel Corporation
+ * Copyright 2008, Sun Microsystems, Inc
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ * Eric C Saxe <eric.saxe@sun.com>
+ * Aubrey Li <aubrey.li@intel.com>
+ */
+
+/*
+ * GPL Disclaimer
+ *
+ * For the avoidance of doubt, except that if any license choice other
+ * than GPL or LGPL is available it will apply instead, Sun elects to
+ * use only the General Public License version 2 (GPLv2) at this time
+ * for any software where a choice of GPL license versions is made
+ * available with the language indicating that GPLv2 or any later
+ * version may be used, or where a choice of which version of the GPL
+ * is applied is otherwise unspecified.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <curses.h>
+#include "powertop.h"
+
+static WINDOW *title_bar_window;
+static WINDOW *cstate_window;
+static WINDOW *wakeup_window;
+static WINDOW *acpi_power_window;
+static WINDOW *eventstat_window;
+static WINDOW *suggestion_window;
+static WINDOW *status_bar_window;
+
+#define print(win, y, x, fmt, args...) \
+ if (dump) \
+ (void) printf(fmt, ## args); \
+ else \
+ (void) mvwprintw(win, y, x, fmt, ## args);
+
+char status_bar_slots[10][40];
+int maxx, maxy;
+
+static void
+zap_windows(void)
+{
+ if (title_bar_window) {
+ (void) delwin(title_bar_window);
+ title_bar_window = NULL;
+ }
+ if (cstate_window) {
+ (void) delwin(cstate_window);
+ cstate_window = NULL;
+ }
+ if (wakeup_window) {
+ (void) delwin(wakeup_window);
+ wakeup_window = NULL;
+ }
+ if (acpi_power_window) {
+ (void) delwin(acpi_power_window);
+ acpi_power_window = NULL;
+ }
+ if (eventstat_window) {
+ (void) delwin(eventstat_window);
+ eventstat_window = NULL;
+ }
+ if (suggestion_window) {
+ (void) delwin(suggestion_window);
+ suggestion_window = NULL;
+ }
+ if (status_bar_window) {
+ (void) delwin(status_bar_window);
+ status_bar_window = NULL;
+ }
+}
+
+void
+cleanup_curses(void)
+{
+ (void) endwin();
+}
+
+/*
+ * This part was re-written to be human readable and easy to modify. Please
+ * try to keep it that way and help us save some time.
+ *
+ * Friendly reminder:
+ * subwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x)
+ */
+void
+setup_windows(void)
+{
+ /*
+ * These variables are used to properly set the initial y position and
+ * number of lines in each subwindow, as the number of supported CPU
+ * states affects their placement.
+ */
+ int cstate_lines, event_lines, pos_y;
+
+ getmaxyx(stdscr, maxy, maxx);
+
+ zap_windows();
+
+ cstate_lines = TITLE_LINE + max((max_cstate+1), npstates);
+
+ pos_y = 0;
+ title_bar_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
+
+ pos_y += NEXT_LINE + BLANK_LINE;
+ cstate_window = subwin(stdscr, cstate_lines, maxx, pos_y, 0);
+
+ pos_y += cstate_lines + BLANK_LINE;
+ wakeup_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
+
+ pos_y += NEXT_LINE;
+ acpi_power_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
+
+ pos_y += NEXT_LINE + BLANK_LINE;
+ event_lines = maxy - SINGLE_LINE_SW - NEXT_LINE - LENGTH_SUGG_SW -
+ pos_y;
+ eventstat_window = subwin(stdscr, event_lines, maxx, pos_y, 0);
+
+ pos_y += event_lines + NEXT_LINE;
+ suggestion_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
+
+ pos_y += BLANK_LINE + NEXT_LINE;
+ status_bar_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0);
+
+ (void) strcpy(status_bar_slots[0], _(" Q - Quit "));
+ (void) strcpy(status_bar_slots[1], _(" R - Refresh "));
+
+ (void) werase(stdscr);
+ (void) wrefresh(status_bar_window);
+}
+
+void
+initialize_curses(void)
+{
+ (void) initscr();
+ (void) start_color();
+
+ /*
+ * Enable keyboard mapping
+ */
+ (void) keypad(stdscr, TRUE);
+
+ /*
+ * Tell curses not to do NL->CR/NL on output
+ */
+ (void) nonl();
+
+ /*
+ * Take input chars one at a time, no wait for \n
+ */
+ (void) cbreak();
+
+ /*
+ * Dont echo input
+ */
+ (void) noecho();
+
+ /*
+ * Turn off cursor
+ */
+ (void) curs_set(0);
+
+ (void) init_pair(PT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK);
+ (void) init_pair(PT_COLOR_HEADER_BAR, COLOR_BLACK, COLOR_WHITE);
+ (void) init_pair(PT_COLOR_ERROR, COLOR_BLACK, COLOR_RED);
+ (void) init_pair(PT_COLOR_RED, COLOR_WHITE, COLOR_RED);
+ (void) init_pair(PT_COLOR_YELLOW, COLOR_WHITE, COLOR_YELLOW);
+ (void) init_pair(PT_COLOR_GREEN, COLOR_WHITE, COLOR_GREEN);
+ (void) init_pair(PT_COLOR_BLUE, COLOR_WHITE, COLOR_BLUE);
+ (void) init_pair(PT_COLOR_BRIGHT, COLOR_WHITE, COLOR_BLACK);
+
+ (void) atexit(cleanup_curses);
+}
+
+void
+show_title_bar(void)
+{
+ int i, x = 0, y = 0;
+ char title_pad[10];
+
+ (void) wattrset(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR));
+ (void) wbkgd(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR));
+ (void) werase(title_bar_window);
+
+ (void) snprintf(title_pad, 10, "%%%ds",
+ (maxx - strlen(TITLE))/2 + strlen(TITLE));
+ /* LINTED: E_SEC_PRINTF_VAR_FMT */
+ print(title_bar_window, y, x, title_pad, TITLE);
+
+ (void) wrefresh(title_bar_window);
+ (void) werase(status_bar_window);
+
+ for (i = 0; i < 10; i++) {
+ if (strlen(status_bar_slots[i]) == 0)
+ continue;
+ (void) wattron(status_bar_window, A_REVERSE);
+ print(status_bar_window, y, x, "%s", status_bar_slots[i]);
+ (void) wattroff(status_bar_window, A_REVERSE);
+ x += strlen(status_bar_slots[i]) + 1;
+ }
+ (void) wnoutrefresh(status_bar_window);
+}
+
+void
+show_cstates(void)
+{
+ char c[100];
+ int i;
+ double total_pstates = 0.0, avg, res;
+
+ if (!dump) {
+ (void) werase(cstate_window);
+ (void) wattrset(cstate_window, COLOR_PAIR(PT_COLOR_DEFAULT));
+ (void) wbkgd(cstate_window, COLOR_PAIR(PT_COLOR_DEFAULT));
+ }
+
+ print(cstate_window, 0, 0, "%s", "Cn\t\t\tAvg residency\n");
+
+ res = (((double)cstate_info[0].total_time / total_c_time)) * 100;
+ (void) sprintf(c, "C0 (cpu running)\t\t(%.1f%%)\n", (float)res);
+ print(cstate_window, 1, 0, "%s", c);
+
+ for (i = 1; i <= max_cstate; i++) {
+ /*
+ * In situations where the load is too intensive, the system
+ * might not transition at all.
+ */
+ if (cstate_info[i].events > 0)
+ avg = (((double)cstate_info[i].total_time/g_ncpus)/
+ cstate_info[i].events);
+ else
+ avg = 0;
+
+ res = ((double)cstate_info[i].total_time/total_c_time) * 100;
+
+ (void) sprintf(c, "C%d\t\t\t%.1fms\t(%.1f%%)\n", i, (float)avg,
+ (float)res);
+ print(cstate_window, i + 1, 0, "%s", c);
+ }
+
+ print(cstate_window, 0, 48, "%s", "P-states (frequencies)\n");
+
+ if (npstates < 2) {
+ (void) sprintf(c, "%4lu Mhz\t%.1f%%",
+ (long)pstate_info[0].speed, 100.0);
+ print(cstate_window, 1, 48, "%s\n", c);
+ } else {
+ for (i = 0; i < npstates; i++) {
+ total_pstates += (double)(pstate_info[i].total_time/
+ g_ncpus/1000000);
+ }
+
+ for (i = 0; i < npstates; i++) {
+ (void) sprintf(c, "%4lu Mhz\t%.1f%%",
+ (long)pstate_info[i].speed,
+ 100 * (pstate_info[i].total_time/g_ncpus/1000000
+ /total_pstates));
+ print(cstate_window, i+1, 48, "%s\n", c);
+ }
+ }
+ if (!dump)
+ (void) wnoutrefresh(cstate_window);
+}
+
+void
+show_acpi_power_line(uint32_t flag, double rate, double rem_cap, double cap,
+ uint32_t state)
+{
+ char buffer[1024];
+
+ (void) sprintf(buffer, _("no ACPI power usage estimate available"));
+
+ if (!dump)
+ (void) werase(acpi_power_window);
+ if (flag) {
+ char *c;
+ (void) sprintf(buffer, "Power usage (ACPI estimate): %.3fW",
+ rate);
+ (void) strcat(buffer, " ");
+ c = &buffer[strlen(buffer)];
+ switch (state) {
+ case 0:
+ (void) sprintf(c, "(running on AC power, fully "
+ "charged)");
+ break;
+ case 1:
+ (void) sprintf(c, "(discharging: %3.1f hours)",
+ rem_cap/rate);
+ break;
+ case 2:
+ (void) sprintf(c, "(charging: %3.1f hours)",
+ (cap - rem_cap)/rate);
+ break;
+ case 4:
+ (void) sprintf(c, "(##critically low battery power##)");
+ break;
+ }
+
+ }
+ print(acpi_power_window, 0, 0, "%s\n", buffer);
+ if (!dump)
+ (void) wnoutrefresh(acpi_power_window);
+}
+
+void
+show_wakeups(double interval)
+{
+ char c[100];
+
+ if (!dump) {
+ (void) werase(wakeup_window);
+ (void) wbkgd(wakeup_window, COLOR_PAIR(PT_COLOR_RED));
+ (void) wattron(wakeup_window, A_BOLD);
+ }
+ (void) sprintf(c, "Wakeups-from-idle per second: %4.1f\tinterval: "
+ "%.1fs", (double)(total_events/interval), interval);
+ print(wakeup_window, 0, 0, "%s\n", c);
+ if (!dump)
+ (void) wnoutrefresh(wakeup_window);
+}
+
+void
+show_eventstats(double interval)
+{
+ char c[100];
+ int i;
+ double events;
+ event_info_t *p_event = event_info;
+
+ if (!dump) {
+ (void) werase(eventstat_window);
+ (void) wattrset(eventstat_window, COLOR_PAIR(PT_COLOR_DEFAULT));
+ (void) wbkgd(eventstat_window, COLOR_PAIR(PT_COLOR_DEFAULT));
+ }
+
+ /*
+ * Sort the event report list
+ */
+ if (top_events > EVENT_NUM_MAX)
+ top_events = EVENT_NUM_MAX;
+
+ qsort((void *)event_info, top_events, sizeof (event_info_t),
+ event_compare);
+
+ print(eventstat_window, 0, 0, "%s", "Top causes for wakeups:\n");
+
+ for (i = 0; i < top_events; i++, p_event++) {
+
+ if (total_events > 0)
+ events = (double)p_event->total_count/
+ (double)total_events;
+ else
+ events = 0;
+
+ (void) sprintf(c, "%4.1f%% (%5.1f)", 100 * events,
+ (double)p_event->total_count/interval);
+ print(eventstat_window, i+1, 0, "%s", c);
+ print(eventstat_window, i+1, 16, "%20s :",
+ p_event->offender_name);
+ print(eventstat_window, i+1, 40, "%-64s\n",
+ p_event->offense_name);
+ }
+ if (!dump)
+ (void) wnoutrefresh(eventstat_window);
+}
+
+void
+show_suggestion(char *sug)
+{
+ (void) werase(suggestion_window);
+ print(suggestion_window, 0, 0, "%s", sug);
+ (void) wnoutrefresh(suggestion_window);
+}
+
+void
+update_windows(void)
+{
+ (void) doupdate();
+}
diff --git a/usr/src/cmd/powertop/events.c b/usr/src/cmd/powertop/events.c
new file mode 100644
index 0000000000..6abbeb003b
--- /dev/null
+++ b/usr/src/cmd/powertop/events.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2008, Intel Corporation
+ * Copyright 2008, Sun Microsystems, Inc
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ * Eric C Saxe <eric.saxe@sun.com>
+ * Aubrey Li <aubrey.li@intel.com>
+ */
+
+/*
+ * GPL Disclaimer
+ *
+ * For the avoidance of doubt, except that if any license choice other
+ * than GPL or LGPL is available it will apply instead, Sun elects to
+ * use only the General Public License version 2 (GPLv2) at this time
+ * for any software where a choice of GPL license versions is made
+ * available with the language indicating that GPLv2 or any later
+ * version may be used, or where a choice of which version of the GPL
+ * is applied is otherwise unspecified.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <dtrace.h>
+#include "powertop.h"
+
+static dtrace_hdl_t *g_dtp;
+/*
+ * DTrace scripts for observing interrupts, callouts and cyclic events
+ * that cause CPU activity. Such activity prevents the processor from
+ * entering lower power states and reducing power consumption.
+ *
+ * g_prog is the default script
+ */
+static const char *g_prog =
+"interrupt-complete"
+"/arg0 != NULL && arg3 !=0/"
+"{"
+" this->devi = (struct dev_info *)arg0;"
+" @interrupts[stringof(`devnamesp[this->devi->devi_major].dn_name),"
+" this->devi->devi_instance] = count();"
+"}"
+""
+"sdt:::callout-start"
+"/(caddr_t)((callout_t *)arg0)->c_func == (caddr_t)&`setrun/"
+"{"
+" this->thr = (kthread_t *)(((callout_t *)arg0)->c_arg);"
+" @events_u[stringof(this->thr->t_procp->p_user.u_comm)] = count();"
+"}"
+""
+"sdt:::callout-start"
+"/(caddr_t)((callout_t *)arg0)->c_func != (caddr_t)&`setrun/"
+"{"
+" @events_k[(caddr_t)((callout_t *)arg0)->c_func] = count();"
+"}"
+""
+"sdt:::cyclic-start"
+"/(caddr_t)((cyclic_t *)arg0)->cy_handler == (caddr_t)&`clock/"
+"{"
+" @events_k[(caddr_t)((cyclic_t *)arg0)->cy_handler] = count();"
+"}"
+""
+"sysinfo:::xcalls"
+"/pid != $pid/"
+"{"
+" @events_x[execname] = sum(arg0);"
+"}";
+
+/*
+ * g_prog_V is enabled through the -v option, it includes cyclic events
+ * in the report, allowing a complete view of system activity
+ */
+static const char *g_prog_v =
+"interrupt-complete"
+"/arg0 != NULL && arg3 !=0/"
+"{"
+" this->devi = (struct dev_info *)arg0;"
+" @interrupts[stringof(`devnamesp[this->devi->devi_major].dn_name),"
+" this->devi->devi_instance] = count();"
+"}"
+""
+"sdt:::callout-start"
+"/(caddr_t)((callout_t *)arg0)->c_func == (caddr_t)&`setrun/"
+"{"
+" this->thr = (kthread_t *)(((callout_t *)arg0)->c_arg);"
+" @events_u[stringof(this->thr->t_procp->p_user.u_comm)] = count();"
+"}"
+""
+"sdt:::callout-start"
+"/(caddr_t)((callout_t *)arg0)->c_func != (caddr_t)&`setrun/"
+"{"
+" @events_k[(caddr_t)((callout_t *)arg0)->c_func] = count();"
+"}"
+""
+"sdt:::cyclic-start"
+"/(caddr_t)((cyclic_t *)arg0)->cy_handler != (caddr_t)&`dtrace_state_deadman &&"
+" (caddr_t)((cyclic_t *)arg0)->cy_handler != (caddr_t)&`dtrace_state_clean/"
+"{"
+" @events_k[(caddr_t)((cyclic_t *)arg0)->cy_handler] = count();"
+"}"
+""
+"sysinfo:::xcalls"
+"/pid != $pid/"
+"{"
+" @events_x[execname] = sum(arg0);"
+"}";
+
+/*ARGSUSED*/
+static int
+walk(const dtrace_aggdata_t *data, void *arg)
+{
+ dtrace_aggdesc_t *aggdesc = data->dtada_desc;
+ dtrace_recdesc_t *rec1, *rec2;
+ dtrace_syminfo_t dts;
+ char *offense_name;
+ uint64_t offender_addr;
+ int32_t *instance;
+ int i;
+ uint64_t n = 0;
+ GElf_Sym sym;
+
+ if (top_events >= EVENT_NUM_MAX)
+ return (0);
+
+ rec1 = &aggdesc->dtagd_rec[1];
+ rec2 = &aggdesc->dtagd_rec[2];
+
+ /*
+ * Report interrupts
+ */
+ if (strcmp(aggdesc->dtagd_name, "interrupts") == 0) {
+ offense_name = data->dtada_data + rec1->dtrd_offset;
+
+ /* LINTED - alignment */
+ instance = (int32_t *)(data->dtada_data + rec2->dtrd_offset);
+ (void) snprintf((char *)(p_event->offender_name),
+ EVENT_NAME_MAX, "%s", "<interrupt>");
+ (void) snprintf((char *)(p_event->offense_name), EVENT_NAME_MAX,
+ "%s#%d", offense_name, *instance);
+ /*
+ * Report kernel events
+ */
+ } else if (strcmp(aggdesc->dtagd_name, "events_k") == 0) {
+
+ (void) snprintf((char *)(p_event->offender_name),
+ EVENT_NAME_MAX, "%s", "<kernel>");
+
+ /*
+ * Casting offender_addr to the wrong type will cause
+ * dtrace_lookup_by_addr to return 0 and the report
+ * to show an address instead of a name.
+ */
+ switch (bit_depth) {
+ case 32:
+ /* LINTED - alignment */
+ offender_addr = *(uint32_t *)(data->dtada_data +
+ rec1->dtrd_offset);
+ break;
+ case 64:
+ /* LINTED - alignment */
+ offender_addr = *(uint64_t *)(data->dtada_data +
+ rec1->dtrd_offset);
+ break;
+ }
+
+ /*
+ * We have the address of the kernel callout.
+ * Try to resolve it into a meaningful symbol
+ */
+ if (dtrace_lookup_by_addr(g_dtp, offender_addr,
+ &sym, &dts) == 0) {
+ (void) snprintf((char *)(p_event->offense_name),
+ EVENT_NAME_MAX, "%s`%s", dts.dts_object,
+ dts.dts_name);
+ } else {
+ (void) snprintf((char *)(p_event->offense_name),
+ EVENT_NAME_MAX, "0x%llx", offender_addr);
+ }
+ /*
+ * Report user events
+ */
+ } else if (strcmp(aggdesc->dtagd_name, "events_u") == 0) {
+ offense_name = data->dtada_data + rec1->dtrd_offset;
+
+ (void) snprintf((char *)(p_event->offender_name),
+ EVENT_NAME_MAX, "%s", offense_name);
+ (void) snprintf((char *)(p_event->offense_name),
+ EVENT_NAME_MAX, "<scheduled timeout expiration>");
+ /*
+ * Report cross calls
+ */
+ } else if (strcmp(aggdesc->dtagd_name, "events_x") == 0) {
+ offense_name = data->dtada_data + rec1->dtrd_offset;
+
+ (void) snprintf((char *)(p_event->offender_name),
+ EVENT_NAME_MAX, "%s", offense_name);
+ (void) snprintf((char *)(p_event->offense_name),
+ EVENT_NAME_MAX, "<cross calls>");
+ /*
+ * Report unknown events
+ */
+ } else {
+ (void) snprintf((char *)(p_event->offender_name),
+ EVENT_NAME_MAX, "%s", "<unknown>");
+ (void) snprintf((char *)(p_event->offense_name),
+ EVENT_NAME_MAX, "%s", "<unknown>");
+ }
+
+ for (i = 0; i < g_ncpus; i++)
+ /* LINTED - alignment */
+ n += *((uint64_t *)(data->dtada_percpu[i]));
+
+ p_event->total_count = n;
+
+ p_event++;
+ top_events++;
+
+ return (DTRACE_AGGWALK_NEXT);
+}
+
+int
+pt_events_stat_prepare(void)
+{
+ dtrace_prog_t *prog;
+ dtrace_proginfo_t info;
+ int err;
+ dtrace_optval_t statustime;
+
+ p_event = event_info;
+
+ if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) {
+ pt_error("%s : cannot open dtrace library: %s\n", __FILE__,
+ dtrace_errmsg(NULL, err));
+ return (-1);
+ }
+
+ /*
+ * Execute different scripts (defined above) depending on
+ * user specified options. Default mode has event_mode empty
+ */
+ switch (event_mode) {
+ default:
+ if ((prog = dtrace_program_strcompile(g_dtp, g_prog,
+ DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) {
+ pt_error("%s : failed to compile g_prog\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ break;
+ case 'v':
+ if ((prog = dtrace_program_strcompile(g_dtp, g_prog_v,
+ DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) {
+ pt_error("%s : failed to compile g_prog_v\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ break;
+ }
+
+ if (dtrace_program_exec(g_dtp, prog, &info) == -1) {
+ pt_error("%s : failed to enable probes\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ if (dtrace_setopt(g_dtp, "aggsize", "128k") == -1) {
+ pt_error("%s : failed to set 'aggsize'\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ if (dtrace_setopt(g_dtp, "aggrate", "0") == -1) {
+ pt_error("%s : failed to set 'aggrate'\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ if (dtrace_setopt(g_dtp, "aggpercpu", 0) == -1) {
+ pt_error("%s : failed to set 'aggpercpu'\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ if (dtrace_go(g_dtp) != 0) {
+ pt_error("%s : dtrace_go() failed\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ if (dtrace_getopt(g_dtp, "statusrate", &statustime) == -1) {
+ pt_error("%s : failed to get 'statusrate'\n", __FILE__);
+ return (dtrace_errno(g_dtp));
+ }
+ return (0);
+}
+
+int
+pt_events_stat_collect(void)
+{
+ p_event = event_info;
+ top_events = 0;
+
+ if (dtrace_status(g_dtp) == -1)
+ return (-1);
+
+ if (dtrace_aggregate_snap(g_dtp) != 0)
+ pt_error("%s : failed to add to aggregate", __FILE__);
+
+ if (dtrace_aggregate_walk_keyvarsorted(g_dtp, walk, NULL) != 0)
+ pt_error("%s : failed to sort aggregate", __FILE__);
+
+ dtrace_aggregate_clear(g_dtp);
+
+ return (0);
+}
diff --git a/usr/src/cmd/powertop/i386/Makefile b/usr/src/cmd/powertop/i386/Makefile
new file mode 100644
index 0000000000..eadb23ed43
--- /dev/null
+++ b/usr/src/cmd/powertop/i386/Makefile
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTPROG32)
diff --git a/usr/src/cmd/powertop/powertop.c b/usr/src/cmd/powertop/powertop.c
new file mode 100644
index 0000000000..22e2307926
--- /dev/null
+++ b/usr/src/cmd/powertop/powertop.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2008, Intel Corporation
+ * Copyright 2008, Sun Microsystems, Inc
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ * Eric C Saxe <eric.saxe@sun.com>
+ * Aubrey Li <aubrey.li@intel.com>
+ */
+
+/*
+ * GPL Disclaimer
+ *
+ * For the avoidance of doubt, except that if any license choice other
+ * than GPL or LGPL is available it will apply instead, Sun elects to
+ * use only the General Public License version 2 (GPLv2) at this time
+ * for any software where a choice of GPL license versions is made
+ * available with the language indicating that GPLv2 or any later
+ * version may be used, or where a choice of which version of the GPL
+ * is applied is otherwise unspecified.
+ */
+
+#include <getopt.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <locale.h>
+#include "powertop.h"
+
+int g_ncpus;
+processorid_t *cpu_table;
+const int true = 1;
+
+int
+main(int argc, char **argv)
+{
+ hrtime_t last, now;
+ uint_t features = 0, user_interval = 0;
+ int ncursesinited = 0, index2 = 0, c, ret, dump_count = 0;
+ double last_time;
+ char *endptr;
+
+ static struct option opts[] = {
+ { "dump", 1, NULL, 'd' },
+ { "time", 1, NULL, 't' },
+ { "help", 0, NULL, 'h' },
+ { "verbose", 0, NULL, 'v' },
+ { 0, 0, NULL, 0 }
+ };
+
+ (void) setlocale(LC_ALL, "");
+ (void) bindtextdomain("powertop", "/usr/share/locale");
+ (void) textdomain("powertop");
+
+ pt_set_progname(argv[0]);
+
+ if ((bit_depth = get_bit_depth()) < 0)
+ exit(EXIT_FAILURE);
+
+ ticktime = ticktime_usr = INTERVAL_DEFAULT;
+ displaytime = 0.0;
+ dump = 0;
+ event_mode = ' ';
+ max_cstate = 0;
+
+ while ((c = getopt_long(argc, argv, "d:vt:h", opts, &index2)) != EOF) {
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'd':
+ if (dump)
+ usage();
+
+ dump = 1;
+ dump_count = (int)strtod(optarg, &endptr);
+
+ if (dump_count <= 0 || *endptr != NULL)
+ usage();
+ break;
+ case 't':
+ if (user_interval)
+ usage();
+
+ user_interval = 1;
+ ticktime = ticktime_usr = (double)strtod(optarg,
+ &endptr);
+
+ if (*endptr != NULL || ticktime < 1 ||
+ ticktime > INTERVAL_MAX)
+ usage();
+ break;
+ case 'v':
+ if (event_mode == 'v')
+ usage();
+
+ event_mode = 'v';
+ break;
+ case 'h':
+ default:
+ usage();
+ return (EXIT_USAGE);
+ }
+ }
+
+ if (optind < argc) {
+ usage();
+ }
+
+ (void) printf("%s (C) 2008 Intel Corporation\n\n", TITLE);
+
+ /*
+ * Enumerate the system's CPUs
+ * Populate cpu_table, g_ncpus
+ */
+ enumerate_cpus();
+
+ /*
+ * If the system is running on battery, find out what's
+ * the kstat module for it
+ */
+ battery_mod_lookup();
+
+ /* Prepare C-state statistics */
+ ret = pt_cpuidle_stat_prepare();
+ if (ret == 0)
+ features |= FEATURE_CSTATE;
+ else
+ /*
+ * PowerTop was unable to run a DTrace program,
+ * most likely for lack of permissions.
+ */
+ exit(EXIT_FAILURE);
+
+ /* Prepare P-state statistics */
+ if (pt_cpufreq_stat_prepare() == 0)
+ features |= FEATURE_PSTATE;
+
+ /* Prepare event statistics */
+ if (pt_events_stat_prepare() != -1)
+ features |= FEATURE_EVENTS;
+
+ (void) printf(_("Collecting data for %.2f second(s) \n"),
+ (float)ticktime);
+
+ last = gethrtime();
+
+ while (true) {
+ fd_set rfds;
+ struct timeval tv;
+ int key, reinit = 0;
+ char keychar;
+
+ /*
+ * Sleep for a while waiting either for input (if we're not
+ * in dump mode) or for the timeout to elapse
+ */
+ FD_ZERO(&rfds);
+ FD_SET(0, &rfds);
+
+ tv.tv_sec = (long)ticktime;
+ tv.tv_usec = (long)((ticktime - tv.tv_sec) * 1000000);
+
+ if (!dump)
+ key = select(1, &rfds, NULL, NULL, &tv);
+ else
+ key = select(1, NULL, NULL, NULL, &tv);
+
+ now = gethrtime();
+
+ g_interval = (double)(now - last)/NANOSEC;
+ last = now;
+
+ top_events = 0;
+ total_events = 0;
+
+ (void) memset(event_info, EVENT_NUM_MAX * sizeof (event_info_t),
+ 0);
+ (void) memset(cstate_info, 2 * sizeof (state_info_t), 0);
+
+ /* Collect idle state transition stats */
+ if (features & FEATURE_CSTATE &&
+ pt_cpuidle_stat_collect(g_interval) < 0) {
+ /* Reinitialize C-state statistics */
+ if (pt_cpuidle_stat_prepare() != 0)
+ exit(EXIT_FAILURE);
+
+ reinit = 1;
+ }
+
+ /* Collect frequency change stats */
+ if (features & FEATURE_PSTATE &&
+ pt_cpufreq_stat_collect(g_interval) < 0) {
+ /* Reinitialize P-state statistics */
+ if (pt_cpufreq_stat_prepare() != 0)
+ exit(EXIT_FAILURE);
+
+ reinit = 1;
+ }
+
+ /* Collect event statistics */
+ if (features & FEATURE_EVENTS &&
+ pt_events_stat_collect() < 0) {
+ /* Reinitialize event statistics */
+ if (pt_events_stat_prepare() != 0)
+ exit(EXIT_FAILURE);
+
+ reinit = 1;
+ }
+
+ if (reinit)
+ continue;
+
+ /*
+ * Initialize curses if we're not dumping and
+ * haven't already done it
+ */
+ if (!dump) {
+ if (!ncursesinited) {
+ initialize_curses();
+ ncursesinited++;
+ }
+ setup_windows();
+ show_title_bar();
+ }
+
+ /* Show CPU power states */
+ if (features & FEATURE_CSTATE)
+ show_cstates();
+
+ /* Show wakeups events affecting PM */
+ if (features & FEATURE_EVENTS) {
+ show_wakeups(g_interval);
+ show_eventstats(g_interval);
+ }
+
+ print_battery();
+
+ displaytime = displaytime - ticktime;
+
+ if (key && !dump) {
+ keychar = toupper(fgetc(stdin));
+
+ switch (keychar) {
+ case 'Q':
+ cleanup_curses();
+ exit(EXIT_SUCCESS);
+ break;
+ case 'R':
+ ticktime = 3;
+ break;
+ }
+ if (keychar == suggestion_key && suggestion_activate) {
+ suggestion_activate();
+ displaytime = -1.0;
+ }
+ }
+ reset_suggestions();
+
+ /* suggests PM */
+ if (geteuid() == 0) {
+ suggest_p_state();
+ } else {
+ suggest_as_root();
+ }
+
+ if (dump_count)
+ dump_count--;
+
+ /* Exits if user requested a dump */
+ if (dump && !dump_count) {
+ print_all_suggestions();
+ exit(EXIT_SUCCESS);
+ }
+
+ /* No key pressed, will suggest something */
+ if (!key && !dump_count)
+ pick_suggestion();
+
+ /* Refresh display */
+ if (!dump) {
+ show_title_bar();
+ update_windows();
+ }
+
+ /*
+ * Update the interval based on how long the CPU was in the
+ * longest c-state during the last snapshot. If the user
+ * specified an interval we skip this bit and keep it fixed.
+ */
+ last_time = (((double)cstate_info[longest_cstate].total_time/
+ g_ncpus)/cstate_info[longest_cstate].events);
+
+ if (!user_interval)
+ if (last_time < INTERVAL_DEFAULT ||
+ (total_events/ticktime) < 1)
+ ticktime = INTERVAL_DEFAULT;
+ else
+ ticktime = INTERVAL_UPDATE(last_time);
+
+ /*
+ * Restore user specified interval after a refresh
+ */
+ if (keychar == 'R' && user_interval)
+ ticktime = ticktime_usr;
+ }
+ return (EXIT_SUCCESS);
+}
+
+void
+suggest_as_root(void)
+{
+ add_suggestion("Suggestion: run as root to get suggestions"
+ " for reducing system power consumption", 40, NULL, NULL,
+ NULL);
+}
diff --git a/usr/src/cmd/powertop/powertop.h b/usr/src/cmd/powertop/powertop.h
new file mode 100644
index 0000000000..f1dee36dbe
--- /dev/null
+++ b/usr/src/cmd/powertop/powertop.h
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2008, Intel Corporation
+ * Copyright 2008, Sun Microsystems, Inc
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ * Eric C Saxe <eric.saxe@sun.com>
+ * Aubrey Li <aubrey.li@intel.com>
+ */
+
+/*
+ * GPL Disclaimer
+ *
+ * For the avoidance of doubt, except that if any license choice other
+ * than GPL or LGPL is available it will apply instead, Sun elects to
+ * use only the General Public License version 2 (GPLv2) at this time
+ * for any software where a choice of GPL license versions is made
+ * available with the language indicating that GPLv2 or any later
+ * version may be used, or where a choice of which version of the GPL
+ * is applied is otherwise unspecified.
+ */
+
+#ifndef __INCLUDE_GUARD_POWERTOP_H_
+#define __INCLUDE_GUARD_POWERTOP_H_
+
+#include <sys/types.h>
+#include <libintl.h>
+#include <sys/processor.h>
+
+#define max(A, B) (((A) < (B)) ? (B) : (A))
+
+#define _(STRING) gettext(STRING)
+
+#define TITLE "OpenSolaris PowerTOP version 1.1"
+
+/*
+ * Exit values. stdlib.h defines EXIT_SUCCESS as 0 and
+ * EXIT_FAILURE as 1
+ */
+#define EXIT_USAGE 2
+
+/*
+ * PowerTop Features
+ * These may not be available everywhere
+ */
+#define FEATURE_CSTATE 0x1
+#define FEATURE_PSTATE 0x2
+#define FEATURE_EVENTS 0x4
+
+#define BIT_DEPTH_BUF 10
+
+#define INTERVAL_DEFAULT 5.0
+#define INTERVAL_MAX 100.0
+#define INTERVAL_UPDATE(l) \
+ ((l/INTERVAL_DEFAULT) * INTERVAL_DEFAULT + INTERVAL_DEFAULT)
+
+#define STATE_NAME_MAX 16
+#define EVENT_NAME_MAX 64
+#define EVENT_NUM_MAX 100
+#define NSTATES 32
+
+/*
+ * Display colors
+ */
+#define PT_COLOR_DEFAULT 1
+#define PT_COLOR_HEADER_BAR 2
+#define PT_COLOR_ERROR 3
+#define PT_COLOR_RED 4
+#define PT_COLOR_YELLOW 5
+#define PT_COLOR_GREEN 6
+#define PT_COLOR_BRIGHT 7
+#define PT_COLOR_BLUE 8
+
+/*
+ * Constants for setup_windows()
+ */
+#define SINGLE_LINE_SW 1
+#define LENGTH_SUGG_SW 2
+#define TITLE_LINE 1
+#define BLANK_LINE 1
+#define NEXT_LINE 1
+
+/*
+ * Structures and typedefs
+ */
+struct line {
+ char *string;
+ int count;
+};
+
+typedef struct event_info {
+ char offender_name[EVENT_NAME_MAX];
+ char offense_name[EVENT_NAME_MAX];
+ uint64_t total_count;
+} event_info_t;
+
+/*
+ * P/C state information
+ */
+typedef struct state_info {
+ char name[STATE_NAME_MAX];
+ hrtime_t total_time;
+ hrtime_t last_time;
+ double events;
+} state_info_t;
+
+typedef struct pstate_info {
+ uint64_t speed;
+ hrtime_t total_time;
+} pstate_info_t;
+
+typedef struct cpu_power_info {
+ uint64_t current_pstate;
+ hrtime_t time_accounted;
+ hrtime_t dtrace_time;
+} cpu_power_info_t;
+
+typedef void (suggestion_func)(void);
+
+/*
+ * Global variables
+ */
+double displaytime;
+
+int bit_depth;
+
+/*
+ * Event accounting
+ */
+int total_events;
+int top_events;
+
+/*
+ * Interval
+ */
+double ticktime, ticktime_usr;
+double g_interval;
+
+/*
+ * Command line arguments
+ */
+int dump;
+char event_mode;
+
+/*
+ * Event info array
+ */
+event_info_t event_info[EVENT_NUM_MAX];
+event_info_t *p_event;
+
+/*
+ * Lookup table, sequential CPU id to Solaris CPU id
+ */
+processorid_t *cpu_table;
+
+/*
+ * Number of idle/frequency states
+ */
+int npstates;
+int max_cstate;
+int longest_cstate;
+
+/*
+ * Total time, used to display different idle states
+ */
+hrtime_t total_c_time;
+
+/*
+ * P/C state info arrays
+ */
+state_info_t cstate_info[NSTATES];
+pstate_info_t pstate_info[NSTATES];
+
+/*
+ * Per CPU power state information
+ */
+cpu_power_info_t *cpu_power_states;
+
+/*
+ * Extern declarations
+ */
+extern struct line *lines;
+extern int linehead;
+extern int linesize;
+extern int linectotal;
+
+extern int g_ncpus;
+
+/*
+ * kstat's battery module
+ */
+extern char *kstat_batt_mod[3];
+extern uint_t kstat_batt_idx;
+
+extern int topcstate;
+extern int topfreq;
+extern int dump;
+
+extern char *prog;
+
+extern char status_bar_slots[10][40];
+
+extern const int true, false;
+
+extern char suggestion_key;
+extern suggestion_func *suggestion_activate;
+
+/*
+ * Suggestions related
+ */
+extern void suggest_p_state(void);
+extern void suggest_as_root(void);
+
+/*
+ * See util.c
+ */
+extern void pt_error(char *, ...);
+extern void pt_set_progname(char *);
+extern void enumerate_cpus(void);
+extern void usage(void);
+extern int get_bit_depth(void);
+extern void battery_mod_lookup(void);
+extern int event_compare(const void *, const void *);
+
+/*
+ * Display/curses related
+ */
+extern void show_title_bar(void);
+extern void setup_windows(void);
+extern void initialize_curses(void);
+extern void show_acpi_power_line(uint32_t flag, double rate,
+ double rem_cap, double cap, uint32_t state);
+extern void show_cstates();
+extern void show_wakeups(double interval);
+extern void show_eventstats(double interval);
+extern void show_suggestion(char *sug);
+extern void cleanup_curses(void);
+extern void update_windows(void);
+
+/*
+ * Suggestions
+ */
+extern void pick_suggestion(void);
+extern void add_suggestion(char *text, int weight, char key,
+ char *keystring, suggestion_func *func);
+extern void reset_suggestions(void);
+extern void print_all_suggestions(void);
+extern void print_battery(void);
+
+/*
+ * DTrace stats
+ */
+extern int pt_cpufreq_stat_prepare(void);
+extern int pt_cpufreq_stat_collect(double interval);
+extern int pt_cpuidle_stat_prepare(void);
+extern int pt_cpuidle_stat_collect(double interval);
+extern int pt_events_stat_prepare(void);
+extern int pt_events_stat_collect(void);
+
+#endif /* __INCLUDE_GUARD_POWERTOP_H_ */
diff --git a/usr/src/cmd/powertop/sparcv9/Makefile b/usr/src/cmd/powertop/sparcv9/Makefile
new file mode 100644
index 0000000000..d99d30ca4c
--- /dev/null
+++ b/usr/src/cmd/powertop/sparcv9/Makefile
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+include ../../Makefile.cmd.64
+
+install: all $(ROOTPROG64)
diff --git a/usr/src/cmd/powertop/suggestions.c b/usr/src/cmd/powertop/suggestions.c
new file mode 100644
index 0000000000..22995b26f7
--- /dev/null
+++ b/usr/src/cmd/powertop/suggestions.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2008, Intel Corporation
+ * Copyright 2008, Sun Microsystems, Inc
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ * Eric C Saxe <eric.saxe@sun.com>
+ * Aubrey Li <aubrey.li@intel.com>
+ */
+
+/*
+ * GPL Disclaimer
+ *
+ * For the avoidance of doubt, except that if any license choice other
+ * than GPL or LGPL is available it will apply instead, Sun elects to
+ * use only the General Public License version 2 (GPLv2) at this time
+ * for any software where a choice of GPL license versions is made
+ * available with the language indicating that GPLv2 or any later
+ * version may be used, or where a choice of which version of the GPL
+ * is applied is otherwise unspecified.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "powertop.h"
+
+char suggestion_key;
+suggestion_func *suggestion_activate;
+
+struct suggestion;
+
+struct suggestion {
+ struct suggestion *next;
+
+ char *string;
+ int weight;
+ char key;
+ char *keystring;
+
+ suggestion_func *func;
+};
+
+static struct suggestion *suggestions;
+static int total_weight;
+
+static char previous[1024];
+
+void
+reset_suggestions(void)
+{
+ struct suggestion *ptr;
+
+ ptr = suggestions;
+
+ while (ptr) {
+ struct suggestion *next;
+
+ next = ptr->next;
+ free(ptr->string);
+ free(ptr->keystring);
+ free(ptr);
+ ptr = next;
+ }
+
+ suggestions = NULL;
+ (void) strcpy(status_bar_slots[8], "");
+
+ suggestion_key = -1;
+ suggestion_activate = NULL;
+ total_weight = 0;
+}
+
+void
+add_suggestion(char *text, int weight, char key, char *keystring,
+ suggestion_func *func)
+{
+ struct suggestion *new;
+
+ if (!text)
+ return;
+
+ new = malloc(sizeof (struct suggestion));
+
+ if (!new)
+ return;
+
+ (void) memset(new, 0, sizeof (struct suggestion));
+
+ new->string = strdup(text);
+ new->weight = weight;
+ new->key = key;
+
+ if (keystring)
+ new->keystring = strdup(keystring);
+
+ new->next = suggestions;
+ new->func = func;
+ suggestions = new;
+ total_weight += weight;
+}
+
+void
+pick_suggestion(void)
+{
+ int weight, value, running = 0;
+ struct suggestion *ptr;
+
+ (void) strcpy(status_bar_slots[8], "");
+ suggestion_key = -1;
+ suggestion_activate = NULL;
+
+ if (total_weight == 0 || suggestions == NULL) {
+ show_suggestion("");
+ return;
+ }
+
+ weight = total_weight;
+
+ if (strlen(previous) && displaytime > 0.0)
+ weight += 50;
+
+ value = rand() % weight;
+ ptr = suggestions;
+
+ while (ptr) {
+ running += ptr->weight;
+
+ if (strcmp(ptr->string, previous) == 0 && displaytime > 0.0)
+ running += 50;
+
+ if (running > value) {
+ if (ptr->keystring)
+ (void) strncpy(status_bar_slots[8],
+ ptr->keystring, 40);
+
+ suggestion_key = ptr->key;
+ suggestion_activate = ptr->func;
+
+ show_suggestion(ptr->string);
+
+ if (strcmp(ptr->string, previous)) {
+ displaytime = 30.0;
+ (void) strcpy(previous, ptr->string);
+ }
+ return;
+ }
+ ptr = ptr->next;
+ }
+
+ show_suggestion("");
+ (void) memset(previous, 0, sizeof (previous));
+ displaytime = -1.0;
+}
+
+void
+print_all_suggestions(void)
+{
+ struct suggestion *ptr;
+
+ for (ptr = suggestions; ptr; ptr = ptr->next)
+ (void) printf("\n%s\n", ptr->string);
+}
diff --git a/usr/src/cmd/powertop/util.c b/usr/src/cmd/powertop/util.c
new file mode 100644
index 0000000000..35bd160f30
--- /dev/null
+++ b/usr/src/cmd/powertop/util.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2008, Intel Corporation
+ * Copyright 2008, Sun Microsystems, Inc
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ * Eric C Saxe <eric.saxe@sun.com>
+ * Aubrey Li <aubrey.li@intel.com>
+ */
+
+/*
+ * GPL Disclaimer
+ *
+ * For the avoidance of doubt, except that if any license choice other
+ * than GPL or LGPL is available it will apply instead, Sun elects to
+ * use only the General Public License version 2 (GPLv2) at this time
+ * for any software where a choice of GPL license versions is made
+ * available with the language indicating that GPLv2 or any later
+ * version may be used, or where a choice of which version of the GPL
+ * is applied is otherwise unspecified.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <strings.h>
+#include <sys/systeminfo.h>
+#include <kstat.h>
+#include <errno.h>
+#include "powertop.h"
+
+static char PROG_FMT[] = "%s: ";
+static char ERR_FMT[] = ": %s";
+static char *progname;
+
+char *kstat_batt_mod[3] = {"NULL", "battery", "acpi_drv"};
+uint_t kstat_batt_idx;
+
+void
+pt_set_progname(char *name)
+{
+ progname = basename(name);
+}
+
+/*PRINTFLIKE1*/
+void
+pt_error(char *format, ...)
+{
+ int err = errno;
+ va_list alist;
+
+ if (progname != NULL)
+ (void) fprintf(stderr, PROG_FMT, progname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+
+ if (strchr(format, '\n') == NULL)
+ (void) fprintf(stderr, gettext(ERR_FMT), strerror(err));
+}
+
+void
+enumerate_cpus(void)
+{
+ int cpuid;
+ int ncpus = 0;
+ int max, cpus_conf;
+
+ max = sysconf(_SC_CPUID_MAX);
+ cpus_conf = sysconf(_SC_NPROCESSORS_CONF);
+ cpu_table = malloc(cpus_conf * sizeof (processorid_t));
+
+ for (cpuid = 0; cpuid < max; cpuid++) {
+ if (p_online(cpuid, P_STATUS) != -1) {
+ cpu_table[ncpus] = cpuid;
+ ncpus++;
+ }
+ }
+ g_ncpus = ncpus;
+}
+
+void
+usage(void)
+{
+ (void) fprintf(stderr, "%s (C) 2008 Intel Corporation\n\n", TITLE);
+ (void) fprintf(stderr, "Usage: powertop [option]\n");
+ (void) fprintf(stderr, " -d, --dump [count] Read wakeups count "
+ "times and print list of top offenders\n");
+ (void) fprintf(stderr, " -t, --time [interval] Default time to gather "
+ "data in seconds [1-100s]\n");
+ (void) fprintf(stderr, " -v, --verbose Verbose mode, reports "
+ "kernel cyclic activity\n");
+ (void) fprintf(stderr, " -h, --help Show this help "
+ "message\n");
+
+ exit(EXIT_USAGE);
+}
+
+int
+get_bit_depth(void)
+{
+ /*
+ * This little routine was derived from isainfo.c to look up
+ * the system's bit depth. It feeds a 10 byte long buffer to
+ * sysinfo (we only need the first word, sysinfo truncates and
+ * \0 terminates the rest) from which we figure out which isa
+ * we're running on.
+ */
+ char buf[BIT_DEPTH_BUF];
+
+ if (sysinfo(SI_ARCHITECTURE_64, buf, BIT_DEPTH_BUF) == -1)
+ if (sysinfo(SI_ARCHITECTURE_32, buf, BIT_DEPTH_BUF) == -1)
+ return (-2);
+
+ if (strcmp(buf, "sparc") == 0 || strcmp(buf, "i386") == 0)
+ return (32);
+
+ if (strcmp(buf, "sparcv9") == 0 || strcmp(buf, "amd64") == 0)
+ return (64);
+
+ return (-3);
+}
+
+/*
+ * Checks if the kstat module for battery information is present and
+ * whether it's called 'battery' or 'acpi_drv'
+ */
+void
+battery_mod_lookup(void)
+{
+ kstat_ctl_t *kc = kstat_open();
+
+ if (kstat_lookup(kc, kstat_batt_mod[1], 0, NULL))
+ kstat_batt_idx = 1;
+ else
+ if (kstat_lookup(kc, kstat_batt_mod[2], 0, NULL))
+ kstat_batt_idx = 2;
+ else
+ kstat_batt_idx = 0;
+
+ (void) kstat_close(kc);
+}
+
+/*
+ * Simple integer comparison routine for the event report qsort(3C).
+ */
+int
+event_compare(const void *p1, const void *p2)
+{
+ event_info_t i = *((event_info_t *)p1);
+ event_info_t j = *((event_info_t *)p2);
+
+ if (i.total_count > j.total_count)
+ return (-1);
+
+ if (i.total_count < j.total_count)
+ return (1);
+
+ return (0);
+}
diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile
index 9c69626855..a150618fe3 100644
--- a/usr/src/pkgdefs/Makefile
+++ b/usr/src/pkgdefs/Makefile
@@ -340,6 +340,7 @@ COMMON_SUBDIRS= \
SUNWpool \
SUNWpoold \
SUNWpoolr \
+ SUNWpowertop \
SUNWppm \
SUNWpppd \
SUNWpppdu \
diff --git a/usr/src/pkgdefs/SUNWpowertop/Makefile b/usr/src/pkgdefs/SUNWpowertop/Makefile
new file mode 100644
index 0000000000..7a1708facd
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWpowertop/Makefile
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+DATAFILES += depend
+
+LICENSEFILES = $(GPLV2)
+
+.KEEP_STATE:
+
+all: $(FILES)
+install: all pkg
+
+include ../Makefile.targ
diff --git a/usr/src/pkgdefs/SUNWpowertop/pkginfo.tmpl b/usr/src/pkgdefs/SUNWpowertop/pkginfo.tmpl
new file mode 100644
index 0000000000..f9d37cbf47
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWpowertop/pkginfo.tmpl
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWpowertop"
+NAME="PowerTOP tool"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+CATEGORY="system"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+DESC="PowerTOP 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"
diff --git a/usr/src/pkgdefs/SUNWpowertop/prototype_com b/usr/src/pkgdefs/SUNWpowertop/prototype_com
new file mode 100644
index 0000000000..709b7eebf4
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWpowertop/prototype_com
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# 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
+#
+# SUNWpowertop
+#
+d none usr 0755 root sys
+d none usr/bin 0755 root bin
+l none usr/bin/powertop=../../usr/lib/isaexec
diff --git a/usr/src/pkgdefs/SUNWpowertop/prototype_i386 b/usr/src/pkgdefs/SUNWpowertop/prototype_i386
new file mode 100644
index 0000000000..38a8af0c76
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWpowertop/prototype_i386
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# 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
+#
+#
+# SUNWpowertop
+#
+d none usr/bin/i86 755 root bin
+f none usr/bin/i86/powertop 555 root bin
+d none usr/bin/amd64 755 root bin
+f none usr/bin/amd64/powertop 555 root bin
diff --git a/usr/src/pkgdefs/SUNWpowertop/prototype_sparc b/usr/src/pkgdefs/SUNWpowertop/prototype_sparc
new file mode 100644
index 0000000000..91c2626d92
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWpowertop/prototype_sparc
@@ -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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# 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
+#
+#
+# SUNWpowertop
+#
+d none usr/bin/sparcv9 755 root bin
+f none usr/bin/sparcv9/powertop 555 root bin