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 != 0 && 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 != 0 && 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 != 0 && 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