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 47 /*ARGSUSED*/ 48 static int 49 walk(const dtrace_aggdata_t *data, void *arg) 50 { 51 dtrace_aggdesc_t *aggdesc = data->dtada_desc; 52 dtrace_recdesc_t *rec1, *rec2, *rec3; 53 dtrace_syminfo_t dts; 54 char *offense_name; 55 uint64_t offender_addr; 56 int32_t *instance, *offender_cpu; 57 int i; 58 uint64_t n = 0; 59 GElf_Sym sym; 60 61 if (g_tog_p_events >= EVENT_NUM_MAX) 62 return (0); 63 64 rec1 = &aggdesc->dtagd_rec[1]; 65 rec2 = &aggdesc->dtagd_rec[2]; 66 67 /* 68 * Report interrupts 69 */ 70 if (strcmp(aggdesc->dtagd_name, "interrupts") == 0) { 71 offense_name = data->dtada_data + rec1->dtrd_offset; 72 73 /* LINTED - alignment */ 74 instance = (int32_t *)(data->dtada_data + rec2->dtrd_offset); 75 (void) snprintf((char *)(g_p_event->offender_name), 76 EVENT_NAME_MAX, "%s", "<interrupt>"); 77 (void) snprintf((char *)(g_p_event->offense_name), 78 EVENT_NAME_MAX, "%s#%d", offense_name, *instance); 79 /* 80 * Report kernel events 81 */ 82 } else if (strcmp(aggdesc->dtagd_name, "events_k") == 0) { 83 84 (void) snprintf((char *)(g_p_event->offender_name), 85 EVENT_NAME_MAX, "%s", "<kernel>"); 86 87 /* 88 * Casting offender_addr to the wrong type will cause 89 * dtrace_lookup_by_addr to return 0 and the report 90 * to show an address instead of a name. 91 */ 92 switch (g_bit_depth) { 93 case 32: 94 /* LINTED - alignment */ 95 offender_addr = *(uint32_t *)(data->dtada_data + 96 rec1->dtrd_offset); 97 break; 98 case 64: 99 /* LINTED - alignment */ 100 offender_addr = *(uint64_t *)(data->dtada_data + 101 rec1->dtrd_offset); 102 break; 103 } 104 105 /* 106 * We have the address of the kernel callout. 107 * Try to resolve it into a meaningful symbol 108 */ 109 if (offender_addr != NULL && dtrace_lookup_by_addr(dtp, 110 offender_addr, &sym, &dts) == 0) { 111 (void) snprintf((char *)(g_p_event->offense_name), 112 EVENT_NAME_MAX, "%s`%s", dts.dts_object, 113 dts.dts_name); 114 } else { 115 (void) snprintf((char *)(g_p_event->offense_name), 116 EVENT_NAME_MAX, "0x%llx", offender_addr); 117 } 118 /* 119 * Report user events 120 */ 121 } else if (strcmp(aggdesc->dtagd_name, "events_u") == 0) { 122 offense_name = data->dtada_data + rec1->dtrd_offset; 123 124 (void) snprintf((char *)(g_p_event->offender_name), 125 EVENT_NAME_MAX, "%s", offense_name); 126 (void) snprintf((char *)(g_p_event->offense_name), 127 EVENT_NAME_MAX, "<scheduled timeout expiration>"); 128 /* 129 * Report cross calls 130 */ 131 } else if (strcmp(aggdesc->dtagd_name, "events_x") == 0) { 132 offense_name = data->dtada_data + rec1->dtrd_offset; 133 134 (void) snprintf((char *)(g_p_event->offender_name), 135 EVENT_NAME_MAX, "%s", offense_name); 136 137 switch (g_bit_depth) { 138 case 32: 139 /* LINTED - alignment */ 140 offender_addr = *(uint32_t *)(data->dtada_data + 141 rec2->dtrd_offset); 142 break; 143 case 64: 144 /* LINTED - alignment */ 145 offender_addr = *(uint64_t *)(data->dtada_data + 146 rec2->dtrd_offset); 147 break; 148 } 149 150 /* 151 * Try to resolve the address of the cross call function. 152 */ 153 if (offender_addr != NULL && dtrace_lookup_by_addr(dtp, 154 offender_addr, &sym, &dts) == 0) { 155 (void) snprintf((char *)(g_p_event->offense_name), 156 EVENT_NAME_MAX, "<xcalls> %s`%s", 157 dts.dts_object, dts.dts_name); 158 } else { 159 (void) snprintf((char *)(g_p_event->offense_name), 160 EVENT_NAME_MAX, "<xcalls>"); 161 } 162 /* 163 * Report cross calls from other CPUs than the one we're observing 164 * with the -C option 165 */ 166 } else if (strcmp(aggdesc->dtagd_name, "events_xc") == 0) { 167 rec3 = &aggdesc->dtagd_rec[3]; 168 offense_name = data->dtada_data + rec1->dtrd_offset; 169 170 (void) snprintf((char *)(g_p_event->offender_name), 171 EVENT_NAME_MAX, "%s", offense_name); 172 173 switch (g_bit_depth) { 174 case 32: 175 /* LINTED - alignment */ 176 offender_addr = *(uint32_t *)(data->dtada_data + 177 rec2->dtrd_offset); 178 break; 179 case 64: 180 /* LINTED - alignment */ 181 offender_addr = *(uint64_t *)(data->dtada_data + 182 rec2->dtrd_offset); 183 break; 184 } 185 /* LINTED - alignment */ 186 offender_cpu = (int32_t *)(data->dtada_data + 187 rec3->dtrd_offset); 188 189 /* 190 * Try to resolve the address of the cross call function. 191 */ 192 if (offender_addr != NULL && dtrace_lookup_by_addr(dtp, 193 offender_addr, &sym, &dts) == 0) { 194 (void) snprintf((char *)(g_p_event->offense_name), 195 EVENT_NAME_MAX, "<xcalls> %s`%s (CPU %d)", 196 dts.dts_object, dts.dts_name, *offender_cpu); 197 } else { 198 (void) snprintf((char *)(g_p_event->offense_name), 199 EVENT_NAME_MAX, "<xcalls> (CPU %d)", 200 *offender_cpu); 201 } 202 /* 203 * Report unknown events 204 */ 205 } else { 206 (void) snprintf((char *)(g_p_event->offender_name), 207 EVENT_NAME_MAX, "%s", "<unknown>"); 208 (void) snprintf((char *)(g_p_event->offense_name), 209 EVENT_NAME_MAX, "%s", "<unknown>"); 210 } 211 212 for (i = 0; i < g_ncpus; i++) 213 /* LINTED - alignment */ 214 n += *((uint64_t *)(data->dtada_percpu[i])); 215 216 g_p_event->total_count = n; 217 218 g_p_event++; 219 g_tog_p_events++; 220 221 return (DTRACE_AGGWALK_NEXT); 222 } 223 224 int 225 pt_events_stat_prepare(void) 226 { 227 dtrace_prog_t *prog; 228 dtrace_proginfo_t info; 229 int err; 230 dtrace_optval_t statustime; 231 char *prog_ptr; 232 233 g_p_event = g_event_info; 234 235 if ((dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) { 236 pt_error("%s : cannot open dtrace library: %s\n", __FILE__, 237 dtrace_errmsg(NULL, err)); 238 return (-1); 239 } 240 241 /* 242 * Execute different scripts (defined in the platform specific file) 243 * depending on user specified options. 244 */ 245 if (PTOP_ON_VERBOSE) { 246 prog_ptr = (char *)g_dtp_events_v; 247 } else { 248 if (PTOP_ON_CPU) 249 prog_ptr = (char *)g_dtp_events_c; 250 else 251 prog_ptr = (char *)g_dtp_events; 252 } 253 254 if ((prog = dtrace_program_strcompile(dtp, prog_ptr, 255 DTRACE_PROBESPEC_NAME, 0, g_argc, g_argv)) == NULL) { 256 pt_error("%s : failed to compile DTrace program\n", __FILE__); 257 return (dtrace_errno(dtp)); 258 } 259 260 if (dtrace_program_exec(dtp, prog, &info) == -1) { 261 pt_error("%s : failed to enable probes\n", __FILE__); 262 return (dtrace_errno(dtp)); 263 } 264 265 if (dtrace_setopt(dtp, "aggsize", "128k") == -1) { 266 pt_error("%s : failed to set 'aggsize'\n", __FILE__); 267 return (dtrace_errno(dtp)); 268 } 269 270 if (dtrace_setopt(dtp, "aggrate", "0") == -1) { 271 pt_error("%s : failed to set 'aggrate'\n", __FILE__); 272 return (dtrace_errno(dtp)); 273 } 274 275 if (dtrace_setopt(dtp, "aggpercpu", 0) == -1) { 276 pt_error("%s : failed to set 'aggpercpu'\n", __FILE__); 277 return (dtrace_errno(dtp)); 278 } 279 280 if (dtrace_go(dtp) != 0) { 281 pt_error("%s : dtrace_go() failed\n", __FILE__); 282 return (dtrace_errno(dtp)); 283 } 284 285 if (dtrace_getopt(dtp, "statusrate", &statustime) == -1) { 286 pt_error("%s : failed to get 'statusrate'\n", __FILE__); 287 return (dtrace_errno(dtp)); 288 } 289 290 return (0); 291 } 292 293 int 294 pt_events_stat_collect(void) 295 { 296 g_p_event = g_event_info; 297 g_tog_p_events = 0; 298 299 if (dtrace_status(dtp) == -1) 300 return (-1); 301 302 if (dtrace_aggregate_snap(dtp) != 0) 303 pt_error("%s : failed to add to aggregate", __FILE__); 304 305 if (dtrace_aggregate_walk_keyvarsorted(dtp, walk, NULL) != 0) 306 pt_error("%s : failed to sort aggregate", __FILE__); 307 308 dtrace_aggregate_clear(dtp); 309 310 return (0); 311 } 312