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
pt_events_walk(const dtrace_aggdata_t * data,void * arg)50636423dbSRafael Vanoni pt_events_walk(const dtrace_aggdata_t *data, void *arg)
51b47b5b34SRafael Vanoni {
52*f795e601SToomas Soome 	dtrace_aggdesc_t	*aggdesc = data->dtada_desc;
53*f795e601SToomas Soome 	dtrace_recdesc_t	*rec1, *rec2, *rec3;
54*f795e601SToomas Soome 	dtrace_syminfo_t	dts;
55*f795e601SToomas Soome 	GElf_Sym		sym;
56b47b5b34SRafael Vanoni 	uint64_t		offender_addr;
57*f795e601SToomas Soome 	uint64_t		n = 0;
58*f795e601SToomas Soome 	int32_t			*instance, *offender_cpu;
59*f795e601SToomas Soome 	int			i;
60*f795e601SToomas Soome 	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 		 */
110*f795e601SToomas Soome 		if (offender_addr != 0 && 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 		 */
154*f795e601SToomas Soome 		if (offender_addr != 0 && 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 		 */
193*f795e601SToomas Soome 		if (offender_addr != 0 && 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
pt_events_stat_prepare(void)227b47b5b34SRafael Vanoni pt_events_stat_prepare(void)
228b47b5b34SRafael Vanoni {
229*f795e601SToomas Soome 	dtrace_prog_t		*prog;
230*f795e601SToomas Soome 	dtrace_proginfo_t	info;
231*f795e601SToomas Soome 	dtrace_optval_t		statustime;
232*f795e601SToomas Soome 	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) {
2382d83778aSRafael Vanoni 		pt_error("cannot open dtrace library for the event report: "
2392d83778aSRafael 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) {
2582d83778aSRafael 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) {
2632d83778aSRafael 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) {
2682d83778aSRafael 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) {
2732d83778aSRafael 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) {
2782d83778aSRafael 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) {
2832d83778aSRafael 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) {
2882d83778aSRafael 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
pt_events_stat_collect(void)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)
3052d83778aSRafael 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)
3082d83778aSRafael 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