1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 4 #include <assert.h> 5 #include <fcntl.h> 6 #include <linux/perf_event.h> 7 #include <linux/bpf.h> 8 #include <sched.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <sys/ioctl.h> 12 #include <sys/resource.h> 13 #include <sys/time.h> 14 #include <sys/types.h> 15 #include <sys/wait.h> 16 #include <unistd.h> 17 18 #include "bpf_load.h" 19 #include "libbpf.h" 20 #include "perf-sys.h" 21 22 #define SAMPLE_PERIOD 0x7fffffffffffffffULL 23 24 static void check_on_cpu(int cpu, struct perf_event_attr *attr) 25 { 26 struct bpf_perf_event_value value2; 27 int pmu_fd, error = 0; 28 cpu_set_t set; 29 __u64 value; 30 31 /* Move to target CPU */ 32 CPU_ZERO(&set); 33 CPU_SET(cpu, &set); 34 assert(sched_setaffinity(0, sizeof(set), &set) == 0); 35 /* Open perf event and attach to the perf_event_array */ 36 pmu_fd = sys_perf_event_open(attr, -1/*pid*/, cpu/*cpu*/, -1/*group_fd*/, 0); 37 if (pmu_fd < 0) { 38 fprintf(stderr, "sys_perf_event_open failed on CPU %d\n", cpu); 39 error = 1; 40 goto on_exit; 41 } 42 assert(bpf_map_update_elem(map_fd[0], &cpu, &pmu_fd, BPF_ANY) == 0); 43 assert(ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0) == 0); 44 /* Trigger the kprobe */ 45 bpf_map_get_next_key(map_fd[1], &cpu, NULL); 46 /* Check the value */ 47 if (bpf_map_lookup_elem(map_fd[1], &cpu, &value)) { 48 fprintf(stderr, "Value missing for CPU %d\n", cpu); 49 error = 1; 50 goto on_exit; 51 } else { 52 fprintf(stderr, "CPU %d: %llu\n", cpu, value); 53 } 54 /* The above bpf_map_lookup_elem should trigger the second kprobe */ 55 if (bpf_map_lookup_elem(map_fd[2], &cpu, &value2)) { 56 fprintf(stderr, "Value2 missing for CPU %d\n", cpu); 57 error = 1; 58 goto on_exit; 59 } else { 60 fprintf(stderr, "CPU %d: counter: %llu, enabled: %llu, running: %llu\n", cpu, 61 value2.counter, value2.enabled, value2.running); 62 } 63 64 on_exit: 65 assert(bpf_map_delete_elem(map_fd[0], &cpu) == 0 || error); 66 assert(ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE, 0) == 0 || error); 67 assert(close(pmu_fd) == 0 || error); 68 assert(bpf_map_delete_elem(map_fd[1], &cpu) == 0 || error); 69 exit(error); 70 } 71 72 static void test_perf_event_array(struct perf_event_attr *attr, 73 const char *name) 74 { 75 int i, status, nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 76 pid_t pid[nr_cpus]; 77 int err = 0; 78 79 printf("Test reading %s counters\n", name); 80 81 for (i = 0; i < nr_cpus; i++) { 82 pid[i] = fork(); 83 assert(pid[i] >= 0); 84 if (pid[i] == 0) { 85 check_on_cpu(i, attr); 86 exit(1); 87 } 88 } 89 90 for (i = 0; i < nr_cpus; i++) { 91 assert(waitpid(pid[i], &status, 0) == pid[i]); 92 err |= status; 93 } 94 95 if (err) 96 printf("Test: %s FAILED\n", name); 97 } 98 99 static void test_bpf_perf_event(void) 100 { 101 struct perf_event_attr attr_cycles = { 102 .freq = 0, 103 .sample_period = SAMPLE_PERIOD, 104 .inherit = 0, 105 .type = PERF_TYPE_HARDWARE, 106 .read_format = 0, 107 .sample_type = 0, 108 .config = PERF_COUNT_HW_CPU_CYCLES, 109 }; 110 struct perf_event_attr attr_clock = { 111 .freq = 0, 112 .sample_period = SAMPLE_PERIOD, 113 .inherit = 0, 114 .type = PERF_TYPE_SOFTWARE, 115 .read_format = 0, 116 .sample_type = 0, 117 .config = PERF_COUNT_SW_CPU_CLOCK, 118 }; 119 struct perf_event_attr attr_raw = { 120 .freq = 0, 121 .sample_period = SAMPLE_PERIOD, 122 .inherit = 0, 123 .type = PERF_TYPE_RAW, 124 .read_format = 0, 125 .sample_type = 0, 126 /* Intel Instruction Retired */ 127 .config = 0xc0, 128 }; 129 struct perf_event_attr attr_l1d_load = { 130 .freq = 0, 131 .sample_period = SAMPLE_PERIOD, 132 .inherit = 0, 133 .type = PERF_TYPE_HW_CACHE, 134 .read_format = 0, 135 .sample_type = 0, 136 .config = 137 PERF_COUNT_HW_CACHE_L1D | 138 (PERF_COUNT_HW_CACHE_OP_READ << 8) | 139 (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16), 140 }; 141 struct perf_event_attr attr_llc_miss = { 142 .freq = 0, 143 .sample_period = SAMPLE_PERIOD, 144 .inherit = 0, 145 .type = PERF_TYPE_HW_CACHE, 146 .read_format = 0, 147 .sample_type = 0, 148 .config = 149 PERF_COUNT_HW_CACHE_LL | 150 (PERF_COUNT_HW_CACHE_OP_READ << 8) | 151 (PERF_COUNT_HW_CACHE_RESULT_MISS << 16), 152 }; 153 struct perf_event_attr attr_msr_tsc = { 154 .freq = 0, 155 .sample_period = 0, 156 .inherit = 0, 157 /* From /sys/bus/event_source/devices/msr/ */ 158 .type = 7, 159 .read_format = 0, 160 .sample_type = 0, 161 .config = 0, 162 }; 163 164 test_perf_event_array(&attr_cycles, "HARDWARE-cycles"); 165 test_perf_event_array(&attr_clock, "SOFTWARE-clock"); 166 test_perf_event_array(&attr_raw, "RAW-instruction-retired"); 167 test_perf_event_array(&attr_l1d_load, "HW_CACHE-L1D-load"); 168 169 /* below tests may fail in qemu */ 170 test_perf_event_array(&attr_llc_miss, "HW_CACHE-LLC-miss"); 171 test_perf_event_array(&attr_msr_tsc, "Dynamic-msr-tsc"); 172 } 173 174 int main(int argc, char **argv) 175 { 176 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 177 char filename[256]; 178 179 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 180 181 setrlimit(RLIMIT_MEMLOCK, &r); 182 if (load_bpf_file(filename)) { 183 printf("%s", bpf_log_buf); 184 return 1; 185 } 186 187 test_bpf_perf_event(); 188 return 0; 189 } 190