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