perf tools: Enable close-on-exec flag on perf file descriptor
authorYann Droneaud <ydroneaud@opteya.com>
Mon, 30 Jun 2014 20:28:47 +0000 (22:28 +0200)
committerJiri Olsa <jolsa@kernel.org>
Fri, 18 Jul 2014 07:09:34 +0000 (09:09 +0200)
In commit a21b0b354d4a ('perf: Introduce a flag to enable
close-on-exec in perf_event_open()'), flag PERF_FLAG_FD_CLOEXEC
was added to perf_event_open(2) syscall to allows userspace
to atomically enable close-on-exec behavor when creating
the file descriptor.

This patch makes perf tools use the new flag if supported
by the kernel, so that the event file descriptors got
automatically closed if perf tool exec a sub-command.

Signed-off-by: Yann Droneaud <ydroneaud@opteya.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/n/1404160127-7475-1-git-send-email-ydroneaud@opteya.com
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
tools/perf/Makefile.perf
tools/perf/bench/mem-memcpy.c
tools/perf/bench/mem-memset.c
tools/perf/builtin-sched.c
tools/perf/tests/bp_signal.c
tools/perf/tests/bp_signal_overflow.c
tools/perf/tests/rdpmc.c
tools/perf/util/cloexec.c [new file with mode: 0644]
tools/perf/util/cloexec.h [new file with mode: 0644]
tools/perf/util/evsel.c
tools/perf/util/record.c

index 3308b22a16603c1684cbaff1e679788ddac4ddf6..2240974b7745874f3e2e39c3ee9f98d18c8c7912 100644 (file)
@@ -376,6 +376,7 @@ LIB_OBJS += $(OUTPUT)util/record.o
 LIB_OBJS += $(OUTPUT)util/srcline.o
 LIB_OBJS += $(OUTPUT)util/data.o
 LIB_OBJS += $(OUTPUT)util/tsc.o
+LIB_OBJS += $(OUTPUT)util/cloexec.o
 
 LIB_OBJS += $(OUTPUT)ui/setup.o
 LIB_OBJS += $(OUTPUT)ui/helpline.o
index e622c3e96efccca0ee10e119555205f960f91c86..2465141b554bf0e135edc427799227032e25345d 100644 (file)
@@ -10,6 +10,7 @@
 #include "../util/util.h"
 #include "../util/parse-options.h"
 #include "../util/header.h"
+#include "../util/cloexec.h"
 #include "bench.h"
 #include "mem-memcpy-arch.h"
 
@@ -83,7 +84,8 @@ static struct perf_event_attr cycle_attr = {
 
 static void init_cycle(void)
 {
-       cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, 0);
+       cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1,
+                                      perf_event_open_cloexec_flag());
 
        if (cycle_fd < 0 && errno == ENOSYS)
                die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
index 2a65468619f0fe15467f11da369afb6d6a5c8d8b..75fc3e65fb2a8025e29d426f2d6b368b7882e577 100644 (file)
@@ -10,6 +10,7 @@
 #include "../util/util.h"
 #include "../util/parse-options.h"
 #include "../util/header.h"
+#include "../util/cloexec.h"
 #include "bench.h"
 #include "mem-memset-arch.h"
 
@@ -83,7 +84,8 @@ static struct perf_event_attr cycle_attr = {
 
 static void init_cycle(void)
 {
-       cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, 0);
+       cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1,
+                                      perf_event_open_cloexec_flag());
 
        if (cycle_fd < 0 && errno == ENOSYS)
                die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
index b7f555add0c8d21664d5dae6a62d90b0e14fe5d6..f83c08c0dd87caaa97a8c0b3958320b65a2ed2ef 100644 (file)
@@ -10,6 +10,7 @@
 #include "util/header.h"
 #include "util/session.h"
 #include "util/tool.h"
+#include "util/cloexec.h"
 
 #include "util/parse-options.h"
 #include "util/trace-event.h"
@@ -434,7 +435,8 @@ static int self_open_counters(void)
        attr.type = PERF_TYPE_SOFTWARE;
        attr.config = PERF_COUNT_SW_TASK_CLOCK;
 
-       fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
+       fd = sys_perf_event_open(&attr, 0, -1, -1,
+                                perf_event_open_cloexec_flag());
 
        if (fd < 0)
                pr_err("Error: sys_perf_event_open() syscall returned "
index aba095489193bb6e224ff11f3f2ee03968df8711..a02b035fd5aa84ba8e473d0902cccb39806a82b5 100644 (file)
@@ -25,6 +25,7 @@
 #include "tests.h"
 #include "debug.h"
 #include "perf.h"
+#include "cloexec.h"
 
 static int fd1;
 static int fd2;
@@ -78,7 +79,8 @@ static int bp_event(void *fn, int setup_signal)
        pe.exclude_kernel = 1;
        pe.exclude_hv = 1;
 
-       fd = sys_perf_event_open(&pe, 0, -1, -1, 0);
+       fd = sys_perf_event_open(&pe, 0, -1, -1,
+                                perf_event_open_cloexec_flag());
        if (fd < 0) {
                pr_debug("failed opening event %llx\n", pe.config);
                return TEST_FAIL;
index 44ac82179708b50d6ab559cb9258c8334ab70fff..e76537724491850342753c0b5ddead454555b899 100644 (file)
@@ -24,6 +24,7 @@
 #include "tests.h"
 #include "debug.h"
 #include "perf.h"
+#include "cloexec.h"
 
 static int overflows;
 
@@ -91,7 +92,8 @@ int test__bp_signal_overflow(void)
        pe.exclude_kernel = 1;
        pe.exclude_hv = 1;
 
-       fd = sys_perf_event_open(&pe, 0, -1, -1, 0);
+       fd = sys_perf_event_open(&pe, 0, -1, -1,
+                                perf_event_open_cloexec_flag());
        if (fd < 0) {
                pr_debug("failed opening event %llx\n", pe.config);
                return TEST_FAIL;
index e59143fd9e711c0461196ce098ec64ec31cd6641..c04d1f268576568f8a5b32c77e95796f40d8b723 100644 (file)
@@ -6,6 +6,7 @@
 #include "perf.h"
 #include "debug.h"
 #include "tests.h"
+#include "cloexec.h"
 
 #if defined(__x86_64__) || defined(__i386__)
 
@@ -104,7 +105,8 @@ static int __test__rdpmc(void)
        sa.sa_sigaction = segfault_handler;
        sigaction(SIGSEGV, &sa, NULL);
 
-       fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
+       fd = sys_perf_event_open(&attr, 0, -1, -1,
+                                perf_event_open_cloexec_flag());
        if (fd < 0) {
                pr_err("Error: sys_perf_event_open() syscall returned "
                       "with %d (%s)\n", fd, strerror(errno));
diff --git a/tools/perf/util/cloexec.c b/tools/perf/util/cloexec.c
new file mode 100644 (file)
index 0000000..c5d05ec
--- /dev/null
@@ -0,0 +1,57 @@
+#include "util.h"
+#include "../perf.h"
+#include "cloexec.h"
+#include "asm/bug.h"
+
+static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
+
+static int perf_flag_probe(void)
+{
+       /* use 'safest' configuration as used in perf_evsel__fallback() */
+       struct perf_event_attr attr = {
+               .type = PERF_COUNT_SW_CPU_CLOCK,
+               .config = PERF_COUNT_SW_CPU_CLOCK,
+       };
+       int fd;
+       int err;
+
+       /* check cloexec flag */
+       fd = sys_perf_event_open(&attr, 0, -1, -1,
+                                PERF_FLAG_FD_CLOEXEC);
+       err = errno;
+
+       if (fd >= 0) {
+               close(fd);
+               return 1;
+       }
+
+       WARN_ONCE(err != EINVAL,
+                 "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n",
+                 err, strerror(err));
+
+       /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */
+       fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
+       err = errno;
+
+       if (WARN_ONCE(fd < 0,
+                     "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n",
+                     err, strerror(err)))
+               return -1;
+
+       close(fd);
+
+       return 0;
+}
+
+unsigned long perf_event_open_cloexec_flag(void)
+{
+       static bool probed;
+
+       if (!probed) {
+               if (perf_flag_probe() <= 0)
+                       flag = 0;
+               probed = true;
+       }
+
+       return flag;
+}
diff --git a/tools/perf/util/cloexec.h b/tools/perf/util/cloexec.h
new file mode 100644 (file)
index 0000000..94a5a7d
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __PERF_CLOEXEC_H
+#define __PERF_CLOEXEC_H
+
+unsigned long perf_event_open_cloexec_flag(void);
+
+#endif /* __PERF_CLOEXEC_H */
index 90f58cdd0fb088ac3e068160c2eb649f3c233263..21a373ebea226a8208c3ccf2aaee9ad92ad4156d 100644 (file)
@@ -29,6 +29,7 @@ static struct {
        bool sample_id_all;
        bool exclude_guest;
        bool mmap2;
+       bool cloexec;
 } perf_missing_features;
 
 #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
@@ -994,7 +995,7 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
                              struct thread_map *threads)
 {
        int cpu, thread;
-       unsigned long flags = 0;
+       unsigned long flags = PERF_FLAG_FD_CLOEXEC;
        int pid = -1, err;
        enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE;
 
@@ -1003,11 +1004,13 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
                return -ENOMEM;
 
        if (evsel->cgrp) {
-               flags = PERF_FLAG_PID_CGROUP;
+               flags |= PERF_FLAG_PID_CGROUP;
                pid = evsel->cgrp->fd;
        }
 
 fallback_missing_features:
+       if (perf_missing_features.cloexec)
+               flags &= ~(unsigned long)PERF_FLAG_FD_CLOEXEC;
        if (perf_missing_features.mmap2)
                evsel->attr.mmap2 = 0;
        if (perf_missing_features.exclude_guest)
@@ -1076,7 +1079,10 @@ try_fallback:
        if (err != -EINVAL || cpu > 0 || thread > 0)
                goto out_close;
 
-       if (!perf_missing_features.mmap2 && evsel->attr.mmap2) {
+       if (!perf_missing_features.cloexec && (flags & PERF_FLAG_FD_CLOEXEC)) {
+               perf_missing_features.cloexec = true;
+               goto fallback_missing_features;
+       } else if (!perf_missing_features.mmap2 && evsel->attr.mmap2) {
                perf_missing_features.mmap2 = true;
                goto fallback_missing_features;
        } else if (!perf_missing_features.exclude_guest &&
index 165723152cfb64007e32080054f59223a08fb735..fe8079edbdc11a239ee26524ac51d04b3d40f895 100644 (file)
@@ -4,6 +4,7 @@
 #include "parse-events.h"
 #include <api/fs/fs.h>
 #include "util.h"
+#include "cloexec.h"
 
 typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel);
 
@@ -11,6 +12,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
 {
        struct perf_evlist *evlist;
        struct perf_evsel *evsel;
+       unsigned long flags = perf_event_open_cloexec_flag();
        int err = -EAGAIN, fd;
 
        evlist = perf_evlist__new();
@@ -22,14 +24,14 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
 
        evsel = perf_evlist__first(evlist);
 
-       fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0);
+       fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, flags);
        if (fd < 0)
                goto out_delete;
        close(fd);
 
        fn(evsel);
 
-       fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0);
+       fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, flags);
        if (fd < 0) {
                if (errno == EINVAL)
                        err = -EINVAL;
@@ -219,7 +221,8 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str)
                cpu = evlist->cpus->map[0];
        }
 
-       fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0);
+       fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1,
+                                perf_event_open_cloexec_flag());
        if (fd >= 0) {
                close(fd);
                ret = true;