1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * sampleip: sample instruction pointer and frequency count in a BPF map. 4 * 5 * Copyright 2016 Netflix, Inc. 6 */ 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <unistd.h> 10 #include <errno.h> 11 #include <signal.h> 12 #include <string.h> 13 #include <assert.h> 14 #include <linux/perf_event.h> 15 #include <linux/ptrace.h> 16 #include <linux/bpf.h> 17 #include <sys/ioctl.h> 18 #include <bpf/libbpf.h> 19 #include "bpf_load.h" 20 #include "perf-sys.h" 21 #include "trace_helpers.h" 22 23 #define DEFAULT_FREQ 99 24 #define DEFAULT_SECS 5 25 #define MAX_IPS 8192 26 #define PAGE_OFFSET 0xffff880000000000 27 28 static int nr_cpus; 29 30 static void usage(void) 31 { 32 printf("USAGE: sampleip [-F freq] [duration]\n"); 33 printf(" -F freq # sample frequency (Hertz), default 99\n"); 34 printf(" duration # sampling duration (seconds), default 5\n"); 35 } 36 37 static int sampling_start(int *pmu_fd, int freq) 38 { 39 int i; 40 41 struct perf_event_attr pe_sample_attr = { 42 .type = PERF_TYPE_SOFTWARE, 43 .freq = 1, 44 .sample_period = freq, 45 .config = PERF_COUNT_SW_CPU_CLOCK, 46 .inherit = 1, 47 }; 48 49 for (i = 0; i < nr_cpus; i++) { 50 pmu_fd[i] = sys_perf_event_open(&pe_sample_attr, -1 /* pid */, i, 51 -1 /* group_fd */, 0 /* flags */); 52 if (pmu_fd[i] < 0) { 53 fprintf(stderr, "ERROR: Initializing perf sampling\n"); 54 return 1; 55 } 56 assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_SET_BPF, 57 prog_fd[0]) == 0); 58 assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_ENABLE, 0) == 0); 59 } 60 61 return 0; 62 } 63 64 static void sampling_end(int *pmu_fd) 65 { 66 int i; 67 68 for (i = 0; i < nr_cpus; i++) 69 close(pmu_fd[i]); 70 } 71 72 struct ipcount { 73 __u64 ip; 74 __u32 count; 75 }; 76 77 /* used for sorting */ 78 struct ipcount counts[MAX_IPS]; 79 80 static int count_cmp(const void *p1, const void *p2) 81 { 82 return ((struct ipcount *)p1)->count - ((struct ipcount *)p2)->count; 83 } 84 85 static void print_ip_map(int fd) 86 { 87 struct ksym *sym; 88 __u64 key, next_key; 89 __u32 value; 90 int i, max; 91 92 printf("%-19s %-32s %s\n", "ADDR", "KSYM", "COUNT"); 93 94 /* fetch IPs and counts */ 95 key = 0, i = 0; 96 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { 97 bpf_map_lookup_elem(fd, &next_key, &value); 98 counts[i].ip = next_key; 99 counts[i++].count = value; 100 key = next_key; 101 } 102 max = i; 103 104 /* sort and print */ 105 qsort(counts, max, sizeof(struct ipcount), count_cmp); 106 for (i = 0; i < max; i++) { 107 if (counts[i].ip > PAGE_OFFSET) { 108 sym = ksym_search(counts[i].ip); 109 if (!sym) { 110 printf("ksym not found. Is kallsyms loaded?\n"); 111 continue; 112 } 113 114 printf("0x%-17llx %-32s %u\n", counts[i].ip, sym->name, 115 counts[i].count); 116 } else { 117 printf("0x%-17llx %-32s %u\n", counts[i].ip, "(user)", 118 counts[i].count); 119 } 120 } 121 122 if (max == MAX_IPS) { 123 printf("WARNING: IP hash was full (max %d entries); ", max); 124 printf("may have dropped samples\n"); 125 } 126 } 127 128 static void int_exit(int sig) 129 { 130 printf("\n"); 131 print_ip_map(map_fd[0]); 132 exit(0); 133 } 134 135 int main(int argc, char **argv) 136 { 137 char filename[256]; 138 int *pmu_fd, opt, freq = DEFAULT_FREQ, secs = DEFAULT_SECS; 139 140 /* process arguments */ 141 while ((opt = getopt(argc, argv, "F:h")) != -1) { 142 switch (opt) { 143 case 'F': 144 freq = atoi(optarg); 145 break; 146 case 'h': 147 default: 148 usage(); 149 return 0; 150 } 151 } 152 if (argc - optind == 1) 153 secs = atoi(argv[optind]); 154 if (freq == 0 || secs == 0) { 155 usage(); 156 return 1; 157 } 158 159 /* initialize kernel symbol translation */ 160 if (load_kallsyms()) { 161 fprintf(stderr, "ERROR: loading /proc/kallsyms\n"); 162 return 2; 163 } 164 165 /* create perf FDs for each CPU */ 166 nr_cpus = sysconf(_SC_NPROCESSORS_CONF); 167 pmu_fd = malloc(nr_cpus * sizeof(int)); 168 if (pmu_fd == NULL) { 169 fprintf(stderr, "ERROR: malloc of pmu_fd\n"); 170 return 1; 171 } 172 173 /* load BPF program */ 174 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 175 if (load_bpf_file(filename)) { 176 fprintf(stderr, "ERROR: loading BPF program (errno %d):\n", 177 errno); 178 if (strcmp(bpf_log_buf, "") == 0) 179 fprintf(stderr, "Try: ulimit -l unlimited\n"); 180 else 181 fprintf(stderr, "%s", bpf_log_buf); 182 return 1; 183 } 184 signal(SIGINT, int_exit); 185 signal(SIGTERM, int_exit); 186 187 /* do sampling */ 188 printf("Sampling at %d Hertz for %d seconds. Ctrl-C also ends.\n", 189 freq, secs); 190 if (sampling_start(pmu_fd, freq) != 0) 191 return 1; 192 sleep(secs); 193 sampling_end(pmu_fd); 194 free(pmu_fd); 195 196 /* output sample counts */ 197 print_ip_map(map_fd[0]); 198 199 return 0; 200 } 201