blob: 40e13e8bddae6949ef93a2aafe6304c786454994 [file] [log] [blame]
subrata_modak89823fe2009-04-02 06:48:41 +00001/******************************************************************************/
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 */
Wanlong Gao4548c6c2012-10-19 18:03:36 +080017/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
subrata_modak89823fe2009-04-02 06:48:41 +000018/* */
19/******************************************************************************/
20/*
21Here's a little test program that checks whether software counters
22(specifically, the task clock counter) work correctly when they're in
23a group with hardware counters.
24
25What it does is to create several groups, each with one hardware
26counter, counting instructions, plus a task clock counter. It needs
27to 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
29multiplexed. It also creates an overall task clock counter.
30
31Then it spins for a while, and then stops all the counters and reads
32them. It takes the total of the task clock counters in the groups and
33computes the ratio of that total to the overall execution time from
34the overall task clock counter.
35
36That ratio should be equal to the number of actual hardware counters
37that can count instructions. If the task clock counters in the groups
38don't stop when their group gets taken off the PMU, the ratio will
39instead be close to N+4. The program will declare that the test fails
40if the ratio is greater than N (actually, N + 0.0001 to allow for FP
41rounding errors).
42
43Could someone run this on x86 on the latest PCL tree and let me know
44what happens? I don't have an x86 crash box easily to hand. On
45powerpc, it passes, but I think that is because I am missing setting
46counter->prev_count in arch/powerpc/kernel/perf_counter.c, and I think
47that means that enabling/disabling a group with a task clock counter
48in it won't work correctly (I'll do a test program for that next).
49
Xiaoguang Wangf40983c2014-04-10 12:08:46 +080050Usage is: ./performance_counter02 [-v]
subrata_modak89823fe2009-04-02 06:48:41 +000051
Xiaoguang Wangf40983c2014-04-10 12:08:46 +080052The -v flag makes it print out the values of each counter.
subrata_modak89823fe2009-04-02 06:48:41 +000053*/
54
subrata_modak89823fe2009-04-02 06:48:41 +000055#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>
yaberauneyaef772532009-10-09 17:55:43 +000063#include "config.h"
64#include <sys/prctl.h>
subrata_modak89823fe2009-04-02 06:48:41 +000065#include <sys/types.h>
66#include <linux/types.h>
subrata_modak89823fe2009-04-02 06:48:41 +000067
Xiaoguang Wangf40983c2014-04-10 12:08:46 +080068#if HAVE_PERF_EVENT_ATTR
69# include <linux/perf_event.h>
70#endif
71
subrata_modak89823fe2009-04-02 06:48:41 +000072#include "test.h"
Xiaoguang Wangf40983c2014-04-10 12:08:46 +080073#include "safe_macros.h"
subrata_modakd0762042009-06-23 14:21:32 +000074#include "linux_syscall_numbers.h"
subrata_modak89823fe2009-04-02 06:48:41 +000075
Xing Gu166c45f2014-08-07 10:05:01 +080076char *TCID = "perf_event_open02";
Xiaoguang Wangf40983c2014-04-10 12:08:46 +080077int TST_TOTAL = 1;
subrata_modak89823fe2009-04-02 06:48:41 +000078
Xiaoguang Wangf40983c2014-04-10 12:08:46 +080079#if HAVE_PERF_EVENT_ATTR
subrata_modak89823fe2009-04-02 06:48:41 +000080
Xiaoguang Wangf40983c2014-04-10 12:08:46 +080081#define MAX_CTRS 1000
82#define LOOPS 1000000000
subrata_modak89823fe2009-04-02 06:48:41 +000083
Xiaoguang Wangf40983c2014-04-10 12:08:46 +080084static int count_hardware_counters(void);
85static void setup(void);
86static void verify(void);
87static void cleanup(void);
88static void help(void);
subrata_modak89823fe2009-04-02 06:48:41 +000089
Xiaoguang Wangf40983c2014-04-10 12:08:46 +080090static int n, nhw;
91static int verbose;
92static option_t options[] = {
93 {"v", &verbose, NULL},
94 {NULL, NULL, NULL},
subrata_modak89823fe2009-04-02 06:48:41 +000095};
96
Xiaoguang Wangf40983c2014-04-10 12:08:46 +080097static int tsk0;
98static int hwfd[MAX_CTRS], tskfd[MAX_CTRS];
subrata_modak89823fe2009-04-02 06:48:41 +000099
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800100int main(int ac, char **av)
subrata_modak89823fe2009-04-02 06:48:41 +0000101{
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800102 int lc;
Cyril Hrubis0b9589f2014-05-27 17:40:33 +0200103 const char *msg;
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800104
105 msg = parse_opts(ac, av, options, help);
106 if (msg != NULL)
107 tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
108
109 setup();
110
111 for (lc = 0; TEST_LOOPING(lc); lc++) {
112 tst_count = 0;
113 verify();
114 }
115
116 cleanup();
117 tst_exit();
subrata_modak89823fe2009-04-02 06:48:41 +0000118}
119
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800120static int perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
121 int cpu, int group_fd, unsigned long flags)
122{
123 int ret;
subrata_modak89823fe2009-04-02 06:48:41 +0000124
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800125 ret = ltp_syscall(__NR_perf_event_open, hw_event, pid, cpu,
126 group_fd, flags);
127 return ret;
128}
129
130
131static void do_work(void)
subrata_modak89823fe2009-04-02 06:48:41 +0000132{
133 int i;
134
135 for (i = 0; i < LOOPS; ++i)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800136 asm volatile (""::"g" (i));
subrata_modak89823fe2009-04-02 06:48:41 +0000137}
138
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800139struct read_format {
140 unsigned long long value;
141 /* if PERF_FORMAT_TOTAL_TIME_ENABLED */
142 unsigned long long time_enabled;
143 /* if PERF_FORMAT_TOTAL_TIME_RUNNING */
144 unsigned long long time_running;
145};
yaberauneya1a1b1fb2009-11-27 08:15:35 +0000146
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800147static int count_hardware_counters(void)
subrata_modak89823fe2009-04-02 06:48:41 +0000148{
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800149 struct perf_event_attr hw_event;
150 int i, hwctrs = 0;
151 int fdarry[MAX_CTRS];
152 struct read_format buf;
subrata_modak89823fe2009-04-02 06:48:41 +0000153
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800154 memset(&hw_event, 0, sizeof(struct perf_event_attr));
155
156 hw_event.type = PERF_TYPE_HARDWARE;
157 hw_event.size = sizeof(struct perf_event_attr);
158 hw_event.disabled = 1;
159 hw_event.config = PERF_COUNT_HW_INSTRUCTIONS;
160 hw_event.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
161 PERF_FORMAT_TOTAL_TIME_RUNNING;
162
163 for (i = 0; i < MAX_CTRS; i++) {
164 fdarry[i] = perf_event_open(&hw_event, 0, -1, -1, 0);
165 if (fdarry[i] == -1) {
Cyril Hrubisadedb782014-08-21 11:20:12 +0200166 if (errno == ENOENT) {
167 tst_brkm(TCONF, cleanup,
168 "PERF_COUNT_HW_INSTRUCTIONS not supported");
169 }
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800170 tst_brkm(TBROK | TERRNO, cleanup,
171 "perf_event_open failed at iteration:%d", i);
172 }
173
174 if (prctl(PR_TASK_PERF_EVENTS_ENABLE) == -1) {
175 tst_brkm(TBROK | TERRNO, cleanup,
176 "prctl(PR_TASK_PERF_EVENTS_ENABLE) failed");
177 }
178
179 do_work();
180
181 if (prctl(PR_TASK_PERF_EVENTS_DISABLE) == -1) {
182 tst_brkm(TBROK | TERRNO, cleanup,
183 "prctl(PR_TASK_PERF_EVENTS_DISABLE) failed");
184 }
185
186 if (read(fdarry[i], &buf, sizeof(buf)) != sizeof(buf)) {
187 tst_brkm(TBROK | TERRNO, cleanup,
188 "error reading counter(s)");
189 }
190
191 if (verbose == 1) {
192 printf("at iteration:%d value:%lld time_enabled:%lld "
193 "time_running:%lld\n", i, buf.value,
194 buf.time_enabled, buf.time_running);
195 }
196
197 /*
198 * Normally time_enabled and time_running are the same value.
199 * But if more events are started than available counter slots
200 * on the PMU, then multiplexing happens and events run only
201 * part of the time. Time_enabled and time_running's values
202 * will be different. In this case the time_enabled and time_
203 * running values can be used to scale an estimated value for
204 * the count. So if buf.time_enabled and buf.time_running are
205 * not equal, we can think that PMU hardware counters
206 * multiplexing happens and the number of the opened events
207 * are the number of max available hardware counters.
208 */
209 if (buf.time_enabled != buf.time_running) {
210 hwctrs = i;
subrata_modak89823fe2009-04-02 06:48:41 +0000211 break;
subrata_modak89823fe2009-04-02 06:48:41 +0000212 }
213 }
214
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800215 for (i = 0; i <= hwctrs; i++)
216 SAFE_CLOSE(cleanup, fdarry[i]);
subrata_modak89823fe2009-04-02 06:48:41 +0000217
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800218 return hwctrs;
219}
220
221static void setup(void)
222{
223 int i;
224 struct perf_event_attr tsk_event, hw_event;
225
226 /*
227 * According to perf_event_open's manpage, the official way of
228 * knowing if perf_event_open() support is enabled is checking for
229 * the existence of the file /proc/sys/kernel/perf_event_paranoid.
230 */
231 if (access("/proc/sys/kernel/perf_event_paranoid", F_OK) == -1)
232 tst_brkm(TCONF, NULL, "Kernel doesn't have perf_event support");
233
234 tst_sig(NOFORK, DEF_HANDLER, cleanup);
235
236 TEST_PAUSE;
237
238 nhw = count_hardware_counters();
subrata_modak89823fe2009-04-02 06:48:41 +0000239 n = nhw + 4;
240
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800241 memset(&hw_event, 0, sizeof(struct perf_event_attr));
242 memset(&tsk_event, 0, sizeof(struct perf_event_attr));
243
244 tsk_event.type = PERF_TYPE_SOFTWARE;
245 tsk_event.size = sizeof(struct perf_event_attr);
subrata_modak89823fe2009-04-02 06:48:41 +0000246 tsk_event.disabled = 1;
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800247 tsk_event.config = PERF_COUNT_SW_TASK_CLOCK;
subrata_modak89823fe2009-04-02 06:48:41 +0000248
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800249 hw_event.type = PERF_TYPE_HARDWARE;
250 hw_event.size = sizeof(struct perf_event_attr);
subrata_modak89823fe2009-04-02 06:48:41 +0000251 hw_event.disabled = 1;
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800252 hw_event.config = PERF_COUNT_HW_INSTRUCTIONS;
subrata_modak89823fe2009-04-02 06:48:41 +0000253
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800254 tsk0 = perf_event_open(&tsk_event, 0, -1, -1, 0);
subrata_modak89823fe2009-04-02 06:48:41 +0000255 if (tsk0 == -1) {
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800256 tst_brkm(TBROK | TERRNO, cleanup, "perf_event_open failed");
yaberauneya1a1b1fb2009-11-27 08:15:35 +0000257 } else {
yaberauneya1a1b1fb2009-11-27 08:15:35 +0000258 tsk_event.disabled = 0;
259 for (i = 0; i < n; ++i) {
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800260 hwfd[i] = perf_event_open(&hw_event, 0, -1, -1, 0);
261 tskfd[i] = perf_event_open(&tsk_event, 0, -1,
262 hwfd[i], 0);
yaberauneya1a1b1fb2009-11-27 08:15:35 +0000263 if (tskfd[i] == -1 || hwfd[i] == -1) {
264 tst_brkm(TBROK | TERRNO, cleanup,
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800265 "perf_event_open failed");
yaberauneya1a1b1fb2009-11-27 08:15:35 +0000266 }
subrata_modak89823fe2009-04-02 06:48:41 +0000267 }
268 }
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800269}
subrata_modak89823fe2009-04-02 06:48:41 +0000270
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800271static void cleanup(void)
272{
273 int i;
274
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800275 for (i = 0; i < n; i++) {
276 if (hwfd[i] > 0 && close(hwfd[i]) == -1)
277 tst_resm(TWARN | TERRNO, "close(%d) failed", hwfd[i]);
278 if (tskfd[i] > 0 && close(tskfd[i]) == -1)
279 tst_resm(TWARN | TERRNO, "close(%d) failed", tskfd[i]);
280 }
281
282 if (tsk0 > 0 && close(tsk0) == -1)
283 tst_resm(TWARN | TERRNO, "close(%d) failed", tsk0);
284}
285
286static void verify(void)
287{
288 unsigned long long vt0, vt[MAX_CTRS], vh[MAX_CTRS];
289 unsigned long long vtsum = 0, vhsum = 0;
290 int i;
291 double ratio;
292
293 if (prctl(PR_TASK_PERF_EVENTS_ENABLE) == -1) {
294 tst_brkm(TBROK | TERRNO, cleanup,
295 "prctl(PR_TASK_PERF_EVENTS_ENABLE) failed");
296 }
297
subrata_modak89823fe2009-04-02 06:48:41 +0000298 do_work();
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800299
300 if (prctl(PR_TASK_PERF_EVENTS_DISABLE) == -1) {
301 tst_brkm(TBROK | TERRNO, cleanup,
302 "prctl(PR_TASK_PERF_EVENTS_DISABLE) failed");
303 }
subrata_modak89823fe2009-04-02 06:48:41 +0000304
305 if (read(tsk0, &vt0, sizeof(vt0)) != sizeof(vt0)) {
yaberauneya1a1b1fb2009-11-27 08:15:35 +0000306 tst_brkm(TBROK | TERRNO, cleanup,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800307 "error reading task clock counter");
subrata_modak89823fe2009-04-02 06:48:41 +0000308 }
309
subrata_modak89823fe2009-04-02 06:48:41 +0000310 for (i = 0; i < n; ++i) {
311 if (read(tskfd[i], &vt[i], sizeof(vt[i])) != sizeof(vt[i]) ||
312 read(hwfd[i], &vh[i], sizeof(vh[i])) != sizeof(vh[i])) {
yaberauneya1a1b1fb2009-11-27 08:15:35 +0000313 tst_brkm(TBROK | TERRNO, cleanup,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800314 "error reading counter(s)");
subrata_modak89823fe2009-04-02 06:48:41 +0000315 }
316 vtsum += vt[i];
317 vhsum += vh[i];
318 }
319
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800320 tst_resm(TINFO, "overall task clock: %llu", vt0);
321 tst_resm(TINFO, "hw sum: %llu, task clock sum: %llu", vhsum, vtsum);
322
323 if (verbose == 1) {
subrata_modak89823fe2009-04-02 06:48:41 +0000324 printf("hw counters:");
325 for (i = 0; i < n; ++i)
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800326 printf(" %llu", vh[i]);
subrata_modak89823fe2009-04-02 06:48:41 +0000327 printf("\ntask clock counters:");
328 for (i = 0; i < n; ++i)
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800329 printf(" %llu", vt[i]);
subrata_modak89823fe2009-04-02 06:48:41 +0000330 printf("\n");
331 }
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800332
subrata_modak89823fe2009-04-02 06:48:41 +0000333 ratio = (double)vtsum / vt0;
yaberauneya1a1b1fb2009-11-27 08:15:35 +0000334 tst_resm(TINFO, "ratio: %.2f", ratio);
subrata_modak89823fe2009-04-02 06:48:41 +0000335 if (ratio > nhw + 0.0001) {
yaberauneya1a1b1fb2009-11-27 08:15:35 +0000336 tst_resm(TFAIL, "test failed (ratio was greater than )");
337 } else {
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800338 tst_resm(TPASS, "test passed");
subrata_modak89823fe2009-04-02 06:48:41 +0000339 }
Garrett Cooper49f76222010-12-19 10:22:13 -0800340}
Xiaoguang Wangf40983c2014-04-10 12:08:46 +0800341
342static void help(void)
343{
344 printf("-v print verbose infomation\n");
345}
346
347#else
348
349int main(void)
350{
351 tst_brkm(TCONF, NULL, "This system doesn't have "
352 "header file:<linux/perf_event.h> or "
353 "no struct perf_event_attr defined");
354}
355#endif