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