1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * User Events Perf Events Test Program |
4 | * |
5 | * Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com> |
6 | */ |
7 | |
8 | #include <errno.h> |
9 | #include <linux/user_events.h> |
10 | #include <linux/perf_event.h> |
11 | #include <stdio.h> |
12 | #include <stdlib.h> |
13 | #include <fcntl.h> |
14 | #include <sys/ioctl.h> |
15 | #include <sys/stat.h> |
16 | #include <unistd.h> |
17 | #include <asm/unistd.h> |
18 | |
19 | #include "../kselftest_harness.h" |
20 | #include "user_events_selftests.h" |
21 | |
22 | const char *data_file = "/sys/kernel/tracing/user_events_data" ; |
23 | const char *id_file = "/sys/kernel/tracing/events/user_events/__test_event/id" ; |
24 | const char *fmt_file = "/sys/kernel/tracing/events/user_events/__test_event/format" ; |
25 | |
26 | struct event { |
27 | __u32 index; |
28 | __u32 field1; |
29 | __u32 field2; |
30 | }; |
31 | |
32 | static long perf_event_open(struct perf_event_attr *pe, pid_t pid, |
33 | int cpu, int group_fd, unsigned long flags) |
34 | { |
35 | return syscall(__NR_perf_event_open, pe, pid, cpu, group_fd, flags); |
36 | } |
37 | |
38 | static int get_id(void) |
39 | { |
40 | FILE *fp = fopen(id_file, "r" ); |
41 | int ret, id = 0; |
42 | |
43 | if (!fp) |
44 | return -1; |
45 | |
46 | ret = fscanf(fp, "%d" , &id); |
47 | fclose(fp); |
48 | |
49 | if (ret != 1) |
50 | return -1; |
51 | |
52 | return id; |
53 | } |
54 | |
55 | static int get_offset(void) |
56 | { |
57 | FILE *fp = fopen(fmt_file, "r" ); |
58 | int ret, c, last = 0, offset = 0; |
59 | |
60 | if (!fp) |
61 | return -1; |
62 | |
63 | /* Read until empty line */ |
64 | while (true) { |
65 | c = getc(fp); |
66 | |
67 | if (c == EOF) |
68 | break; |
69 | |
70 | if (last == '\n' && c == '\n') |
71 | break; |
72 | |
73 | last = c; |
74 | } |
75 | |
76 | ret = fscanf(fp, "\tfield:u32 field1;\toffset:%d;" , &offset); |
77 | fclose(fp); |
78 | |
79 | if (ret != 1) |
80 | return -1; |
81 | |
82 | return offset; |
83 | } |
84 | |
85 | static int clear(int *check) |
86 | { |
87 | struct user_unreg unreg = {0}; |
88 | |
89 | unreg.size = sizeof(unreg); |
90 | unreg.disable_bit = 31; |
91 | unreg.disable_addr = (__u64)check; |
92 | |
93 | int fd = open(data_file, O_RDWR); |
94 | |
95 | if (fd == -1) |
96 | return -1; |
97 | |
98 | if (ioctl(fd, DIAG_IOCSUNREG, &unreg) == -1) |
99 | if (errno != ENOENT) |
100 | return -1; |
101 | |
102 | if (ioctl(fd, DIAG_IOCSDEL, "__test_event" ) == -1) |
103 | if (errno != ENOENT) |
104 | return -1; |
105 | |
106 | close(fd); |
107 | |
108 | return 0; |
109 | } |
110 | |
111 | FIXTURE(user) { |
112 | int data_fd; |
113 | int check; |
114 | bool umount; |
115 | }; |
116 | |
117 | FIXTURE_SETUP(user) { |
118 | USER_EVENT_FIXTURE_SETUP(return, self->umount); |
119 | |
120 | self->data_fd = open(data_file, O_RDWR); |
121 | ASSERT_NE(-1, self->data_fd); |
122 | } |
123 | |
124 | FIXTURE_TEARDOWN(user) { |
125 | USER_EVENT_FIXTURE_TEARDOWN(self->umount); |
126 | |
127 | close(self->data_fd); |
128 | |
129 | if (clear(check: &self->check) != 0) |
130 | printf("WARNING: Clear didn't work!\n" ); |
131 | } |
132 | |
133 | TEST_F(user, perf_write) { |
134 | struct perf_event_attr pe = {0}; |
135 | struct user_reg reg = {0}; |
136 | struct event event; |
137 | struct perf_event_mmap_page *perf_page; |
138 | int page_size = sysconf(_SC_PAGESIZE); |
139 | int id, fd, offset; |
140 | __u32 *val; |
141 | |
142 | reg.size = sizeof(reg); |
143 | reg.name_args = (__u64)"__test_event u32 field1; u32 field2" ; |
144 | reg.enable_bit = 31; |
145 | reg.enable_addr = (__u64)&self->check; |
146 | reg.enable_size = sizeof(self->check); |
147 | |
148 | /* Register should work */ |
149 | ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); |
150 | ASSERT_EQ(0, reg.write_index); |
151 | ASSERT_EQ(0, self->check); |
152 | |
153 | /* Id should be there */ |
154 | id = get_id(); |
155 | ASSERT_NE(-1, id); |
156 | offset = get_offset(); |
157 | ASSERT_NE(-1, offset); |
158 | |
159 | pe.type = PERF_TYPE_TRACEPOINT; |
160 | pe.size = sizeof(pe); |
161 | pe.config = id; |
162 | pe.sample_type = PERF_SAMPLE_RAW; |
163 | pe.sample_period = 1; |
164 | pe.wakeup_events = 1; |
165 | |
166 | /* Tracepoint attach should work */ |
167 | fd = perf_event_open(pe: &pe, pid: 0, cpu: -1, group_fd: -1, flags: 0); |
168 | ASSERT_NE(-1, fd); |
169 | |
170 | perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0); |
171 | ASSERT_NE(MAP_FAILED, perf_page); |
172 | |
173 | /* Status should be updated */ |
174 | ASSERT_EQ(1 << reg.enable_bit, self->check); |
175 | |
176 | event.index = reg.write_index; |
177 | event.field1 = 0xc001; |
178 | event.field2 = 0xc01a; |
179 | |
180 | /* Ensure write shows up at correct offset */ |
181 | ASSERT_NE(-1, write(self->data_fd, &event, sizeof(event))); |
182 | val = (void *)(((char *)perf_page) + perf_page->data_offset); |
183 | ASSERT_EQ(PERF_RECORD_SAMPLE, *val); |
184 | /* Skip over header and size, move to offset */ |
185 | val += 3; |
186 | val = (void *)((char *)val) + offset; |
187 | /* Ensure correct */ |
188 | ASSERT_EQ(event.field1, *val++); |
189 | ASSERT_EQ(event.field2, *val++); |
190 | |
191 | munmap(perf_page, page_size * 2); |
192 | close(fd); |
193 | |
194 | /* Status should be updated */ |
195 | ASSERT_EQ(0, self->check); |
196 | } |
197 | |
198 | TEST_F(user, perf_empty_events) { |
199 | struct perf_event_attr pe = {0}; |
200 | struct user_reg reg = {0}; |
201 | struct perf_event_mmap_page *perf_page; |
202 | int page_size = sysconf(_SC_PAGESIZE); |
203 | int id, fd; |
204 | __u32 *val; |
205 | |
206 | reg.size = sizeof(reg); |
207 | reg.name_args = (__u64)"__test_event" ; |
208 | reg.enable_bit = 31; |
209 | reg.enable_addr = (__u64)&self->check; |
210 | reg.enable_size = sizeof(self->check); |
211 | |
212 | /* Register should work */ |
213 | ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); |
214 | ASSERT_EQ(0, reg.write_index); |
215 | ASSERT_EQ(0, self->check); |
216 | |
217 | /* Id should be there */ |
218 | id = get_id(); |
219 | ASSERT_NE(-1, id); |
220 | |
221 | pe.type = PERF_TYPE_TRACEPOINT; |
222 | pe.size = sizeof(pe); |
223 | pe.config = id; |
224 | pe.sample_type = PERF_SAMPLE_RAW; |
225 | pe.sample_period = 1; |
226 | pe.wakeup_events = 1; |
227 | |
228 | /* Tracepoint attach should work */ |
229 | fd = perf_event_open(pe: &pe, pid: 0, cpu: -1, group_fd: -1, flags: 0); |
230 | ASSERT_NE(-1, fd); |
231 | |
232 | perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0); |
233 | ASSERT_NE(MAP_FAILED, perf_page); |
234 | |
235 | /* Status should be updated */ |
236 | ASSERT_EQ(1 << reg.enable_bit, self->check); |
237 | |
238 | /* Ensure write shows up at correct offset */ |
239 | ASSERT_NE(-1, write(self->data_fd, ®.write_index, |
240 | sizeof(reg.write_index))); |
241 | val = (void *)(((char *)perf_page) + perf_page->data_offset); |
242 | ASSERT_EQ(PERF_RECORD_SAMPLE, *val); |
243 | |
244 | munmap(perf_page, page_size * 2); |
245 | close(fd); |
246 | |
247 | /* Status should be updated */ |
248 | ASSERT_EQ(0, self->check); |
249 | } |
250 | |
251 | int main(int argc, char **argv) |
252 | { |
253 | return test_harness_run(argc, argv); |
254 | } |
255 | |