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