1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright (c) 2013-2015 PLUMgrid, http://plumgrid.com |
3 | */ |
4 | #include <stdio.h> |
5 | #include <stdlib.h> |
6 | #include <signal.h> |
7 | #include <unistd.h> |
8 | #include <stdbool.h> |
9 | #include <string.h> |
10 | |
11 | #include <bpf/bpf.h> |
12 | #include <bpf/libbpf.h> |
13 | #include "bpf_util.h" |
14 | |
15 | #define SLOTS 100 |
16 | |
17 | static void clear_stats(int fd) |
18 | { |
19 | unsigned int nr_cpus = bpf_num_possible_cpus(); |
20 | __u64 values[nr_cpus]; |
21 | __u32 key; |
22 | |
23 | memset(values, 0, sizeof(values)); |
24 | for (key = 0; key < SLOTS; key++) |
25 | bpf_map_update_elem(fd, &key, values, BPF_ANY); |
26 | } |
27 | |
28 | const char *color[] = { |
29 | "\033[48;5;255m" , |
30 | "\033[48;5;252m" , |
31 | "\033[48;5;250m" , |
32 | "\033[48;5;248m" , |
33 | "\033[48;5;246m" , |
34 | "\033[48;5;244m" , |
35 | "\033[48;5;242m" , |
36 | "\033[48;5;240m" , |
37 | "\033[48;5;238m" , |
38 | "\033[48;5;236m" , |
39 | "\033[48;5;234m" , |
40 | "\033[48;5;232m" , |
41 | }; |
42 | const int num_colors = ARRAY_SIZE(color); |
43 | |
44 | const char nocolor[] = "\033[00m" ; |
45 | |
46 | const char *sym[] = { |
47 | " " , |
48 | " " , |
49 | "." , |
50 | "." , |
51 | "*" , |
52 | "*" , |
53 | "o" , |
54 | "o" , |
55 | "O" , |
56 | "O" , |
57 | "#" , |
58 | "#" , |
59 | }; |
60 | |
61 | bool full_range = false; |
62 | bool text_only = false; |
63 | |
64 | static void print_banner(void) |
65 | { |
66 | if (full_range) |
67 | printf(format: "|1ns |10ns |100ns |1us |10us |100us" |
68 | " |1ms |10ms |100ms |1s |10s\n" ); |
69 | else |
70 | printf(format: "|1us |10us |100us |1ms |10ms " |
71 | "|100ms |1s |10s\n" ); |
72 | } |
73 | |
74 | static void print_hist(int fd) |
75 | { |
76 | unsigned int nr_cpus = bpf_num_possible_cpus(); |
77 | __u64 total_events = 0; |
78 | long values[nr_cpus]; |
79 | __u64 max_cnt = 0; |
80 | __u64 cnt[SLOTS]; |
81 | __u64 value; |
82 | __u32 key; |
83 | int i; |
84 | |
85 | for (key = 0; key < SLOTS; key++) { |
86 | bpf_map_lookup_elem(fd, &key, values); |
87 | value = 0; |
88 | for (i = 0; i < nr_cpus; i++) |
89 | value += values[i]; |
90 | cnt[key] = value; |
91 | total_events += value; |
92 | if (value > max_cnt) |
93 | max_cnt = value; |
94 | } |
95 | clear_stats(fd); |
96 | for (key = full_range ? 0 : 29; key < SLOTS; key++) { |
97 | int c = num_colors * cnt[key] / (max_cnt + 1); |
98 | |
99 | if (text_only) |
100 | printf(format: "%s" , sym[c]); |
101 | else |
102 | printf(format: "%s %s" , color[c], nocolor); |
103 | } |
104 | printf(format: " # %lld\n" , total_events); |
105 | } |
106 | |
107 | int main(int ac, char **argv) |
108 | { |
109 | struct bpf_link *links[2]; |
110 | struct bpf_program *prog; |
111 | struct bpf_object *obj; |
112 | char filename[256]; |
113 | int map_fd, i, j = 0; |
114 | |
115 | for (i = 1; i < ac; i++) { |
116 | if (strcmp(s1: argv[i], s2: "-a" ) == 0) { |
117 | full_range = true; |
118 | } else if (strcmp(s1: argv[i], s2: "-t" ) == 0) { |
119 | text_only = true; |
120 | } else if (strcmp(s1: argv[i], s2: "-h" ) == 0) { |
121 | printf(format: "Usage:\n" |
122 | " -a display wider latency range\n" |
123 | " -t text only\n" ); |
124 | return 1; |
125 | } |
126 | } |
127 | |
128 | snprintf(s: filename, maxlen: sizeof(filename), format: "%s.bpf.o" , argv[0]); |
129 | obj = bpf_object__open_file(filename, NULL); |
130 | if (libbpf_get_error(obj)) { |
131 | fprintf(stderr, format: "ERROR: opening BPF object file failed\n" ); |
132 | return 0; |
133 | } |
134 | |
135 | /* load BPF program */ |
136 | if (bpf_object__load(obj)) { |
137 | fprintf(stderr, format: "ERROR: loading BPF object file failed\n" ); |
138 | goto cleanup; |
139 | } |
140 | |
141 | map_fd = bpf_object__find_map_fd_by_name(obj, "lat_map" ); |
142 | if (map_fd < 0) { |
143 | fprintf(stderr, format: "ERROR: finding a map in obj file failed\n" ); |
144 | goto cleanup; |
145 | } |
146 | |
147 | bpf_object__for_each_program(prog, obj) { |
148 | links[j] = bpf_program__attach(prog); |
149 | if (libbpf_get_error(links[j])) { |
150 | fprintf(stderr, format: "ERROR: bpf_program__attach failed\n" ); |
151 | links[j] = NULL; |
152 | goto cleanup; |
153 | } |
154 | j++; |
155 | } |
156 | |
157 | printf(format: " heatmap of IO latency\n" ); |
158 | if (text_only) |
159 | printf(format: " %s" , sym[num_colors - 1]); |
160 | else |
161 | printf(format: " %s %s" , color[num_colors - 1], nocolor); |
162 | printf(format: " - many events with this latency\n" ); |
163 | |
164 | if (text_only) |
165 | printf(format: " %s" , sym[0]); |
166 | else |
167 | printf(format: " %s %s" , color[0], nocolor); |
168 | printf(format: " - few events\n" ); |
169 | |
170 | for (i = 0; ; i++) { |
171 | if (i % 20 == 0) |
172 | print_banner(); |
173 | print_hist(fd: map_fd); |
174 | sleep(seconds: 2); |
175 | } |
176 | |
177 | cleanup: |
178 | for (j--; j >= 0; j--) |
179 | bpf_link__destroy(links[j]); |
180 | |
181 | bpf_object__close(obj); |
182 | return 0; |
183 | } |
184 | |