blob: 726e5de4c795eaba9ffea73d5d8914b5b36dedce [file] [edit]
// SPDX-License-Identifier: GPL-2.0
/*
* Re-arm a sampling event from its overflow signal handler with
* PERF_EVENT_IOC_REFRESH and check that every overflow is delivered.
*/
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>
#include "cloexec.h"
#include "debug.h"
#include "tests.h"
#include "tp_pmu.h"
#include <internal/lib.h> // page_size
#include "../perf-sys.h"
#define NR_EVENTS 1000
/* A real-time signal is queued, so no overflow can be coalesced away. */
#define REFRESH_SIGNAL (SIGRTMIN + 1)
static volatile sig_atomic_t overflows, hups, refresh_errno;
static void sig_handler(int signum __maybe_unused, siginfo_t *info,
void *uc __maybe_unused)
{
int saved_errno = errno;
overflows++;
if (info->si_code == POLL_HUP)
hups++;
if (ioctl(info->si_fd, PERF_EVENT_IOC_REFRESH, 1) < 0)
refresh_errno = errno;
errno = saved_errno;
}
/* Count overflow signals for NR_EVENTS calls of trigger(), period 1. */
static int count_overflows(struct perf_event_attr *attr, void (*trigger)(int i))
{
struct sigaction sa, old_sa;
uint64_t count = 0;
int fd, i, err = TEST_FAIL;
attr->size = sizeof(*attr);
attr->sample_period = 1;
attr->disabled = 1;
fd = sys_perf_event_open(attr, 0, -1, -1, perf_event_open_cloexec_flag());
if (fd < 0) {
int open_errno = errno;
pr_debug("failed opening event: %s\n", strerror(open_errno));
return open_errno == EACCES || open_errno == EPERM ? TEST_SKIP : TEST_FAIL;
}
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = sig_handler;
sa.sa_flags = SA_SIGINFO;
if (sigaction(REFRESH_SIGNAL, &sa, &old_sa) < 0) {
pr_debug("failed setting up signal handler\n");
close(fd);
return TEST_FAIL;
}
if (fcntl(fd, F_SETFL, O_RDWR | O_NONBLOCK | O_ASYNC) < 0 ||
fcntl(fd, F_SETSIG, REFRESH_SIGNAL) < 0 ||
fcntl(fd, F_SETOWN, getpid()) < 0) {
pr_debug("failed setting up signal delivery: %s\n", strerror(errno));
goto out;
}
overflows = 0;
hups = 0;
refresh_errno = 0;
if (ioctl(fd, PERF_EVENT_IOC_REFRESH, 1) < 0) {
pr_debug("PERF_EVENT_IOC_REFRESH failed: %s\n", strerror(errno));
goto out;
}
for (i = 0; i < NR_EVENTS; i++)
trigger(i);
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
if (read(fd, &count, sizeof(count)) != sizeof(count))
pr_debug("failed to read the event count\n");
pr_debug("%d events, count %" PRIu64 ", %d overflows (%d POLL_HUP), re-arm errno %d\n",
NR_EVENTS, count, overflows, hups, refresh_errno);
/* Unrelated page faults can only add overflows, never hide one. */
if (overflows >= NR_EVENTS)
err = TEST_OK;
out:
/* Close first: a late signal must still find our handler. */
close(fd);
sigaction(REFRESH_SIGNAL, &old_sa, NULL);
return err;
}
static char *pages;
static void touch_page(int i)
{
pages[(long)i * page_size] = 1;
}
static int test__event_refresh_sw(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
struct perf_event_attr attr = {
.type = PERF_TYPE_SOFTWARE,
.config = PERF_COUNT_SW_PAGE_FAULTS,
};
int err;
pages = mmap(NULL, (long)NR_EVENTS * page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (pages == MAP_FAILED)
return TEST_FAIL;
/* One fault per touched page: a huge page would cover many of them. */
madvise(pages, (long)NR_EVENTS * page_size, MADV_NOHUGEPAGE);
err = count_overflows(&attr, touch_page);
munmap(pages, (long)NR_EVENTS * page_size);
return err;
}
static void call_getppid(int i __maybe_unused)
{
syscall(SYS_getppid);
}
static int test__event_refresh_tp(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
struct perf_event_attr attr = {
.type = PERF_TYPE_TRACEPOINT,
};
int id = tp_pmu__id("syscalls", "sys_enter_getppid");
if (id < 0) {
pr_debug("no syscalls:sys_enter_getppid tracepoint\n");
return TEST_SKIP;
}
attr.config = id;
return count_overflows(&attr, call_getppid);
}
static struct test_case tests__event_refresh[] = {
TEST_CASE_REASON("Software event", event_refresh_sw, "permissions"),
TEST_CASE_REASON("Tracepoint event", event_refresh_tp, "permissions"),
{ .name = NULL, }
};
struct test_suite suite__event_refresh = {
.desc = "Event re-armed with PERF_EVENT_IOC_REFRESH",
.test_cases = tests__event_refresh,
};