18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci#include "perf-sys.h" 48c2ecf20Sopenharmony_ci#include "util/cloexec.h" 58c2ecf20Sopenharmony_ci#include "util/evlist.h" 68c2ecf20Sopenharmony_ci#include "util/evsel.h" 78c2ecf20Sopenharmony_ci#include "util/parse-events.h" 88c2ecf20Sopenharmony_ci#include "util/perf_api_probe.h" 98c2ecf20Sopenharmony_ci#include <perf/cpumap.h> 108c2ecf20Sopenharmony_ci#include <errno.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_citypedef void (*setup_probe_fn_t)(struct evsel *evsel); 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci struct evlist *evlist; 178c2ecf20Sopenharmony_ci struct evsel *evsel; 188c2ecf20Sopenharmony_ci unsigned long flags = perf_event_open_cloexec_flag(); 198c2ecf20Sopenharmony_ci int err = -EAGAIN, fd; 208c2ecf20Sopenharmony_ci static pid_t pid = -1; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci evlist = evlist__new(); 238c2ecf20Sopenharmony_ci if (!evlist) 248c2ecf20Sopenharmony_ci return -ENOMEM; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (parse_events(evlist, str, NULL)) 278c2ecf20Sopenharmony_ci goto out_delete; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci evsel = evlist__first(evlist); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci while (1) { 328c2ecf20Sopenharmony_ci fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, -1, flags); 338c2ecf20Sopenharmony_ci if (fd < 0) { 348c2ecf20Sopenharmony_ci if (pid == -1 && errno == EACCES) { 358c2ecf20Sopenharmony_ci pid = 0; 368c2ecf20Sopenharmony_ci continue; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci goto out_delete; 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci break; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci close(fd); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci fn(evsel); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci fd = sys_perf_event_open(&evsel->core.attr, pid, cpu, -1, flags); 478c2ecf20Sopenharmony_ci if (fd < 0) { 488c2ecf20Sopenharmony_ci if (errno == EINVAL) 498c2ecf20Sopenharmony_ci err = -EINVAL; 508c2ecf20Sopenharmony_ci goto out_delete; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci close(fd); 538c2ecf20Sopenharmony_ci err = 0; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciout_delete: 568c2ecf20Sopenharmony_ci evlist__delete(evlist); 578c2ecf20Sopenharmony_ci return err; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic bool perf_probe_api(setup_probe_fn_t fn) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci const char *try[] = {"cycles:u", "instructions:u", "cpu-clock:u", NULL}; 638c2ecf20Sopenharmony_ci struct perf_cpu_map *cpus; 648c2ecf20Sopenharmony_ci int cpu, ret, i = 0; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci cpus = perf_cpu_map__new(NULL); 678c2ecf20Sopenharmony_ci if (!cpus) 688c2ecf20Sopenharmony_ci return false; 698c2ecf20Sopenharmony_ci cpu = cpus->map[0]; 708c2ecf20Sopenharmony_ci perf_cpu_map__put(cpus); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci do { 738c2ecf20Sopenharmony_ci ret = perf_do_probe_api(fn, cpu, try[i++]); 748c2ecf20Sopenharmony_ci if (!ret) 758c2ecf20Sopenharmony_ci return true; 768c2ecf20Sopenharmony_ci } while (ret == -EAGAIN && try[i]); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return false; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void perf_probe_sample_identifier(struct evsel *evsel) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci evsel->core.attr.sample_type |= PERF_SAMPLE_IDENTIFIER; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void perf_probe_comm_exec(struct evsel *evsel) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci evsel->core.attr.comm_exec = 1; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic void perf_probe_context_switch(struct evsel *evsel) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci evsel->core.attr.context_switch = 1; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void perf_probe_text_poke(struct evsel *evsel) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci evsel->core.attr.text_poke = 1; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cibool perf_can_sample_identifier(void) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci return perf_probe_api(perf_probe_sample_identifier); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cibool perf_can_comm_exec(void) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci return perf_probe_api(perf_probe_comm_exec); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cibool perf_can_record_switch_events(void) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci return perf_probe_api(perf_probe_context_switch); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cibool perf_can_record_text_poke_events(void) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci return perf_probe_api(perf_probe_text_poke); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cibool perf_can_record_cpu_wide(void) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct perf_event_attr attr = { 1248c2ecf20Sopenharmony_ci .type = PERF_TYPE_SOFTWARE, 1258c2ecf20Sopenharmony_ci .config = PERF_COUNT_SW_CPU_CLOCK, 1268c2ecf20Sopenharmony_ci .exclude_kernel = 1, 1278c2ecf20Sopenharmony_ci }; 1288c2ecf20Sopenharmony_ci struct perf_cpu_map *cpus; 1298c2ecf20Sopenharmony_ci int cpu, fd; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci cpus = perf_cpu_map__new(NULL); 1328c2ecf20Sopenharmony_ci if (!cpus) 1338c2ecf20Sopenharmony_ci return false; 1348c2ecf20Sopenharmony_ci cpu = cpus->map[0]; 1358c2ecf20Sopenharmony_ci perf_cpu_map__put(cpus); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci fd = sys_perf_event_open(&attr, -1, cpu, -1, 0); 1388c2ecf20Sopenharmony_ci if (fd < 0) 1398c2ecf20Sopenharmony_ci return false; 1408c2ecf20Sopenharmony_ci close(fd); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return true; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * Architectures are expected to know if AUX area sampling is supported by the 1478c2ecf20Sopenharmony_ci * hardware. Here we check for kernel support. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_cibool perf_can_aux_sample(void) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct perf_event_attr attr = { 1528c2ecf20Sopenharmony_ci .size = sizeof(struct perf_event_attr), 1538c2ecf20Sopenharmony_ci .exclude_kernel = 1, 1548c2ecf20Sopenharmony_ci /* 1558c2ecf20Sopenharmony_ci * Non-zero value causes the kernel to calculate the effective 1568c2ecf20Sopenharmony_ci * attribute size up to that byte. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci .aux_sample_size = 1, 1598c2ecf20Sopenharmony_ci }; 1608c2ecf20Sopenharmony_ci int fd; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci fd = sys_perf_event_open(&attr, -1, 0, -1, 0); 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * If the kernel attribute is big enough to contain aux_sample_size 1658c2ecf20Sopenharmony_ci * then we assume that it is supported. We are relying on the kernel to 1668c2ecf20Sopenharmony_ci * validate the attribute size before anything else that could be wrong. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci if (fd < 0 && errno == E2BIG) 1698c2ecf20Sopenharmony_ci return false; 1708c2ecf20Sopenharmony_ci if (fd >= 0) 1718c2ecf20Sopenharmony_ci close(fd); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return true; 1748c2ecf20Sopenharmony_ci} 175