linux/tools/perf/util/cloexec.c
<<
>>
Prefs
   1#include <errno.h>
   2#include <sched.h>
   3#include "util.h"
   4#include "../perf.h"
   5#include "cloexec.h"
   6#include "asm/bug.h"
   7#include "debug.h"
   8#include <unistd.h>
   9#include <asm/unistd.h>
  10#include <sys/syscall.h>
  11
  12static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
  13
  14int __weak sched_getcpu(void)
  15{
  16#ifdef __NR_getcpu
  17        unsigned cpu;
  18        int err = syscall(__NR_getcpu, &cpu, NULL, NULL);
  19        if (!err)
  20                return cpu;
  21#else
  22        errno = ENOSYS;
  23#endif
  24        return -1;
  25}
  26
  27static int perf_flag_probe(void)
  28{
  29        /* use 'safest' configuration as used in perf_evsel__fallback() */
  30        struct perf_event_attr attr = {
  31                .type = PERF_TYPE_SOFTWARE,
  32                .config = PERF_COUNT_SW_CPU_CLOCK,
  33                .exclude_kernel = 1,
  34        };
  35        int fd;
  36        int err;
  37        int cpu;
  38        pid_t pid = -1;
  39        char sbuf[STRERR_BUFSIZE];
  40
  41        cpu = sched_getcpu();
  42        if (cpu < 0)
  43                cpu = 0;
  44
  45        /*
  46         * Using -1 for the pid is a workaround to avoid gratuitous jump label
  47         * changes.
  48         */
  49        while (1) {
  50                /* check cloexec flag */
  51                fd = sys_perf_event_open(&attr, pid, cpu, -1,
  52                                         PERF_FLAG_FD_CLOEXEC);
  53                if (fd < 0 && pid == -1 && errno == EACCES) {
  54                        pid = 0;
  55                        continue;
  56                }
  57                break;
  58        }
  59        err = errno;
  60
  61        if (fd >= 0) {
  62                close(fd);
  63                return 1;
  64        }
  65
  66        WARN_ONCE(err != EINVAL && err != EBUSY,
  67                  "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n",
  68                  err, str_error_r(err, sbuf, sizeof(sbuf)));
  69
  70        /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */
  71        while (1) {
  72                fd = sys_perf_event_open(&attr, pid, cpu, -1, 0);
  73                if (fd < 0 && pid == -1 && errno == EACCES) {
  74                        pid = 0;
  75                        continue;
  76                }
  77                break;
  78        }
  79        err = errno;
  80
  81        if (fd >= 0)
  82                close(fd);
  83
  84        if (WARN_ONCE(fd < 0 && err != EBUSY,
  85                      "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n",
  86                      err, str_error_r(err, sbuf, sizeof(sbuf))))
  87                return -1;
  88
  89        return 0;
  90}
  91
  92unsigned long perf_event_open_cloexec_flag(void)
  93{
  94        static bool probed;
  95
  96        if (!probed) {
  97                if (perf_flag_probe() <= 0)
  98                        flag = 0;
  99                probed = true;
 100        }
 101
 102        return flag;
 103}
 104