xref: /aosp_15_r20/external/bcc/libbpf-tools/tcprtt.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 // Copyright (c) 2021 Wenbo Zhang
3 //
4 // Based on tcprtt(8) from BCC by zhenwei pi.
5 // 06-Aug-2021   Wenbo Zhang   Created this.
6 #define _DEFAULT_SOURCE
7 #include <arpa/inet.h>
8 #include <argp.h>
9 #include <stdio.h>
10 #include <signal.h>
11 #include <unistd.h>
12 #include <time.h>
13 #include <bpf/libbpf.h>
14 #include <bpf/bpf.h>
15 #include "tcprtt.h"
16 #include "tcprtt.skel.h"
17 #include "trace_helpers.h"
18 
19 static struct env {
20 	__u16 lport;
21 	__u16 rport;
22 	__u32 laddr;
23 	__u32 raddr;
24 	__u8 laddr_v6[IPV6_LEN];
25 	__u8 raddr_v6[IPV6_LEN];
26 	bool milliseconds;
27 	time_t duration;
28 	time_t interval;
29 	bool timestamp;
30 	bool laddr_hist;
31 	bool raddr_hist;
32 	bool extended;
33 	bool verbose;
34 } env = {
35 	.interval = 99999999,
36 };
37 
38 static volatile bool exiting;
39 
40 const char *argp_program_version = "tcprtt 0.1";
41 const char *argp_program_bug_address =
42 	"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
43 const char argp_program_doc[] =
44 "Summarize TCP RTT as a histogram.\n"
45 "\n"
46 "USAGE: \n"
47 "\n"
48 "EXAMPLES:\n"
49 "    tcprtt            # summarize TCP RTT\n"
50 "    tcprtt -i 1 -d 10 # print 1 second summaries, 10 times\n"
51 "    tcprtt -m -T      # summarize in millisecond, and timestamps\n"
52 "    tcprtt -p         # filter for local port\n"
53 "    tcprtt -P         # filter for remote port\n"
54 "    tcprtt -a         # filter for local address\n"
55 "    tcprtt -A         # filter for remote address\n"
56 "    tcprtt -b         # show sockets histogram by local address\n"
57 "    tcprtt -B         # show sockets histogram by remote address\n"
58 "    tcprtt -e         # show extension summary(average)\n";
59 
60 static const struct argp_option opts[] = {
61 	{ "interval", 'i', "INTERVAL", 0, "summary interval, seconds" },
62 	{ "duration", 'd', "DURATION", 0, "total duration of trace, seconds" },
63 	{ "timestamp", 'T', NULL, 0, "include timestamp on output" },
64 	{ "millisecond", 'm', NULL, 0, "millisecond histogram" },
65 	{ "lport", 'p', "LPORT", 0, "filter for local port" },
66 	{ "rport", 'P', "RPORT", 0, "filter for remote port" },
67 	{ "laddr", 'a', "LADDR", 0, "filter for local address" },
68 	{ "raddr", 'A', "RADDR", 0, "filter for remote address" },
69 	{ "byladdr", 'b', NULL, 0,
70 	  "show sockets histogram by local address" },
71 	{ "byraddr", 'B', NULL, 0,
72 	  "show sockets histogram by remote address" },
73 	{ "extension", 'e', NULL, 0, "show extension summary(average)" },
74 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
75 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
76 	{},
77 };
78 
parse_arg(int key,char * arg,struct argp_state * state)79 static error_t parse_arg(int key, char *arg, struct argp_state *state)
80 {
81 	struct in_addr addr;
82 	struct in6_addr addr_v6;
83 
84 	switch (key) {
85 	case 'h':
86 		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
87 		break;
88 	case 'v':
89 		env.verbose = true;
90 		break;
91 	case 'i':
92 		errno = 0;
93 		env.interval = strtol(arg, NULL, 10);
94 		if (errno || env.interval <= 0) {
95 			fprintf(stderr, "invalid interval: %s\n", arg);
96 			argp_usage(state);
97 		}
98 		break;
99 	case 'd':
100 		errno = 0;
101 		env.duration = strtol(arg, NULL, 10);
102 		if (errno || env.duration <= 0) {
103 			fprintf(stderr, "invalid duration: %s\n", arg);
104 			argp_usage(state);
105 		}
106 		break;
107 	case 'T':
108 		env.timestamp = true;
109 		break;
110 	case 'm':
111 		env.milliseconds = true;
112 		break;
113 	case 'p':
114 		errno = 0;
115 		env.lport = strtoul(arg, NULL, 10);
116 		if (errno) {
117 			fprintf(stderr, "invalid lport: %s\n", arg);
118 			argp_usage(state);
119 		}
120 		env.lport = htons(env.lport);
121 		break;
122 	case 'P':
123 		errno = 0;
124 		env.rport = strtoul(arg, NULL, 10);
125 		if (errno) {
126 			fprintf(stderr, "invalid rport: %s\n", arg);
127 			argp_usage(state);
128 		}
129 		env.rport = htons(env.rport);
130 		break;
131 	case 'a':
132                 if (strchr(arg, ':')) {
133                         if (inet_pton(AF_INET6, arg, &addr_v6) < 1) {
134                                 fprintf(stderr, "invalid local IPv6 address: %s\n", arg);
135                                 argp_usage(state);
136                         }
137                         memcpy(env.laddr_v6, &addr_v6, sizeof(env.laddr_v6));
138                 } else {
139                         if (inet_pton(AF_INET, arg, &addr) < 0) {
140                                 fprintf(stderr, "invalid local address: %s\n", arg);
141                                 argp_usage(state);
142                         }
143                         env.laddr = addr.s_addr;
144                 }
145 		break;
146 	case 'A':
147                 if (strchr(arg, ':')) {
148                         if (inet_pton(AF_INET6, arg, &addr_v6) < 1) {
149                                 fprintf(stderr, "invalid remote address: %s\n", arg);
150                                 argp_usage(state);
151                         }
152                         memcpy(env.raddr_v6, &addr_v6, sizeof(env.raddr_v6));
153                 } else {
154                         if (inet_pton(AF_INET, arg, &addr) < 0) {
155                                 fprintf(stderr, "invalid remote address: %s\n", arg);
156                                 argp_usage(state);
157                         }
158                         env.raddr = addr.s_addr;
159                 }
160 		break;
161 	case 'b':
162 		env.laddr_hist = true;
163 		break;
164 	case 'B':
165 		env.raddr_hist = true;
166 		break;
167 	case 'e':
168 		env.extended = true;
169 		break;
170 	default:
171 		return ARGP_ERR_UNKNOWN;
172 	}
173 	return 0;
174 }
175 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)176 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
177 {
178 	if (level == LIBBPF_DEBUG && !env.verbose)
179 		return 0;
180 	return vfprintf(stderr, format, args);
181 }
182 
sig_handler(int sig)183 static void sig_handler(int sig)
184 {
185 	exiting = true;
186 }
187 
print_map(struct bpf_map * map)188 static int print_map(struct bpf_map *map)
189 {
190 	const char *units = env.milliseconds ? "msecs" : "usecs";
191 	struct hist_key *lookup_key = NULL, next_key;
192 	int err, fd = bpf_map__fd(map);
193 	struct hist hist;
194 
195 	while (!bpf_map_get_next_key(fd, lookup_key, &next_key)) {
196 		err = bpf_map_lookup_elem(fd, &next_key, &hist);
197 		if (err < 0) {
198 			fprintf(stderr, "failed to lookup infos: %d\n", err);
199 			return -1;
200 		}
201 
202 
203 		if (env.laddr_hist)
204 			printf("Local Address = ");
205 		else if (env.raddr_hist)
206 			printf("Remote Address = ");
207 		else
208 			printf("All Addresses = ****** ");
209 
210 		if (env.laddr_hist || env.raddr_hist) {
211 			__u16 family = next_key.family;
212 			char str[INET6_ADDRSTRLEN];
213 
214 			if (!inet_ntop(family, next_key.addr, str, sizeof(str))) {
215 				perror("converting IP to string:");
216 				return -1;
217 			}
218 
219 			printf("%s ", str);
220 		}
221 
222 		if (env.extended)
223 			printf("[AVG %llu]", hist.latency / hist.cnt);
224 		printf("\n");
225 		print_log2_hist(hist.slots, MAX_SLOTS, units);
226 		lookup_key = &next_key;
227 	}
228 
229 	lookup_key = NULL;
230 	while (!bpf_map_get_next_key(fd, lookup_key, &next_key)) {
231 		err = bpf_map_delete_elem(fd, &next_key);
232 		if (err < 0) {
233 			fprintf(stderr, "failed to cleanup infos: %d\n", err);
234 			return -1;
235 		}
236 		lookup_key = &next_key;
237 	}
238 
239 	return 0;
240 }
241 
main(int argc,char ** argv)242 int main(int argc, char **argv)
243 {
244 	static const struct argp argp = {
245 		.options = opts,
246 		.parser = parse_arg,
247 		.doc = argp_program_doc,
248 	};
249 	__u8 zero_addr_v6[IPV6_LEN] = {};
250 	struct tcprtt_bpf *obj;
251 	__u64 time_end = 0;
252 	struct tm *tm;
253 	char ts[32];
254 	time_t t;
255 	int err;
256 
257 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
258 	if (err)
259 		return err;
260 
261 	if ((env.laddr || env.raddr)
262 		&& (memcmp(env.laddr_v6, zero_addr_v6, sizeof(env.laddr_v6)) || memcmp(env.raddr_v6, zero_addr_v6, sizeof(env.raddr_v6)))) {
263 		fprintf(stderr, "It is not permitted to filter by both IPv4 and IPv6\n");
264 		return 1;
265 	}
266 
267 	libbpf_set_print(libbpf_print_fn);
268 
269 	obj = tcprtt_bpf__open();
270 	if (!obj) {
271 		fprintf(stderr, "failed to open BPF object\n");
272 		return 1;
273 	}
274 
275 	obj->rodata->targ_laddr_hist = env.laddr_hist;
276 	obj->rodata->targ_raddr_hist = env.raddr_hist;
277 	obj->rodata->targ_show_ext = env.extended;
278 	obj->rodata->targ_sport = env.lport;
279 	obj->rodata->targ_dport = env.rport;
280 	obj->rodata->targ_saddr = env.laddr;
281 	obj->rodata->targ_daddr = env.raddr;
282 	memcpy(obj->rodata->targ_saddr_v6, env.laddr_v6, sizeof(obj->rodata->targ_saddr_v6));
283 	memcpy(obj->rodata->targ_daddr_v6, env.raddr_v6, sizeof(obj->rodata->targ_daddr_v6));
284 	obj->rodata->targ_ms = env.milliseconds;
285 
286 	if (fentry_can_attach("tcp_rcv_established", NULL))
287 		bpf_program__set_autoload(obj->progs.tcp_rcv_kprobe, false);
288 	else
289 		bpf_program__set_autoload(obj->progs.tcp_rcv, false);
290 
291 	err = tcprtt_bpf__load(obj);
292 	if (err) {
293 		fprintf(stderr, "failed to load BPF object: %d\n", err);
294 		goto cleanup;
295 	}
296 
297 	err = tcprtt_bpf__attach(obj);
298 	if (err) {
299 		fprintf(stderr, "failed to attach BPF programs: %d\n", err);
300 		goto cleanup;
301 	}
302 
303 	signal(SIGINT, sig_handler);
304 
305 	printf("Tracing TCP RTT");
306 	if (env.duration)
307 		printf(" for %ld secs.\n", env.duration);
308 	else
309 		printf("... Hit Ctrl-C to end.\n");
310 
311 	/* setup duration */
312 	if (env.duration)
313 		time_end = get_ktime_ns() + env.duration * NSEC_PER_SEC;
314 
315 	/* main: poll */
316 	while (1) {
317 		sleep(env.interval);
318 		printf("\n");
319 
320 		if (env.timestamp) {
321 			time(&t);
322 			tm = localtime(&t);
323 			strftime(ts, sizeof(ts), "%H:%M:%S", tm);
324 			printf("%-8s\n", ts);
325 		}
326 
327 		err = print_map(obj->maps.hists);
328 		if (err)
329 			break;
330 
331 		if (env.duration && get_ktime_ns() > time_end)
332 			goto cleanup;
333 
334 		if (exiting)
335 			break;
336 	}
337 
338 cleanup:
339 	tcprtt_bpf__destroy(obj);
340 	return err != 0;
341 }
342