13a616389SBeau Belgrave // SPDX-License-Identifier: GPL-2.0
23a616389SBeau Belgrave /*
33a616389SBeau Belgrave * User Events Perf Events Test Program
43a616389SBeau Belgrave *
53a616389SBeau Belgrave * Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com>
63a616389SBeau Belgrave */
73a616389SBeau Belgrave
83a616389SBeau Belgrave #include <errno.h>
93a616389SBeau Belgrave #include <linux/user_events.h>
103a616389SBeau Belgrave #include <linux/perf_event.h>
113a616389SBeau Belgrave #include <stdio.h>
123a616389SBeau Belgrave #include <stdlib.h>
133a616389SBeau Belgrave #include <fcntl.h>
143a616389SBeau Belgrave #include <sys/ioctl.h>
153a616389SBeau Belgrave #include <sys/stat.h>
163a616389SBeau Belgrave #include <unistd.h>
173a616389SBeau Belgrave #include <asm/unistd.h>
183a616389SBeau Belgrave
193a616389SBeau Belgrave #include "../kselftest_harness.h"
20a06023a8SBeau Belgrave #include "user_events_selftests.h"
213a616389SBeau Belgrave
224336cc15SRoss Zwisler const char *data_file = "/sys/kernel/tracing/user_events_data";
234336cc15SRoss Zwisler const char *id_file = "/sys/kernel/tracing/events/user_events/__test_event/id";
244336cc15SRoss Zwisler const char *fmt_file = "/sys/kernel/tracing/events/user_events/__test_event/format";
253a616389SBeau Belgrave
263a616389SBeau Belgrave struct event {
273a616389SBeau Belgrave __u32 index;
283a616389SBeau Belgrave __u32 field1;
293a616389SBeau Belgrave __u32 field2;
303a616389SBeau Belgrave };
313a616389SBeau Belgrave
perf_event_open(struct perf_event_attr * pe,pid_t pid,int cpu,int group_fd,unsigned long flags)323a616389SBeau Belgrave static long perf_event_open(struct perf_event_attr *pe, pid_t pid,
333a616389SBeau Belgrave int cpu, int group_fd, unsigned long flags)
343a616389SBeau Belgrave {
353a616389SBeau Belgrave return syscall(__NR_perf_event_open, pe, pid, cpu, group_fd, flags);
363a616389SBeau Belgrave }
373a616389SBeau Belgrave
get_id(void)383a616389SBeau Belgrave static int get_id(void)
393a616389SBeau Belgrave {
403a616389SBeau Belgrave FILE *fp = fopen(id_file, "r");
413a616389SBeau Belgrave int ret, id = 0;
423a616389SBeau Belgrave
433a616389SBeau Belgrave if (!fp)
443a616389SBeau Belgrave return -1;
453a616389SBeau Belgrave
463a616389SBeau Belgrave ret = fscanf(fp, "%d", &id);
473a616389SBeau Belgrave fclose(fp);
483a616389SBeau Belgrave
493a616389SBeau Belgrave if (ret != 1)
503a616389SBeau Belgrave return -1;
513a616389SBeau Belgrave
523a616389SBeau Belgrave return id;
533a616389SBeau Belgrave }
543a616389SBeau Belgrave
get_offset(void)553a616389SBeau Belgrave static int get_offset(void)
563a616389SBeau Belgrave {
573a616389SBeau Belgrave FILE *fp = fopen(fmt_file, "r");
583a616389SBeau Belgrave int ret, c, last = 0, offset = 0;
593a616389SBeau Belgrave
603a616389SBeau Belgrave if (!fp)
613a616389SBeau Belgrave return -1;
623a616389SBeau Belgrave
633a616389SBeau Belgrave /* Read until empty line */
643a616389SBeau Belgrave while (true) {
653a616389SBeau Belgrave c = getc(fp);
663a616389SBeau Belgrave
673a616389SBeau Belgrave if (c == EOF)
683a616389SBeau Belgrave break;
693a616389SBeau Belgrave
703a616389SBeau Belgrave if (last == '\n' && c == '\n')
713a616389SBeau Belgrave break;
723a616389SBeau Belgrave
733a616389SBeau Belgrave last = c;
743a616389SBeau Belgrave }
753a616389SBeau Belgrave
763a616389SBeau Belgrave ret = fscanf(fp, "\tfield:u32 field1;\toffset:%d;", &offset);
773a616389SBeau Belgrave fclose(fp);
783a616389SBeau Belgrave
793a616389SBeau Belgrave if (ret != 1)
803a616389SBeau Belgrave return -1;
813a616389SBeau Belgrave
823a616389SBeau Belgrave return offset;
833a616389SBeau Belgrave }
843a616389SBeau Belgrave
clear(int * check)854b56c21bSsunliming static int clear(int *check)
864b56c21bSsunliming {
874b56c21bSsunliming struct user_unreg unreg = {0};
884b56c21bSsunliming
894b56c21bSsunliming unreg.size = sizeof(unreg);
904b56c21bSsunliming unreg.disable_bit = 31;
914b56c21bSsunliming unreg.disable_addr = (__u64)check;
924b56c21bSsunliming
934b56c21bSsunliming int fd = open(data_file, O_RDWR);
944b56c21bSsunliming
954b56c21bSsunliming if (fd == -1)
964b56c21bSsunliming return -1;
974b56c21bSsunliming
984b56c21bSsunliming if (ioctl(fd, DIAG_IOCSUNREG, &unreg) == -1)
994b56c21bSsunliming if (errno != ENOENT)
1004b56c21bSsunliming return -1;
1014b56c21bSsunliming
1024b56c21bSsunliming if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1)
1034b56c21bSsunliming if (errno != ENOENT)
1044b56c21bSsunliming return -1;
1054b56c21bSsunliming
1064b56c21bSsunliming close(fd);
1074b56c21bSsunliming
1084b56c21bSsunliming return 0;
1094b56c21bSsunliming }
1104b56c21bSsunliming
FIXTURE(user)1113a616389SBeau Belgrave FIXTURE(user) {
1123a616389SBeau Belgrave int data_fd;
1130d309f04SBeau Belgrave int check;
114*8ed99af4SBeau Belgrave bool umount;
1153a616389SBeau Belgrave };
1163a616389SBeau Belgrave
FIXTURE_SETUP(user)1173a616389SBeau Belgrave FIXTURE_SETUP(user) {
118*8ed99af4SBeau Belgrave USER_EVENT_FIXTURE_SETUP(return, self->umount);
119a06023a8SBeau Belgrave
1203a616389SBeau Belgrave self->data_fd = open(data_file, O_RDWR);
1213a616389SBeau Belgrave ASSERT_NE(-1, self->data_fd);
1223a616389SBeau Belgrave }
1233a616389SBeau Belgrave
FIXTURE_TEARDOWN(user)1243a616389SBeau Belgrave FIXTURE_TEARDOWN(user) {
125*8ed99af4SBeau Belgrave USER_EVENT_FIXTURE_TEARDOWN(self->umount);
126*8ed99af4SBeau Belgrave
1273a616389SBeau Belgrave close(self->data_fd);
1284b56c21bSsunliming
1294b56c21bSsunliming if (clear(&self->check) != 0)
1304b56c21bSsunliming printf("WARNING: Clear didn't work!\n");
1313a616389SBeau Belgrave }
1323a616389SBeau Belgrave
TEST_F(user,perf_write)1333a616389SBeau Belgrave TEST_F(user, perf_write) {
1343a616389SBeau Belgrave struct perf_event_attr pe = {0};
1353a616389SBeau Belgrave struct user_reg reg = {0};
1363a616389SBeau Belgrave struct event event;
1373a616389SBeau Belgrave struct perf_event_mmap_page *perf_page;
1380d309f04SBeau Belgrave int page_size = sysconf(_SC_PAGESIZE);
1393a616389SBeau Belgrave int id, fd, offset;
1403a616389SBeau Belgrave __u32 *val;
1413a616389SBeau Belgrave
1423a616389SBeau Belgrave reg.size = sizeof(reg);
1433a616389SBeau Belgrave reg.name_args = (__u64)"__test_event u32 field1; u32 field2";
1440d309f04SBeau Belgrave reg.enable_bit = 31;
1450d309f04SBeau Belgrave reg.enable_addr = (__u64)&self->check;
1460d309f04SBeau Belgrave reg.enable_size = sizeof(self->check);
1473a616389SBeau Belgrave
1483a616389SBeau Belgrave /* Register should work */
1493a616389SBeau Belgrave ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®));
1503a616389SBeau Belgrave ASSERT_EQ(0, reg.write_index);
1510d309f04SBeau Belgrave ASSERT_EQ(0, self->check);
1523a616389SBeau Belgrave
1533a616389SBeau Belgrave /* Id should be there */
1543a616389SBeau Belgrave id = get_id();
1553a616389SBeau Belgrave ASSERT_NE(-1, id);
1563a616389SBeau Belgrave offset = get_offset();
1573a616389SBeau Belgrave ASSERT_NE(-1, offset);
1583a616389SBeau Belgrave
1593a616389SBeau Belgrave pe.type = PERF_TYPE_TRACEPOINT;
1603a616389SBeau Belgrave pe.size = sizeof(pe);
1613a616389SBeau Belgrave pe.config = id;
1623a616389SBeau Belgrave pe.sample_type = PERF_SAMPLE_RAW;
1633a616389SBeau Belgrave pe.sample_period = 1;
1643a616389SBeau Belgrave pe.wakeup_events = 1;
1653a616389SBeau Belgrave
1663a616389SBeau Belgrave /* Tracepoint attach should work */
1673a616389SBeau Belgrave fd = perf_event_open(&pe, 0, -1, -1, 0);
1683a616389SBeau Belgrave ASSERT_NE(-1, fd);
1693a616389SBeau Belgrave
1703a616389SBeau Belgrave perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0);
1713a616389SBeau Belgrave ASSERT_NE(MAP_FAILED, perf_page);
1723a616389SBeau Belgrave
1733a616389SBeau Belgrave /* Status should be updated */
1740d309f04SBeau Belgrave ASSERT_EQ(1 << reg.enable_bit, self->check);
1753a616389SBeau Belgrave
1763a616389SBeau Belgrave event.index = reg.write_index;
1773a616389SBeau Belgrave event.field1 = 0xc001;
1783a616389SBeau Belgrave event.field2 = 0xc01a;
1793a616389SBeau Belgrave
1803a616389SBeau Belgrave /* Ensure write shows up at correct offset */
1813a616389SBeau Belgrave ASSERT_NE(-1, write(self->data_fd, &event, sizeof(event)));
1823a616389SBeau Belgrave val = (void *)(((char *)perf_page) + perf_page->data_offset);
1833a616389SBeau Belgrave ASSERT_EQ(PERF_RECORD_SAMPLE, *val);
1843a616389SBeau Belgrave /* Skip over header and size, move to offset */
1853a616389SBeau Belgrave val += 3;
1863a616389SBeau Belgrave val = (void *)((char *)val) + offset;
1873a616389SBeau Belgrave /* Ensure correct */
1883a616389SBeau Belgrave ASSERT_EQ(event.field1, *val++);
1893a616389SBeau Belgrave ASSERT_EQ(event.field2, *val++);
1900d309f04SBeau Belgrave
1910d309f04SBeau Belgrave munmap(perf_page, page_size * 2);
1920d309f04SBeau Belgrave close(fd);
1930d309f04SBeau Belgrave
1940d309f04SBeau Belgrave /* Status should be updated */
1950d309f04SBeau Belgrave ASSERT_EQ(0, self->check);
1963a616389SBeau Belgrave }
1973a616389SBeau Belgrave
TEST_F(user,perf_empty_events)19842187bdcSsunliming TEST_F(user, perf_empty_events) {
19942187bdcSsunliming struct perf_event_attr pe = {0};
20042187bdcSsunliming struct user_reg reg = {0};
20142187bdcSsunliming struct perf_event_mmap_page *perf_page;
20242187bdcSsunliming int page_size = sysconf(_SC_PAGESIZE);
20342187bdcSsunliming int id, fd;
20442187bdcSsunliming __u32 *val;
20542187bdcSsunliming
20642187bdcSsunliming reg.size = sizeof(reg);
20742187bdcSsunliming reg.name_args = (__u64)"__test_event";
20842187bdcSsunliming reg.enable_bit = 31;
20942187bdcSsunliming reg.enable_addr = (__u64)&self->check;
21042187bdcSsunliming reg.enable_size = sizeof(self->check);
21142187bdcSsunliming
21242187bdcSsunliming /* Register should work */
21342187bdcSsunliming ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®));
21442187bdcSsunliming ASSERT_EQ(0, reg.write_index);
21542187bdcSsunliming ASSERT_EQ(0, self->check);
21642187bdcSsunliming
21742187bdcSsunliming /* Id should be there */
21842187bdcSsunliming id = get_id();
21942187bdcSsunliming ASSERT_NE(-1, id);
22042187bdcSsunliming
22142187bdcSsunliming pe.type = PERF_TYPE_TRACEPOINT;
22242187bdcSsunliming pe.size = sizeof(pe);
22342187bdcSsunliming pe.config = id;
22442187bdcSsunliming pe.sample_type = PERF_SAMPLE_RAW;
22542187bdcSsunliming pe.sample_period = 1;
22642187bdcSsunliming pe.wakeup_events = 1;
22742187bdcSsunliming
22842187bdcSsunliming /* Tracepoint attach should work */
22942187bdcSsunliming fd = perf_event_open(&pe, 0, -1, -1, 0);
23042187bdcSsunliming ASSERT_NE(-1, fd);
23142187bdcSsunliming
23242187bdcSsunliming perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0);
23342187bdcSsunliming ASSERT_NE(MAP_FAILED, perf_page);
23442187bdcSsunliming
23542187bdcSsunliming /* Status should be updated */
23642187bdcSsunliming ASSERT_EQ(1 << reg.enable_bit, self->check);
23742187bdcSsunliming
23842187bdcSsunliming /* Ensure write shows up at correct offset */
23942187bdcSsunliming ASSERT_NE(-1, write(self->data_fd, ®.write_index,
24042187bdcSsunliming sizeof(reg.write_index)));
24142187bdcSsunliming val = (void *)(((char *)perf_page) + perf_page->data_offset);
24242187bdcSsunliming ASSERT_EQ(PERF_RECORD_SAMPLE, *val);
24342187bdcSsunliming
24442187bdcSsunliming munmap(perf_page, page_size * 2);
24542187bdcSsunliming close(fd);
24642187bdcSsunliming
24742187bdcSsunliming /* Status should be updated */
24842187bdcSsunliming ASSERT_EQ(0, self->check);
24942187bdcSsunliming }
25042187bdcSsunliming
main(int argc,char ** argv)2513a616389SBeau Belgrave int main(int argc, char **argv)
2523a616389SBeau Belgrave {
2533a616389SBeau Belgrave return test_harness_run(argc, argv);
2543a616389SBeau Belgrave }
255