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