xref: /illumos-gate/usr/src/cmd/powertop/common/events.c (revision c1381f4429cde25f5ee926dfa005545a7f220ba9)
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