1 /******************************************************************************/
2 /* */
3 /* Ingo Molnar <mingo@elte.hu>, 2009 */
4 /* Copyright (c) Linux Test Project, 2014-2022 */
5 /* */
6 /* This program is free software; you can redistribute it and/or modify */
7 /* it under the terms of the GNU General Public License as published by */
8 /* the Free Software Foundation; either version 2 of the License, or */
9 /* (at your option) any later version. */
10 /* */
11 /* This program is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
14 /* the GNU General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU General Public License */
17 /* along with this program; if not, write to the Free Software */
18 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19 /* */
20 /******************************************************************************/
21
22 /*
23 * Very simple performance counter testcase.
24 * Picked up from: http://lkml.org/lkml/2008/12/5/17
25 */
26
27 #include <sys/types.h>
28 #include <sys/ioctl.h>
29 #include <sys/stat.h>
30 #include <sys/time.h>
31 #include <sys/uio.h>
32 #include <linux/unistd.h>
33 #include <assert.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <stdio.h>
39 #include <fcntl.h>
40 #include <stdint.h>
41 #include "config.h"
42 #include <linux/perf_event.h>
43
44 #include "test.h"
45 #include "lapi/syscalls.h"
46 #include "safe_macros.h"
47
48 char *TCID = "perf_event_open01";
49
50 static void setup(void);
51 static void cleanup(void);
52
53 static struct test_case_t {
54 uint32_t type;
55 const char *config_name;
56 unsigned long long config;
57 } event_types[] = {
58 { PERF_TYPE_HARDWARE, "PERF_COUNT_HW_INSTRUCTIONS",
59 PERF_COUNT_HW_INSTRUCTIONS },
60 { PERF_TYPE_HARDWARE, "PERF_COUNT_HW_CACHE_REFERENCES",
61 PERF_COUNT_HW_CACHE_REFERENCES },
62 { PERF_TYPE_HARDWARE, "PERF_COUNT_HW_CACHE_MISSES",
63 PERF_COUNT_HW_CACHE_MISSES },
64 { PERF_TYPE_HARDWARE, "PERF_COUNT_HW_BRANCH_INSTRUCTIONS",
65 PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
66 { PERF_TYPE_HARDWARE, "PERF_COUNT_HW_BRANCH_MISSES",
67 PERF_COUNT_HW_BRANCH_MISSES },
68 { PERF_TYPE_SOFTWARE, "PERF_COUNT_SW_CPU_CLOCK",
69 PERF_COUNT_SW_CPU_CLOCK },
70 { PERF_TYPE_SOFTWARE, "PERF_COUNT_SW_TASK_CLOCK",
71 PERF_COUNT_SW_TASK_CLOCK },
72 };
73
74 int TST_TOTAL = ARRAY_SIZE(event_types);
75
76 static void verify(struct test_case_t *tc);
77 static struct perf_event_attr pe;
78
main(int ac,char ** av)79 int main(int ac, char **av)
80 {
81 int i, lc;
82
83 tst_parse_opts(ac, av, NULL, NULL);
84
85 setup();
86
87 for (lc = 0; TEST_LOOPING(lc); lc++) {
88 tst_count = 0;
89
90 for (i = 0; i < TST_TOTAL; i++)
91 verify(&event_types[i]);
92 }
93
94 cleanup();
95 tst_exit();
96 }
97
setup(void)98 static void setup(void)
99 {
100 /*
101 * According to perf_event_open's manpage, the official way of
102 * knowing if perf_event_open() support is enabled is checking for
103 * the existence of the file /proc/sys/kernel/perf_event_paranoid.
104 */
105 if (access("/proc/sys/kernel/perf_event_paranoid", F_OK) == -1)
106 tst_brkm(TCONF, NULL, "Kernel doesn't have perf_event support");
107
108 tst_sig(NOFORK, DEF_HANDLER, cleanup);
109
110 TEST_PAUSE;
111
112 pe.size = sizeof(struct perf_event_attr);
113 pe.disabled = 1;
114 pe.exclude_kernel = 1;
115 pe.exclude_hv = 1;
116 }
117
118
perf_event_open(struct perf_event_attr * hw_event,pid_t pid,int cpu,int group_fd,unsigned long flags)119 static int perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
120 int cpu, int group_fd, unsigned long flags)
121 {
122 int ret;
123
124 ret = tst_syscall(__NR_perf_event_open, hw_event, pid, cpu,
125 group_fd, flags);
126 return ret;
127 }
128
129 /* do_work() is copied form performance_counter02.c */
130 #define LOOPS 100000000
131
do_work(void)132 static void do_work(void)
133 {
134 int i;
135
136 for (i = 0; i < LOOPS; ++i)
137 asm volatile ("" : : "g" (i));
138 }
139
verify(struct test_case_t * tc)140 static void verify(struct test_case_t *tc)
141 {
142 unsigned long long count;
143 int fd, ret;
144
145 pe.type = tc->type;
146 pe.config = tc->config;
147
148 TEST(perf_event_open(&pe, 0, -1, -1, 0));
149 if (TEST_RETURN == -1) {
150 if (TEST_ERRNO == ENOENT || TEST_ERRNO == EOPNOTSUPP ||
151 TEST_ERRNO == ENODEV) {
152 tst_resm(TCONF | TTERRNO,
153 "perf_event_open for %s not supported",
154 tc->config_name);
155 } else {
156 tst_brkm(TFAIL | TTERRNO, cleanup,
157 "perf_event_open %s failed unexpectedly",
158 tc->config_name);
159 }
160 return;
161 }
162
163 fd = TEST_RETURN;
164
165 if (ioctl(fd, PERF_EVENT_IOC_RESET, 0) == -1) {
166 tst_brkm(TFAIL | TERRNO, cleanup,
167 "ioctl set PERF_EVENT_IOC_RESET failed");
168 }
169
170 if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) == -1) {
171 tst_brkm(TFAIL | TERRNO, cleanup,
172 "ioctl set PERF_EVENT_IOC_ENABLE failed");
173 }
174
175 do_work();
176
177 if (ioctl(fd, PERF_EVENT_IOC_DISABLE, 0) == -1) {
178 tst_brkm(TFAIL | TERRNO, cleanup,
179 "ioctl set PERF_EVENT_IOC_RESET failed");
180 }
181
182 ret = read(fd, &count, sizeof(count));
183 if (ret == sizeof(count)) {
184 tst_resm(TINFO, "read event counter succeeded, "
185 "value: %llu", count);
186 tst_resm(TPASS, "test PERF_TYPE_HARDWARE: %s succeeded",
187 tc->config_name);
188 } else {
189 tst_resm(TFAIL | TERRNO, "read event counter failed");
190 }
191
192 SAFE_CLOSE(cleanup, fd);
193
194 }
195
cleanup(void)196 static void cleanup(void)
197 {
198 }
199