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 int i, ret; 89 90 unreg.size = sizeof(unreg); 91 unreg.disable_bit = 31; 92 unreg.disable_addr = (__u64)check; 93 94 int fd = open(data_file, O_RDWR); 95 96 if (fd == -1) 97 return -1; 98 99 if (ioctl(fd, DIAG_IOCSUNREG, &unreg) == -1) 100 if (errno != ENOENT) 101 return -1; 102 103 /* 104 * Deleting the event drops its last reference, but the unregister 105 * above defers that put (and the freeing of the enabler) past an RCU 106 * grace period. The delete can therefore transiently fail with -EBUSY 107 * until that reference is dropped. Retry for up to ~10 seconds so the 108 * event is actually gone before the next test registers the same name. 109 */ 110 for (i = 0; i < 10000; ++i) { 111 ret = ioctl(fd, DIAG_IOCSDEL, "__test_event"); 112 113 if (ret == 0 || errno == ENOENT) { 114 ret = 0; 115 break; 116 } 117 118 if (errno != EBUSY) { 119 close(fd); 120 return -1; 121 } 122 123 usleep(1000); 124 } 125 126 close(fd); 127 128 return ret; 129 } 130 131 FIXTURE(user) { 132 int data_fd; 133 int check; 134 bool umount; 135 }; 136 137 FIXTURE_SETUP(user) { 138 USER_EVENT_FIXTURE_SETUP(return, self->umount); 139 140 self->data_fd = open(data_file, O_RDWR); 141 ASSERT_NE(-1, self->data_fd); 142 } 143 144 FIXTURE_TEARDOWN(user) { 145 USER_EVENT_FIXTURE_TEARDOWN(self->umount); 146 147 close(self->data_fd); 148 149 if (clear(&self->check) != 0) 150 printf("WARNING: Clear didn't work!\n"); 151 } 152 153 TEST_F(user, perf_write) { 154 struct perf_event_attr pe = {0}; 155 struct user_reg reg = {0}; 156 struct event event; 157 struct perf_event_mmap_page *perf_page; 158 int page_size = sysconf(_SC_PAGESIZE); 159 int id, fd, offset; 160 __u32 *val; 161 162 reg.size = sizeof(reg); 163 reg.name_args = (__u64)"__test_event u32 field1; u32 field2"; 164 reg.enable_bit = 31; 165 reg.enable_addr = (__u64)&self->check; 166 reg.enable_size = sizeof(self->check); 167 168 /* Register should work */ 169 ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); 170 ASSERT_EQ(0, reg.write_index); 171 ASSERT_EQ(0, self->check); 172 173 /* Id should be there */ 174 id = get_id(); 175 ASSERT_NE(-1, id); 176 offset = get_offset(); 177 ASSERT_NE(-1, offset); 178 179 pe.type = PERF_TYPE_TRACEPOINT; 180 pe.size = sizeof(pe); 181 pe.config = id; 182 pe.sample_type = PERF_SAMPLE_RAW; 183 pe.sample_period = 1; 184 pe.wakeup_events = 1; 185 186 /* Tracepoint attach should work */ 187 fd = perf_event_open(&pe, 0, -1, -1, 0); 188 ASSERT_NE(-1, fd); 189 190 perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0); 191 ASSERT_NE(MAP_FAILED, perf_page); 192 193 /* Status should be updated */ 194 ASSERT_EQ(1 << reg.enable_bit, self->check); 195 196 event.index = reg.write_index; 197 event.field1 = 0xc001; 198 event.field2 = 0xc01a; 199 200 /* Ensure write shows up at correct offset */ 201 ASSERT_NE(-1, write(self->data_fd, &event, sizeof(event))); 202 val = (void *)(((char *)perf_page) + perf_page->data_offset); 203 ASSERT_EQ(PERF_RECORD_SAMPLE, *val); 204 /* Skip over header and size, move to offset */ 205 val += 3; 206 val = (void *)((char *)val) + offset; 207 /* Ensure correct */ 208 ASSERT_EQ(event.field1, *val++); 209 ASSERT_EQ(event.field2, *val++); 210 211 munmap(perf_page, page_size * 2); 212 close(fd); 213 214 /* Status should be updated */ 215 ASSERT_EQ(0, self->check); 216 } 217 218 TEST_F(user, perf_empty_events) { 219 struct perf_event_attr pe = {0}; 220 struct user_reg reg = {0}; 221 struct perf_event_mmap_page *perf_page; 222 int page_size = sysconf(_SC_PAGESIZE); 223 int id, fd; 224 __u32 *val; 225 226 reg.size = sizeof(reg); 227 reg.name_args = (__u64)"__test_event"; 228 reg.enable_bit = 31; 229 reg.enable_addr = (__u64)&self->check; 230 reg.enable_size = sizeof(self->check); 231 232 /* Register should work */ 233 ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); 234 ASSERT_EQ(0, reg.write_index); 235 ASSERT_EQ(0, self->check); 236 237 /* Id should be there */ 238 id = get_id(); 239 ASSERT_NE(-1, id); 240 241 pe.type = PERF_TYPE_TRACEPOINT; 242 pe.size = sizeof(pe); 243 pe.config = id; 244 pe.sample_type = PERF_SAMPLE_RAW; 245 pe.sample_period = 1; 246 pe.wakeup_events = 1; 247 248 /* Tracepoint attach should work */ 249 fd = perf_event_open(&pe, 0, -1, -1, 0); 250 ASSERT_NE(-1, fd); 251 252 perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0); 253 ASSERT_NE(MAP_FAILED, perf_page); 254 255 /* Status should be updated */ 256 ASSERT_EQ(1 << reg.enable_bit, self->check); 257 258 /* Ensure write shows up at correct offset */ 259 ASSERT_NE(-1, write(self->data_fd, (void *)®.write_index, 260 sizeof(reg.write_index))); 261 val = (void *)(((char *)perf_page) + perf_page->data_offset); 262 ASSERT_EQ(PERF_RECORD_SAMPLE, *val); 263 264 munmap(perf_page, page_size * 2); 265 close(fd); 266 267 /* Status should be updated */ 268 ASSERT_EQ(0, self->check); 269 } 270 271 int main(int argc, char **argv) 272 { 273 return test_harness_run(argc, argv); 274 } 275