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