xref: /aosp_15_r20/external/bcc/libbpf-tools/tcplife.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1*387f9dfdSAndroid Build Coastguard Worker // SPDX-License-Identifier: GPL-2.0
2*387f9dfdSAndroid Build Coastguard Worker 
3*387f9dfdSAndroid Build Coastguard Worker /*
4*387f9dfdSAndroid Build Coastguard Worker  * tcplife      Trace the lifespan of TCP sessions and summarize.
5*387f9dfdSAndroid Build Coastguard Worker  *
6*387f9dfdSAndroid Build Coastguard Worker  * Copyright (c) 2022 Hengqi Chen
7*387f9dfdSAndroid Build Coastguard Worker  *
8*387f9dfdSAndroid Build Coastguard Worker  * Based on tcplife(8) from BCC by Brendan Gregg.
9*387f9dfdSAndroid Build Coastguard Worker  * 02-Jun-2022   Hengqi Chen   Created this.
10*387f9dfdSAndroid Build Coastguard Worker  */
11*387f9dfdSAndroid Build Coastguard Worker #include <argp.h>
12*387f9dfdSAndroid Build Coastguard Worker #include <errno.h>
13*387f9dfdSAndroid Build Coastguard Worker #include <signal.h>
14*387f9dfdSAndroid Build Coastguard Worker #include <time.h>
15*387f9dfdSAndroid Build Coastguard Worker #include <arpa/inet.h>
16*387f9dfdSAndroid Build Coastguard Worker #include <sys/socket.h>
17*387f9dfdSAndroid Build Coastguard Worker 
18*387f9dfdSAndroid Build Coastguard Worker #include "btf_helpers.h"
19*387f9dfdSAndroid Build Coastguard Worker #include "tcplife.h"
20*387f9dfdSAndroid Build Coastguard Worker #include "tcplife.skel.h"
21*387f9dfdSAndroid Build Coastguard Worker 
22*387f9dfdSAndroid Build Coastguard Worker #define PERF_BUFFER_PAGES	16
23*387f9dfdSAndroid Build Coastguard Worker #define PERF_POLL_TIMEOUT_MS	100
24*387f9dfdSAndroid Build Coastguard Worker 
25*387f9dfdSAndroid Build Coastguard Worker static volatile sig_atomic_t exiting = 0;
26*387f9dfdSAndroid Build Coastguard Worker 
27*387f9dfdSAndroid Build Coastguard Worker static pid_t target_pid = 0;
28*387f9dfdSAndroid Build Coastguard Worker static short target_family = 0;
29*387f9dfdSAndroid Build Coastguard Worker static char *target_sports = NULL;
30*387f9dfdSAndroid Build Coastguard Worker static char *target_dports = NULL;
31*387f9dfdSAndroid Build Coastguard Worker static int column_width = 15;
32*387f9dfdSAndroid Build Coastguard Worker static bool emit_timestamp = false;
33*387f9dfdSAndroid Build Coastguard Worker static bool verbose = false;
34*387f9dfdSAndroid Build Coastguard Worker 
35*387f9dfdSAndroid Build Coastguard Worker const char *argp_program_version = "tcplife 0.1";
36*387f9dfdSAndroid Build Coastguard Worker const char *argp_program_bug_address =
37*387f9dfdSAndroid Build Coastguard Worker 	"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
38*387f9dfdSAndroid Build Coastguard Worker const char argp_program_doc[] =
39*387f9dfdSAndroid Build Coastguard Worker "Trace the lifespan of TCP sessions and summarize.\n"
40*387f9dfdSAndroid Build Coastguard Worker "\n"
41*387f9dfdSAndroid Build Coastguard Worker "USAGE: tcplife [-h] [-p PID] [-4] [-6] [-L] [-D] [-T] [-w]\n"
42*387f9dfdSAndroid Build Coastguard Worker "\n"
43*387f9dfdSAndroid Build Coastguard Worker "EXAMPLES:\n"
44*387f9dfdSAndroid Build Coastguard Worker "    tcplife -p 1215             # only trace PID 1215\n"
45*387f9dfdSAndroid Build Coastguard Worker "    tcplife -p 1215 -4          # trace IPv4 only\n";
46*387f9dfdSAndroid Build Coastguard Worker 
47*387f9dfdSAndroid Build Coastguard Worker static const struct argp_option opts[] = {
48*387f9dfdSAndroid Build Coastguard Worker 	{ "pid", 'p', "PID", 0, "Process ID to trace" },
49*387f9dfdSAndroid Build Coastguard Worker 	{ "ipv4", '4', NULL, 0, "Trace IPv4 only" },
50*387f9dfdSAndroid Build Coastguard Worker 	{ "ipv6", '6', NULL, 0, "Trace IPv6 only" },
51*387f9dfdSAndroid Build Coastguard Worker 	{ "wide", 'w', NULL, 0, "Wide column output (fits IPv6 addresses)" },
52*387f9dfdSAndroid Build Coastguard Worker 	{ "time", 'T', NULL, 0, "Include timestamp on output" },
53*387f9dfdSAndroid Build Coastguard Worker 	{ "localport", 'L', "LOCALPORT", 0, "Comma-separated list of local ports to trace." },
54*387f9dfdSAndroid Build Coastguard Worker 	{ "remoteport", 'D', "REMOTEPORT", 0, "Comma-separated list of remote ports to trace." },
55*387f9dfdSAndroid Build Coastguard Worker 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
56*387f9dfdSAndroid Build Coastguard Worker 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
57*387f9dfdSAndroid Build Coastguard Worker 	{},
58*387f9dfdSAndroid Build Coastguard Worker };
59*387f9dfdSAndroid Build Coastguard Worker 
parse_arg(int key,char * arg,struct argp_state * state)60*387f9dfdSAndroid Build Coastguard Worker static error_t parse_arg(int key, char *arg, struct argp_state *state)
61*387f9dfdSAndroid Build Coastguard Worker {
62*387f9dfdSAndroid Build Coastguard Worker 	long n;
63*387f9dfdSAndroid Build Coastguard Worker 
64*387f9dfdSAndroid Build Coastguard Worker 	switch (key) {
65*387f9dfdSAndroid Build Coastguard Worker 	case 'p':
66*387f9dfdSAndroid Build Coastguard Worker 		errno = 0;
67*387f9dfdSAndroid Build Coastguard Worker 		n = strtol(arg, NULL, 10);
68*387f9dfdSAndroid Build Coastguard Worker 		if (errno || n <= 0) {
69*387f9dfdSAndroid Build Coastguard Worker 			fprintf(stderr, "Invalid PID: %s\n", arg);
70*387f9dfdSAndroid Build Coastguard Worker 			argp_usage(state);
71*387f9dfdSAndroid Build Coastguard Worker 		}
72*387f9dfdSAndroid Build Coastguard Worker 		target_pid = n;
73*387f9dfdSAndroid Build Coastguard Worker 		break;
74*387f9dfdSAndroid Build Coastguard Worker 	case '4':
75*387f9dfdSAndroid Build Coastguard Worker 		target_family = AF_INET;
76*387f9dfdSAndroid Build Coastguard Worker 		break;
77*387f9dfdSAndroid Build Coastguard Worker 	case '6':
78*387f9dfdSAndroid Build Coastguard Worker 		target_family = AF_INET6;
79*387f9dfdSAndroid Build Coastguard Worker 		break;
80*387f9dfdSAndroid Build Coastguard Worker 	case 'w':
81*387f9dfdSAndroid Build Coastguard Worker 		column_width = 39;
82*387f9dfdSAndroid Build Coastguard Worker 		break;
83*387f9dfdSAndroid Build Coastguard Worker 	case 'L':
84*387f9dfdSAndroid Build Coastguard Worker 		target_sports = strdup(arg);
85*387f9dfdSAndroid Build Coastguard Worker 		break;
86*387f9dfdSAndroid Build Coastguard Worker 	case 'D':
87*387f9dfdSAndroid Build Coastguard Worker 		target_dports = strdup(arg);
88*387f9dfdSAndroid Build Coastguard Worker 		break;
89*387f9dfdSAndroid Build Coastguard Worker 	case 'T':
90*387f9dfdSAndroid Build Coastguard Worker 		emit_timestamp = true;
91*387f9dfdSAndroid Build Coastguard Worker 		break;
92*387f9dfdSAndroid Build Coastguard Worker 	case 'v':
93*387f9dfdSAndroid Build Coastguard Worker 		verbose = true;
94*387f9dfdSAndroid Build Coastguard Worker 		break;
95*387f9dfdSAndroid Build Coastguard Worker 	case 'h':
96*387f9dfdSAndroid Build Coastguard Worker 		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
97*387f9dfdSAndroid Build Coastguard Worker 		break;
98*387f9dfdSAndroid Build Coastguard Worker 	default:
99*387f9dfdSAndroid Build Coastguard Worker 		return ARGP_ERR_UNKNOWN;
100*387f9dfdSAndroid Build Coastguard Worker 	}
101*387f9dfdSAndroid Build Coastguard Worker 	return 0;
102*387f9dfdSAndroid Build Coastguard Worker }
103*387f9dfdSAndroid Build Coastguard Worker 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)104*387f9dfdSAndroid Build Coastguard Worker static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
105*387f9dfdSAndroid Build Coastguard Worker {
106*387f9dfdSAndroid Build Coastguard Worker 	if (level == LIBBPF_DEBUG && !verbose)
107*387f9dfdSAndroid Build Coastguard Worker 		return 0;
108*387f9dfdSAndroid Build Coastguard Worker 	return vfprintf(stderr, format, args);
109*387f9dfdSAndroid Build Coastguard Worker }
110*387f9dfdSAndroid Build Coastguard Worker 
sig_int(int signo)111*387f9dfdSAndroid Build Coastguard Worker static void sig_int(int signo)
112*387f9dfdSAndroid Build Coastguard Worker {
113*387f9dfdSAndroid Build Coastguard Worker 	exiting = 1;
114*387f9dfdSAndroid Build Coastguard Worker }
115*387f9dfdSAndroid Build Coastguard Worker 
handle_event(void * ctx,int cpu,void * data,__u32 data_sz)116*387f9dfdSAndroid Build Coastguard Worker static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
117*387f9dfdSAndroid Build Coastguard Worker {
118*387f9dfdSAndroid Build Coastguard Worker 	char ts[32], saddr[48], daddr[48];
119*387f9dfdSAndroid Build Coastguard Worker 	struct event *e = data;
120*387f9dfdSAndroid Build Coastguard Worker 	struct tm *tm;
121*387f9dfdSAndroid Build Coastguard Worker 	time_t t;
122*387f9dfdSAndroid Build Coastguard Worker 
123*387f9dfdSAndroid Build Coastguard Worker 	if (emit_timestamp) {
124*387f9dfdSAndroid Build Coastguard Worker 		time(&t);
125*387f9dfdSAndroid Build Coastguard Worker 		tm = localtime(&t);
126*387f9dfdSAndroid Build Coastguard Worker 		strftime(ts, sizeof(ts), "%H:%M:%S", tm);
127*387f9dfdSAndroid Build Coastguard Worker 		printf("%8s ", ts);
128*387f9dfdSAndroid Build Coastguard Worker 	}
129*387f9dfdSAndroid Build Coastguard Worker 
130*387f9dfdSAndroid Build Coastguard Worker 	inet_ntop(e->family, &e->saddr, saddr, sizeof(saddr));
131*387f9dfdSAndroid Build Coastguard Worker 	inet_ntop(e->family, &e->daddr, daddr, sizeof(daddr));
132*387f9dfdSAndroid Build Coastguard Worker 
133*387f9dfdSAndroid Build Coastguard Worker 	printf("%-7d %-16s %-*s %-5d %-*s %-5d %-6.2f %-6.2f %-.2f\n",
134*387f9dfdSAndroid Build Coastguard Worker 	       e->pid, e->comm, column_width, saddr, e->sport, column_width, daddr, e->dport,
135*387f9dfdSAndroid Build Coastguard Worker 	       (double)e->tx_b / 1024, (double)e->rx_b / 1024, (double)e->span_us / 1000);
136*387f9dfdSAndroid Build Coastguard Worker }
137*387f9dfdSAndroid Build Coastguard Worker 
handle_lost_events(void * ctx,int cpu,__u64 lost_cnt)138*387f9dfdSAndroid Build Coastguard Worker static void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
139*387f9dfdSAndroid Build Coastguard Worker {
140*387f9dfdSAndroid Build Coastguard Worker 	fprintf(stderr, "lost %llu events on CPU #%d\n", lost_cnt, cpu);
141*387f9dfdSAndroid Build Coastguard Worker }
142*387f9dfdSAndroid Build Coastguard Worker 
main(int argc,char ** argv)143*387f9dfdSAndroid Build Coastguard Worker int main(int argc, char **argv)
144*387f9dfdSAndroid Build Coastguard Worker {
145*387f9dfdSAndroid Build Coastguard Worker 	LIBBPF_OPTS(bpf_object_open_opts, open_opts);
146*387f9dfdSAndroid Build Coastguard Worker 	static const struct argp argp = {
147*387f9dfdSAndroid Build Coastguard Worker 		.options = opts,
148*387f9dfdSAndroid Build Coastguard Worker 		.parser = parse_arg,
149*387f9dfdSAndroid Build Coastguard Worker 		.doc = argp_program_doc,
150*387f9dfdSAndroid Build Coastguard Worker 	};
151*387f9dfdSAndroid Build Coastguard Worker 	struct tcplife_bpf *obj;
152*387f9dfdSAndroid Build Coastguard Worker 	struct perf_buffer *pb = NULL;
153*387f9dfdSAndroid Build Coastguard Worker 	short port_num;
154*387f9dfdSAndroid Build Coastguard Worker 	char *port;
155*387f9dfdSAndroid Build Coastguard Worker 	int err, i;
156*387f9dfdSAndroid Build Coastguard Worker 
157*387f9dfdSAndroid Build Coastguard Worker 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
158*387f9dfdSAndroid Build Coastguard Worker 	if (err)
159*387f9dfdSAndroid Build Coastguard Worker 		return err;
160*387f9dfdSAndroid Build Coastguard Worker 
161*387f9dfdSAndroid Build Coastguard Worker 	libbpf_set_print(libbpf_print_fn);
162*387f9dfdSAndroid Build Coastguard Worker 
163*387f9dfdSAndroid Build Coastguard Worker 	err = ensure_core_btf(&open_opts);
164*387f9dfdSAndroid Build Coastguard Worker 	if (err) {
165*387f9dfdSAndroid Build Coastguard Worker 		fprintf(stderr, "failed to fetch necessary BTF for CO-RE: %s\n", strerror(-err));
166*387f9dfdSAndroid Build Coastguard Worker 		return 1;
167*387f9dfdSAndroid Build Coastguard Worker 	}
168*387f9dfdSAndroid Build Coastguard Worker 
169*387f9dfdSAndroid Build Coastguard Worker 	obj = tcplife_bpf__open_opts(&open_opts);
170*387f9dfdSAndroid Build Coastguard Worker 	if (!obj) {
171*387f9dfdSAndroid Build Coastguard Worker 		fprintf(stderr, "failed to open BPF object\n");
172*387f9dfdSAndroid Build Coastguard Worker 		return 1;
173*387f9dfdSAndroid Build Coastguard Worker 	}
174*387f9dfdSAndroid Build Coastguard Worker 
175*387f9dfdSAndroid Build Coastguard Worker 	obj->rodata->target_pid = target_pid;
176*387f9dfdSAndroid Build Coastguard Worker 	obj->rodata->target_family = target_family;
177*387f9dfdSAndroid Build Coastguard Worker 
178*387f9dfdSAndroid Build Coastguard Worker 	if (target_sports) {
179*387f9dfdSAndroid Build Coastguard Worker 		i = 0;
180*387f9dfdSAndroid Build Coastguard Worker 		port = strtok(target_sports, ",");
181*387f9dfdSAndroid Build Coastguard Worker 		while (port && i < MAX_PORTS) {
182*387f9dfdSAndroid Build Coastguard Worker 			port_num = strtol(port, NULL, 10);
183*387f9dfdSAndroid Build Coastguard Worker 			obj->rodata->target_sports[i++] = port_num;
184*387f9dfdSAndroid Build Coastguard Worker 			port = strtok(NULL, ",");
185*387f9dfdSAndroid Build Coastguard Worker 		}
186*387f9dfdSAndroid Build Coastguard Worker 		obj->rodata->filter_sport = true;
187*387f9dfdSAndroid Build Coastguard Worker 	}
188*387f9dfdSAndroid Build Coastguard Worker 
189*387f9dfdSAndroid Build Coastguard Worker 	if (target_dports) {
190*387f9dfdSAndroid Build Coastguard Worker 		i = 0;
191*387f9dfdSAndroid Build Coastguard Worker 		port = strtok(target_dports, ",");
192*387f9dfdSAndroid Build Coastguard Worker 		while (port && i < MAX_PORTS) {
193*387f9dfdSAndroid Build Coastguard Worker 			port_num = strtol(port, NULL, 10);
194*387f9dfdSAndroid Build Coastguard Worker 			obj->rodata->target_dports[i++] = port_num;
195*387f9dfdSAndroid Build Coastguard Worker 			port = strtok(NULL, ",");
196*387f9dfdSAndroid Build Coastguard Worker 		}
197*387f9dfdSAndroid Build Coastguard Worker 		obj->rodata->filter_dport = true;
198*387f9dfdSAndroid Build Coastguard Worker 	}
199*387f9dfdSAndroid Build Coastguard Worker 
200*387f9dfdSAndroid Build Coastguard Worker 	err = tcplife_bpf__load(obj);
201*387f9dfdSAndroid Build Coastguard Worker 	if (err) {
202*387f9dfdSAndroid Build Coastguard Worker 		fprintf(stderr, "failed to load BPF object: %d\n", err);
203*387f9dfdSAndroid Build Coastguard Worker 		goto cleanup;
204*387f9dfdSAndroid Build Coastguard Worker 	}
205*387f9dfdSAndroid Build Coastguard Worker 
206*387f9dfdSAndroid Build Coastguard Worker 	err = tcplife_bpf__attach(obj);
207*387f9dfdSAndroid Build Coastguard Worker 	if (err) {
208*387f9dfdSAndroid Build Coastguard Worker 		fprintf(stderr, "failed to attach BPF object: %d\n", err);
209*387f9dfdSAndroid Build Coastguard Worker 		goto cleanup;
210*387f9dfdSAndroid Build Coastguard Worker 	}
211*387f9dfdSAndroid Build Coastguard Worker 
212*387f9dfdSAndroid Build Coastguard Worker 	pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
213*387f9dfdSAndroid Build Coastguard Worker 			      handle_event, handle_lost_events, NULL, NULL);
214*387f9dfdSAndroid Build Coastguard Worker 	if (!pb) {
215*387f9dfdSAndroid Build Coastguard Worker 		err = -errno;
216*387f9dfdSAndroid Build Coastguard Worker 		fprintf(stderr, "failed to open perf buffer: %d\n", err);
217*387f9dfdSAndroid Build Coastguard Worker 		goto cleanup;
218*387f9dfdSAndroid Build Coastguard Worker 	}
219*387f9dfdSAndroid Build Coastguard Worker 
220*387f9dfdSAndroid Build Coastguard Worker 	if (signal(SIGINT, sig_int) == SIG_ERR) {
221*387f9dfdSAndroid Build Coastguard Worker 		fprintf(stderr, "can't set signal handler: %s\n", strerror(errno));
222*387f9dfdSAndroid Build Coastguard Worker 		err = 1;
223*387f9dfdSAndroid Build Coastguard Worker 		goto cleanup;
224*387f9dfdSAndroid Build Coastguard Worker 	}
225*387f9dfdSAndroid Build Coastguard Worker 
226*387f9dfdSAndroid Build Coastguard Worker 	if (emit_timestamp)
227*387f9dfdSAndroid Build Coastguard Worker 		printf("%-8s ", "TIME(s)");
228*387f9dfdSAndroid Build Coastguard Worker 	printf("%-7s %-16s %-*s %-5s %-*s %-5s %-6s %-6s %-s\n",
229*387f9dfdSAndroid Build Coastguard Worker 	       "PID", "COMM", column_width, "LADDR", "LPORT", column_width, "RADDR", "RPORT",
230*387f9dfdSAndroid Build Coastguard Worker 	       "TX_KB", "RX_KB", "MS");
231*387f9dfdSAndroid Build Coastguard Worker 
232*387f9dfdSAndroid Build Coastguard Worker 	while (!exiting) {
233*387f9dfdSAndroid Build Coastguard Worker 		err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS);
234*387f9dfdSAndroid Build Coastguard Worker 		if (err < 0 && err != -EINTR) {
235*387f9dfdSAndroid Build Coastguard Worker 			fprintf(stderr, "error polling perf buffer: %s\n", strerror(-err));
236*387f9dfdSAndroid Build Coastguard Worker 			goto cleanup;
237*387f9dfdSAndroid Build Coastguard Worker 		}
238*387f9dfdSAndroid Build Coastguard Worker 		/* reset err to return 0 if exiting */
239*387f9dfdSAndroid Build Coastguard Worker 		err = 0;
240*387f9dfdSAndroid Build Coastguard Worker 	}
241*387f9dfdSAndroid Build Coastguard Worker 
242*387f9dfdSAndroid Build Coastguard Worker cleanup:
243*387f9dfdSAndroid Build Coastguard Worker 	perf_buffer__free(pb);
244*387f9dfdSAndroid Build Coastguard Worker 	tcplife_bpf__destroy(obj);
245*387f9dfdSAndroid Build Coastguard Worker 	cleanup_core_btf(&open_opts);
246*387f9dfdSAndroid Build Coastguard Worker 	return err != 0;
247*387f9dfdSAndroid Build Coastguard Worker }
248