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