• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "trace_helpers.h"
20 
21 /* Tune the buffer size and wakeup rate. These settings cope with roughly
22  * 50k opens/sec.
23  */
24 #define PERF_BUFFER_PAGES	64
25 #define PERF_BUFFER_TIME_MS	10
26 
27 /* Set the poll timeout when no events occur. This can affect -d accuracy. */
28 #define PERF_POLL_TIMEOUT_MS	100
29 
30 #define NSEC_PER_SEC		1000000000ULL
31 
32 static volatile sig_atomic_t exiting = 0;
33 
34 static struct env {
35 	pid_t pid;
36 	pid_t tid;
37 	uid_t uid;
38 	int duration;
39 	bool verbose;
40 	bool timestamp;
41 	bool print_uid;
42 	bool extended;
43 	bool failed;
44 	char *name;
45 } env = {
46 	.uid = INVALID_UID
47 };
48 
49 const char *argp_program_version = "opensnoop 0.1";
50 const char *argp_program_bug_address =
51 	"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
52 const char argp_program_doc[] =
53 "Trace open family syscalls\n"
54 "\n"
55 "USAGE: opensnoop [-h] [-T] [-U] [-x] [-p PID] [-t TID] [-u UID] [-d DURATION]\n"
56 "                 [-n NAME] [-e]\n"
57 "\n"
58 "EXAMPLES:\n"
59 "    ./opensnoop           # trace all open() syscalls\n"
60 "    ./opensnoop -T        # include timestamps\n"
61 "    ./opensnoop -U        # include UID\n"
62 "    ./opensnoop -x        # only show failed opens\n"
63 "    ./opensnoop -p 181    # only trace PID 181\n"
64 "    ./opensnoop -t 123    # only trace TID 123\n"
65 "    ./opensnoop -u 1000   # only trace UID 1000\n"
66 "    ./opensnoop -d 10     # trace for 10 seconds only\n"
67 "    ./opensnoop -n main   # only print process names containing \"main\"\n"
68 "    ./opensnoop -e        # show extended fields\n";
69 
70 static const struct argp_option opts[] = {
71 	{ "duration", 'd', "DURATION", 0, "Duration to trace"},
72 	{ "extended-fields", 'e', NULL, 0, "Print extended fields"},
73 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help"},
74 	{ "name", 'n', "NAME", 0, "Trace process names containing this"},
75 	{ "pid", 'p', "PID", 0, "Process ID to trace"},
76 	{ "tid", 't', "TID", 0, "Thread ID to trace"},
77 	{ "timestamp", 'T', NULL, 0, "Print timestamp"},
78 	{ "uid", 'u', "UID", 0, "User ID to trace"},
79 	{ "print-uid", 'U', NULL, 0, "Print UID"},
80 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
81 	{ "failed", 'x', NULL, 0, "Failed opens only"},
82 	{},
83 };
84 
parse_arg(int key,char * arg,struct argp_state * state)85 static error_t parse_arg(int key, char *arg, struct argp_state *state)
86 {
87 	static int pos_args;
88 	long int pid, uid, duration;
89 
90 	switch (key) {
91 	case 'e':
92 		env.extended = true;
93 		break;
94 	case 'h':
95 		argp_usage(state);
96 		break;
97 	case 'T':
98 		env.timestamp = true;
99 		break;
100 	case 'U':
101 		env.print_uid = true;
102 		break;
103 	case 'v':
104 		env.verbose = true;
105 		break;
106 	case 'x':
107 		env.failed = true;
108 		break;
109 	case 'd':
110 		errno = 0;
111 		duration = strtol(arg, NULL, 10);
112 		if (errno || duration <= 0) {
113 			fprintf(stderr, "Invalid duration: %s\n", arg);
114 			argp_usage(state);
115 		}
116 		env.duration = duration;
117 		break;
118 	case 'n':
119 		errno = 0;
120 		env.name = arg;
121 		break;
122 	case 'p':
123 		errno = 0;
124 		pid = strtol(arg, NULL, 10);
125 		if (errno || pid <= 0) {
126 			fprintf(stderr, "Invalid PID: %s\n", arg);
127 			argp_usage(state);
128 		}
129 		env.pid = pid;
130 		break;
131 	case 't':
132 		errno = 0;
133 		pid = strtol(arg, NULL, 10);
134 		if (errno || pid <= 0) {
135 			fprintf(stderr, "Invalid TID: %s\n", arg);
136 			argp_usage(state);
137 		}
138 		env.tid = pid;
139 		break;
140 	case 'u':
141 		errno = 0;
142 		uid = strtol(arg, NULL, 10);
143 		if (errno || uid < 0 || uid >= INVALID_UID) {
144 			fprintf(stderr, "Invalid UID %s\n", arg);
145 			argp_usage(state);
146 		}
147 		env.uid = uid;
148 		break;
149 	case ARGP_KEY_ARG:
150 		if (pos_args++) {
151 			fprintf(stderr,
152 				"Unrecognized positional argument: %s\n", arg);
153 			argp_usage(state);
154 		}
155 		errno = 0;
156 		break;
157 	default:
158 		return ARGP_ERR_UNKNOWN;
159 	}
160 	return 0;
161 }
162 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)163 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
164 {
165 	if (level == LIBBPF_DEBUG && !env.verbose)
166 		return 0;
167 	return vfprintf(stderr, format, args);
168 }
169 
sig_int(int signo)170 static void sig_int(int signo)
171 {
172 	exiting = 1;
173 }
174 
handle_event(void * ctx,int cpu,void * data,__u32 data_sz)175 void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
176 {
177 	const struct event *e = data;
178 	struct tm *tm;
179 	char ts[32];
180 	time_t t;
181 	int fd, err;
182 
183 	/* name filtering is currently done in user space */
184 	if (env.name && strstr(e->comm, env.name) == NULL)
185 		return;
186 
187 	/* prepare fields */
188 	time(&t);
189 	tm = localtime(&t);
190 	strftime(ts, sizeof(ts), "%H:%M:%S", tm);
191 	if (e->ret >= 0) {
192 		fd = e->ret;
193 		err = 0;
194 	} else {
195 		fd = -1;
196 		err = - e->ret;
197 	}
198 
199 	/* print output */
200 	if (env.timestamp)
201 		printf("%-8s ", ts);
202 	if (env.print_uid)
203 		printf("%-6d ", e->uid);
204 	printf("%-6d %-16s %3d %3d ", e->pid, e->comm, fd, err);
205 	if (env.extended)
206 		printf("%08o ", e->flags);
207 	printf("%s\n", e->fname);
208 }
209 
handle_lost_events(void * ctx,int cpu,__u64 lost_cnt)210 void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
211 {
212 	fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu);
213 }
214 
main(int argc,char ** argv)215 int main(int argc, char **argv)
216 {
217 	static const struct argp argp = {
218 		.options = opts,
219 		.parser = parse_arg,
220 		.doc = argp_program_doc,
221 	};
222 	struct perf_buffer *pb = NULL;
223 	struct opensnoop_bpf *obj;
224 	__u64 time_end = 0;
225 	int err;
226 
227 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
228 	if (err)
229 		return err;
230 
231 	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
232 	libbpf_set_print(libbpf_print_fn);
233 
234 	obj = opensnoop_bpf__open();
235 	if (!obj) {
236 		fprintf(stderr, "failed to open BPF object\n");
237 		return 1;
238 	}
239 
240 	/* initialize global data (filtering options) */
241 	obj->rodata->targ_tgid = env.pid;
242 	obj->rodata->targ_pid = env.tid;
243 	obj->rodata->targ_uid = env.uid;
244 	obj->rodata->targ_failed = env.failed;
245 
246 #ifdef __aarch64__
247 	/* aarch64 has no open syscall, only openat variants.
248 	 * Disable associated tracepoints that do not exist. See #3344.
249 	 */
250 	bpf_program__set_autoload(
251 		obj->progs.tracepoint__syscalls__sys_enter_open, false);
252 	bpf_program__set_autoload(
253 		obj->progs.tracepoint__syscalls__sys_exit_open, false);
254 #endif
255 
256 	err = opensnoop_bpf__load(obj);
257 	if (err) {
258 		fprintf(stderr, "failed to load BPF object: %d\n", err);
259 		goto cleanup;
260 	}
261 
262 	err = opensnoop_bpf__attach(obj);
263 	if (err) {
264 		fprintf(stderr, "failed to attach BPF programs\n");
265 		goto cleanup;
266 	}
267 
268 	/* print headers */
269 	if (env.timestamp)
270 		printf("%-8s ", "TIME");
271 	if (env.print_uid)
272 		printf("%-6s ", "UID");
273 	printf("%-6s %-16s %3s %3s ", "PID", "COMM", "FD", "ERR");
274 	if (env.extended)
275 		printf("%-8s ", "FLAGS");
276 	printf("%s\n", "PATH");
277 
278 	/* setup event callbacks */
279 	pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
280 			      handle_event, handle_lost_events, NULL, NULL);
281 	if (!pb) {
282 		err = -errno;
283 		fprintf(stderr, "failed to open perf buffer: %d\n", err);
284 		goto cleanup;
285 	}
286 
287 	/* setup duration */
288 	if (env.duration)
289 		time_end = get_ktime_ns() + env.duration * NSEC_PER_SEC;
290 
291 	if (signal(SIGINT, sig_int) == SIG_ERR) {
292 		fprintf(stderr, "can't set signal handler: %s\n", strerror(errno));
293 		err = 1;
294 		goto cleanup;
295 	}
296 
297 	/* main: poll */
298 	while (!exiting) {
299 		err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS);
300 		if (err < 0 && err != -EINTR) {
301 			fprintf(stderr, "error polling perf buffer: %s\n", strerror(-err));
302 			goto cleanup;
303 		}
304 		if (env.duration && get_ktime_ns() > time_end)
305 			goto cleanup;
306 		/* reset err to return 0 if exiting */
307 		err = 0;
308 	}
309 
310 cleanup:
311 	perf_buffer__free(pb);
312 	opensnoop_bpf__destroy(obj);
313 
314 	return err != 0;
315 }
316