blob: 9bd5bb312081b3dbd9ef6d880e53c5f0b6a3a702 [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
olsajirib5114222018-11-16 23:23:37 +01002# @lint-avoid-python-3-compatibility-imports
3#
4# sofdsnoop traces file descriptors passed via socket
5# For Linux, uses BCC, eBPF. Embedded C.
6#
7# USAGE: sofdsnoop
8#
9# Copyright (c) 2018 Jiri Olsa.
10# Licensed under the Apache License, Version 2.0 (the "License")
11#
12# 30-Jul-2018 Jiri Olsa Created this.
13
14from __future__ import print_function
15from bcc import ArgString, BPF
16import os
17import argparse
olsajirib5114222018-11-16 23:23:37 +010018from datetime import datetime, timedelta
19
20# arguments
21examples = """examples:
Jerome Marchand33c9c572019-07-29 15:57:03 +020022 ./sofdsnoop # trace passed file descriptors
olsajirib5114222018-11-16 23:23:37 +010023 ./sofdsnoop -T # include timestamps
24 ./sofdsnoop -p 181 # only trace PID 181
25 ./sofdsnoop -t 123 # only trace TID 123
26 ./sofdsnoop -d 10 # trace for 10 seconds only
27 ./sofdsnoop -n main # only print process names containing "main"
28
29"""
30parser = argparse.ArgumentParser(
31 description="Trace file descriptors passed via socket",
32 formatter_class=argparse.RawDescriptionHelpFormatter,
33 epilog=examples)
34parser.add_argument("-T", "--timestamp", action="store_true",
35 help="include timestamp on output")
36parser.add_argument("-p", "--pid",
37 help="trace this PID only")
38parser.add_argument("-t", "--tid",
39 help="trace this TID only")
40parser.add_argument("-n", "--name",
41 type=ArgString,
42 help="only print process names containing this name")
43parser.add_argument("-d", "--duration",
44 help="total duration of trace in seconds")
45args = parser.parse_args()
46debug = 0
47
48ACTION_SEND=0
49ACTION_RECV=1
50MAX_FD=10
51
52if args.duration:
53 args.duration = timedelta(seconds=int(args.duration))
54
55# define BPF program
56bpf_text = """
57#include <uapi/linux/ptrace.h>
58#include <uapi/linux/limits.h>
59#include <linux/sched.h>
60#include <linux/socket.h>
61#include <net/sock.h>
62
63#define MAX_FD 10
64#define ACTION_SEND 0
65#define ACTION_RECV 1
66
67struct val_t {
68 u64 id;
69 u64 ts;
70 int action;
71 int sock_fd;
72 int fd_cnt;
73 int fd[MAX_FD];
74 char comm[TASK_COMM_LEN];
75};
76
77BPF_HASH(detach_ptr, u64, struct cmsghdr *);
78BPF_HASH(sock_fd, u64, int);
79BPF_PERF_OUTPUT(events);
80
81static void set_fd(int fd)
82{
83 u64 id = bpf_get_current_pid_tgid();
84
85 sock_fd.update(&id, &fd);
86}
87
88static int get_fd(void)
89{
90 u64 id = bpf_get_current_pid_tgid();
91 int *fd;
92
93 fd = sock_fd.lookup(&id);
94 return fd ? *fd : -1;
95}
96
97static void put_fd(void)
98{
99 u64 id = bpf_get_current_pid_tgid();
100
101 sock_fd.delete(&id);
102}
103
104static int sent_1(struct pt_regs *ctx, struct val_t *val, int num, void *data)
105{
106 val->fd_cnt = min(num, MAX_FD);
107
Sumanth Korikkar7f6066d2020-05-20 10:49:56 -0500108 if (bpf_probe_read_kernel(&val->fd[0], MAX_FD * sizeof(int), data))
olsajirib5114222018-11-16 23:23:37 +0100109 return -1;
110
111 events.perf_submit(ctx, val, sizeof(*val));
112 return 0;
113}
114
115#define SEND_1 \
116 if (sent_1(ctx, &val, num, (void *) data)) \
117 return 0; \
118 \
119 num -= MAX_FD; \
120 if (num < 0) \
121 return 0; \
122 \
123 data += MAX_FD;
124
125#define SEND_2 SEND_1 SEND_1
126#define SEND_4 SEND_2 SEND_2
127#define SEND_8 SEND_4 SEND_4
128#define SEND_260 SEND_8 SEND_8 SEND_8 SEND_2
129
130static int send(struct pt_regs *ctx, struct cmsghdr *cmsg, int action)
131{
132 struct val_t val = { 0 };
133 int *data, num, fd;
134 u64 tsp = bpf_ktime_get_ns();
135
136 data = (void *) ((char *) cmsg + sizeof(struct cmsghdr));
137 num = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
138
139 val.id = bpf_get_current_pid_tgid();
140 val.action = action;
141 val.sock_fd = get_fd();
142 val.ts = tsp / 1000;
143
144 if (bpf_get_current_comm(&val.comm, sizeof(val.comm)) != 0)
145 return 0;
146
147 SEND_260
148 return 0;
149}
150
151static bool allow_pid(u64 id)
152{
153 u32 pid = id >> 32; // PID is higher part
154 u32 tid = id; // Cast and get the lower part
155
156 FILTER
157
158 return 1;
159}
160
161int trace_scm_send_entry(struct pt_regs *ctx, struct socket *sock, struct msghdr *hdr)
162{
163 struct cmsghdr *cmsg = NULL;
164
165 if (!allow_pid(bpf_get_current_pid_tgid()))
166 return 0;
167
168 if (hdr->msg_controllen >= sizeof(struct cmsghdr))
169 cmsg = hdr->msg_control;
170
171 if (!cmsg || (cmsg->cmsg_type != SCM_RIGHTS))
172 return 0;
173
174 return send(ctx, cmsg, ACTION_SEND);
175};
176
177int trace_scm_detach_fds_entry(struct pt_regs *ctx, struct msghdr *hdr)
178{
179 struct cmsghdr *cmsg = NULL;
180 u64 id = bpf_get_current_pid_tgid();
181
182 if (!allow_pid(id))
183 return 0;
184
185 if (hdr->msg_controllen >= sizeof(struct cmsghdr))
186 cmsg = hdr->msg_control;
187
188 if (!cmsg)
189 return 0;
190
191 detach_ptr.update(&id, &cmsg);
192 return 0;
193};
194
195int trace_scm_detach_fds_return(struct pt_regs *ctx)
196{
197 struct cmsghdr **cmsgp;
198 u64 id = bpf_get_current_pid_tgid();
199
200 if (!allow_pid(id))
201 return 0;
202
203 cmsgp = detach_ptr.lookup(&id);
204
205 if (!cmsgp)
206 return 0;
207
208 return send(ctx, *cmsgp, ACTION_RECV);
209}
210
211int syscall__sendmsg(struct pt_regs *ctx, u64 fd, u64 msg, u64 flags)
212{
213 struct pt_regs p;
214
215 if (!allow_pid(bpf_get_current_pid_tgid()))
216 return 0;
217
218 set_fd(fd);
219 return 0;
220}
221
222int trace_sendmsg_return(struct pt_regs *ctx)
223{
224 if (!allow_pid(bpf_get_current_pid_tgid()))
225 return 0;
226
227 put_fd();
228 return 0;
229}
230
231int syscall__recvmsg(struct pt_regs *ctx, u64 fd, u64 msg, u64 flags)
232{
233 struct pt_regs p;
234
235 if (!allow_pid(bpf_get_current_pid_tgid()))
236 return 0;
237
238 fd = fd;
239
240 set_fd(fd);
241 return 0;
242}
243
244int trace_recvmsg_return(struct pt_regs *ctx)
245{
246 if (!allow_pid(bpf_get_current_pid_tgid()))
247 return 0;
248
249 put_fd();
250 return 0;
251}
252
253"""
254
255if args.tid: # TID trumps PID
256 bpf_text = bpf_text.replace('FILTER',
257 'if (tid != %s) { return 0; }' % args.tid)
258elif args.pid:
259 bpf_text = bpf_text.replace('FILTER',
260 'if (pid != %s) { return 0; }' % args.pid)
261else:
262 bpf_text = bpf_text.replace('FILTER', '')
263
264# initialize BPF
265b = BPF(text=bpf_text)
266
267syscall_fnname = b.get_syscall_fnname("sendmsg")
268if BPF.ksymname(syscall_fnname) != -1:
269 b.attach_kprobe(event=syscall_fnname, fn_name="syscall__sendmsg")
270 b.attach_kretprobe(event=syscall_fnname, fn_name="trace_sendmsg_return")
271
272syscall_fnname = b.get_syscall_fnname("recvmsg")
273if BPF.ksymname(syscall_fnname) != -1:
274 b.attach_kprobe(event=syscall_fnname, fn_name="syscall__recvmsg")
275 b.attach_kretprobe(event=syscall_fnname, fn_name="trace_recvmsg_return")
276
277b.attach_kprobe(event="__scm_send", fn_name="trace_scm_send_entry")
278b.attach_kprobe(event="scm_detach_fds", fn_name="trace_scm_detach_fds_entry")
279b.attach_kretprobe(event="scm_detach_fds", fn_name="trace_scm_detach_fds_return")
280
olsajirib5114222018-11-16 23:23:37 +0100281initial_ts = 0
282
olsajirib5114222018-11-16 23:23:37 +0100283# header
284if args.timestamp:
285 print("%-14s" % ("TIME(s)"), end="")
286print("%-6s %-6s %-16s %-25s %-5s %s" %
287 ("ACTION", "TID", "COMM", "SOCKET", "FD", "NAME"))
288
289def get_file(pid, fd):
290 proc = "/proc/%d/fd/%d" % (pid, fd)
291 try:
292 return os.readlink(proc)
293 except OSError as err:
294 return "N/A"
295
296# process event
297def print_event(cpu, data, size):
Xiaozhou Liu51d62d32019-02-15 13:03:05 +0800298 event = b["events"].event(data)
olsajirib5114222018-11-16 23:23:37 +0100299 tid = event.id & 0xffffffff;
300
301 cnt = min(MAX_FD, event.fd_cnt);
302
303 if args.name and bytes(args.name) not in event.comm:
304 return
305
306 for i in range(0, cnt):
307 global initial_ts
308
309 if not initial_ts:
310 initial_ts = event.ts
311
312 if args.timestamp:
313 delta = event.ts - initial_ts
314 print("%-14.9f" % (float(delta) / 1000000), end="")
315
316 print("%-6s %-6d %-16s " %
317 ("SEND" if event.action == ACTION_SEND else "RECV",
318 tid, event.comm.decode()), end = '')
319
320 sock = "%d:%s" % (event.sock_fd, get_file(tid, event.sock_fd))
321 print("%-25s " % sock, end = '')
322
323 fd = event.fd[i]
324 fd_file = get_file(tid, fd) if event.action == ACTION_SEND else ""
325 print("%-5d %s" % (fd, fd_file))
326
327# loop with callback to print_event
328b["events"].open_perf_buffer(print_event, page_cnt=64)
329start_time = datetime.now()
330while not args.duration or datetime.now() - start_time < args.duration:
Prashant Bholef2e063c2019-01-11 15:41:54 +0900331 try:
332 b.perf_buffer_poll(timeout=1000)
333 except KeyboardInterrupt:
334 exit()