18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * sampleip: sample instruction pointer and frequency count in a BPF map. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2016 Netflix, Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <stdio.h> 88c2ecf20Sopenharmony_ci#include <stdlib.h> 98c2ecf20Sopenharmony_ci#include <unistd.h> 108c2ecf20Sopenharmony_ci#include <errno.h> 118c2ecf20Sopenharmony_ci#include <signal.h> 128c2ecf20Sopenharmony_ci#include <string.h> 138c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 148c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 158c2ecf20Sopenharmony_ci#include <linux/bpf.h> 168c2ecf20Sopenharmony_ci#include <bpf/bpf.h> 178c2ecf20Sopenharmony_ci#include <bpf/libbpf.h> 188c2ecf20Sopenharmony_ci#include "perf-sys.h" 198c2ecf20Sopenharmony_ci#include "trace_helpers.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define DEFAULT_FREQ 99 228c2ecf20Sopenharmony_ci#define DEFAULT_SECS 5 238c2ecf20Sopenharmony_ci#define MAX_IPS 8192 248c2ecf20Sopenharmony_ci#define PAGE_OFFSET 0xffff880000000000 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int map_fd; 278c2ecf20Sopenharmony_cistatic int nr_cpus; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void usage(void) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci printf("USAGE: sampleip [-F freq] [duration]\n"); 328c2ecf20Sopenharmony_ci printf(" -F freq # sample frequency (Hertz), default 99\n"); 338c2ecf20Sopenharmony_ci printf(" duration # sampling duration (seconds), default 5\n"); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int sampling_start(int freq, struct bpf_program *prog, 378c2ecf20Sopenharmony_ci struct bpf_link *links[]) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci int i, pmu_fd; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci struct perf_event_attr pe_sample_attr = { 428c2ecf20Sopenharmony_ci .type = PERF_TYPE_SOFTWARE, 438c2ecf20Sopenharmony_ci .freq = 1, 448c2ecf20Sopenharmony_ci .sample_period = freq, 458c2ecf20Sopenharmony_ci .config = PERF_COUNT_SW_CPU_CLOCK, 468c2ecf20Sopenharmony_ci .inherit = 1, 478c2ecf20Sopenharmony_ci }; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci for (i = 0; i < nr_cpus; i++) { 508c2ecf20Sopenharmony_ci pmu_fd = sys_perf_event_open(&pe_sample_attr, -1 /* pid */, i, 518c2ecf20Sopenharmony_ci -1 /* group_fd */, 0 /* flags */); 528c2ecf20Sopenharmony_ci if (pmu_fd < 0) { 538c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: Initializing perf sampling\n"); 548c2ecf20Sopenharmony_ci return 1; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci links[i] = bpf_program__attach_perf_event(prog, pmu_fd); 578c2ecf20Sopenharmony_ci if (libbpf_get_error(links[i])) { 588c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: Attach perf event\n"); 598c2ecf20Sopenharmony_ci links[i] = NULL; 608c2ecf20Sopenharmony_ci close(pmu_fd); 618c2ecf20Sopenharmony_ci return 1; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void sampling_end(struct bpf_link *links[]) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci int i; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci for (i = 0; i < nr_cpus; i++) 738c2ecf20Sopenharmony_ci bpf_link__destroy(links[i]); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct ipcount { 778c2ecf20Sopenharmony_ci __u64 ip; 788c2ecf20Sopenharmony_ci __u32 count; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* used for sorting */ 828c2ecf20Sopenharmony_cistruct ipcount counts[MAX_IPS]; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int count_cmp(const void *p1, const void *p2) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci return ((struct ipcount *)p1)->count - ((struct ipcount *)p2)->count; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void print_ip_map(int fd) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct ksym *sym; 928c2ecf20Sopenharmony_ci __u64 key, next_key; 938c2ecf20Sopenharmony_ci __u32 value; 948c2ecf20Sopenharmony_ci int i, max; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci printf("%-19s %-32s %s\n", "ADDR", "KSYM", "COUNT"); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* fetch IPs and counts */ 998c2ecf20Sopenharmony_ci key = 0, i = 0; 1008c2ecf20Sopenharmony_ci while (bpf_map_get_next_key(fd, &key, &next_key) == 0) { 1018c2ecf20Sopenharmony_ci bpf_map_lookup_elem(fd, &next_key, &value); 1028c2ecf20Sopenharmony_ci counts[i].ip = next_key; 1038c2ecf20Sopenharmony_ci counts[i++].count = value; 1048c2ecf20Sopenharmony_ci key = next_key; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci max = i; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* sort and print */ 1098c2ecf20Sopenharmony_ci qsort(counts, max, sizeof(struct ipcount), count_cmp); 1108c2ecf20Sopenharmony_ci for (i = 0; i < max; i++) { 1118c2ecf20Sopenharmony_ci if (counts[i].ip > PAGE_OFFSET) { 1128c2ecf20Sopenharmony_ci sym = ksym_search(counts[i].ip); 1138c2ecf20Sopenharmony_ci if (!sym) { 1148c2ecf20Sopenharmony_ci printf("ksym not found. Is kallsyms loaded?\n"); 1158c2ecf20Sopenharmony_ci continue; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci printf("0x%-17llx %-32s %u\n", counts[i].ip, sym->name, 1198c2ecf20Sopenharmony_ci counts[i].count); 1208c2ecf20Sopenharmony_ci } else { 1218c2ecf20Sopenharmony_ci printf("0x%-17llx %-32s %u\n", counts[i].ip, "(user)", 1228c2ecf20Sopenharmony_ci counts[i].count); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (max == MAX_IPS) { 1278c2ecf20Sopenharmony_ci printf("WARNING: IP hash was full (max %d entries); ", max); 1288c2ecf20Sopenharmony_ci printf("may have dropped samples\n"); 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void int_exit(int sig) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci printf("\n"); 1358c2ecf20Sopenharmony_ci print_ip_map(map_fd); 1368c2ecf20Sopenharmony_ci exit(0); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciint main(int argc, char **argv) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci int opt, freq = DEFAULT_FREQ, secs = DEFAULT_SECS, error = 1; 1428c2ecf20Sopenharmony_ci struct bpf_object *obj = NULL; 1438c2ecf20Sopenharmony_ci struct bpf_program *prog; 1448c2ecf20Sopenharmony_ci struct bpf_link **links; 1458c2ecf20Sopenharmony_ci char filename[256]; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* process arguments */ 1488c2ecf20Sopenharmony_ci while ((opt = getopt(argc, argv, "F:h")) != -1) { 1498c2ecf20Sopenharmony_ci switch (opt) { 1508c2ecf20Sopenharmony_ci case 'F': 1518c2ecf20Sopenharmony_ci freq = atoi(optarg); 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case 'h': 1548c2ecf20Sopenharmony_ci default: 1558c2ecf20Sopenharmony_ci usage(); 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci if (argc - optind == 1) 1608c2ecf20Sopenharmony_ci secs = atoi(argv[optind]); 1618c2ecf20Sopenharmony_ci if (freq == 0 || secs == 0) { 1628c2ecf20Sopenharmony_ci usage(); 1638c2ecf20Sopenharmony_ci return 1; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* initialize kernel symbol translation */ 1678c2ecf20Sopenharmony_ci if (load_kallsyms()) { 1688c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: loading /proc/kallsyms\n"); 1698c2ecf20Sopenharmony_ci return 2; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* create perf FDs for each CPU */ 1738c2ecf20Sopenharmony_ci nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); 1748c2ecf20Sopenharmony_ci links = calloc(nr_cpus, sizeof(struct bpf_link *)); 1758c2ecf20Sopenharmony_ci if (!links) { 1768c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: malloc of links\n"); 1778c2ecf20Sopenharmony_ci goto cleanup; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 1818c2ecf20Sopenharmony_ci obj = bpf_object__open_file(filename, NULL); 1828c2ecf20Sopenharmony_ci if (libbpf_get_error(obj)) { 1838c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: opening BPF object file failed\n"); 1848c2ecf20Sopenharmony_ci obj = NULL; 1858c2ecf20Sopenharmony_ci goto cleanup; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci prog = bpf_object__find_program_by_name(obj, "do_sample"); 1898c2ecf20Sopenharmony_ci if (!prog) { 1908c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: finding a prog in obj file failed\n"); 1918c2ecf20Sopenharmony_ci goto cleanup; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* load BPF program */ 1958c2ecf20Sopenharmony_ci if (bpf_object__load(obj)) { 1968c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: loading BPF object file failed\n"); 1978c2ecf20Sopenharmony_ci goto cleanup; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci map_fd = bpf_object__find_map_fd_by_name(obj, "ip_map"); 2018c2ecf20Sopenharmony_ci if (map_fd < 0) { 2028c2ecf20Sopenharmony_ci fprintf(stderr, "ERROR: finding a map in obj file failed\n"); 2038c2ecf20Sopenharmony_ci goto cleanup; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci signal(SIGINT, int_exit); 2078c2ecf20Sopenharmony_ci signal(SIGTERM, int_exit); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* do sampling */ 2108c2ecf20Sopenharmony_ci printf("Sampling at %d Hertz for %d seconds. Ctrl-C also ends.\n", 2118c2ecf20Sopenharmony_ci freq, secs); 2128c2ecf20Sopenharmony_ci if (sampling_start(freq, prog, links) != 0) 2138c2ecf20Sopenharmony_ci goto cleanup; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci sleep(secs); 2168c2ecf20Sopenharmony_ci error = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cicleanup: 2198c2ecf20Sopenharmony_ci sampling_end(links); 2208c2ecf20Sopenharmony_ci /* output sample counts */ 2218c2ecf20Sopenharmony_ci if (!error) 2228c2ecf20Sopenharmony_ci print_ip_map(map_fd); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci free(links); 2258c2ecf20Sopenharmony_ci bpf_object__close(obj); 2268c2ecf20Sopenharmony_ci return error; 2278c2ecf20Sopenharmony_ci} 228