162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * User Events Perf Events Test Program 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <errno.h> 962306a36Sopenharmony_ci#include <linux/user_events.h> 1062306a36Sopenharmony_ci#include <linux/perf_event.h> 1162306a36Sopenharmony_ci#include <stdio.h> 1262306a36Sopenharmony_ci#include <stdlib.h> 1362306a36Sopenharmony_ci#include <fcntl.h> 1462306a36Sopenharmony_ci#include <sys/ioctl.h> 1562306a36Sopenharmony_ci#include <sys/stat.h> 1662306a36Sopenharmony_ci#include <unistd.h> 1762306a36Sopenharmony_ci#include <asm/unistd.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "../kselftest_harness.h" 2062306a36Sopenharmony_ci#include "user_events_selftests.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ciconst char *data_file = "/sys/kernel/tracing/user_events_data"; 2362306a36Sopenharmony_ciconst char *id_file = "/sys/kernel/tracing/events/user_events/__test_event/id"; 2462306a36Sopenharmony_ciconst char *fmt_file = "/sys/kernel/tracing/events/user_events/__test_event/format"; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct event { 2762306a36Sopenharmony_ci __u32 index; 2862306a36Sopenharmony_ci __u32 field1; 2962306a36Sopenharmony_ci __u32 field2; 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic long perf_event_open(struct perf_event_attr *pe, pid_t pid, 3362306a36Sopenharmony_ci int cpu, int group_fd, unsigned long flags) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci return syscall(__NR_perf_event_open, pe, pid, cpu, group_fd, flags); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int get_id(void) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci FILE *fp = fopen(id_file, "r"); 4162306a36Sopenharmony_ci int ret, id = 0; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (!fp) 4462306a36Sopenharmony_ci return -1; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci ret = fscanf(fp, "%d", &id); 4762306a36Sopenharmony_ci fclose(fp); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (ret != 1) 5062306a36Sopenharmony_ci return -1; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return id; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int get_offset(void) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci FILE *fp = fopen(fmt_file, "r"); 5862306a36Sopenharmony_ci int ret, c, last = 0, offset = 0; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (!fp) 6162306a36Sopenharmony_ci return -1; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* Read until empty line */ 6462306a36Sopenharmony_ci while (true) { 6562306a36Sopenharmony_ci c = getc(fp); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (c == EOF) 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (last == '\n' && c == '\n') 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci last = c; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci ret = fscanf(fp, "\tfield:u32 field1;\toffset:%d;", &offset); 7762306a36Sopenharmony_ci fclose(fp); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (ret != 1) 8062306a36Sopenharmony_ci return -1; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return offset; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int clear(int *check) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct user_unreg unreg = {0}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci unreg.size = sizeof(unreg); 9062306a36Sopenharmony_ci unreg.disable_bit = 31; 9162306a36Sopenharmony_ci unreg.disable_addr = (__u64)check; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci int fd = open(data_file, O_RDWR); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (fd == -1) 9662306a36Sopenharmony_ci return -1; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (ioctl(fd, DIAG_IOCSUNREG, &unreg) == -1) 9962306a36Sopenharmony_ci if (errno != ENOENT) 10062306a36Sopenharmony_ci return -1; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1) 10362306a36Sopenharmony_ci if (errno != ENOENT) 10462306a36Sopenharmony_ci return -1; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci close(fd); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciFIXTURE(user) { 11262306a36Sopenharmony_ci int data_fd; 11362306a36Sopenharmony_ci int check; 11462306a36Sopenharmony_ci bool umount; 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciFIXTURE_SETUP(user) { 11862306a36Sopenharmony_ci USER_EVENT_FIXTURE_SETUP(return, self->umount); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci self->data_fd = open(data_file, O_RDWR); 12162306a36Sopenharmony_ci ASSERT_NE(-1, self->data_fd); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ciFIXTURE_TEARDOWN(user) { 12562306a36Sopenharmony_ci USER_EVENT_FIXTURE_TEARDOWN(self->umount); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci close(self->data_fd); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (clear(&self->check) != 0) 13062306a36Sopenharmony_ci printf("WARNING: Clear didn't work!\n"); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ciTEST_F(user, perf_write) { 13462306a36Sopenharmony_ci struct perf_event_attr pe = {0}; 13562306a36Sopenharmony_ci struct user_reg reg = {0}; 13662306a36Sopenharmony_ci struct event event; 13762306a36Sopenharmony_ci struct perf_event_mmap_page *perf_page; 13862306a36Sopenharmony_ci int page_size = sysconf(_SC_PAGESIZE); 13962306a36Sopenharmony_ci int id, fd, offset; 14062306a36Sopenharmony_ci __u32 *val; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci reg.size = sizeof(reg); 14362306a36Sopenharmony_ci reg.name_args = (__u64)"__test_event u32 field1; u32 field2"; 14462306a36Sopenharmony_ci reg.enable_bit = 31; 14562306a36Sopenharmony_ci reg.enable_addr = (__u64)&self->check; 14662306a36Sopenharmony_ci reg.enable_size = sizeof(self->check); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Register should work */ 14962306a36Sopenharmony_ci ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); 15062306a36Sopenharmony_ci ASSERT_EQ(0, reg.write_index); 15162306a36Sopenharmony_ci ASSERT_EQ(0, self->check); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* Id should be there */ 15462306a36Sopenharmony_ci id = get_id(); 15562306a36Sopenharmony_ci ASSERT_NE(-1, id); 15662306a36Sopenharmony_ci offset = get_offset(); 15762306a36Sopenharmony_ci ASSERT_NE(-1, offset); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci pe.type = PERF_TYPE_TRACEPOINT; 16062306a36Sopenharmony_ci pe.size = sizeof(pe); 16162306a36Sopenharmony_ci pe.config = id; 16262306a36Sopenharmony_ci pe.sample_type = PERF_SAMPLE_RAW; 16362306a36Sopenharmony_ci pe.sample_period = 1; 16462306a36Sopenharmony_ci pe.wakeup_events = 1; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* Tracepoint attach should work */ 16762306a36Sopenharmony_ci fd = perf_event_open(&pe, 0, -1, -1, 0); 16862306a36Sopenharmony_ci ASSERT_NE(-1, fd); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0); 17162306a36Sopenharmony_ci ASSERT_NE(MAP_FAILED, perf_page); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Status should be updated */ 17462306a36Sopenharmony_ci ASSERT_EQ(1 << reg.enable_bit, self->check); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci event.index = reg.write_index; 17762306a36Sopenharmony_ci event.field1 = 0xc001; 17862306a36Sopenharmony_ci event.field2 = 0xc01a; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Ensure write shows up at correct offset */ 18162306a36Sopenharmony_ci ASSERT_NE(-1, write(self->data_fd, &event, sizeof(event))); 18262306a36Sopenharmony_ci val = (void *)(((char *)perf_page) + perf_page->data_offset); 18362306a36Sopenharmony_ci ASSERT_EQ(PERF_RECORD_SAMPLE, *val); 18462306a36Sopenharmony_ci /* Skip over header and size, move to offset */ 18562306a36Sopenharmony_ci val += 3; 18662306a36Sopenharmony_ci val = (void *)((char *)val) + offset; 18762306a36Sopenharmony_ci /* Ensure correct */ 18862306a36Sopenharmony_ci ASSERT_EQ(event.field1, *val++); 18962306a36Sopenharmony_ci ASSERT_EQ(event.field2, *val++); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci munmap(perf_page, page_size * 2); 19262306a36Sopenharmony_ci close(fd); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* Status should be updated */ 19562306a36Sopenharmony_ci ASSERT_EQ(0, self->check); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciTEST_F(user, perf_empty_events) { 19962306a36Sopenharmony_ci struct perf_event_attr pe = {0}; 20062306a36Sopenharmony_ci struct user_reg reg = {0}; 20162306a36Sopenharmony_ci struct perf_event_mmap_page *perf_page; 20262306a36Sopenharmony_ci int page_size = sysconf(_SC_PAGESIZE); 20362306a36Sopenharmony_ci int id, fd; 20462306a36Sopenharmony_ci __u32 *val; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci reg.size = sizeof(reg); 20762306a36Sopenharmony_ci reg.name_args = (__u64)"__test_event"; 20862306a36Sopenharmony_ci reg.enable_bit = 31; 20962306a36Sopenharmony_ci reg.enable_addr = (__u64)&self->check; 21062306a36Sopenharmony_ci reg.enable_size = sizeof(self->check); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Register should work */ 21362306a36Sopenharmony_ci ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); 21462306a36Sopenharmony_ci ASSERT_EQ(0, reg.write_index); 21562306a36Sopenharmony_ci ASSERT_EQ(0, self->check); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Id should be there */ 21862306a36Sopenharmony_ci id = get_id(); 21962306a36Sopenharmony_ci ASSERT_NE(-1, id); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci pe.type = PERF_TYPE_TRACEPOINT; 22262306a36Sopenharmony_ci pe.size = sizeof(pe); 22362306a36Sopenharmony_ci pe.config = id; 22462306a36Sopenharmony_ci pe.sample_type = PERF_SAMPLE_RAW; 22562306a36Sopenharmony_ci pe.sample_period = 1; 22662306a36Sopenharmony_ci pe.wakeup_events = 1; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* Tracepoint attach should work */ 22962306a36Sopenharmony_ci fd = perf_event_open(&pe, 0, -1, -1, 0); 23062306a36Sopenharmony_ci ASSERT_NE(-1, fd); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci perf_page = mmap(NULL, page_size * 2, PROT_READ, MAP_SHARED, fd, 0); 23362306a36Sopenharmony_ci ASSERT_NE(MAP_FAILED, perf_page); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* Status should be updated */ 23662306a36Sopenharmony_ci ASSERT_EQ(1 << reg.enable_bit, self->check); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Ensure write shows up at correct offset */ 23962306a36Sopenharmony_ci ASSERT_NE(-1, write(self->data_fd, ®.write_index, 24062306a36Sopenharmony_ci sizeof(reg.write_index))); 24162306a36Sopenharmony_ci val = (void *)(((char *)perf_page) + perf_page->data_offset); 24262306a36Sopenharmony_ci ASSERT_EQ(PERF_RECORD_SAMPLE, *val); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci munmap(perf_page, page_size * 2); 24562306a36Sopenharmony_ci close(fd); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Status should be updated */ 24862306a36Sopenharmony_ci ASSERT_EQ(0, self->check); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ciint main(int argc, char **argv) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci return test_harness_run(argc, argv); 25462306a36Sopenharmony_ci} 255