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/debug/tracing/user_events_data"; 22 const char *status_file = "/sys/kernel/debug/tracing/user_events_status"; 23 const char *id_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/id"; 24 const char *fmt_file = "/sys/kernel/debug/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 inline int status_check(char *status_page, int status_bit) 39 { 40 return status_page[status_bit >> 3] & (1 << (status_bit & 7)); 41 } 42 43 static int get_id(void) 44 { 45 FILE *fp = fopen(id_file, "r"); 46 int ret, id = 0; 47 48 if (!fp) 49 return -1; 50 51 ret = fscanf(fp, "%d", &id); 52 fclose(fp); 53 54 if (ret != 1) 55 return -1; 56 57 return id; 58 } 59 60 static int get_offset(void) 61 { 62 FILE *fp = fopen(fmt_file, "r"); 63 int ret, c, last = 0, offset = 0; 64 65 if (!fp) 66 return -1; 67 68 /* Read until empty line */ 69 while (true) { 70 c = getc(fp); 71 72 if (c == EOF) 73 break; 74 75 if (last == '\n' && c == '\n') 76 break; 77 78 last = c; 79 } 80 81 ret = fscanf(fp, "\tfield:u32 field1;\toffset:%d;", &offset); 82 fclose(fp); 83 84 if (ret != 1) 85 return -1; 86 87 return offset; 88 } 89 90 FIXTURE(user) { 91 int status_fd; 92 int data_fd; 93 }; 94 95 FIXTURE_SETUP(user) { 96 self->status_fd = open(status_file, O_RDONLY); 97 ASSERT_NE(-1, self->status_fd); 98 99 self->data_fd = open(data_file, O_RDWR); 100 ASSERT_NE(-1, self->data_fd); 101 } 102 103 FIXTURE_TEARDOWN(user) { 104 close(self->status_fd); 105 close(self->data_fd); 106 } 107 108 TEST_F(user, perf_write) { 109 struct perf_event_attr pe = {0}; 110 struct user_reg reg = {0}; 111 int page_size = sysconf(_SC_PAGESIZE); 112 char *status_page; 113 struct event event; 114 struct perf_event_mmap_page *perf_page; 115 int id, fd, offset; 116 __u32 *val; 117 118 reg.size = sizeof(reg); 119 reg.name_args = (__u64)"__test_event u32 field1; u32 field2"; 120 121 status_page = mmap(NULL, page_size, PROT_READ, MAP_SHARED, 122 self->status_fd, 0); 123 ASSERT_NE(MAP_FAILED, status_page); 124 125 /* Register should work */ 126 ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); 127 ASSERT_EQ(0, reg.write_index); 128 ASSERT_NE(0, reg.status_bit); 129 ASSERT_EQ(0, status_check(status_page, reg.status_bit)); 130 131 /* Id should be there */ 132 id = get_id(); 133 ASSERT_NE(-1, id); 134 offset = get_offset(); 135 ASSERT_NE(-1, offset); 136 137 pe.type = PERF_TYPE_TRACEPOINT; 138 pe.size = sizeof(pe); 139 pe.config = id; 140 pe.sample_type = PERF_SAMPLE_RAW; 141 pe.sample_period = 1; 142 pe.wakeup_events = 1; 143 144 /* Tracepoint attach should work */ 145 fd = perf_event_open(&pe, 0, -1, -1, 0); 146 ASSERT_NE(-1, fd); 147 148 perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0); 149 ASSERT_NE(MAP_FAILED, perf_page); 150 151 /* Status should be updated */ 152 ASSERT_NE(0, status_check(status_page, reg.status_bit)); 153 154 event.index = reg.write_index; 155 event.field1 = 0xc001; 156 event.field2 = 0xc01a; 157 158 /* Ensure write shows up at correct offset */ 159 ASSERT_NE(-1, write(self->data_fd, &event, sizeof(event))); 160 val = (void *)(((char *)perf_page) + perf_page->data_offset); 161 ASSERT_EQ(PERF_RECORD_SAMPLE, *val); 162 /* Skip over header and size, move to offset */ 163 val += 3; 164 val = (void *)((char *)val) + offset; 165 /* Ensure correct */ 166 ASSERT_EQ(event.field1, *val++); 167 ASSERT_EQ(event.field2, *val++); 168 } 169 170 int main(int argc, char **argv) 171 { 172 return test_harness_run(argc, argv); 173 } 174