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