18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#define _GNU_SOURCE 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <assert.h> 58c2ecf20Sopenharmony_ci#include <fcntl.h> 68c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 78c2ecf20Sopenharmony_ci#include <sched.h> 88c2ecf20Sopenharmony_ci#include <stdio.h> 98c2ecf20Sopenharmony_ci#include <stdlib.h> 108c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 118c2ecf20Sopenharmony_ci#include <sys/resource.h> 128c2ecf20Sopenharmony_ci#include <sys/time.h> 138c2ecf20Sopenharmony_ci#include <sys/types.h> 148c2ecf20Sopenharmony_ci#include <sys/wait.h> 158c2ecf20Sopenharmony_ci#include <unistd.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <bpf/bpf.h> 188c2ecf20Sopenharmony_ci#include <bpf/libbpf.h> 198c2ecf20Sopenharmony_ci#include "perf-sys.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define SAMPLE_PERIOD 0x7fffffffffffffffULL 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* counters, values, values2 */ 248c2ecf20Sopenharmony_cistatic int map_fd[3]; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic void check_on_cpu(int cpu, struct perf_event_attr *attr) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct bpf_perf_event_value value2; 298c2ecf20Sopenharmony_ci int pmu_fd, error = 0; 308c2ecf20Sopenharmony_ci cpu_set_t set; 318c2ecf20Sopenharmony_ci __u64 value; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci /* Move to target CPU */ 348c2ecf20Sopenharmony_ci CPU_ZERO(&set); 358c2ecf20Sopenharmony_ci CPU_SET(cpu, &set); 368c2ecf20Sopenharmony_ci assert(sched_setaffinity(0, sizeof(set), &set) == 0); 378c2ecf20Sopenharmony_ci /* Open perf event and attach to the perf_event_array */ 388c2ecf20Sopenharmony_ci pmu_fd = sys_perf_event_open(attr, -1/*pid*/, cpu/*cpu*/, -1/*group_fd*/, 0); 398c2ecf20Sopenharmony_ci if (pmu_fd < 0) { 408c2ecf20Sopenharmony_ci fprintf(stderr, "sys_perf_event_open failed on CPU %d\n", cpu); 418c2ecf20Sopenharmony_ci error = 1; 428c2ecf20Sopenharmony_ci goto on_exit; 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci assert(bpf_map_update_elem(map_fd[0], &cpu, &pmu_fd, BPF_ANY) == 0); 458c2ecf20Sopenharmony_ci assert(ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0) == 0); 468c2ecf20Sopenharmony_ci /* Trigger the kprobe */ 478c2ecf20Sopenharmony_ci bpf_map_get_next_key(map_fd[1], &cpu, NULL); 488c2ecf20Sopenharmony_ci /* Check the value */ 498c2ecf20Sopenharmony_ci if (bpf_map_lookup_elem(map_fd[1], &cpu, &value)) { 508c2ecf20Sopenharmony_ci fprintf(stderr, "Value missing for CPU %d\n", cpu); 518c2ecf20Sopenharmony_ci error = 1; 528c2ecf20Sopenharmony_ci goto on_exit; 538c2ecf20Sopenharmony_ci } else { 548c2ecf20Sopenharmony_ci fprintf(stderr, "CPU %d: %llu\n", cpu, value); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci /* The above bpf_map_lookup_elem should trigger the second kprobe */ 578c2ecf20Sopenharmony_ci if (bpf_map_lookup_elem(map_fd[2], &cpu, &value2)) { 588c2ecf20Sopenharmony_ci fprintf(stderr, "Value2 missing for CPU %d\n", cpu); 598c2ecf20Sopenharmony_ci error = 1; 608c2ecf20Sopenharmony_ci goto on_exit; 618c2ecf20Sopenharmony_ci } else { 628c2ecf20Sopenharmony_ci fprintf(stderr, "CPU %d: counter: %llu, enabled: %llu, running: %llu\n", cpu, 638c2ecf20Sopenharmony_ci value2.counter, value2.enabled, value2.running); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cion_exit: 678c2ecf20Sopenharmony_ci assert(bpf_map_delete_elem(map_fd[0], &cpu) == 0 || error); 688c2ecf20Sopenharmony_ci assert(ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE, 0) == 0 || error); 698c2ecf20Sopenharmony_ci assert(close(pmu_fd) == 0 || error); 708c2ecf20Sopenharmony_ci assert(bpf_map_delete_elem(map_fd[1], &cpu) == 0 || error); 718c2ecf20Sopenharmony_ci exit(error); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void test_perf_event_array(struct perf_event_attr *attr, 758c2ecf20Sopenharmony_ci const char *name) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int i, status, nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 788c2ecf20Sopenharmony_ci pid_t pid[nr_cpus]; 798c2ecf20Sopenharmony_ci int err = 0; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci printf("Test reading %s counters\n", name); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci for (i = 0; i < nr_cpus; i++) { 848c2ecf20Sopenharmony_ci pid[i] = fork(); 858c2ecf20Sopenharmony_ci assert(pid[i] >= 0); 868c2ecf20Sopenharmony_ci if (pid[i] == 0) { 878c2ecf20Sopenharmony_ci check_on_cpu(i, attr); 888c2ecf20Sopenharmony_ci exit(1); 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci for (i = 0; i < nr_cpus; i++) { 938c2ecf20Sopenharmony_ci assert(waitpid(pid[i], &status, 0) == pid[i]); 948c2ecf20Sopenharmony_ci err |= status; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (err) 988c2ecf20Sopenharmony_ci printf("Test: %s FAILED\n", name); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic void test_bpf_perf_event(void) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct perf_event_attr attr_cycles = { 1048c2ecf20Sopenharmony_ci .freq = 0, 1058c2ecf20Sopenharmony_ci .sample_period = SAMPLE_PERIOD, 1068c2ecf20Sopenharmony_ci .inherit = 0, 1078c2ecf20Sopenharmony_ci .type = PERF_TYPE_HARDWARE, 1088c2ecf20Sopenharmony_ci .read_format = 0, 1098c2ecf20Sopenharmony_ci .sample_type = 0, 1108c2ecf20Sopenharmony_ci .config = PERF_COUNT_HW_CPU_CYCLES, 1118c2ecf20Sopenharmony_ci }; 1128c2ecf20Sopenharmony_ci struct perf_event_attr attr_clock = { 1138c2ecf20Sopenharmony_ci .freq = 0, 1148c2ecf20Sopenharmony_ci .sample_period = SAMPLE_PERIOD, 1158c2ecf20Sopenharmony_ci .inherit = 0, 1168c2ecf20Sopenharmony_ci .type = PERF_TYPE_SOFTWARE, 1178c2ecf20Sopenharmony_ci .read_format = 0, 1188c2ecf20Sopenharmony_ci .sample_type = 0, 1198c2ecf20Sopenharmony_ci .config = PERF_COUNT_SW_CPU_CLOCK, 1208c2ecf20Sopenharmony_ci }; 1218c2ecf20Sopenharmony_ci struct perf_event_attr attr_raw = { 1228c2ecf20Sopenharmony_ci .freq = 0, 1238c2ecf20Sopenharmony_ci .sample_period = SAMPLE_PERIOD, 1248c2ecf20Sopenharmony_ci .inherit = 0, 1258c2ecf20Sopenharmony_ci .type = PERF_TYPE_RAW, 1268c2ecf20Sopenharmony_ci .read_format = 0, 1278c2ecf20Sopenharmony_ci .sample_type = 0, 1288c2ecf20Sopenharmony_ci /* Intel Instruction Retired */ 1298c2ecf20Sopenharmony_ci .config = 0xc0, 1308c2ecf20Sopenharmony_ci }; 1318c2ecf20Sopenharmony_ci struct perf_event_attr attr_l1d_load = { 1328c2ecf20Sopenharmony_ci .freq = 0, 1338c2ecf20Sopenharmony_ci .sample_period = SAMPLE_PERIOD, 1348c2ecf20Sopenharmony_ci .inherit = 0, 1358c2ecf20Sopenharmony_ci .type = PERF_TYPE_HW_CACHE, 1368c2ecf20Sopenharmony_ci .read_format = 0, 1378c2ecf20Sopenharmony_ci .sample_type = 0, 1388c2ecf20Sopenharmony_ci .config = 1398c2ecf20Sopenharmony_ci PERF_COUNT_HW_CACHE_L1D | 1408c2ecf20Sopenharmony_ci (PERF_COUNT_HW_CACHE_OP_READ << 8) | 1418c2ecf20Sopenharmony_ci (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16), 1428c2ecf20Sopenharmony_ci }; 1438c2ecf20Sopenharmony_ci struct perf_event_attr attr_llc_miss = { 1448c2ecf20Sopenharmony_ci .freq = 0, 1458c2ecf20Sopenharmony_ci .sample_period = SAMPLE_PERIOD, 1468c2ecf20Sopenharmony_ci .inherit = 0, 1478c2ecf20Sopenharmony_ci .type = PERF_TYPE_HW_CACHE, 1488c2ecf20Sopenharmony_ci .read_format = 0, 1498c2ecf20Sopenharmony_ci .sample_type = 0, 1508c2ecf20Sopenharmony_ci .config = 1518c2ecf20Sopenharmony_ci PERF_COUNT_HW_CACHE_LL | 1528c2ecf20Sopenharmony_ci (PERF_COUNT_HW_CACHE_OP_READ << 8) | 1538c2ecf20Sopenharmony_ci (PERF_COUNT_HW_CACHE_RESULT_MISS << 16), 1548c2ecf20Sopenharmony_ci }; 1558c2ecf20Sopenharmony_ci struct perf_event_attr attr_msr_tsc = { 1568c2ecf20Sopenharmony_ci .freq = 0, 1578c2ecf20Sopenharmony_ci .sample_period = 0, 1588c2ecf20Sopenharmony_ci .inherit = 0, 1598c2ecf20Sopenharmony_ci /* From /sys/bus/event_source/devices/msr/ */ 1608c2ecf20Sopenharmony_ci .type = 7, 1618c2ecf20Sopenharmony_ci .read_format = 0, 1628c2ecf20Sopenharmony_ci .sample_type = 0, 1638c2ecf20Sopenharmony_ci .config = 0, 1648c2ecf20Sopenharmony_ci }; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci test_perf_event_array(&attr_cycles, "HARDWARE-cycles"); 1678c2ecf20Sopenharmony_ci test_perf_event_array(&attr_clock, "SOFTWARE-clock"); 1688c2ecf20Sopenharmony_ci test_perf_event_array(&attr_raw, "RAW-instruction-retired"); 1698c2ecf20Sopenharmony_ci test_perf_event_array(&attr_l1d_load, "HW_CACHE-L1D-load"); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* below tests may fail in qemu */ 1728c2ecf20Sopenharmony_ci test_perf_event_array(&attr_llc_miss, "HW_CACHE-LLC-miss"); 1738c2ecf20Sopenharmony_ci test_perf_event_array(&attr_msr_tsc, "Dynamic-msr-tsc"); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ciint main(int argc, char **argv) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 1798c2ecf20Sopenharmony_ci struct bpf_link *links[2]; 1808c2ecf20Sopenharmony_ci struct bpf_program *prog; 1818c2ecf20Sopenharmony_ci struct bpf_object *obj; 1828c2ecf20Sopenharmony_ci char filename[256]; 1838c2ecf20Sopenharmony_ci int i = 0; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci setrlimit(RLIMIT_MEMLOCK, &r); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 1888c2ecf20Sopenharmony_ci obj = bpf_object__open_file(filename, NULL); 1898c2ecf20Sopenharmony_ci if (libbpf_get_error(obj)) { 1908c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: opening BPF object file failed\n"); 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* load BPF program */ 1958c2ecf20Sopenharmony_ci if (bpf_object__load(obj)) { 1968c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: loading BPF object file failed\n"); 1978c2ecf20Sopenharmony_ci goto cleanup; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci map_fd[0] = bpf_object__find_map_fd_by_name(obj, "counters"); 2018c2ecf20Sopenharmony_ci map_fd[1] = bpf_object__find_map_fd_by_name(obj, "values"); 2028c2ecf20Sopenharmony_ci map_fd[2] = bpf_object__find_map_fd_by_name(obj, "values2"); 2038c2ecf20Sopenharmony_ci if (map_fd[0] < 0 || map_fd[1] < 0 || map_fd[2] < 0) { 2048c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: finding a map in obj file failed\n"); 2058c2ecf20Sopenharmony_ci goto cleanup; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci bpf_object__for_each_program(prog, obj) { 2098c2ecf20Sopenharmony_ci links[i] = bpf_program__attach(prog); 2108c2ecf20Sopenharmony_ci if (libbpf_get_error(links[i])) { 2118c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: bpf_program__attach failed\n"); 2128c2ecf20Sopenharmony_ci links[i] = NULL; 2138c2ecf20Sopenharmony_ci goto cleanup; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci i++; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci test_bpf_perf_event(); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cicleanup: 2218c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) 2228c2ecf20Sopenharmony_ci bpf_link__destroy(links[i]); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci bpf_object__close(obj); 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci} 227