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
pt_events_walk(const dtrace_aggdata_t * data,void * arg)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
pt_events_stat_prepare(void)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("cannot open dtrace library for the event report: "
239 "%s\n", 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("failed to compile the event report program\n");
259 return (dtrace_errno(dtp));
260 }
261
262 if (dtrace_program_exec(dtp, prog, &info) == -1) {
263 pt_error("failed to enable probes for the event report\n");
264 return (dtrace_errno(dtp));
265 }
266
267 if (dtrace_setopt(dtp, "aggsize", "128k") == -1) {
268 pt_error("failed to set 'aggsize' for the event report\n");
269 return (dtrace_errno(dtp));
270 }
271
272 if (dtrace_setopt(dtp, "aggrate", "0") == -1) {
273 pt_error("failed to set 'aggrate' for the event report\n");
274 return (dtrace_errno(dtp));
275 }
276
277 if (dtrace_setopt(dtp, "aggpercpu", 0) == -1) {
278 pt_error("failed to set 'aggpercpu' for the event report\n");
279 return (dtrace_errno(dtp));
280 }
281
282 if (dtrace_go(dtp) != 0) {
283 pt_error("failed to start the event report observation\n");
284 return (dtrace_errno(dtp));
285 }
286
287 if (dtrace_getopt(dtp, "statusrate", &statustime) == -1) {
288 pt_error("failed to get 'statusrate' for the event report\n");
289 return (dtrace_errno(dtp));
290 }
291
292 return (0);
293 }
294
295 int
pt_events_stat_collect(void)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("failed to collect data for the event report\n");
306
307 if (dtrace_aggregate_walk_keyvarsorted(dtp, pt_events_walk, NULL) != 0)
308 pt_error("failed to sort data for the event report\n");
309
310 dtrace_aggregate_clear(dtp);
311
312 return (0);
313 }
314