[PATCH v2 4/8] libperf: Add support for overflow handling of sampling events

From: Charlie Jenkins
Date: Mon Jul 29 2024 - 12:48:15 EST


From: Shunsuke Nakamura <nakamura.shun@xxxxxxxxxxx>

Extend the fields of the opts structure to set up overflow handling
for sampling events. Also, add processing to set signal handlers in
perf_evsel__open_opts.

Signed-off-by: Shunsuke Nakamura <nakamura.shun@xxxxxxxxxxx>
Signed-off-by: Charlie Jenkins <charlie@xxxxxxxxxxxx>
---
tools/lib/perf/Documentation/libperf.txt | 6 ++-
tools/lib/perf/Makefile | 1 +
tools/lib/perf/evsel.c | 79 ++++++++++++++++++++++++++++++++
tools/lib/perf/include/perf/evsel.h | 6 ++-
tools/lib/perf/tests/test-evlist.c | 1 -
5 files changed, 90 insertions(+), 3 deletions(-)

diff --git a/tools/lib/perf/Documentation/libperf.txt b/tools/lib/perf/Documentation/libperf.txt
index 83827b94617a..bb99534d5855 100644
--- a/tools/lib/perf/Documentation/libperf.txt
+++ b/tools/lib/perf/Documentation/libperf.txt
@@ -137,8 +137,12 @@ SYNOPSIS
size_t sz;

unsigned long open_flags; /* perf_event_open flags */
+ int fcntl_flags;
+ unsigned int signal;
+ int owner_type; /* value for F_SETOWN_EX */
+ struct sigaction *sigact;
};
- #define perf_evsel_open_opts__last_field open_flags
+ #define perf_evsel_open_opts__last_field sigact

#define LIBPERF_OPTS(TYPE, NAME, ...)

diff --git a/tools/lib/perf/Makefile b/tools/lib/perf/Makefile
index 3a9b2140aa04..9dade2ad91bd 100644
--- a/tools/lib/perf/Makefile
+++ b/tools/lib/perf/Makefile
@@ -75,6 +75,7 @@ override CFLAGS += -Werror -Wall
override CFLAGS += -fPIC
override CFLAGS += $(INCLUDES)
override CFLAGS += -fvisibility=hidden
+override CFLAGS += -D_GNU_SOURCE

all:

diff --git a/tools/lib/perf/evsel.c b/tools/lib/perf/evsel.c
index 96ecf3e5c8b4..17d3d9a88c23 100644
--- a/tools/lib/perf/evsel.c
+++ b/tools/lib/perf/evsel.c
@@ -562,6 +562,79 @@ void perf_counts_values__scale(struct perf_counts_values *count,
*pscaled = scaled;
}

+static int perf_evsel__run_fcntl(struct perf_evsel *evsel,
+ unsigned int cmd, unsigned long arg,
+ int cpu_map_idx)
+{
+ int thread;
+
+ for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) {
+ int err;
+ int *fd = FD(evsel, cpu_map_idx, thread);
+
+ if (!fd || *fd < 0)
+ return -1;
+
+ err = fcntl(*fd, cmd, arg);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int perf_evsel__set_signal_handler(struct perf_evsel *evsel,
+ struct perf_evsel_open_opts *opts)
+{
+ unsigned int fcntl_flags;
+ unsigned int signal;
+ struct f_owner_ex owner;
+ struct sigaction *sigact;
+ int cpu_map_idx;
+ int err = 0;
+
+ fcntl_flags = OPTS_GET(opts, fcntl_flags, (O_RDWR | O_NONBLOCK | O_ASYNC));
+ signal = OPTS_GET(opts, signal, SIGIO);
+ owner.type = OPTS_GET(opts, owner_type, F_OWNER_PID);
+ sigact = OPTS_GET(opts, sigact, NULL);
+
+ if (fcntl_flags == 0 && signal == 0 && !owner.type == 0 && sigact == 0)
+ return err;
+
+ err = sigaction(signal, sigact, NULL);
+ if (err)
+ return err;
+
+ switch (owner.type) {
+ case F_OWNER_PID:
+ owner.pid = getpid();
+ break;
+ case F_OWNER_TID:
+ owner.pid = syscall(SYS_gettid);
+ break;
+ case F_OWNER_PGRP:
+ default:
+ return -1;
+ }
+
+ for (cpu_map_idx = 0; cpu_map_idx < xyarray__max_x(evsel->fd); cpu_map_idx++) {
+ err = perf_evsel__run_fcntl(evsel, F_SETFL, fcntl_flags, cpu_map_idx);
+ if (err)
+ return err;
+
+ err = perf_evsel__run_fcntl(evsel, F_SETSIG, signal, cpu_map_idx);
+ if (err)
+ return err;
+
+ err = perf_evsel__run_fcntl(evsel, F_SETOWN_EX,
+ (unsigned long)&owner, cpu_map_idx);
+ if (err)
+ return err;
+ }
+
+ return err;
+}
+
int perf_evsel__open_opts(struct perf_evsel *evsel, struct perf_cpu_map *cpus,
struct perf_thread_map *threads,
struct perf_evsel_open_opts *opts)
@@ -576,6 +649,12 @@ int perf_evsel__open_opts(struct perf_evsel *evsel, struct perf_cpu_map *cpus,
evsel->open_flags = OPTS_GET(opts, open_flags, 0);

err = perf_evsel__open(evsel, cpus, threads);
+ if (err)
+ return err;
+
+ err = perf_evsel__set_signal_handler(evsel, opts);
+ if (err)
+ return err;

return err;
}
diff --git a/tools/lib/perf/include/perf/evsel.h b/tools/lib/perf/include/perf/evsel.h
index 8eb3927f3cd0..344808f23371 100644
--- a/tools/lib/perf/include/perf/evsel.h
+++ b/tools/lib/perf/include/perf/evsel.h
@@ -31,9 +31,13 @@ struct perf_evsel_open_opts {
size_t sz;

unsigned long open_flags; /* perf_event_open flags */
+ int fcntl_flags;
+ int signal;
+ int owner_type; /* value for F_SETOWN_EX */
+ struct sigaction *sigact;
};

-#define perf_evsel_open_opts__last_field open_flags
+#define perf_evsel_open_opts__last_field sigact

#define LIBPERF_OPTS(TYPE, NAME, ...) \
struct TYPE NAME = ({ \
diff --git a/tools/lib/perf/tests/test-evlist.c b/tools/lib/perf/tests/test-evlist.c
index 10f70cb41ff1..3a833f0349d3 100644
--- a/tools/lib/perf/tests/test-evlist.c
+++ b/tools/lib/perf/tests/test-evlist.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-#define _GNU_SOURCE // needed for sched.h to get sched_[gs]etaffinity and CPU_(ZERO,SET)
#include <inttypes.h>
#include <sched.h>
#include <stdio.h>

--
2.44.0