1 /******************************************************************************/
2 /*                                                                            */
3 /* Paul Mackerras <paulus@samba.org>, 2009                                    */
4 /*                                                                            */
5 /* This program is free software;  you can redistribute it and/or modify      */
6 /* it under the terms of the GNU General Public License as published by       */
7 /* the Free Software Foundation; either version 2 of the License, or          */
8 /* (at your option) any later version.                                        */
9 /*                                                                            */
10 /* This program is distributed in the hope that it will be useful,            */
11 /* but WITHOUT ANY WARRANTY;  without even the implied warranty of            */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See                  */
13 /* the GNU General Public License for more details.                           */
14 /*                                                                            */
15 /* You should have received a copy of the GNU General Public License          */
16 /* along with this program;  if not, write to the Free Software               */
17 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA    */
18 /*                                                                            */
19 /******************************************************************************/
20 /*
21 Here's a little test program that checks whether software counters
22 (specifically, the task clock counter) work correctly when they're in
23 a group with hardware counters.
24 
25 What it does is to create several groups, each with one hardware
26 counter, counting instructions, plus a task clock counter.  It needs
27 to know an upper bound N on the number of hardware counters you have
28 (N defaults to 8), and it creates N+4 groups to force them to be
29 multiplexed.  It also creates an overall task clock counter.
30 
31 Then it spins for a while, and then stops all the counters and reads
32 them.  It takes the total of the task clock counters in the groups and
33 computes the ratio of that total to the overall execution time from
34 the overall task clock counter.
35 
36 That ratio should be equal to the number of actual hardware counters
37 that can count instructions.  If the task clock counters in the groups
38 don't stop when their group gets taken off the PMU, the ratio will
39 instead be close to N+4.  The program will declare that the test fails
40 if the ratio is greater than N (actually, N + 0.0001 to allow for FP
41 rounding errors).
42 
43 Could someone run this on x86 on the latest PCL tree and let me know
44 what happens?  I don't have an x86 crash box easily to hand.  On
45 powerpc, it passes, but I think that is because I am missing setting
46 counter->prev_count in arch/powerpc/kernel/perf_counter.c, and I think
47 that means that enabling/disabling a group with a task clock counter
48 in it won't work correctly (I'll do a test program for that next).
49 
50 Usage is: ./performance_counter02  [-v]
51 
52 The -v flag makes it print out the values of each counter.
53 */
54 
55 #include <stdio.h>
56 #include <stddef.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <fcntl.h>
60 #include <poll.h>
61 #include <unistd.h>
62 #include <errno.h>
63 #include "config.h"
64 #include <sys/prctl.h>
65 #include <sys/types.h>
66 #include <linux/types.h>
67 #include <sched.h>
68 
69 #if HAVE_PERF_EVENT_ATTR
70 # include <linux/perf_event.h>
71 #endif
72 
73 #include "test.h"
74 #include "safe_macros.h"
75 #include "lapi/syscalls.h"
76 
77 char *TCID = "perf_event_open02";
78 int TST_TOTAL = 1;
79 
80 #if HAVE_PERF_EVENT_ATTR
81 
82 #define MAX_CTRS	1000
83 #define LOOPS		100000000
84 
85 static int count_hardware_counters(void);
86 static void setup(void);
87 static void verify(void);
88 static void cleanup(void);
89 static void help(void);
90 
91 static int n, nhw;
92 static int verbose;
93 static option_t options[] = {
94 	{"v", &verbose, NULL},
95 	{NULL, NULL, NULL},
96 };
97 
98 static int tsk0;
99 static int hwfd[MAX_CTRS], tskfd[MAX_CTRS];
100 
main(int ac,char ** av)101 int main(int ac, char **av)
102 {
103 	int lc;
104 
105 	tst_parse_opts(ac, av, options, help);
106 
107 	setup();
108 
109 	for (lc = 0; TEST_LOOPING(lc); lc++) {
110 		tst_count = 0;
111 		verify();
112 	}
113 
114 	cleanup();
115 	tst_exit();
116 }
117 
perf_event_open(struct perf_event_attr * hw_event,pid_t pid,int cpu,int group_fd,unsigned long flags)118 static int perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
119 	int cpu, int group_fd, unsigned long flags)
120 {
121 	int ret;
122 
123 	ret = ltp_syscall(__NR_perf_event_open, hw_event, pid, cpu,
124 			  group_fd, flags);
125 	return ret;
126 }
127 
128 
do_work(void)129 static void do_work(void)
130 {
131 	int i;
132 
133 	for (i = 0; i < LOOPS; ++i)
134 		asm volatile (""::"g" (i));
135 }
136 
137 struct read_format {
138 	unsigned long long value;
139 	/* if PERF_FORMAT_TOTAL_TIME_ENABLED */
140 	unsigned long long time_enabled;
141 	/* if PERF_FORMAT_TOTAL_TIME_RUNNING */
142 	unsigned long long time_running;
143 };
144 
count_hardware_counters(void)145 static int count_hardware_counters(void)
146 {
147 	struct perf_event_attr hw_event;
148 	int i, hwctrs = 0;
149 	int fdarry[MAX_CTRS];
150 	struct read_format buf;
151 
152 	memset(&hw_event, 0, sizeof(struct perf_event_attr));
153 
154 	hw_event.type = PERF_TYPE_HARDWARE;
155 	hw_event.size = sizeof(struct perf_event_attr);
156 	hw_event.disabled = 1;
157 	hw_event.config =  PERF_COUNT_HW_INSTRUCTIONS;
158 	hw_event.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
159 		PERF_FORMAT_TOTAL_TIME_RUNNING;
160 
161 	for (i = 0; i < MAX_CTRS; i++) {
162 		fdarry[i] = perf_event_open(&hw_event, 0, -1, -1, 0);
163 		if (fdarry[i] == -1) {
164 			if (errno == ENOENT || errno == ENODEV) {
165 				tst_brkm(TCONF | TERRNO, cleanup,
166 				         "PERF_COUNT_HW_INSTRUCTIONS not supported");
167 			}
168 			tst_brkm(TBROK | TERRNO, cleanup,
169 				 "perf_event_open failed at iteration:%d", i);
170 		}
171 
172 		if (prctl(PR_TASK_PERF_EVENTS_ENABLE) == -1) {
173 			tst_brkm(TBROK | TERRNO, cleanup,
174 				 "prctl(PR_TASK_PERF_EVENTS_ENABLE) failed");
175 		}
176 
177 		do_work();
178 
179 		if (prctl(PR_TASK_PERF_EVENTS_DISABLE) == -1) {
180 			tst_brkm(TBROK | TERRNO, cleanup,
181 				 "prctl(PR_TASK_PERF_EVENTS_DISABLE) failed");
182 		}
183 
184 		if (read(fdarry[i], &buf, sizeof(buf)) != sizeof(buf)) {
185 			tst_brkm(TBROK | TERRNO, cleanup,
186 				 "error reading counter(s)");
187 		}
188 
189 		if (verbose == 1) {
190 			printf("at iteration:%d value:%lld time_enabled:%lld "
191 			       "time_running:%lld\n", i, buf.value,
192 			       buf.time_enabled, buf.time_running);
193 		}
194 
195 		/*
196 		 * Normally time_enabled and time_running are the same value.
197 		 * But if more events are started than available counter slots
198 		 * on the PMU, then multiplexing happens and events run only
199 		 * part of the time. Time_enabled and time_running's values
200 		 * will be different. In this case the time_enabled and time_
201 		 * running values can be used to scale an estimated value for
202 		 * the count. So if buf.time_enabled and buf.time_running are
203 		 * not equal, we can think that PMU hardware counters
204 		 * multiplexing happens and the number of the opened events
205 		 * are the number of max available hardware counters.
206 		 */
207 		if (buf.time_enabled != buf.time_running) {
208 			hwctrs = i;
209 			break;
210 		}
211 	}
212 
213 	for (i = 0; i <= hwctrs; i++)
214 		SAFE_CLOSE(cleanup, fdarry[i]);
215 
216 	return hwctrs;
217 }
218 
setup(void)219 static void setup(void)
220 {
221 	int i;
222 	struct perf_event_attr tsk_event, hw_event;
223 
224 	/*
225 	 * According to perf_event_open's manpage, the official way of
226 	 * knowing if perf_event_open() support is enabled is checking for
227 	 * the existence of the file /proc/sys/kernel/perf_event_paranoid.
228 	 */
229 	if (access("/proc/sys/kernel/perf_event_paranoid", F_OK) == -1)
230 		tst_brkm(TCONF, NULL, "Kernel doesn't have perf_event support");
231 
232 	tst_sig(NOFORK, DEF_HANDLER, cleanup);
233 
234 	TEST_PAUSE;
235 
236 	nhw = count_hardware_counters();
237 	n = nhw + 4;
238 
239 	memset(&hw_event, 0, sizeof(struct perf_event_attr));
240 	memset(&tsk_event, 0, sizeof(struct perf_event_attr));
241 
242 	tsk_event.type =  PERF_TYPE_SOFTWARE;
243 	tsk_event.size = sizeof(struct perf_event_attr);
244 	tsk_event.disabled = 1;
245 	tsk_event.config = PERF_COUNT_SW_TASK_CLOCK;
246 
247 	hw_event.type = PERF_TYPE_HARDWARE;
248 	hw_event.size = sizeof(struct perf_event_attr);
249 	hw_event.disabled = 1;
250 	hw_event.config =  PERF_COUNT_HW_INSTRUCTIONS;
251 
252 	tsk0 = perf_event_open(&tsk_event, 0, -1, -1, 0);
253 	if (tsk0 == -1) {
254 		tst_brkm(TBROK | TERRNO, cleanup, "perf_event_open failed");
255 	} else {
256 		tsk_event.disabled = 0;
257 		for (i = 0; i < n; ++i) {
258 			hwfd[i] = perf_event_open(&hw_event, 0, -1, -1, 0);
259 			tskfd[i] = perf_event_open(&tsk_event, 0, -1,
260 						   hwfd[i], 0);
261 			if (tskfd[i] == -1 || hwfd[i] == -1) {
262 				tst_brkm(TBROK | TERRNO, cleanup,
263 					 "perf_event_open failed");
264 			}
265 		}
266 	}
267 }
268 
cleanup(void)269 static void cleanup(void)
270 {
271 	int i;
272 
273 	for (i = 0; i < n; i++) {
274 		if (hwfd[i] > 0 && close(hwfd[i]) == -1)
275 			tst_resm(TWARN | TERRNO, "close(%d) failed", hwfd[i]);
276 		if (tskfd[i] > 0 && close(tskfd[i]) == -1)
277 			tst_resm(TWARN | TERRNO, "close(%d) failed", tskfd[i]);
278 	}
279 
280 	if (tsk0 > 0 && close(tsk0) == -1)
281 		tst_resm(TWARN | TERRNO, "close(%d) failed", tsk0);
282 }
283 
verify(void)284 static void verify(void)
285 {
286 	unsigned long long vt0, vt[MAX_CTRS], vh[MAX_CTRS];
287 	unsigned long long vtsum = 0, vhsum = 0;
288 	int i;
289 	double ratio;
290 	struct sched_param sparam = {.sched_priority = 1};
291 
292 	if (sched_setscheduler(0, SCHED_FIFO, &sparam)) {
293 		tst_brkm(TBROK | TERRNO, cleanup,
294 			 "sched_setscheduler(0, SCHED_FIFO, ...) failed");
295 	}
296 
297 	if (prctl(PR_TASK_PERF_EVENTS_ENABLE) == -1) {
298 		tst_brkm(TBROK | TERRNO, cleanup,
299 			 "prctl(PR_TASK_PERF_EVENTS_ENABLE) failed");
300 	}
301 
302 	do_work();
303 
304 	if (prctl(PR_TASK_PERF_EVENTS_DISABLE) == -1) {
305 		tst_brkm(TBROK | TERRNO, cleanup,
306 			 "prctl(PR_TASK_PERF_EVENTS_DISABLE) failed");
307 	}
308 
309 	sparam.sched_priority = 0;
310 	if (sched_setscheduler(0, SCHED_OTHER, &sparam)) {
311 		tst_brkm(TBROK | TERRNO, cleanup,
312 			 "sched_setscheduler(0, SCHED_OTHER, ...) failed");
313 	}
314 
315 	if (read(tsk0, &vt0, sizeof(vt0)) != sizeof(vt0)) {
316 		tst_brkm(TBROK | TERRNO, cleanup,
317 			 "error reading task clock counter");
318 	}
319 
320 	for (i = 0; i < n; ++i) {
321 		if (read(tskfd[i], &vt[i], sizeof(vt[i])) != sizeof(vt[i]) ||
322 		    read(hwfd[i], &vh[i], sizeof(vh[i])) != sizeof(vh[i])) {
323 			tst_brkm(TBROK | TERRNO, cleanup,
324 				 "error reading counter(s)");
325 		}
326 		vtsum += vt[i];
327 		vhsum += vh[i];
328 	}
329 
330 	tst_resm(TINFO, "overall task clock: %llu", vt0);
331 	tst_resm(TINFO, "hw sum: %llu, task clock sum: %llu", vhsum, vtsum);
332 
333 	if (verbose == 1) {
334 		printf("hw counters:");
335 		for (i = 0; i < n; ++i)
336 			printf(" %llu", vh[i]);
337 		printf("\ntask clock counters:");
338 		for (i = 0; i < n; ++i)
339 			printf(" %llu", vt[i]);
340 		printf("\n");
341 	}
342 
343 	ratio = (double)vtsum / vt0;
344 	tst_resm(TINFO, "ratio: %lf", ratio);
345 	if (ratio > nhw + 0.0001) {
346 		tst_resm(TFAIL, "test failed (ratio was greater than )");
347 	} else {
348 		tst_resm(TPASS, "test passed");
349 	}
350 }
351 
help(void)352 static void help(void)
353 {
354 	printf("  -v      Print verbose information\n");
355 }
356 
357 #else
358 
main(void)359 int main(void)
360 {
361 	tst_brkm(TCONF, NULL, "This system doesn't have "
362 		 "header file:<linux/perf_event.h> or "
363 		 "no struct perf_event_attr defined");
364 }
365 #endif
366