diff options
author | Brendan Gregg <bgregg@netflix.com> | 2016-09-01 21:37:26 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-09-02 13:46:45 -0400 |
commit | 72874418e4b9e2673c26a810b0ae9f418b573ee3 (patch) | |
tree | ce7e1ec61c8c12ef2b02ac5bb67b6ed8d2023f64 /samples | |
parent | 1c47910ef80135ac89e4d0b471d123572cee5535 (diff) |
samples/bpf: add sampleip example
sample instruction pointer and frequency count in a BPF map
Signed-off-by: Brendan Gregg <bgregg@netflix.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'samples')
-rw-r--r-- | samples/bpf/Makefile | 4 | ||||
-rw-r--r-- | samples/bpf/sampleip_kern.c | 38 | ||||
-rw-r--r-- | samples/bpf/sampleip_user.c | 196 |
3 files changed, 238 insertions, 0 deletions
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index a69cf9045285..12b7304d55dc 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile | |||
@@ -26,6 +26,7 @@ hostprogs-y += xdp1 | |||
26 | hostprogs-y += xdp2 | 26 | hostprogs-y += xdp2 |
27 | hostprogs-y += test_current_task_under_cgroup | 27 | hostprogs-y += test_current_task_under_cgroup |
28 | hostprogs-y += trace_event | 28 | hostprogs-y += trace_event |
29 | hostprogs-y += sampleip | ||
29 | 30 | ||
30 | test_verifier-objs := test_verifier.o libbpf.o | 31 | test_verifier-objs := test_verifier.o libbpf.o |
31 | test_maps-objs := test_maps.o libbpf.o | 32 | test_maps-objs := test_maps.o libbpf.o |
@@ -54,6 +55,7 @@ xdp2-objs := bpf_load.o libbpf.o xdp1_user.o | |||
54 | test_current_task_under_cgroup-objs := bpf_load.o libbpf.o \ | 55 | test_current_task_under_cgroup-objs := bpf_load.o libbpf.o \ |
55 | test_current_task_under_cgroup_user.o | 56 | test_current_task_under_cgroup_user.o |
56 | trace_event-objs := bpf_load.o libbpf.o trace_event_user.o | 57 | trace_event-objs := bpf_load.o libbpf.o trace_event_user.o |
58 | sampleip-objs := bpf_load.o libbpf.o sampleip_user.o | ||
57 | 59 | ||
58 | # Tell kbuild to always build the programs | 60 | # Tell kbuild to always build the programs |
59 | always := $(hostprogs-y) | 61 | always := $(hostprogs-y) |
@@ -82,6 +84,7 @@ always += xdp1_kern.o | |||
82 | always += xdp2_kern.o | 84 | always += xdp2_kern.o |
83 | always += test_current_task_under_cgroup_kern.o | 85 | always += test_current_task_under_cgroup_kern.o |
84 | always += trace_event_kern.o | 86 | always += trace_event_kern.o |
87 | always += sampleip_kern.o | ||
85 | 88 | ||
86 | HOSTCFLAGS += -I$(objtree)/usr/include | 89 | HOSTCFLAGS += -I$(objtree)/usr/include |
87 | 90 | ||
@@ -107,6 +110,7 @@ HOSTLOADLIBES_xdp1 += -lelf | |||
107 | HOSTLOADLIBES_xdp2 += -lelf | 110 | HOSTLOADLIBES_xdp2 += -lelf |
108 | HOSTLOADLIBES_test_current_task_under_cgroup += -lelf | 111 | HOSTLOADLIBES_test_current_task_under_cgroup += -lelf |
109 | HOSTLOADLIBES_trace_event += -lelf | 112 | HOSTLOADLIBES_trace_event += -lelf |
113 | HOSTLOADLIBES_sampleip += -lelf | ||
110 | 114 | ||
111 | # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: | 115 | # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: |
112 | # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang | 116 | # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang |
diff --git a/samples/bpf/sampleip_kern.c b/samples/bpf/sampleip_kern.c new file mode 100644 index 000000000000..774a681f374a --- /dev/null +++ b/samples/bpf/sampleip_kern.c | |||
@@ -0,0 +1,38 @@ | |||
1 | /* Copyright 2016 Netflix, Inc. | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or | ||
4 | * modify it under the terms of version 2 of the GNU General Public | ||
5 | * License as published by the Free Software Foundation. | ||
6 | */ | ||
7 | #include <linux/version.h> | ||
8 | #include <linux/ptrace.h> | ||
9 | #include <uapi/linux/bpf.h> | ||
10 | #include <uapi/linux/bpf_perf_event.h> | ||
11 | #include "bpf_helpers.h" | ||
12 | |||
13 | #define MAX_IPS 8192 | ||
14 | |||
15 | struct bpf_map_def SEC("maps") ip_map = { | ||
16 | .type = BPF_MAP_TYPE_HASH, | ||
17 | .key_size = sizeof(u64), | ||
18 | .value_size = sizeof(u32), | ||
19 | .max_entries = MAX_IPS, | ||
20 | }; | ||
21 | |||
22 | SEC("perf_event") | ||
23 | int do_sample(struct bpf_perf_event_data *ctx) | ||
24 | { | ||
25 | u64 ip; | ||
26 | u32 *value, init_val = 1; | ||
27 | |||
28 | ip = ctx->regs.ip; | ||
29 | value = bpf_map_lookup_elem(&ip_map, &ip); | ||
30 | if (value) | ||
31 | *value += 1; | ||
32 | else | ||
33 | /* E2BIG not tested for this example only */ | ||
34 | bpf_map_update_elem(&ip_map, &ip, &init_val, BPF_NOEXIST); | ||
35 | |||
36 | return 0; | ||
37 | } | ||
38 | char _license[] SEC("license") = "GPL"; | ||
diff --git a/samples/bpf/sampleip_user.c b/samples/bpf/sampleip_user.c new file mode 100644 index 000000000000..260a6bdd6413 --- /dev/null +++ b/samples/bpf/sampleip_user.c | |||
@@ -0,0 +1,196 @@ | |||
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 <stdio.h> | ||
13 | #include <unistd.h> | ||
14 | #include <errno.h> | ||
15 | #include <signal.h> | ||
16 | #include <string.h> | ||
17 | #include <assert.h> | ||
18 | #include <linux/perf_event.h> | ||
19 | #include <linux/ptrace.h> | ||
20 | #include <linux/bpf.h> | ||
21 | #include <sys/ioctl.h> | ||
22 | #include "libbpf.h" | ||
23 | #include "bpf_load.h" | ||
24 | |||
25 | #define DEFAULT_FREQ 99 | ||
26 | #define DEFAULT_SECS 5 | ||
27 | #define MAX_IPS 8192 | ||
28 | #define PAGE_OFFSET 0xffff880000000000 | ||
29 | |||
30 | static int nr_cpus; | ||
31 | |||
32 | static void usage(void) | ||
33 | { | ||
34 | printf("USAGE: sampleip [-F freq] [duration]\n"); | ||
35 | printf(" -F freq # sample frequency (Hertz), default 99\n"); | ||
36 | printf(" duration # sampling duration (seconds), default 5\n"); | ||
37 | } | ||
38 | |||
39 | static int sampling_start(int *pmu_fd, int freq) | ||
40 | { | ||
41 | int i; | ||
42 | |||
43 | struct perf_event_attr pe_sample_attr = { | ||
44 | .type = PERF_TYPE_SOFTWARE, | ||
45 | .freq = 1, | ||
46 | .sample_period = freq, | ||
47 | .config = PERF_COUNT_SW_CPU_CLOCK, | ||
48 | .inherit = 1, | ||
49 | }; | ||
50 | |||
51 | for (i = 0; i < nr_cpus; i++) { | ||
52 | pmu_fd[i] = perf_event_open(&pe_sample_attr, -1 /* pid */, i, | ||
53 | -1 /* group_fd */, 0 /* flags */); | ||
54 | if (pmu_fd[i] < 0) { | ||
55 | fprintf(stderr, "ERROR: Initializing perf sampling\n"); | ||
56 | return 1; | ||
57 | } | ||
58 | assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_SET_BPF, | ||
59 | prog_fd[0]) == 0); | ||
60 | assert(ioctl(pmu_fd[i], PERF_EVENT_IOC_ENABLE, 0) == 0); | ||
61 | } | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static void sampling_end(int *pmu_fd) | ||
67 | { | ||
68 | int i; | ||
69 | |||
70 | for (i = 0; i < nr_cpus; i++) | ||
71 | close(pmu_fd[i]); | ||
72 | } | ||
73 | |||
74 | struct ipcount { | ||
75 | __u64 ip; | ||
76 | __u32 count; | ||
77 | }; | ||
78 | |||
79 | /* used for sorting */ | ||
80 | struct ipcount counts[MAX_IPS]; | ||
81 | |||
82 | static int count_cmp(const void *p1, const void *p2) | ||
83 | { | ||
84 | return ((struct ipcount *)p1)->count - ((struct ipcount *)p2)->count; | ||
85 | } | ||
86 | |||
87 | static void print_ip_map(int fd) | ||
88 | { | ||
89 | struct ksym *sym; | ||
90 | __u64 key, next_key; | ||
91 | __u32 value; | ||
92 | int i, max; | ||
93 | |||
94 | printf("%-19s %-32s %s\n", "ADDR", "KSYM", "COUNT"); | ||
95 | |||
96 | /* fetch IPs and counts */ | ||
97 | key = 0, i = 0; | ||
98 | while (bpf_get_next_key(fd, &key, &next_key) == 0) { | ||
99 | bpf_lookup_elem(fd, &next_key, &value); | ||
100 | counts[i].ip = next_key; | ||
101 | counts[i++].count = value; | ||
102 | key = next_key; | ||
103 | } | ||
104 | max = i; | ||
105 | |||
106 | /* sort and print */ | ||
107 | qsort(counts, max, sizeof(struct ipcount), count_cmp); | ||
108 | for (i = 0; i < max; i++) { | ||
109 | if (counts[i].ip > PAGE_OFFSET) { | ||
110 | sym = ksym_search(counts[i].ip); | ||
111 | printf("0x%-17llx %-32s %u\n", counts[i].ip, sym->name, | ||
112 | counts[i].count); | ||
113 | } else { | ||
114 | printf("0x%-17llx %-32s %u\n", counts[i].ip, "(user)", | ||
115 | counts[i].count); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | if (max == MAX_IPS) { | ||
120 | printf("WARNING: IP hash was full (max %d entries); ", max); | ||
121 | printf("may have dropped samples\n"); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | static void int_exit(int sig) | ||
126 | { | ||
127 | printf("\n"); | ||
128 | print_ip_map(map_fd[0]); | ||
129 | exit(0); | ||
130 | } | ||
131 | |||
132 | int main(int argc, char **argv) | ||
133 | { | ||
134 | char filename[256]; | ||
135 | int *pmu_fd, opt, freq = DEFAULT_FREQ, secs = DEFAULT_SECS; | ||
136 | |||
137 | /* process arguments */ | ||
138 | while ((opt = getopt(argc, argv, "F:h")) != -1) { | ||
139 | switch (opt) { | ||
140 | case 'F': | ||
141 | freq = atoi(optarg); | ||
142 | break; | ||
143 | case 'h': | ||
144 | default: | ||
145 | usage(); | ||
146 | return 0; | ||
147 | } | ||
148 | } | ||
149 | if (argc - optind == 1) | ||
150 | secs = atoi(argv[optind]); | ||
151 | if (freq == 0 || secs == 0) { | ||
152 | usage(); | ||
153 | return 1; | ||
154 | } | ||
155 | |||
156 | /* initialize kernel symbol translation */ | ||
157 | if (load_kallsyms()) { | ||
158 | fprintf(stderr, "ERROR: loading /proc/kallsyms\n"); | ||
159 | return 2; | ||
160 | } | ||
161 | |||
162 | /* create perf FDs for each CPU */ | ||
163 | nr_cpus = sysconf(_SC_NPROCESSORS_CONF); | ||
164 | pmu_fd = malloc(nr_cpus * sizeof(int)); | ||
165 | if (pmu_fd == NULL) { | ||
166 | fprintf(stderr, "ERROR: malloc of pmu_fd\n"); | ||
167 | return 1; | ||
168 | } | ||
169 | |||
170 | /* load BPF program */ | ||
171 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); | ||
172 | if (load_bpf_file(filename)) { | ||
173 | fprintf(stderr, "ERROR: loading BPF program (errno %d):\n", | ||
174 | errno); | ||
175 | if (strcmp(bpf_log_buf, "") == 0) | ||
176 | fprintf(stderr, "Try: ulimit -l unlimited\n"); | ||
177 | else | ||
178 | fprintf(stderr, "%s", bpf_log_buf); | ||
179 | return 1; | ||
180 | } | ||
181 | signal(SIGINT, int_exit); | ||
182 | |||
183 | /* do sampling */ | ||
184 | printf("Sampling at %d Hertz for %d seconds. Ctrl-C also ends.\n", | ||
185 | freq, secs); | ||
186 | if (sampling_start(pmu_fd, freq) != 0) | ||
187 | return 1; | ||
188 | sleep(secs); | ||
189 | sampling_end(pmu_fd); | ||
190 | free(pmu_fd); | ||
191 | |||
192 | /* output sample counts */ | ||
193 | print_ip_map(map_fd[0]); | ||
194 | |||
195 | return 0; | ||
196 | } | ||