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