xref: /linux/samples/bpf/sampleip_user.c (revision 72874418e4b9e2673c26a810b0ae9f418b573ee3)
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