1N/A/*
1N/A * Copyright 2009, Intel Corporation
2N/A * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
1N/A *
1N/A * This file is part of PowerTOP
1N/A *
1N/A * This program file is free software; you can redistribute it and/or modify it
1N/A * under the terms of the GNU General Public License as published by the
1N/A * Free Software Foundation; version 2 of the License.
1N/A *
1N/A * This program is distributed in the hope that it will be useful, but WITHOUT
1N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1N/A * for more details.
1N/A *
1N/A * You should have received a copy of the GNU General Public License
1N/A * along with this program in a file named COPYING; if not, write to the
1N/A * Free Software Foundation, Inc.,
1N/A * 51 Franklin Street, Fifth Floor,
1N/A * Boston, MA 02110-1301 USA
1N/A *
1N/A * Authors:
1N/A * Arjan van de Ven <arjan@linux.intel.com>
1N/A * Eric C Saxe <eric.saxe@sun.com>
1N/A * Aubrey Li <aubrey.li@intel.com>
1N/A */
1N/A
1N/A/*
1N/A * GPL Disclaimer
1N/A *
1N/A * For the avoidance of doubt, except that if any license choice other
1N/A * than GPL or LGPL is available it will apply instead, Sun elects to
1N/A * use only the General Public License version 2 (GPLv2) at this time
1N/A * for any software where a choice of GPL license versions is made
1N/A * available with the language indicating that GPLv2 or any later
1N/A * version may be used, or where a choice of which version of the GPL
1N/A * is applied is otherwise unspecified.
1N/A */
1N/A
1N/A#include <stdarg.h>
1N/A#include <stdlib.h>
1N/A#include <libgen.h>
1N/A#include <unistd.h>
1N/A#include <strings.h>
1N/A#include <sys/systeminfo.h>
1N/A#include <kstat.h>
2N/A#include <time.h>
1N/A#include <errno.h>
1N/A#include "powertop.h"
1N/A
1N/Astatic char PROG_FMT[] = "%s: ";
1N/Astatic char ERR_FMT[] = ": %s";
1N/Astatic char *progname;
1N/A
1N/Avoid
1N/Apt_set_progname(char *name)
1N/A{
1N/A progname = basename(name);
1N/A}
1N/A
1N/A/*PRINTFLIKE1*/
1N/Avoid
1N/Apt_error(char *format, ...)
1N/A{
1N/A int err = errno;
1N/A va_list alist;
1N/A
1N/A if (g_gui)
1N/A return;
1N/A
1N/A if (progname != NULL)
1N/A (void) fprintf(stderr, PROG_FMT, progname);
1N/A
1N/A va_start(alist, format);
1N/A (void) vfprintf(stderr, format, alist);
1N/A va_end(alist);
1N/A
1N/A if (strchr(format, '\n') == NULL)
1N/A (void) fprintf(stderr, ERR_FMT, strerror(err));
1N/A}
1N/A
1N/A/*
1N/A * Returns the number of online CPUs.
1N/A */
1N/Auint_t
1N/Apt_enumerate_cpus(void)
1N/A{
1N/A int cpuid;
1N/A int max, cpus_conf;
1N/A uint_t ncpus = 0;
1N/A
1N/A max = sysconf(_SC_CPUID_MAX);
1N/A cpus_conf = sysconf(_SC_NPROCESSORS_CONF);
1N/A
1N/A /* Fall back to one CPU if any of the sysconf calls above failed */
1N/A if (max == -1 || cpus_conf == -1) {
1N/A max = cpus_conf = 1;
1N/A }
1N/A
2N/A /* Free any previous cpu table */
2N/A if (g_cpu_table != NULL)
2N/A free(g_cpu_table);
2N/A
1N/A if ((g_cpu_table = malloc(cpus_conf * sizeof (processorid_t))) == NULL)
1N/A return (0);
1N/A
1N/A for (cpuid = 0; cpuid < max; cpuid++) {
2N/A if (p_online(cpuid, P_STATUS) == P_ONLINE) {
1N/A g_cpu_table[ncpus] = cpuid;
1N/A ncpus++;
1N/A }
1N/A }
1N/A return (ncpus);
1N/A}
2N/A/*
2N/A * Simple integer comparison routine for the event report qsort(3C).
2N/A */
2N/Aint
2N/Apt_event_compare(const void *p1, const void *p2)
2N/A{
2N/A event_info_t *ip = ((event_info_t *)p1);
2N/A event_info_t *jp = ((event_info_t *)p2);
2N/A
2N/A if (ip->total_count > jp->total_count)
2N/A return (-1);
2N/A
2N/A if (ip->total_count < jp->total_count)
2N/A return (1);
2N/A
2N/A return (0);
2N/A}
1N/A
1N/Avoid
1N/Apt_usage(void)
1N/A{
1N/A (void) fprintf(stderr, "%s %s\n\n", TITLE, COPYRIGHT_INTEL);
1N/A (void) fprintf(stderr, "usage: powertop [option]\n");
1N/A (void) fprintf(stderr, " -d, --dump [count] Read wakeups count "
1N/A "times and print list of top offenders\n");
1N/A (void) fprintf(stderr, " -t, --time [interval] Default time to gather "
1N/A "data in seconds [1-30s]\n");
1N/A (void) fprintf(stderr, " -v, --verbose Verbose mode, reports "
1N/A "kernel cyclic activity\n");
1N/A (void) fprintf(stderr, " -c, --cpu [CPU] Only observe a specific"
1N/A " CPU\n");
1N/A (void) fprintf(stderr, " -h, --help Show this help "
1N/A "message\n");
1N/A}
1N/A
1N/Aint
1N/Apt_get_bit_depth(void)
1N/A{
1N/A /*
1N/A * This little routine was derived from isainfo.c to look up
1N/A * the system's bit depth. It feeds a 10 byte long buffer to
1N/A * sysinfo (we only need the first word, sysinfo truncates and
1N/A * \0 terminates the rest) from which we figure out which isa
1N/A * we're running on.
1N/A */
1N/A char buf[BIT_DEPTH_BUF];
1N/A
1N/A if (sysinfo(SI_ARCHITECTURE_64, buf, BIT_DEPTH_BUF) == -1)
1N/A if (sysinfo(SI_ARCHITECTURE_32, buf, BIT_DEPTH_BUF) == -1)
1N/A return (-2);
1N/A
1N/A if (strcmp(buf, "sparc") == 0 || strcmp(buf, "i386") == 0)
1N/A return (32);
1N/A
1N/A if (strcmp(buf, "sparcv9") == 0 || strcmp(buf, "amd64") == 0)
1N/A return (64);
1N/A
1N/A return (-3);
1N/A}
1N/A
2N/Avoid
2N/Apt_kstat_cpu_walk(struct snapshot *old, struct snapshot *new)
2N/A{
2N/A int i;
2N/A
2N/A if (new == NULL)
2N/A return;
2N/A
2N/A for (i = 0; i < CPU_MAX_PSTATES; i++) {
2N/A g_pstate_info[i].total_time = 0;
2N/A }
2N/A for (i = 0; i < CPU_MAX_CSTATES; i++) {
2N/A g_cstate_info[i].total_time = 0;
2N/A g_cstate_info[i].last_time = 0;
2N/A g_cstate_info[i].events = 0;
2N/A }
2N/A g_total_c_time = 0;
2N/A g_total_p_time = 0;
2N/A g_total_events = 0;
2N/A
2N/A switch (g_op_mode) {
2N/A case PT_MODE_CPU:
2N/A pt_cpufreq_kstat_account_one(old, new);
2N/A break;
2N/A case PT_MODE_DEFAULT:
2N/A default:
2N/A pt_cpufreq_kstat_account_all(old, new);
2N/A break;
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Apt_cpufreq_kstat_account_all(struct snapshot *old, struct snapshot *new)
2N/A{
2N/A struct cpu_snapshot *cpu = NULL;
2N/A struct cpu_snapshot *newcpu = NULL;
2N/A int i;
2N/A
2N/A if (new == NULL)
2N/A return;
2N/A
2N/A for (i = 0; i < new->s_num_cpus; i++) {
2N/A cpu = NULL;
2N/A newcpu = &new->s_cpus[i];
2N/A if (old) {
2N/A cpu = &old->s_cpus[i];
2N/A }
2N/A if (newcpu == NULL)
2N/A return;
2N/A
2N/A pt_update_cpu_kstats(cpu, newcpu);
2N/A }
2N/A}
2N/A
2N/A
2N/Avoid
2N/Apt_cpufreq_kstat_account_one(struct snapshot *old, struct snapshot *new)
2N/A{
2N/A struct cpu_snapshot *cpu = NULL;
2N/A struct cpu_snapshot *newcpu = NULL;
2N/A int i = g_observed_cpu;
2N/A
2N/A if (i >= g_ncpus || i < 0 || new == NULL)
2N/A return;
2N/A
2N/A newcpu = &new->s_cpus[i];
2N/A if (old) {
2N/A cpu = &old->s_cpus[i];
2N/A }
2N/A if (newcpu == NULL)
2N/A return;
2N/A
2N/A pt_update_cpu_kstats(cpu, newcpu);
2N/A}
2N/A
2N/A
2N/Avoid
2N/Apt_update_cpu_kstats(struct cpu_snapshot *c1, struct cpu_snapshot *c2)
2N/A{
2N/A char *s1, *s2;
2N/A uint64_t c_nsec[CPU_MAX_CSTATES]; /* more p than c states */
2N/A uint64_t c_count[CPU_MAX_CSTATES];
2N/A uint64_t p_nsec[CPU_MAX_PSTATES];
2N/A int max_pstates, max_cstates;
2N/A int i, j, n;
2N/A /*
2N/A * 2^64 - 1; includes null terminator
2N/A * max string will contain n-1 ':'s and one null terminator
2N/A */
2N/A int max_strlen = MAX_UINT64_STRLEN * CPU_MAX_PSTATES;
2N/A
2N/A /*
2N/A * the first output will have c1 = NULL, to give results since boot
2N/A */
2N/A if (c1) {
2N/A /* check there are stats to report */
2N/A if (!CPU_ACTIVE(c1))
2N/A return;
2N/A }
2N/A
2N/A /* check there are stats to report */
2N/A if (c2) {
2N/A if (!CPU_ACTIVE(c2))
2N/A return;
2N/A }
2N/A
2N/A if (c2->cs_id >= g_ncpus || c2->cs_id < 0)
2N/A return;
2N/A
2N/A s1 = malloc(max_strlen);
2N/A if (s1 == NULL)
2N/A return;
2N/A s2 = malloc(max_strlen);
2N/A if (s2 == NULL) {
2N/A free(s1);
2N/A return;
2N/A }
2N/A
2N/A if (g_features & FEATURE_CSTATE) {
2N/A max_cstates = (int)pt_kstat_long(&c2->cs_cpu_info,
2N/A "supported_max_cstates", &g_supported_max_cstates_index);
2N/A g_max_cstates = ++max_cstates;
2N/A if (max_cstates > CPU_MAX_CSTATES) {
2N/A pt_error("max_cstates %d out of range\n",
2N/A max_cstates);
2N/A exit(2);
2N/A }
2N/A
2N/A for (i = 0; i < max_cstates; i++) {
2N/A c_nsec[i] = 0;
2N/A c_count[i] = 0;
2N/A }
2N/A (void) snprintf(s1, max_strlen, "%s", c1->cstates_nsec);
2N/A (void) snprintf(s2, max_strlen, "%s", c2->cstates_nsec);
2N/A n = pt_delta_str(s1, s2, c_nsec, max_cstates);
2N/A if (n != max_cstates) {
2N/A free(s1);
2N/A free(s2);
2N/A return;
2N/A }
2N/A
2N/A (void) snprintf(s1, max_strlen, "%s", c1->cstates_count);
2N/A (void) snprintf(s2, max_strlen, "%s", c2->cstates_count);
2N/A n = pt_delta_str(s1, s2, c_count, max_cstates);
2N/A if (n != max_cstates) {
2N/A free(s1);
2N/A free(s2);
2N/A return;
2N/A }
2N/A /*
2N/A * C-State Kstats are in C(n) ... C(0) Order
2N/A * Powertop displays thenm in C(0) to C(n) order.
2N/A */
2N/A
2N/A for (i = 0; i < max_cstates; i++) {
2N/A j = max_cstates - i - 1;
2N/A g_cstate_info[i].events += c_count[j];
2N/A g_cstate_info[i].last_time = c_nsec[j];
2N/A g_cstate_info[i].total_time += c_nsec[j];
2N/A g_total_c_time += c_nsec[j];
2N/A }
2N/A /* g_total_events is tranitions into C0 */
2N/A if (max_cstates > 0)
2N/A g_total_events += c_count[max_cstates - 1];
2N/A }
2N/A if (g_features & FEATURE_PSTATE) {
2N/A max_pstates = (int)pt_kstat_long(&c2->cs_cpu_info,
2N/A "supported_max_pstates",
2N/A &g_supported_max_pstates_index);
2N/A g_max_pstates = ++max_pstates;
2N/A
2N/A if (max_pstates > CPU_MAX_PSTATES) {
2N/A pt_error("max_pstates %d out of range\n",
2N/A max_pstates);
2N/A exit(2);
2N/A }
2N/A
2N/A for (i = 0; i < max_pstates; i++) {
2N/A p_nsec[i] = 0;
2N/A }
2N/A
2N/A (void) snprintf(s1, max_strlen, "%s", c1->pstates_nsec);
2N/A (void) snprintf(s2, max_strlen, "%s", c2->pstates_nsec);
2N/A n = pt_delta_str(s1, s2, p_nsec, max_pstates);
2N/A if (n != max_pstates) {
2N/A free(s1);
2N/A free(s2);
2N/A return;
2N/A }
2N/A
2N/A for (i = 0; i < max_pstates; i++) {
2N/A
2N/A /*
2N/A * P-State Kstats are in P(n) .. P(0) order
2N/A * powertop displays them is this order.
2N/A */
2N/A g_pstate_info[i].total_time += p_nsec[i];
2N/A g_total_p_time += p_nsec[i];
2N/A }
2N/A }
2N/A free(s1);
2N/A free(s2);
2N/A}
2N/A
1N/A/*
2N/A * The snapshot has changed for whatever reason. We need to regenerate
2N/A * all information pertaining to the cpus including the record offsets.
2N/A */
2N/Aint
2N/Apt_generate_cpu_information(kstat_ctl_t *kc)
2N/A{
2N/A
2N/A size_t i;
2N/A struct kstat_cpu_records *kstat_cpu_information;
2N/A int max_cpus;
2N/A
2N/A /*
2N/A * Re-enumerate the system's CPUs, populate cpu_table, g_ncpus
2N/A */
2N/A if ((g_ncpus = pt_enumerate_cpus()) == 0)
2N/A exit(EXIT_FAILURE);
2N/A if (!PT_ON_CPU)
2N/A g_ncpus_observed = g_ncpus;
2N/A
2N/A max_cpus = g_ncpus;
2N/A
2N/A if (g_ss_state_info.kstat_cpu_information)
2N/A free(g_ss_state_info.kstat_cpu_information);
2N/A if ((g_ss_state_info.kstat_cpu_information =
2N/A (struct kstat_cpu_records *)calloc(max_cpus,
2N/A sizeof (struct kstat_cpu_records))) == NULL)
2N/A return (errno);
2N/A kstat_cpu_information = g_ss_state_info.kstat_cpu_information;
2N/A
2N/A /*
2N/A * Walk the cpu snapshot chain, looking for the information we desire.
2N/A */
2N/A
2N/A g_ss_state_info.kstat_cpus_active = 0;
2N/A for (i = 0; i < max_cpus; i++) {
2N/A kstat_cpu_information[i].cs_state = p_online(i, P_STATUS);
2N/A /* If no valid CPU is present, move on to the next one */
2N/A if (!(CPU_ONLINE(kstat_cpu_information[i].cs_state))) {
2N/A kstat_cpu_information[i].cs_id = ID_NO_CPU;
2N/A if (PT_ON_CPU) {
2N/A return (-1);
2N/A }
2N/A continue;
2N/A }
2N/A kstat_cpu_information[i].cs_id = i;
2N/A
2N/A g_ss_state_info.kstat_cpus_active++;
2N/A
2N/A if ((kstat_cpu_information[i].cpu_info_ksp_ptr =
2N/A kstat_lookup(kc, "cpu_info", i, NULL)) == NULL)
2N/A return (errno);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Note: the following helpers do not clean up on the failure case,
2N/A * because pt_free_snapshot() is called in main each time through the loop
1N/A */
1N/Aint
2N/Apt_acquire_cpus(struct snapshot *ss, kstat_ctl_t *kc)
1N/A{
2N/A kstat_t *ksp;
2N/A kstat_named_t *knp;
2N/A size_t i;
2N/A struct kstat_cpu_records *kstat_cpu_information = NULL;
2N/A int rtc, len;
2N/A
2N/A ss->s_num_cpus = g_ncpus;
2N/A if ((ss->s_cpus = calloc(ss->s_num_cpus,
2N/A sizeof (struct cpu_snapshot))) == NULL)
2N/A return (errno);
2N/A
2N/A /*
2N/A * If the snap has changed or we have not obtained the
2N/A * kstat_cpu_information, then go and get the cpu information.
2N/A */
2N/A if ((ss->snap_changed || !g_ss_state_info.kstat_cpu_information) &&
2N/A ((rtc = pt_generate_cpu_information(kc)) != 0))
2N/A return (rtc);
2N/A
2N/A kstat_cpu_information = g_ss_state_info.kstat_cpu_information;
2N/A
2N/A /*
2N/A * Read the data in that changes between the intervals.
2N/A */
2N/A g_ss_state_info.kstat_cpus_active = 0;
2N/A for (i = 0; i < ss->s_num_cpus; i++) {
2N/A ss->s_cpus[i].cs_id = kstat_cpu_information[i].cs_id;
2N/A ss->s_cpus[i].cs_state = kstat_cpu_information[i].cs_state;
2N/A /* If no valid CPU is present, move on to the next one */
2N/A if (ss->s_cpus[i].cs_state == ID_NO_CPU)
2N/A continue;
2N/A
2N/A if (!CPU_ACTIVE(&ss->s_cpus[i]))
2N/A continue;
2N/A g_ss_state_info.kstat_cpus_active++;
2N/A
2N/A ksp = kstat_cpu_information[i].cpu_info_ksp_ptr;
2N/A if (kstat_read(kc, ksp, NULL) == -1)
2N/A return (errno);
2N/A if (pt_kstat_copy(ksp, &ss->s_cpus[i].cs_cpu_info))
2N/A return (errno);
2N/A
2N/A /* now copy the cstate and pstate strings */
2N/A
2N/A if (g_features & FEATURE_CSTATE) {
2N/A knp = pt_kstat_data_lookup(ksp, "cstates_nsec",
2N/A &g_cstates_nsec_index);
2N/A if (knp == NULL)
2N/A return (errno);
2N/A if (knp->value.str.addr.ptr == NULL)
2N/A return (ENOENT);
2N/A len = strlen(knp->value.str.addr.ptr);
2N/A (void) memcpy(&ss->s_cpus[i].cstates_nsec,
2N/A knp->value.str.addr.ptr, len);
2N/A
2N/A knp = pt_kstat_data_lookup(ksp, "cstates_count",
2N/A &g_cstates_count_index);
2N/A if (knp == NULL)
2N/A return (errno);
2N/A if (knp->value.str.addr.ptr == NULL)
2N/A return (ENOENT);
2N/A len = strlen(knp->value.str.addr.ptr);
2N/A (void) memcpy(&ss->s_cpus[i].cstates_count,
2N/A knp->value.str.addr.ptr, len);
2N/A }
2N/A
2N/A if (g_features & FEATURE_PSTATE) {
2N/A knp = pt_kstat_data_lookup(ksp, "pstates_nsec",
2N/A &g_pstates_nsec_index);
2N/A if (knp == NULL)
2N/A return (errno);
2N/A if (knp->value.str.addr.ptr == NULL)
2N/A return (ENOENT);
2N/A len = strlen(knp->value.str.addr.ptr);
2N/A (void) memcpy(&ss->s_cpus[i].pstates_nsec,
2N/A knp->value.str.addr.ptr, len);
2N/A }
2N/A }
2N/A errno = 0;
2N/A return (errno);
2N/A}
2N/A
2N/A/*
2N/A * Note: the following helpers do not clean up on the failure case,
2N/A * because pt_free_snapshot() is called in main each time through the loop
2N/A */
2N/Aint
2N/Apt_acquire_cpu(struct snapshot *ss, kstat_ctl_t *kc)
2N/A{
2N/A kstat_t *ksp;
2N/A kstat_named_t *knp;
2N/A struct kstat_cpu_records *kstat_cpu_information = NULL;
2N/A int rtc;
2N/A int i = g_observed_cpu;
2N/A
2N/A if (i < 0 || i >= g_ncpus) {
2N/A errno = EINVAL;
2N/A return (errno);
2N/A }
2N/A ss->s_num_cpus = g_ncpus;
2N/A if ((ss->s_cpus = calloc(ss->s_num_cpus,
2N/A sizeof (struct cpu_snapshot))) == NULL)
2N/A return (errno);
2N/A
2N/A /*
2N/A * If the snap has changed or we have not obtained the
2N/A * kstat_cpu_information, then go and get the cpu information.
2N/A */
2N/A if (ss->snap_changed || !g_ss_state_info.kstat_cpu_information)
2N/A if ((rtc = pt_generate_cpu_information(kc)) != 0)
2N/A return (rtc);
2N/A
2N/A kstat_cpu_information = g_ss_state_info.kstat_cpu_information;
2N/A
2N/A /*
2N/A * Read the data in that changes between the intervals.
2N/A */
2N/A ss->s_cpus[i].cs_id = kstat_cpu_information[i].cs_id;
2N/A ss->s_cpus[i].cs_state = kstat_cpu_information[i].cs_state;
2N/A ksp = kstat_cpu_information[i].cpu_info_ksp_ptr;
2N/A if (kstat_read(kc, ksp, NULL) == -1)
2N/A return (errno);
2N/A if (pt_kstat_copy(ksp, &ss->s_cpus[i].cs_cpu_info))
2N/A return (errno);
2N/A
2N/A /*
2N/A * now copy the cstate and pstate strings
2N/A */
2N/A if (g_features & FEATURE_CSTATE) {
2N/A knp = pt_kstat_data_lookup(ksp, "cstates_nsec",
2N/A &g_cstates_nsec_index);
2N/A if (knp == NULL)
2N/A return (errno);
2N/A if (knp->value.str.addr.ptr == NULL)
2N/A return (ENOENT);
2N/A (void) memcpy(&ss->s_cpus[i].cstates_nsec,
2N/A knp->value.str.addr.ptr, strlen(knp->value.str.addr.ptr));
2N/A
2N/A knp = pt_kstat_data_lookup(ksp, "cstates_count",
2N/A &g_cstates_count_index);
2N/A if (knp == NULL)
2N/A return (errno);
2N/A if (knp->value.str.addr.ptr == NULL)
2N/A return (ENOENT);
2N/A (void) memcpy(&ss->s_cpus[i].cstates_count,
2N/A knp->value.str.addr.ptr, strlen(knp->value.str.addr.ptr));
2N/A }
2N/A
2N/A if (g_features & FEATURE_PSTATE) {
2N/A knp = pt_kstat_data_lookup(ksp, "pstates_nsec",
2N/A &g_pstates_nsec_index);
2N/A if (knp == NULL)
2N/A return (errno);
2N/A if (knp->value.str.addr.ptr == NULL)
2N/A return (ENOENT);
2N/A (void) memcpy(&ss->s_cpus[i].pstates_nsec,
2N/A knp->value.str.addr.ptr, strlen(knp->value.str.addr.ptr));
2N/A }
2N/A
2N/A errno = 0;
2N/A return (errno);
2N/A}
2N/A
2N/Astruct snapshot *
2N/Apt_acquire_kstat_snapshot(kstat_ctl_t **kc_passed)
2N/A{
2N/A struct snapshot *ss = NULL;
2N/A kstat_ctl_t *kc = *kc_passed;
2N/A int err = 0;
2N/A int retry_cnt = 0;
1N/A
2N/A ss = pt_safe_alloc(sizeof (struct snapshot));
2N/A if (ss == NULL) {
2N/A pt_error("acquiring snapshot failed, no memory");
2N/A exit(2);
2N/A }
2N/A (void) memset(ss, 0, sizeof (struct snapshot));
2N/A
2N/A while ((ss->snap_changed = kstat_chain_update(kc)) == -1) {
2N/A kc->kc_chain_id = (kid_t)ioctl(kc->kc_kd,
2N/A KSTAT_IOC_CHAIN_ID, NULL);
2N/A if (errno == EAGAIN) {
2N/A (void) nanosleep(&g_rqtp, NULL);
2N/A }
2N/A retry_cnt++;
2N/A if (retry_cnt > PT_KSTAT_CHAIN_RETRYS)
2N/A break;
2N/A }
2N/A if (retry_cnt > PT_KSTAT_CHAIN_RETRYS) {
2N/A err = kstat_close(kc);
2N/A kc = kstat_open();
2N/A g_kc = kc;
2N/A }
2N/A if (kc == NULL) {
2N/A pt_error("kstat_chain_update failed");
2N/A exit(2);
2N/A }
2N/A if (PT_ON_CPU)
2N/A err = pt_acquire_cpu(ss, kc);
2N/A else
2N/A err = pt_acquire_cpus(ss, kc);
2N/A
2N/A if (err)
2N/A return (NULL);
2N/A else
2N/A return (ss);
2N/A}
2N/A
2N/Avoid
2N/Apt_free_snapshot(struct snapshot *ss)
2N/A{
2N/A size_t i;
2N/A
2N/A if (ss == NULL)
2N/A return;
2N/A if (ss->s_cpus) {
2N/A for (i = 0; i < ss->s_num_cpus; i++) {
2N/A SAFE_FREE(ss->s_cpus[i].cs_cpu_info.ks_data,
2N/A sizeof (ss->s_cpus[i].cs_cpu_info.ks_data));
2N/A }
2N/A SAFE_FREE(ss->s_cpus, sizeof (struct cpu_snapshot *));
2N/A }
2N/A SAFE_FREE(ss, sizeof (struct snapshot));
2N/A}
2N/A
2N/Akstat_ctl_t *
2N/Apt_open_kstat(void)
2N/A{
2N/A kstat_ctl_t *kc;
2N/A
2N/A while ((kc = kstat_open()) == NULL) {
2N/A if (errno == EAGAIN)
2N/A (void) nanosleep(&g_rqtp, NULL);
2N/A else {
2N/A pt_error("kstat_open failed");
2N/A exit(2);
2N/A }
2N/A }
2N/A return (kc);
2N/A}
2N/A
2N/Avoid *
2N/Apt_safe_alloc(size_t size)
2N/A{
2N/A void *ptr;
2N/A
2N/A while ((ptr = malloc(size)) == NULL) {
2N/A if (errno == EAGAIN) {
2N/A (void) nanosleep(&g_rqtp, NULL);
2N/A } else {
2N/A pt_error("malloc failed");
2N/A exit(2);
2N/A }
2N/A }
2N/A return (ptr);
2N/A}
1N/A
2N/A/*
2N/A * Convert a ':'-separated null terminated string into an array of values
2N/A * Assumptions:
2N/A * It is OK to modify the input string
2N/A * There will always be at least two entries (e.g. one ':') in the string.
2N/A * Return the number of entries found, or 0 for a misformed string
2N/A */
2N/Aint
2N/Apt_delta_str_helper(char *s, uint64_t *values, int entries)
2N/A{
2N/A int i;
2N/A char *cp, *sp = s;
2N/A
2N/A if (entries <= 1 || *s == '\0')
2N/A return (0);
2N/A for (i = 0; i < (entries - 1); i++) {
2N/A cp = strchr(sp, ':');
2N/A if (cp == NULL)
2N/A break;
2N/A *cp = '\0';
2N/A values[i] = strtoull(sp, 0, NULL);
2N/A sp = cp + 1;
2N/A }
2N/A if (i == (entries - 1)) {
2N/A values[i] = strtoull(sp, 0, NULL);
2N/A return (entries);
2N/A } else {
2N/A return (0);
2N/A }
2N/A}
1N/A
2N/A/*
2N/A * Convert a pair of strings of ':'-separated uint64 numbers into an array of
2N/A * the difference between the values of the numbers in each position.
2N/A * Assumes, based on knowing the context in which it is called:
2N/A * both strings have the same number of entries
2N/A * some numbers may be "empty" (e.g. the string may contain consecutive :'s
2N/A * if an entry in one string is empty then the other will be too
2N/A * the difference between two empty strings is computed as 0
2N/A * numbers represent nanoseconds, which won't wrap in our lifetime
2N/A * Returns the number of entries found in the string, else 0 if misformed.
2N/A */
2N/Aint
2N/Apt_delta_str(char *s1, char *s2, uint64_t *delta, int entries)
2N/A{
2N/A uint64_t s1_vals[CPU_MAX_PSTATES];
2N/A uint64_t s2_vals[CPU_MAX_PSTATES];
2N/A int i, n1, n2;
2N/A
2N/A if (entries > CPU_MAX_PSTATES)
2N/A return (0);
2N/A n1 = pt_delta_str_helper(s1, s1_vals, entries);
2N/A if (n1 == 0)
2N/A return (0);
2N/A n2 = pt_delta_str_helper(s2, s2_vals, entries);
2N/A if (n1 != n2 || n1 != entries)
2N/A return (0);
2N/A for (i = 0; i < entries; i++)
2N/A delta[i] = s2_vals[i] - s1_vals[i];
2N/A return (entries);
2N/A}
2N/A
2N/Along
2N/Apt_kstat_long(kstat_t *ksp, char *name, int *index)
2N/A{
2N/A kstat_named_t *kp;
2N/A
2N/A kp = pt_kstat_data_lookup(ksp, name, index);
2N/A if (kp != NULL)
2N/A return (kp->value.l);
2N/A else {
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/Aint
2N/Apt_kstat_copy(const kstat_t *src, kstat_t *dst)
2N/A{
2N/A *dst = *src;
2N/A
2N/A if (src->ks_data != NULL) {
2N/A if ((dst->ks_data = malloc(src->ks_data_size)) == NULL)
2N/A return (-1);
2N/A bcopy(src->ks_data, dst->ks_data, src->ks_data_size);
2N/A dst->ks_data_size = src->ks_data_size;
2N/A } else {
2N/A dst->ks_data = NULL;
2N/A dst->ks_data_size = 0;
2N/A }
1N/A return (0);
1N/A}
2N/A
2N/A/*
2N/A * If index_ptr integer value is > -1 then the index points to the
2N/A * string entry in the ks_data that we are interested in. Otherwise
2N/A * we will need to walk the array.
2N/A */
2N/Avoid *
2N/Apt_kstat_data_lookup(kstat_t *ksp, char *name, int *index_ptr)
2N/A{
2N/A int i;
2N/A int size;
2N/A int index;
2N/A char *namep, *datap;
2N/A
2N/A if (ksp == NULL) {
2N/A return (NULL);
2N/A }
2N/A switch (ksp->ks_type) {
2N/A case KSTAT_TYPE_NAMED:
2N/A size = sizeof (kstat_named_t);
2N/A namep = KSTAT_NAMED_PTR(ksp)->name;
2N/A break;
2N/A case KSTAT_TYPE_TIMER:
2N/A size = sizeof (kstat_timer_t);
2N/A namep = KSTAT_TIMER_PTR(ksp)->name;
2N/A break;
2N/A default:
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A
2N/A index = *index_ptr;
2N/A if (index >= 0) {
2N/A /* Short cut to the information. */
2N/A datap = ksp->ks_data;
2N/A datap = &datap[size*index];
2N/A return (datap);
2N/A }
2N/A
2N/A /* Need to go find the string. */
2N/A datap = ksp->ks_data;
2N/A for (i = 0; i < ksp->ks_ndata; i++) {
2N/A if (strcmp(name, namep) == 0) {
2N/A *index_ptr = i;
2N/A return (datap);
2N/A }
2N/A namep += size;
2N/A datap += size;
2N/A }
2N/A errno = ENOENT;
2N/A return (NULL);
2N/A}