xref: /aosp_15_r20/external/bcc/libbpf-tools/opensnoop.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 // Copyright (c) 2019 Facebook
3 // Copyright (c) 2020 Netflix
4 //
5 // Based on opensnoop(8) from BCC by Brendan Gregg and others.
6 // 14-Feb-2020   Brendan Gregg   Created this.
7 #include <argp.h>
8 #include <signal.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/time.h>
13 #include <time.h>
14 #include <unistd.h>
15 #include <bpf/libbpf.h>
16 #include <bpf/bpf.h>
17 #include "opensnoop.h"
18 #include "opensnoop.skel.h"
19 #include "btf_helpers.h"
20 #include "trace_helpers.h"
21 #ifdef USE_BLAZESYM
22 #include "blazesym.h"
23 #endif
24 
25 /* Tune the buffer size and wakeup rate. These settings cope with roughly
26  * 50k opens/sec.
27  */
28 #define PERF_BUFFER_PAGES	64
29 #define PERF_BUFFER_TIME_MS	10
30 
31 /* Set the poll timeout when no events occur. This can affect -d accuracy. */
32 #define PERF_POLL_TIMEOUT_MS	100
33 
34 #define NSEC_PER_SEC		1000000000ULL
35 
36 static volatile sig_atomic_t exiting = 0;
37 
38 #ifdef USE_BLAZESYM
39 static blazesym *symbolizer;
40 #endif
41 
42 static struct env {
43 	pid_t pid;
44 	pid_t tid;
45 	uid_t uid;
46 	int duration;
47 	bool verbose;
48 	bool timestamp;
49 	bool print_uid;
50 	bool extended;
51 	bool failed;
52 	char *name;
53 #ifdef USE_BLAZESYM
54 	bool callers;
55 #endif
56 } env = {
57 	.uid = INVALID_UID
58 };
59 
60 const char *argp_program_version = "opensnoop 0.1";
61 const char *argp_program_bug_address =
62 	"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
63 const char argp_program_doc[] =
64 "Trace open family syscalls\n"
65 "\n"
66 "USAGE: opensnoop [-h] [-T] [-U] [-x] [-p PID] [-t TID] [-u UID] [-d DURATION]\n"
67 #ifdef USE_BLAZESYM
68 "                 [-n NAME] [-e] [-c]\n"
69 #else
70 "                 [-n NAME] [-e]\n"
71 #endif
72 "\n"
73 "EXAMPLES:\n"
74 "    ./opensnoop           # trace all open() syscalls\n"
75 "    ./opensnoop -T        # include timestamps\n"
76 "    ./opensnoop -U        # include UID\n"
77 "    ./opensnoop -x        # only show failed opens\n"
78 "    ./opensnoop -p 181    # only trace PID 181\n"
79 "    ./opensnoop -t 123    # only trace TID 123\n"
80 "    ./opensnoop -u 1000   # only trace UID 1000\n"
81 "    ./opensnoop -d 10     # trace for 10 seconds only\n"
82 "    ./opensnoop -n main   # only print process names containing \"main\"\n"
83 "    ./opensnoop -e        # show extended fields\n"
84 #ifdef USE_BLAZESYM
85 "    ./opensnoop -c        # show calling functions\n"
86 #endif
87 "";
88 
89 static const struct argp_option opts[] = {
90 	{ "duration", 'd', "DURATION", 0, "Duration to trace"},
91 	{ "extended-fields", 'e', NULL, 0, "Print extended fields"},
92 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help"},
93 	{ "name", 'n', "NAME", 0, "Trace process names containing this"},
94 	{ "pid", 'p', "PID", 0, "Process ID to trace"},
95 	{ "tid", 't', "TID", 0, "Thread ID to trace"},
96 	{ "timestamp", 'T', NULL, 0, "Print timestamp"},
97 	{ "uid", 'u', "UID", 0, "User ID to trace"},
98 	{ "print-uid", 'U', NULL, 0, "Print UID"},
99 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
100 	{ "failed", 'x', NULL, 0, "Failed opens only"},
101 #ifdef USE_BLAZESYM
102 	{ "callers", 'c', NULL, 0, "Show calling functions"},
103 #endif
104 	{},
105 };
106 
parse_arg(int key,char * arg,struct argp_state * state)107 static error_t parse_arg(int key, char *arg, struct argp_state *state)
108 {
109 	static int pos_args;
110 	long int pid, uid, duration;
111 
112 	switch (key) {
113 	case 'e':
114 		env.extended = true;
115 		break;
116 	case 'h':
117 		argp_usage(state);
118 		break;
119 	case 'T':
120 		env.timestamp = true;
121 		break;
122 	case 'U':
123 		env.print_uid = true;
124 		break;
125 	case 'v':
126 		env.verbose = true;
127 		break;
128 	case 'x':
129 		env.failed = true;
130 		break;
131 	case 'd':
132 		errno = 0;
133 		duration = strtol(arg, NULL, 10);
134 		if (errno || duration <= 0) {
135 			fprintf(stderr, "Invalid duration: %s\n", arg);
136 			argp_usage(state);
137 		}
138 		env.duration = duration;
139 		break;
140 	case 'n':
141 		errno = 0;
142 		env.name = arg;
143 		break;
144 	case 'p':
145 		errno = 0;
146 		pid = strtol(arg, NULL, 10);
147 		if (errno || pid <= 0) {
148 			fprintf(stderr, "Invalid PID: %s\n", arg);
149 			argp_usage(state);
150 		}
151 		env.pid = pid;
152 		break;
153 	case 't':
154 		errno = 0;
155 		pid = strtol(arg, NULL, 10);
156 		if (errno || pid <= 0) {
157 			fprintf(stderr, "Invalid TID: %s\n", arg);
158 			argp_usage(state);
159 		}
160 		env.tid = pid;
161 		break;
162 	case 'u':
163 		errno = 0;
164 		uid = strtol(arg, NULL, 10);
165 		if (errno || uid < 0 || uid >= INVALID_UID) {
166 			fprintf(stderr, "Invalid UID %s\n", arg);
167 			argp_usage(state);
168 		}
169 		env.uid = uid;
170 		break;
171 #ifdef USE_BLAZESYM
172 	case 'c':
173 		env.callers = true;
174 		break;
175 #endif
176 	case ARGP_KEY_ARG:
177 		if (pos_args++) {
178 			fprintf(stderr,
179 				"Unrecognized positional argument: %s\n", arg);
180 			argp_usage(state);
181 		}
182 		errno = 0;
183 		break;
184 	default:
185 		return ARGP_ERR_UNKNOWN;
186 	}
187 	return 0;
188 }
189 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)190 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
191 {
192 	if (level == LIBBPF_DEBUG && !env.verbose)
193 		return 0;
194 	return vfprintf(stderr, format, args);
195 }
196 
sig_int(int signo)197 static void sig_int(int signo)
198 {
199 	exiting = 1;
200 }
201 
handle_event(void * ctx,int cpu,void * data,__u32 data_sz)202 void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
203 {
204 	const struct event *e = data;
205 	struct tm *tm;
206 #ifdef USE_BLAZESYM
207 	sym_src_cfg cfgs[] = {
208 		{ .src_type = SRC_T_PROCESS, .params = { .process = { .pid = e->pid }}},
209 	};
210 	const blazesym_result *result = NULL;
211 	const blazesym_csym *sym;
212 	int i, j;
213 #endif
214 	int sps_cnt;
215 	char ts[32];
216 	time_t t;
217 	int fd, err;
218 
219 	/* name filtering is currently done in user space */
220 	if (env.name && strstr(e->comm, env.name) == NULL)
221 		return;
222 
223 	/* prepare fields */
224 	time(&t);
225 	tm = localtime(&t);
226 	strftime(ts, sizeof(ts), "%H:%M:%S", tm);
227 	if (e->ret >= 0) {
228 		fd = e->ret;
229 		err = 0;
230 	} else {
231 		fd = -1;
232 		err = - e->ret;
233 	}
234 
235 #ifdef USE_BLAZESYM
236 	if (env.callers)
237 		result = blazesym_symbolize(symbolizer, cfgs, 1, (const uint64_t *)&e->callers, 2);
238 #endif
239 
240 	/* print output */
241 	sps_cnt = 0;
242 	if (env.timestamp) {
243 		printf("%-8s ", ts);
244 		sps_cnt += 9;
245 	}
246 	if (env.print_uid) {
247 		printf("%-7d ", e->uid);
248 		sps_cnt += 8;
249 	}
250 	printf("%-6d %-16s %3d %3d ", e->pid, e->comm, fd, err);
251 	sps_cnt += 7 + 17 + 4 + 4;
252 	if (env.extended) {
253 		printf("%08o ", e->flags);
254 		sps_cnt += 9;
255 	}
256 	printf("%s\n", e->fname);
257 
258 #ifdef USE_BLAZESYM
259 	for (i = 0; result && i < result->size; i++) {
260 		if (result->entries[i].size == 0)
261 			continue;
262 		sym = &result->entries[i].syms[0];
263 
264 		for (j = 0; j < sps_cnt; j++)
265 			printf(" ");
266 		if (sym->line_no)
267 			printf("%s:%ld\n", sym->symbol, sym->line_no);
268 		else
269 			printf("%s\n", sym->symbol);
270 	}
271 
272 	blazesym_result_free(result);
273 #endif
274 }
275 
handle_lost_events(void * ctx,int cpu,__u64 lost_cnt)276 void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
277 {
278 	fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
279 }
280 
main(int argc,char ** argv)281 int main(int argc, char **argv)
282 {
283 	LIBBPF_OPTS(bpf_object_open_opts, open_opts);
284 	static const struct argp argp = {
285 		.options = opts,
286 		.parser = parse_arg,
287 		.doc = argp_program_doc,
288 	};
289 	struct perf_buffer *pb = NULL;
290 	struct opensnoop_bpf *obj;
291 	__u64 time_end = 0;
292 	int err;
293 
294 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
295 	if (err)
296 		return err;
297 
298 	libbpf_set_print(libbpf_print_fn);
299 
300 	err = ensure_core_btf(&open_opts);
301 	if (err) {
302 		fprintf(stderr, "failed to fetch necessary BTF for CO-RE: %s\n", strerror(-err));
303 		return 1;
304 	}
305 
306 	obj = opensnoop_bpf__open_opts(&open_opts);
307 	if (!obj) {
308 		fprintf(stderr, "failed to open BPF object\n");
309 		return 1;
310 	}
311 
312 	/* initialize global data (filtering options) */
313 	obj->rodata->targ_tgid = env.pid;
314 	obj->rodata->targ_pid = env.tid;
315 	obj->rodata->targ_uid = env.uid;
316 	obj->rodata->targ_failed = env.failed;
317 
318 	/* aarch64 and riscv64 don't have open syscall */
319 	if (!tracepoint_exists("syscalls", "sys_enter_open")) {
320 		bpf_program__set_autoload(obj->progs.tracepoint__syscalls__sys_enter_open, false);
321 		bpf_program__set_autoload(obj->progs.tracepoint__syscalls__sys_exit_open, false);
322 	}
323 
324 	err = opensnoop_bpf__load(obj);
325 	if (err) {
326 		fprintf(stderr, "failed to load BPF object: %d\n", err);
327 		goto cleanup;
328 	}
329 
330 	err = opensnoop_bpf__attach(obj);
331 	if (err) {
332 		fprintf(stderr, "failed to attach BPF programs\n");
333 		goto cleanup;
334 	}
335 
336 #ifdef USE_BLAZESYM
337 	if (env.callers)
338 		symbolizer = blazesym_new();
339 #endif
340 
341 	/* print headers */
342 	if (env.timestamp)
343 		printf("%-8s ", "TIME");
344 	if (env.print_uid)
345 		printf("%-7s ", "UID");
346 	printf("%-6s %-16s %3s %3s ", "PID", "COMM", "FD", "ERR");
347 	if (env.extended)
348 		printf("%-8s ", "FLAGS");
349 	printf("%s", "PATH");
350 #ifdef USE_BLAZESYM
351 	if (env.callers)
352 		printf("/CALLER");
353 #endif
354 	printf("\n");
355 
356 	/* setup event callbacks */
357 	pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
358 			      handle_event, handle_lost_events, NULL, NULL);
359 	if (!pb) {
360 		err = -errno;
361 		fprintf(stderr, "failed to open perf buffer: %d\n", err);
362 		goto cleanup;
363 	}
364 
365 	/* setup duration */
366 	if (env.duration)
367 		time_end = get_ktime_ns() + env.duration * NSEC_PER_SEC;
368 
369 	if (signal(SIGINT, sig_int) == SIG_ERR) {
370 		fprintf(stderr, "can't set signal handler: %s\n", strerror(errno));
371 		err = 1;
372 		goto cleanup;
373 	}
374 
375 	/* main: poll */
376 	while (!exiting) {
377 		err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS);
378 		if (err < 0 && err != -EINTR) {
379 			fprintf(stderr, "error polling perf buffer: %s\n", strerror(-err));
380 			goto cleanup;
381 		}
382 		if (env.duration && get_ktime_ns() > time_end)
383 			goto cleanup;
384 		/* reset err to return 0 if exiting */
385 		err = 0;
386 	}
387 
388 cleanup:
389 	perf_buffer__free(pb);
390 	opensnoop_bpf__destroy(obj);
391 	cleanup_core_btf(&open_opts);
392 #ifdef USE_BLAZESYM
393 	blazesym_free(symbolizer);
394 #endif
395 
396 	return err != 0;
397 }
398