1b47b5b34SRafael Vanoni /* 2b47b5b34SRafael Vanoni * Copyright 2009, Intel Corporation 3b47b5b34SRafael Vanoni * Copyright 2009, Sun Microsystems, Inc 4b47b5b34SRafael Vanoni * 5b47b5b34SRafael Vanoni * This file is part of PowerTOP 6b47b5b34SRafael Vanoni * 7b47b5b34SRafael Vanoni * This program file is free software; you can redistribute it and/or modify it 8b47b5b34SRafael Vanoni * under the terms of the GNU General Public License as published by the 9b47b5b34SRafael Vanoni * Free Software Foundation; version 2 of the License. 10b47b5b34SRafael Vanoni * 11b47b5b34SRafael Vanoni * This program is distributed in the hope that it will be useful, but WITHOUT 12b47b5b34SRafael Vanoni * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13b47b5b34SRafael Vanoni * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14b47b5b34SRafael Vanoni * for more details. 15b47b5b34SRafael Vanoni * 16b47b5b34SRafael Vanoni * You should have received a copy of the GNU General Public License 17b47b5b34SRafael Vanoni * along with this program in a file named COPYING; if not, write to the 18b47b5b34SRafael Vanoni * Free Software Foundation, Inc., 19b47b5b34SRafael Vanoni * 51 Franklin Street, Fifth Floor, 20b47b5b34SRafael Vanoni * Boston, MA 02110-1301 USA 21b47b5b34SRafael Vanoni * 22b47b5b34SRafael Vanoni * Authors: 23b47b5b34SRafael Vanoni * Arjan van de Ven <arjan@linux.intel.com> 24b47b5b34SRafael Vanoni * Eric C Saxe <eric.saxe@sun.com> 25b47b5b34SRafael Vanoni * Aubrey Li <aubrey.li@intel.com> 26b47b5b34SRafael Vanoni */ 27b47b5b34SRafael Vanoni 28b47b5b34SRafael Vanoni /* 29b47b5b34SRafael Vanoni * GPL Disclaimer 30b47b5b34SRafael Vanoni * 31b47b5b34SRafael Vanoni * For the avoidance of doubt, except that if any license choice other 32b47b5b34SRafael Vanoni * than GPL or LGPL is available it will apply instead, Sun elects to 33b47b5b34SRafael Vanoni * use only the General Public License version 2 (GPLv2) at this time 34b47b5b34SRafael Vanoni * for any software where a choice of GPL license versions is made 35b47b5b34SRafael Vanoni * available with the language indicating that GPLv2 or any later 36b47b5b34SRafael Vanoni * version may be used, or where a choice of which version of the GPL 37b47b5b34SRafael Vanoni * is applied is otherwise unspecified. 38b47b5b34SRafael Vanoni */ 39b47b5b34SRafael Vanoni 40b47b5b34SRafael Vanoni #include <string.h> 41b47b5b34SRafael Vanoni #include <stdlib.h> 42b47b5b34SRafael Vanoni #include <dtrace.h> 43b47b5b34SRafael Vanoni #include "powertop.h" 44b47b5b34SRafael Vanoni 45b47b5b34SRafael Vanoni static dtrace_hdl_t *dtp; 46636423dbSRafael Vanoni static event_info_t *event; 47b47b5b34SRafael Vanoni 48b47b5b34SRafael Vanoni /*ARGSUSED*/ 49b47b5b34SRafael Vanoni static int 50636423dbSRafael Vanoni pt_events_walk(const dtrace_aggdata_t *data, void *arg) 51b47b5b34SRafael Vanoni { 52b47b5b34SRafael Vanoni dtrace_aggdesc_t *aggdesc = data->dtada_desc; 53b47b5b34SRafael Vanoni dtrace_recdesc_t *rec1, *rec2, *rec3; 54b47b5b34SRafael Vanoni dtrace_syminfo_t dts; 55636423dbSRafael Vanoni GElf_Sym sym; 56b47b5b34SRafael Vanoni uint64_t offender_addr; 57636423dbSRafael Vanoni uint64_t n = 0; 58b47b5b34SRafael Vanoni int32_t *instance, *offender_cpu; 59b47b5b34SRafael Vanoni int i; 60636423dbSRafael Vanoni char *offense_name; 61b47b5b34SRafael Vanoni 62636423dbSRafael Vanoni if (g_top_events >= EVENT_NUM_MAX) 63b47b5b34SRafael Vanoni return (0); 64b47b5b34SRafael Vanoni 65b47b5b34SRafael Vanoni rec1 = &aggdesc->dtagd_rec[1]; 66b47b5b34SRafael Vanoni rec2 = &aggdesc->dtagd_rec[2]; 67b47b5b34SRafael Vanoni 68b47b5b34SRafael Vanoni /* 69b47b5b34SRafael Vanoni * Report interrupts 70b47b5b34SRafael Vanoni */ 71b47b5b34SRafael Vanoni if (strcmp(aggdesc->dtagd_name, "interrupts") == 0) { 72b47b5b34SRafael Vanoni offense_name = data->dtada_data + rec1->dtrd_offset; 73b47b5b34SRafael Vanoni 74b47b5b34SRafael Vanoni /* LINTED - alignment */ 75b47b5b34SRafael Vanoni instance = (int32_t *)(data->dtada_data + rec2->dtrd_offset); 76636423dbSRafael Vanoni (void) snprintf((char *)(event->offender_name), 77b47b5b34SRafael Vanoni EVENT_NAME_MAX, "%s", "<interrupt>"); 78636423dbSRafael Vanoni (void) snprintf((char *)(event->offense_name), 79b47b5b34SRafael Vanoni EVENT_NAME_MAX, "%s#%d", offense_name, *instance); 80b47b5b34SRafael Vanoni /* 81b47b5b34SRafael Vanoni * Report kernel events 82b47b5b34SRafael Vanoni */ 83b47b5b34SRafael Vanoni } else if (strcmp(aggdesc->dtagd_name, "events_k") == 0) { 84b47b5b34SRafael Vanoni 85636423dbSRafael Vanoni (void) snprintf((char *)(event->offender_name), 86b47b5b34SRafael Vanoni EVENT_NAME_MAX, "%s", "<kernel>"); 87b47b5b34SRafael Vanoni 88b47b5b34SRafael Vanoni /* 89b47b5b34SRafael Vanoni * Casting offender_addr to the wrong type will cause 90b47b5b34SRafael Vanoni * dtrace_lookup_by_addr to return 0 and the report 91b47b5b34SRafael Vanoni * to show an address instead of a name. 92b47b5b34SRafael Vanoni */ 93b47b5b34SRafael Vanoni switch (g_bit_depth) { 94b47b5b34SRafael Vanoni case 32: 95b47b5b34SRafael Vanoni /* LINTED - alignment */ 96b47b5b34SRafael Vanoni offender_addr = *(uint32_t *)(data->dtada_data + 97b47b5b34SRafael Vanoni rec1->dtrd_offset); 98b47b5b34SRafael Vanoni break; 99b47b5b34SRafael Vanoni case 64: 100b47b5b34SRafael Vanoni /* LINTED - alignment */ 101b47b5b34SRafael Vanoni offender_addr = *(uint64_t *)(data->dtada_data + 102b47b5b34SRafael Vanoni rec1->dtrd_offset); 103b47b5b34SRafael Vanoni break; 104b47b5b34SRafael Vanoni } 105b47b5b34SRafael Vanoni 106b47b5b34SRafael Vanoni /* 107b47b5b34SRafael Vanoni * We have the address of the kernel callout. 108b47b5b34SRafael Vanoni * Try to resolve it into a meaningful symbol 109b47b5b34SRafael Vanoni */ 110b47b5b34SRafael Vanoni if (offender_addr != NULL && dtrace_lookup_by_addr(dtp, 111b47b5b34SRafael Vanoni offender_addr, &sym, &dts) == 0) { 112636423dbSRafael Vanoni (void) snprintf((char *)(event->offense_name), 113b47b5b34SRafael Vanoni EVENT_NAME_MAX, "%s`%s", dts.dts_object, 114b47b5b34SRafael Vanoni dts.dts_name); 115b47b5b34SRafael Vanoni } else { 116636423dbSRafael Vanoni (void) snprintf((char *)(event->offense_name), 117b47b5b34SRafael Vanoni EVENT_NAME_MAX, "0x%llx", offender_addr); 118b47b5b34SRafael Vanoni } 119b47b5b34SRafael Vanoni /* 120b47b5b34SRafael Vanoni * Report user events 121b47b5b34SRafael Vanoni */ 122b47b5b34SRafael Vanoni } else if (strcmp(aggdesc->dtagd_name, "events_u") == 0) { 123b47b5b34SRafael Vanoni offense_name = data->dtada_data + rec1->dtrd_offset; 124b47b5b34SRafael Vanoni 125636423dbSRafael Vanoni (void) snprintf((char *)(event->offender_name), 126b47b5b34SRafael Vanoni EVENT_NAME_MAX, "%s", offense_name); 127636423dbSRafael Vanoni (void) snprintf((char *)(event->offense_name), 128b47b5b34SRafael Vanoni EVENT_NAME_MAX, "<scheduled timeout expiration>"); 129b47b5b34SRafael Vanoni /* 130b47b5b34SRafael Vanoni * Report cross calls 131b47b5b34SRafael Vanoni */ 132b47b5b34SRafael Vanoni } else if (strcmp(aggdesc->dtagd_name, "events_x") == 0) { 133b47b5b34SRafael Vanoni offense_name = data->dtada_data + rec1->dtrd_offset; 134b47b5b34SRafael Vanoni 135636423dbSRafael Vanoni (void) snprintf((char *)(event->offender_name), 136b47b5b34SRafael Vanoni EVENT_NAME_MAX, "%s", offense_name); 137b47b5b34SRafael Vanoni 138b47b5b34SRafael Vanoni switch (g_bit_depth) { 139b47b5b34SRafael Vanoni case 32: 140b47b5b34SRafael Vanoni /* LINTED - alignment */ 141b47b5b34SRafael Vanoni offender_addr = *(uint32_t *)(data->dtada_data + 142b47b5b34SRafael Vanoni rec2->dtrd_offset); 143b47b5b34SRafael Vanoni break; 144b47b5b34SRafael Vanoni case 64: 145b47b5b34SRafael Vanoni /* LINTED - alignment */ 146b47b5b34SRafael Vanoni offender_addr = *(uint64_t *)(data->dtada_data + 147b47b5b34SRafael Vanoni rec2->dtrd_offset); 148b47b5b34SRafael Vanoni break; 149b47b5b34SRafael Vanoni } 150b47b5b34SRafael Vanoni 151b47b5b34SRafael Vanoni /* 152b47b5b34SRafael Vanoni * Try to resolve the address of the cross call function. 153b47b5b34SRafael Vanoni */ 154b47b5b34SRafael Vanoni if (offender_addr != NULL && dtrace_lookup_by_addr(dtp, 155b47b5b34SRafael Vanoni offender_addr, &sym, &dts) == 0) { 156636423dbSRafael Vanoni (void) snprintf((char *)(event->offense_name), 157b47b5b34SRafael Vanoni EVENT_NAME_MAX, "<xcalls> %s`%s", 158b47b5b34SRafael Vanoni dts.dts_object, dts.dts_name); 159b47b5b34SRafael Vanoni } else { 160636423dbSRafael Vanoni (void) snprintf((char *)(event->offense_name), 161b47b5b34SRafael Vanoni EVENT_NAME_MAX, "<xcalls>"); 162b47b5b34SRafael Vanoni } 163b47b5b34SRafael Vanoni /* 164b47b5b34SRafael Vanoni * Report cross calls from other CPUs than the one we're observing 165b47b5b34SRafael Vanoni * with the -C option 166b47b5b34SRafael Vanoni */ 167b47b5b34SRafael Vanoni } else if (strcmp(aggdesc->dtagd_name, "events_xc") == 0) { 168b47b5b34SRafael Vanoni rec3 = &aggdesc->dtagd_rec[3]; 169b47b5b34SRafael Vanoni offense_name = data->dtada_data + rec1->dtrd_offset; 170b47b5b34SRafael Vanoni 171636423dbSRafael Vanoni (void) snprintf((char *)(event->offender_name), 172b47b5b34SRafael Vanoni EVENT_NAME_MAX, "%s", offense_name); 173b47b5b34SRafael Vanoni 174b47b5b34SRafael Vanoni switch (g_bit_depth) { 175b47b5b34SRafael Vanoni case 32: 176b47b5b34SRafael Vanoni /* LINTED - alignment */ 177b47b5b34SRafael Vanoni offender_addr = *(uint32_t *)(data->dtada_data + 178b47b5b34SRafael Vanoni rec2->dtrd_offset); 179b47b5b34SRafael Vanoni break; 180b47b5b34SRafael Vanoni case 64: 181b47b5b34SRafael Vanoni /* LINTED - alignment */ 182b47b5b34SRafael Vanoni offender_addr = *(uint64_t *)(data->dtada_data + 183b47b5b34SRafael Vanoni rec2->dtrd_offset); 184b47b5b34SRafael Vanoni break; 185b47b5b34SRafael Vanoni } 186b47b5b34SRafael Vanoni /* LINTED - alignment */ 187b47b5b34SRafael Vanoni offender_cpu = (int32_t *)(data->dtada_data + 188b47b5b34SRafael Vanoni rec3->dtrd_offset); 189b47b5b34SRafael Vanoni 190b47b5b34SRafael Vanoni /* 191b47b5b34SRafael Vanoni * Try to resolve the address of the cross call function. 192b47b5b34SRafael Vanoni */ 193b47b5b34SRafael Vanoni if (offender_addr != NULL && dtrace_lookup_by_addr(dtp, 194b47b5b34SRafael Vanoni offender_addr, &sym, &dts) == 0) { 195636423dbSRafael Vanoni (void) snprintf((char *)(event->offense_name), 196b47b5b34SRafael Vanoni EVENT_NAME_MAX, "<xcalls> %s`%s (CPU %d)", 197b47b5b34SRafael Vanoni dts.dts_object, dts.dts_name, *offender_cpu); 198b47b5b34SRafael Vanoni } else { 199636423dbSRafael Vanoni (void) snprintf((char *)(event->offense_name), 200b47b5b34SRafael Vanoni EVENT_NAME_MAX, "<xcalls> (CPU %d)", 201b47b5b34SRafael Vanoni *offender_cpu); 202b47b5b34SRafael Vanoni } 203b47b5b34SRafael Vanoni /* 204b47b5b34SRafael Vanoni * Report unknown events 205b47b5b34SRafael Vanoni */ 206b47b5b34SRafael Vanoni } else { 207636423dbSRafael Vanoni (void) snprintf((char *)(event->offender_name), 208b47b5b34SRafael Vanoni EVENT_NAME_MAX, "%s", "<unknown>"); 209636423dbSRafael Vanoni (void) snprintf((char *)(event->offense_name), 210b47b5b34SRafael Vanoni EVENT_NAME_MAX, "%s", "<unknown>"); 211b47b5b34SRafael Vanoni } 212b47b5b34SRafael Vanoni 213636423dbSRafael Vanoni for (i = 0; i < g_ncpus; i++) { 214b47b5b34SRafael Vanoni /* LINTED - alignment */ 215b47b5b34SRafael Vanoni n += *((uint64_t *)(data->dtada_percpu[i])); 216636423dbSRafael Vanoni } 217b47b5b34SRafael Vanoni 218636423dbSRafael Vanoni event->total_count = n; 219b47b5b34SRafael Vanoni 220636423dbSRafael Vanoni event++; 221636423dbSRafael Vanoni g_top_events++; 222b47b5b34SRafael Vanoni 223b47b5b34SRafael Vanoni return (DTRACE_AGGWALK_NEXT); 224b47b5b34SRafael Vanoni } 225b47b5b34SRafael Vanoni 226b47b5b34SRafael Vanoni int 227b47b5b34SRafael Vanoni pt_events_stat_prepare(void) 228b47b5b34SRafael Vanoni { 229b47b5b34SRafael Vanoni dtrace_prog_t *prog; 230b47b5b34SRafael Vanoni dtrace_proginfo_t info; 231b47b5b34SRafael Vanoni dtrace_optval_t statustime; 232636423dbSRafael Vanoni int err; 233b47b5b34SRafael Vanoni char *prog_ptr; 234b47b5b34SRafael Vanoni 235636423dbSRafael Vanoni event = g_event_info; 236b47b5b34SRafael Vanoni 237b47b5b34SRafael Vanoni if ((dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) { 238*2d83778aSRafael Vanoni pt_error("cannot open dtrace library for the event report: " 239*2d83778aSRafael Vanoni "%s\n", dtrace_errmsg(NULL, err)); 240b47b5b34SRafael Vanoni return (-1); 241b47b5b34SRafael Vanoni } 242b47b5b34SRafael Vanoni 243b47b5b34SRafael Vanoni /* 244b47b5b34SRafael Vanoni * Execute different scripts (defined in the platform specific file) 245b47b5b34SRafael Vanoni * depending on user specified options. 246b47b5b34SRafael Vanoni */ 247636423dbSRafael Vanoni if (PT_ON_VERBOSE) { 248b47b5b34SRafael Vanoni prog_ptr = (char *)g_dtp_events_v; 249b47b5b34SRafael Vanoni } else { 250636423dbSRafael Vanoni if (PT_ON_CPU) 251b47b5b34SRafael Vanoni prog_ptr = (char *)g_dtp_events_c; 252b47b5b34SRafael Vanoni else 253b47b5b34SRafael Vanoni prog_ptr = (char *)g_dtp_events; 254b47b5b34SRafael Vanoni } 255b47b5b34SRafael Vanoni 256b47b5b34SRafael Vanoni if ((prog = dtrace_program_strcompile(dtp, prog_ptr, 257b47b5b34SRafael Vanoni DTRACE_PROBESPEC_NAME, 0, g_argc, g_argv)) == NULL) { 258*2d83778aSRafael Vanoni pt_error("failed to compile the event report program\n"); 259b47b5b34SRafael Vanoni return (dtrace_errno(dtp)); 260b47b5b34SRafael Vanoni } 261b47b5b34SRafael Vanoni 262b47b5b34SRafael Vanoni if (dtrace_program_exec(dtp, prog, &info) == -1) { 263*2d83778aSRafael Vanoni pt_error("failed to enable probes for the event report\n"); 264b47b5b34SRafael Vanoni return (dtrace_errno(dtp)); 265b47b5b34SRafael Vanoni } 266b47b5b34SRafael Vanoni 267b47b5b34SRafael Vanoni if (dtrace_setopt(dtp, "aggsize", "128k") == -1) { 268*2d83778aSRafael Vanoni pt_error("failed to set 'aggsize' for the event report\n"); 269b47b5b34SRafael Vanoni return (dtrace_errno(dtp)); 270b47b5b34SRafael Vanoni } 271b47b5b34SRafael Vanoni 272b47b5b34SRafael Vanoni if (dtrace_setopt(dtp, "aggrate", "0") == -1) { 273*2d83778aSRafael Vanoni pt_error("failed to set 'aggrate' for the event report\n"); 274b47b5b34SRafael Vanoni return (dtrace_errno(dtp)); 275b47b5b34SRafael Vanoni } 276b47b5b34SRafael Vanoni 277b47b5b34SRafael Vanoni if (dtrace_setopt(dtp, "aggpercpu", 0) == -1) { 278*2d83778aSRafael Vanoni pt_error("failed to set 'aggpercpu' for the event report\n"); 279b47b5b34SRafael Vanoni return (dtrace_errno(dtp)); 280b47b5b34SRafael Vanoni } 281b47b5b34SRafael Vanoni 282b47b5b34SRafael Vanoni if (dtrace_go(dtp) != 0) { 283*2d83778aSRafael Vanoni pt_error("failed to start the event report observation\n"); 284b47b5b34SRafael Vanoni return (dtrace_errno(dtp)); 285b47b5b34SRafael Vanoni } 286b47b5b34SRafael Vanoni 287b47b5b34SRafael Vanoni if (dtrace_getopt(dtp, "statusrate", &statustime) == -1) { 288*2d83778aSRafael Vanoni pt_error("failed to get 'statusrate' for the event report\n"); 289b47b5b34SRafael Vanoni return (dtrace_errno(dtp)); 290b47b5b34SRafael Vanoni } 291b47b5b34SRafael Vanoni 292b47b5b34SRafael Vanoni return (0); 293b47b5b34SRafael Vanoni } 294b47b5b34SRafael Vanoni 295b47b5b34SRafael Vanoni int 296b47b5b34SRafael Vanoni pt_events_stat_collect(void) 297b47b5b34SRafael Vanoni { 298636423dbSRafael Vanoni g_top_events = 0; 299636423dbSRafael Vanoni event = g_event_info; 300b47b5b34SRafael Vanoni 301b47b5b34SRafael Vanoni if (dtrace_status(dtp) == -1) 302b47b5b34SRafael Vanoni return (-1); 303b47b5b34SRafael Vanoni 304b47b5b34SRafael Vanoni if (dtrace_aggregate_snap(dtp) != 0) 305*2d83778aSRafael Vanoni pt_error("failed to collect data for the event report\n"); 306b47b5b34SRafael Vanoni 307636423dbSRafael Vanoni if (dtrace_aggregate_walk_keyvarsorted(dtp, pt_events_walk, NULL) != 0) 308*2d83778aSRafael Vanoni pt_error("failed to sort data for the event report\n"); 309b47b5b34SRafael Vanoni 310b47b5b34SRafael Vanoni dtrace_aggregate_clear(dtp); 311b47b5b34SRafael Vanoni 312b47b5b34SRafael Vanoni return (0); 313b47b5b34SRafael Vanoni } 314