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