xref: /openbmc/linux/tools/testing/selftests/user_events/perf_test.c (revision 50768a425b46ad7d98f6d88c22d41aa026c463cf)
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, &reg));
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, &reg));
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, &reg.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