1 /* Copyright (c) 2016 Facebook 2 * 3 * This program is free software; you can redistribute it and/or 4 * modify it under the terms of version 2 of the GNU General Public 5 * License as published by the Free Software Foundation. 6 */ 7 #include <stdio.h> 8 #include <unistd.h> 9 #include <stdlib.h> 10 #include <stdbool.h> 11 #include <string.h> 12 #include <fcntl.h> 13 #include <poll.h> 14 #include <sys/ioctl.h> 15 #include <linux/perf_event.h> 16 #include <linux/bpf.h> 17 #include <signal.h> 18 #include <assert.h> 19 #include <errno.h> 20 #include <sys/resource.h> 21 #include "libbpf.h" 22 #include "bpf_load.h" 23 #include "perf-sys.h" 24 25 #define SAMPLE_FREQ 50 26 27 static bool sys_read_seen, sys_write_seen; 28 29 static void print_ksym(__u64 addr) 30 { 31 struct ksym *sym; 32 33 if (!addr) 34 return; 35 sym = ksym_search(addr); 36 printf("%s;", sym->name); 37 if (!strcmp(sym->name, "sys_read")) 38 sys_read_seen = true; 39 else if (!strcmp(sym->name, "sys_write")) 40 sys_write_seen = true; 41 } 42 43 static void print_addr(__u64 addr) 44 { 45 if (!addr) 46 return; 47 printf("%llx;", addr); 48 } 49 50 #define TASK_COMM_LEN 16 51 52 struct key_t { 53 char comm[TASK_COMM_LEN]; 54 __u32 kernstack; 55 __u32 userstack; 56 }; 57 58 static void print_stack(struct key_t *key, __u64 count) 59 { 60 __u64 ip[PERF_MAX_STACK_DEPTH] = {}; 61 static bool warned; 62 int i; 63 64 printf("%3lld %s;", count, key->comm); 65 if (bpf_map_lookup_elem(map_fd[1], &key->kernstack, ip) != 0) { 66 printf("---;"); 67 } else { 68 for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--) 69 print_ksym(ip[i]); 70 } 71 printf("-;"); 72 if (bpf_map_lookup_elem(map_fd[1], &key->userstack, ip) != 0) { 73 printf("---;"); 74 } else { 75 for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--) 76 print_addr(ip[i]); 77 } 78 if (count < 6) 79 printf("\r"); 80 else 81 printf("\n"); 82 83 if (key->kernstack == -EEXIST && !warned) { 84 printf("stackmap collisions seen. Consider increasing size\n"); 85 warned = true; 86 } else if ((int)key->kernstack < 0 && (int)key->userstack < 0) { 87 printf("err stackid %d %d\n", key->kernstack, key->userstack); 88 } 89 } 90 91 static void int_exit(int sig) 92 { 93 kill(0, SIGKILL); 94 exit(0); 95 } 96 97 static void print_stacks(void) 98 { 99 struct key_t key = {}, next_key; 100 __u64 value; 101 __u32 stackid = 0, next_id; 102 int fd = map_fd[0], stack_map = map_fd[1]; 103 104 sys_read_seen = sys_write_seen = false; 105 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { 106 bpf_map_lookup_elem(fd, &next_key, &value); 107 print_stack(&next_key, value); 108 bpf_map_delete_elem(fd, &next_key); 109 key = next_key; 110 } 111 printf("\n"); 112 if (!sys_read_seen || !sys_write_seen) { 113 printf("BUG kernel stack doesn't contain sys_read() and sys_write()\n"); 114 int_exit(0); 115 } 116 117 /* clear stack map */ 118 while (bpf_map_get_next_key(stack_map, &stackid, &next_id) == 0) { 119 bpf_map_delete_elem(stack_map, &next_id); 120 stackid = next_id; 121 } 122 } 123 124 static void test_perf_event_all_cpu(struct perf_event_attr *attr) 125 { 126 int nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 127 int *pmu_fd = malloc(nr_cpus * sizeof(int)); 128 int i, error = 0; 129 130 /* open perf_event on all cpus */ 131 for (i = 0; i < nr_cpus; i++) { 132 pmu_fd[i] = sys_perf_event_open(attr, -1, i, -1, 0); 133 if (pmu_fd[i] < 0) { 134 printf("sys_perf_event_open failed\n"); 135 error = 1; 136 goto all_cpu_err; 137 } 138 assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_SET_BPF, prog_fd[0]) == 0); 139 assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_ENABLE) == 0); 140 } 141 system("dd if=/dev/zero of=/dev/null count=5000k status=none"); 142 print_stacks(); 143 all_cpu_err: 144 for (i--; i >= 0; i--) { 145 ioctl(pmu_fd[i], PERF_EVENT_IOC_DISABLE); 146 close(pmu_fd[i]); 147 } 148 free(pmu_fd); 149 if (error) 150 int_exit(0); 151 } 152 153 static void test_perf_event_task(struct perf_event_attr *attr) 154 { 155 int pmu_fd; 156 157 /* open task bound event */ 158 pmu_fd = sys_perf_event_open(attr, 0, -1, -1, 0); 159 if (pmu_fd < 0) { 160 printf("sys_perf_event_open failed\n"); 161 int_exit(0); 162 } 163 assert(ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd[0]) == 0); 164 assert(ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE) == 0); 165 system("dd if=/dev/zero of=/dev/null count=5000k status=none"); 166 print_stacks(); 167 ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE); 168 close(pmu_fd); 169 } 170 171 static void test_bpf_perf_event(void) 172 { 173 struct perf_event_attr attr_type_hw = { 174 .sample_freq = SAMPLE_FREQ, 175 .freq = 1, 176 .type = PERF_TYPE_HARDWARE, 177 .config = PERF_COUNT_HW_CPU_CYCLES, 178 .inherit = 1, 179 }; 180 struct perf_event_attr attr_type_sw = { 181 .sample_freq = SAMPLE_FREQ, 182 .freq = 1, 183 .type = PERF_TYPE_SOFTWARE, 184 .config = PERF_COUNT_SW_CPU_CLOCK, 185 .inherit = 1, 186 }; 187 struct perf_event_attr attr_hw_cache_l1d = { 188 .sample_freq = SAMPLE_FREQ, 189 .freq = 1, 190 .type = PERF_TYPE_HW_CACHE, 191 .config = 192 PERF_COUNT_HW_CACHE_L1D | 193 (PERF_COUNT_HW_CACHE_OP_READ << 8) | 194 (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16), 195 .inherit = 1, 196 }; 197 struct perf_event_attr attr_hw_cache_branch_miss = { 198 .sample_freq = SAMPLE_FREQ, 199 .freq = 1, 200 .type = PERF_TYPE_HW_CACHE, 201 .config = 202 PERF_COUNT_HW_CACHE_BPU | 203 (PERF_COUNT_HW_CACHE_OP_READ << 8) | 204 (PERF_COUNT_HW_CACHE_RESULT_MISS << 16), 205 .inherit = 1, 206 }; 207 struct perf_event_attr attr_type_raw = { 208 .sample_freq = SAMPLE_FREQ, 209 .freq = 1, 210 .type = PERF_TYPE_RAW, 211 /* Intel Instruction Retired */ 212 .config = 0xc0, 213 .inherit = 1, 214 }; 215 216 printf("Test HW_CPU_CYCLES\n"); 217 test_perf_event_all_cpu(&attr_type_hw); 218 test_perf_event_task(&attr_type_hw); 219 220 printf("Test SW_CPU_CLOCK\n"); 221 test_perf_event_all_cpu(&attr_type_sw); 222 test_perf_event_task(&attr_type_sw); 223 224 printf("Test HW_CACHE_L1D\n"); 225 test_perf_event_all_cpu(&attr_hw_cache_l1d); 226 test_perf_event_task(&attr_hw_cache_l1d); 227 228 printf("Test HW_CACHE_BPU\n"); 229 test_perf_event_all_cpu(&attr_hw_cache_branch_miss); 230 test_perf_event_task(&attr_hw_cache_branch_miss); 231 232 printf("Test Instruction Retired\n"); 233 test_perf_event_all_cpu(&attr_type_raw); 234 test_perf_event_task(&attr_type_raw); 235 236 printf("*** PASS ***\n"); 237 } 238 239 240 int main(int argc, char **argv) 241 { 242 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 243 char filename[256]; 244 245 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 246 setrlimit(RLIMIT_MEMLOCK, &r); 247 248 signal(SIGINT, int_exit); 249 signal(SIGTERM, int_exit); 250 251 if (load_kallsyms()) { 252 printf("failed to process /proc/kallsyms\n"); 253 return 1; 254 } 255 256 if (load_bpf_file(filename)) { 257 printf("%s", bpf_log_buf); 258 return 2; 259 } 260 261 if (fork() == 0) { 262 read_trace_pipe(); 263 return 0; 264 } 265 test_bpf_perf_event(); 266 int_exit(0); 267 return 0; 268 } 269