1*b47b5b34SRafael Vanoni /* 2*b47b5b34SRafael Vanoni * Copyright 2009, Intel Corporation 3*b47b5b34SRafael Vanoni * Copyright 2009, Sun Microsystems, Inc 4*b47b5b34SRafael Vanoni * 5*b47b5b34SRafael Vanoni * This file is part of PowerTOP 6*b47b5b34SRafael Vanoni * 7*b47b5b34SRafael Vanoni * This program file is free software; you can redistribute it and/or modify it 8*b47b5b34SRafael Vanoni * under the terms of the GNU General Public License as published by the 9*b47b5b34SRafael Vanoni * Free Software Foundation; version 2 of the License. 10*b47b5b34SRafael Vanoni * 11*b47b5b34SRafael Vanoni * This program is distributed in the hope that it will be useful, but WITHOUT 12*b47b5b34SRafael Vanoni * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13*b47b5b34SRafael Vanoni * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14*b47b5b34SRafael Vanoni * for more details. 15*b47b5b34SRafael Vanoni * 16*b47b5b34SRafael Vanoni * You should have received a copy of the GNU General Public License 17*b47b5b34SRafael Vanoni * along with this program in a file named COPYING; if not, write to the 18*b47b5b34SRafael Vanoni * Free Software Foundation, Inc., 19*b47b5b34SRafael Vanoni * 51 Franklin Street, Fifth Floor, 20*b47b5b34SRafael Vanoni * Boston, MA 02110-1301 USA 21*b47b5b34SRafael Vanoni * 22*b47b5b34SRafael Vanoni * Authors: 23*b47b5b34SRafael Vanoni * Arjan van de Ven <arjan@linux.intel.com> 24*b47b5b34SRafael Vanoni * Eric C Saxe <eric.saxe@sun.com> 25*b47b5b34SRafael Vanoni * Aubrey Li <aubrey.li@intel.com> 26*b47b5b34SRafael Vanoni */ 27*b47b5b34SRafael Vanoni 28*b47b5b34SRafael Vanoni /* 29*b47b5b34SRafael Vanoni * GPL Disclaimer 30*b47b5b34SRafael Vanoni * 31*b47b5b34SRafael Vanoni * For the avoidance of doubt, except that if any license choice other 32*b47b5b34SRafael Vanoni * than GPL or LGPL is available it will apply instead, Sun elects to 33*b47b5b34SRafael Vanoni * use only the General Public License version 2 (GPLv2) at this time 34*b47b5b34SRafael Vanoni * for any software where a choice of GPL license versions is made 35*b47b5b34SRafael Vanoni * available with the language indicating that GPLv2 or any later 36*b47b5b34SRafael Vanoni * version may be used, or where a choice of which version of the GPL 37*b47b5b34SRafael Vanoni * is applied is otherwise unspecified. 38*b47b5b34SRafael Vanoni */ 39*b47b5b34SRafael Vanoni 40*b47b5b34SRafael Vanoni #include <string.h> 41*b47b5b34SRafael Vanoni #include <dtrace.h> 42*b47b5b34SRafael Vanoni #include "powertop.h" 43*b47b5b34SRafael Vanoni 44*b47b5b34SRafael Vanoni static dtrace_hdl_t *dtp; 45*b47b5b34SRafael Vanoni 46*b47b5b34SRafael Vanoni /* 47*b47b5b34SRafael Vanoni * Buffer containing DTrace program to track CPU idle state transitions 48*b47b5b34SRafael Vanoni */ 49*b47b5b34SRafael Vanoni static const char *dtp_cpuidle = 50*b47b5b34SRafael Vanoni ":::idle-state-transition" 51*b47b5b34SRafael Vanoni "/arg0 != 0/" 52*b47b5b34SRafael Vanoni "{" 53*b47b5b34SRafael Vanoni " self->start = timestamp;" 54*b47b5b34SRafael Vanoni " self->state = arg0;" 55*b47b5b34SRafael Vanoni "}" 56*b47b5b34SRafael Vanoni "" 57*b47b5b34SRafael Vanoni ":::idle-state-transition" 58*b47b5b34SRafael Vanoni "/arg0 == 0 && self->start/" 59*b47b5b34SRafael Vanoni "{" 60*b47b5b34SRafael Vanoni " @number[self->state] = count();" 61*b47b5b34SRafael Vanoni " @times[self->state] = sum((timestamp - self->start)/1000000);" 62*b47b5b34SRafael Vanoni " self->start = 0;" 63*b47b5b34SRafael Vanoni " self->state = 0;" 64*b47b5b34SRafael Vanoni "}"; 65*b47b5b34SRafael Vanoni 66*b47b5b34SRafael Vanoni /* 67*b47b5b34SRafael Vanoni * Same as above but only for a specific CPU 68*b47b5b34SRafael Vanoni */ 69*b47b5b34SRafael Vanoni static const char *dtp_cpuidle_c = 70*b47b5b34SRafael Vanoni ":::idle-state-transition" 71*b47b5b34SRafael Vanoni "/cpu == $0 &&" 72*b47b5b34SRafael Vanoni " arg0 != 0/" 73*b47b5b34SRafael Vanoni "{" 74*b47b5b34SRafael Vanoni " self->start = timestamp;" 75*b47b5b34SRafael Vanoni " self->state = arg0;" 76*b47b5b34SRafael Vanoni "}" 77*b47b5b34SRafael Vanoni "" 78*b47b5b34SRafael Vanoni ":::idle-state-transition" 79*b47b5b34SRafael Vanoni "/cpu == $0 &&" 80*b47b5b34SRafael Vanoni " arg0 == 0 && self->start/" 81*b47b5b34SRafael Vanoni "{" 82*b47b5b34SRafael Vanoni " @number[self->state] = count();" 83*b47b5b34SRafael Vanoni " @times[self->state] = sum((timestamp - self->start)/1000000);" 84*b47b5b34SRafael Vanoni " self->start = 0;" 85*b47b5b34SRafael Vanoni " self->state = 0;" 86*b47b5b34SRafael Vanoni "}"; 87*b47b5b34SRafael Vanoni 88*b47b5b34SRafael Vanoni static int pt_cpuidle_dtrace_walk(const dtrace_aggdata_t *, void *); 89*b47b5b34SRafael Vanoni 90*b47b5b34SRafael Vanoni /* 91*b47b5b34SRafael Vanoni * Perform setup necessary to track CPU idle state transitions 92*b47b5b34SRafael Vanoni */ 93*b47b5b34SRafael Vanoni int 94*b47b5b34SRafael Vanoni pt_cpuidle_stat_prepare(void) 95*b47b5b34SRafael Vanoni { 96*b47b5b34SRafael Vanoni dtrace_prog_t *prog; 97*b47b5b34SRafael Vanoni dtrace_proginfo_t info; 98*b47b5b34SRafael Vanoni dtrace_optval_t statustime; 99*b47b5b34SRafael Vanoni int err; 100*b47b5b34SRafael Vanoni char *prog_ptr; 101*b47b5b34SRafael Vanoni 102*b47b5b34SRafael Vanoni if ((dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) { 103*b47b5b34SRafael Vanoni pt_error("%s : cannot open dtrace library: %s\n", __FILE__, 104*b47b5b34SRafael Vanoni dtrace_errmsg(NULL, err)); 105*b47b5b34SRafael Vanoni return (-1); 106*b47b5b34SRafael Vanoni } 107*b47b5b34SRafael Vanoni 108*b47b5b34SRafael Vanoni /* 109*b47b5b34SRafael Vanoni * Execute different scripts (defined above) depending on 110*b47b5b34SRafael Vanoni * user specified options. 111*b47b5b34SRafael Vanoni */ 112*b47b5b34SRafael Vanoni if (PTOP_ON_CPU) 113*b47b5b34SRafael Vanoni prog_ptr = (char *)dtp_cpuidle_c; 114*b47b5b34SRafael Vanoni else 115*b47b5b34SRafael Vanoni prog_ptr = (char *)dtp_cpuidle; 116*b47b5b34SRafael Vanoni 117*b47b5b34SRafael Vanoni if ((prog = dtrace_program_strcompile(dtp, prog_ptr, 118*b47b5b34SRafael Vanoni DTRACE_PROBESPEC_NAME, 0, g_argc, g_argv)) == NULL) { 119*b47b5b34SRafael Vanoni pt_error("%s : C-State DTrace probes unavailable\n", __FILE__); 120*b47b5b34SRafael Vanoni return (dtrace_errno(dtp)); 121*b47b5b34SRafael Vanoni } 122*b47b5b34SRafael Vanoni 123*b47b5b34SRafael Vanoni if (dtrace_program_exec(dtp, prog, &info) == -1) { 124*b47b5b34SRafael Vanoni pt_error("%s : failed to enable C State probes\n", __FILE__); 125*b47b5b34SRafael Vanoni return (dtrace_errno(dtp)); 126*b47b5b34SRafael Vanoni } 127*b47b5b34SRafael Vanoni 128*b47b5b34SRafael Vanoni if (dtrace_setopt(dtp, "aggsize", "128k") == -1) { 129*b47b5b34SRafael Vanoni pt_error("%s : failed to set C-state 'aggsize'\n", __FILE__); 130*b47b5b34SRafael Vanoni } 131*b47b5b34SRafael Vanoni 132*b47b5b34SRafael Vanoni if (dtrace_setopt(dtp, "aggrate", "0") == -1) { 133*b47b5b34SRafael Vanoni pt_error("%s : failed to set C-state'aggrate'\n", __FILE__); 134*b47b5b34SRafael Vanoni } 135*b47b5b34SRafael Vanoni 136*b47b5b34SRafael Vanoni if (dtrace_setopt(dtp, "aggpercpu", 0) == -1) { 137*b47b5b34SRafael Vanoni pt_error("%s : failed to set C-state 'aggpercpu'\n", __FILE__); 138*b47b5b34SRafael Vanoni } 139*b47b5b34SRafael Vanoni 140*b47b5b34SRafael Vanoni if (dtrace_go(dtp) != 0) { 141*b47b5b34SRafael Vanoni pt_error("%s : failed to start C-state observation", __FILE__); 142*b47b5b34SRafael Vanoni return (dtrace_errno(dtp)); 143*b47b5b34SRafael Vanoni } 144*b47b5b34SRafael Vanoni 145*b47b5b34SRafael Vanoni if (dtrace_getopt(dtp, "statusrate", &statustime) == -1) { 146*b47b5b34SRafael Vanoni pt_error("%s : failed to get C-state 'statusrate'\n", __FILE__); 147*b47b5b34SRafael Vanoni return (dtrace_errno(dtp)); 148*b47b5b34SRafael Vanoni } 149*b47b5b34SRafael Vanoni 150*b47b5b34SRafael Vanoni return (0); 151*b47b5b34SRafael Vanoni } 152*b47b5b34SRafael Vanoni 153*b47b5b34SRafael Vanoni /* 154*b47b5b34SRafael Vanoni * The DTrace probes have been enabled, and are tracking CPU idle state 155*b47b5b34SRafael Vanoni * transitions. Take a snapshot of the aggregations, and invoke the aggregation 156*b47b5b34SRafael Vanoni * walker to process any records. The walker does most of the accounting work 157*b47b5b34SRafael Vanoni * chalking up time spent into the g_cstate_info structure. 158*b47b5b34SRafael Vanoni */ 159*b47b5b34SRafael Vanoni int 160*b47b5b34SRafael Vanoni pt_cpuidle_stat_collect(double interval) 161*b47b5b34SRafael Vanoni { 162*b47b5b34SRafael Vanoni int i; 163*b47b5b34SRafael Vanoni hrtime_t t = 0; 164*b47b5b34SRafael Vanoni 165*b47b5b34SRafael Vanoni /* 166*b47b5b34SRafael Vanoni * Zero out the interval time reported by DTrace for 167*b47b5b34SRafael Vanoni * this interval 168*b47b5b34SRafael Vanoni */ 169*b47b5b34SRafael Vanoni for (i = 0; i < NSTATES; i++) { 170*b47b5b34SRafael Vanoni g_cstate_info[i].total_time = 0; 171*b47b5b34SRafael Vanoni g_cstate_info[i].events = 0; 172*b47b5b34SRafael Vanoni } 173*b47b5b34SRafael Vanoni 174*b47b5b34SRafael Vanoni /* 175*b47b5b34SRafael Vanoni * Assume that all the time spent in this interval will 176*b47b5b34SRafael Vanoni * be the default "0" state. The DTrace walker will reallocate 177*b47b5b34SRafael Vanoni * time out of the default bucket as it processes aggregation 178*b47b5b34SRafael Vanoni * records for time spent in other states. 179*b47b5b34SRafael Vanoni */ 180*b47b5b34SRafael Vanoni g_cstate_info[0].total_time = (long)(interval * g_ncpus_observed * 181*b47b5b34SRafael Vanoni 1000); 182*b47b5b34SRafael Vanoni 183*b47b5b34SRafael Vanoni if (dtrace_status(dtp) == -1) 184*b47b5b34SRafael Vanoni return (-1); 185*b47b5b34SRafael Vanoni 186*b47b5b34SRafael Vanoni if (dtrace_aggregate_snap(dtp) != 0) 187*b47b5b34SRafael Vanoni pt_error("%s : failed to add to aggregation", __FILE__); 188*b47b5b34SRafael Vanoni 189*b47b5b34SRafael Vanoni if (dtrace_aggregate_walk_keyvarsorted(dtp, pt_cpuidle_dtrace_walk, 190*b47b5b34SRafael Vanoni NULL) != 0) 191*b47b5b34SRafael Vanoni pt_error("%s : failed to sort aggregation", __FILE__); 192*b47b5b34SRafael Vanoni 193*b47b5b34SRafael Vanoni dtrace_aggregate_clear(dtp); 194*b47b5b34SRafael Vanoni 195*b47b5b34SRafael Vanoni /* 196*b47b5b34SRafael Vanoni * Populate g_cstate_info with the correct amount of time spent 197*b47b5b34SRafael Vanoni * in each C state and update the number of C states in g_max_cstate 198*b47b5b34SRafael Vanoni */ 199*b47b5b34SRafael Vanoni g_total_c_time = 0; 200*b47b5b34SRafael Vanoni for (i = 0; i < NSTATES; i++) { 201*b47b5b34SRafael Vanoni if (g_cstate_info[i].total_time > 0) { 202*b47b5b34SRafael Vanoni g_total_c_time += g_cstate_info[i].total_time; 203*b47b5b34SRafael Vanoni if (i > g_max_cstate) 204*b47b5b34SRafael Vanoni g_max_cstate = i; 205*b47b5b34SRafael Vanoni if (g_cstate_info[i].last_time > t) { 206*b47b5b34SRafael Vanoni t = g_cstate_info[i].last_time; 207*b47b5b34SRafael Vanoni g_longest_cstate = i; 208*b47b5b34SRafael Vanoni } 209*b47b5b34SRafael Vanoni } 210*b47b5b34SRafael Vanoni } 211*b47b5b34SRafael Vanoni 212*b47b5b34SRafael Vanoni return (0); 213*b47b5b34SRafael Vanoni } 214*b47b5b34SRafael Vanoni 215*b47b5b34SRafael Vanoni /* 216*b47b5b34SRafael Vanoni * DTrace aggregation walker that sorts through a snapshot of data records 217*b47b5b34SRafael Vanoni * collected during firings of the idle-state-transition probe. 218*b47b5b34SRafael Vanoni * 219*b47b5b34SRafael Vanoni * XXX A way of querying the current idle state for a CPU is needed in addition 220*b47b5b34SRafael Vanoni * to logic similar to that in cpufreq.c 221*b47b5b34SRafael Vanoni */ 222*b47b5b34SRafael Vanoni /*ARGSUSED*/ 223*b47b5b34SRafael Vanoni static int 224*b47b5b34SRafael Vanoni pt_cpuidle_dtrace_walk(const dtrace_aggdata_t *data, void *arg) 225*b47b5b34SRafael Vanoni { 226*b47b5b34SRafael Vanoni dtrace_aggdesc_t *aggdesc = data->dtada_desc; 227*b47b5b34SRafael Vanoni dtrace_recdesc_t *rec; 228*b47b5b34SRafael Vanoni uint64_t n = 0; 229*b47b5b34SRafael Vanoni int32_t state; 230*b47b5b34SRafael Vanoni int i; 231*b47b5b34SRafael Vanoni 232*b47b5b34SRafael Vanoni rec = &aggdesc->dtagd_rec[1]; 233*b47b5b34SRafael Vanoni /* LINTED - alignment */ 234*b47b5b34SRafael Vanoni state = *(int32_t *)(data->dtada_data + rec->dtrd_offset); 235*b47b5b34SRafael Vanoni 236*b47b5b34SRafael Vanoni if (strcmp(aggdesc->dtagd_name, "number") == 0) { 237*b47b5b34SRafael Vanoni for (i = 0; i < g_ncpus; i++) { 238*b47b5b34SRafael Vanoni /* LINTED - alignment */ 239*b47b5b34SRafael Vanoni n += *((uint64_t *)(data->dtada_percpu[i])); 240*b47b5b34SRafael Vanoni } 241*b47b5b34SRafael Vanoni g_total_events += n; 242*b47b5b34SRafael Vanoni g_cstate_info[state].events += n; 243*b47b5b34SRafael Vanoni } 244*b47b5b34SRafael Vanoni else 245*b47b5b34SRafael Vanoni if (strcmp(aggdesc->dtagd_name, "times") == 0) { 246*b47b5b34SRafael Vanoni for (i = 0; i < g_ncpus; i++) { 247*b47b5b34SRafael Vanoni /* LINTED - alignment */ 248*b47b5b34SRafael Vanoni n += *((uint64_t *)(data->dtada_percpu[i])); 249*b47b5b34SRafael Vanoni } 250*b47b5b34SRafael Vanoni g_cstate_info[state].last_time = n; 251*b47b5b34SRafael Vanoni g_cstate_info[state].total_time += n; 252*b47b5b34SRafael Vanoni if (g_cstate_info[0].total_time >= n) 253*b47b5b34SRafael Vanoni g_cstate_info[0].total_time -= n; 254*b47b5b34SRafael Vanoni } 255*b47b5b34SRafael Vanoni 256*b47b5b34SRafael Vanoni return (DTRACE_AGGWALK_NEXT); 257*b47b5b34SRafael Vanoni } 258