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