xref: /aosp_15_r20/external/bcc/tools/ttysnoop.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python
2*387f9dfdSAndroid Build Coastguard Worker# @lint-avoid-python-3-compatibility-imports
3*387f9dfdSAndroid Build Coastguard Worker#
4*387f9dfdSAndroid Build Coastguard Worker# ttysnoop   Watch live output from a tty or pts device.
5*387f9dfdSAndroid Build Coastguard Worker#            For Linux, uses BCC, eBPF. Embedded C.
6*387f9dfdSAndroid Build Coastguard Worker#
7*387f9dfdSAndroid Build Coastguard Worker# Due to a limited buffer size (see BUFSIZE), some commands (eg, a vim
8*387f9dfdSAndroid Build Coastguard Worker# session) are likely to be printed a little messed up.
9*387f9dfdSAndroid Build Coastguard Worker#
10*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) 2016 Brendan Gregg.
11*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License")
12*387f9dfdSAndroid Build Coastguard Worker#
13*387f9dfdSAndroid Build Coastguard Worker# Idea: from ttywatcher.
14*387f9dfdSAndroid Build Coastguard Worker#
15*387f9dfdSAndroid Build Coastguard Worker# 15-Oct-2016   Brendan Gregg   Created this.
16*387f9dfdSAndroid Build Coastguard Worker# 13-Dec-2022   Rong Tao        Detect whether kfunc is supported.
17*387f9dfdSAndroid Build Coastguard Worker# 07-Jan-2023   Rong Tao        Support ITER_UBUF(CO-RE way)
18*387f9dfdSAndroid Build Coastguard Worker
19*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function
20*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF
21*387f9dfdSAndroid Build Coastguard Workerfrom subprocess import call
22*387f9dfdSAndroid Build Coastguard Workerimport argparse
23*387f9dfdSAndroid Build Coastguard Workerfrom sys import argv
24*387f9dfdSAndroid Build Coastguard Workerimport sys
25*387f9dfdSAndroid Build Coastguard Workerfrom os import stat
26*387f9dfdSAndroid Build Coastguard Worker
27*387f9dfdSAndroid Build Coastguard Workerdef usage():
28*387f9dfdSAndroid Build Coastguard Worker    print("USAGE: %s [-Ch] {PTS | /dev/ttydev}  # try -h for help" % argv[0])
29*387f9dfdSAndroid Build Coastguard Worker    exit()
30*387f9dfdSAndroid Build Coastguard Worker
31*387f9dfdSAndroid Build Coastguard Worker# arguments
32*387f9dfdSAndroid Build Coastguard Workerexamples = """examples:
33*387f9dfdSAndroid Build Coastguard Worker    ./ttysnoop /dev/pts/2          # snoop output from /dev/pts/2
34*387f9dfdSAndroid Build Coastguard Worker    ./ttysnoop 2                   # snoop output from /dev/pts/2 (shortcut)
35*387f9dfdSAndroid Build Coastguard Worker    ./ttysnoop /dev/console        # snoop output from the system console
36*387f9dfdSAndroid Build Coastguard Worker    ./ttysnoop /dev/tty0           # snoop output from /dev/tty0
37*387f9dfdSAndroid Build Coastguard Worker    ./ttysnoop /dev/pts/2 -s 1024  # snoop output from /dev/pts/2 with data size 1024
38*387f9dfdSAndroid Build Coastguard Worker    ./ttysnoop /dev/pts/2 -c 2     # snoop output from /dev/pts/2 with 2 checks for 256 bytes of data in buffer
39*387f9dfdSAndroid Build Coastguard Worker                                     (potentially retrieving 512 bytes)
40*387f9dfdSAndroid Build Coastguard Worker"""
41*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(
42*387f9dfdSAndroid Build Coastguard Worker    description="Snoop output from a pts or tty device, eg, a shell",
43*387f9dfdSAndroid Build Coastguard Worker    formatter_class=argparse.RawDescriptionHelpFormatter,
44*387f9dfdSAndroid Build Coastguard Worker    epilog=examples)
45*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-C", "--noclear", action="store_true",
46*387f9dfdSAndroid Build Coastguard Worker    help="don't clear the screen")
47*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("device", default="-1",
48*387f9dfdSAndroid Build Coastguard Worker    help="path to a tty device (eg, /dev/tty0) or pts number")
49*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-s", "--datasize", default="256",
50*387f9dfdSAndroid Build Coastguard Worker    help="size of the transmitting buffer (default 256)")
51*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-c", "--datacount", default="16",
52*387f9dfdSAndroid Build Coastguard Worker    help="number of times we check for 'data-size' data (default 16)")
53*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true",
54*387f9dfdSAndroid Build Coastguard Worker    help=argparse.SUPPRESS)
55*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args()
56*387f9dfdSAndroid Build Coastguard Workerdebug = 0
57*387f9dfdSAndroid Build Coastguard Worker
58*387f9dfdSAndroid Build Coastguard Workerif args.device == "-1":
59*387f9dfdSAndroid Build Coastguard Worker    usage()
60*387f9dfdSAndroid Build Coastguard Worker
61*387f9dfdSAndroid Build Coastguard Workerpath = args.device
62*387f9dfdSAndroid Build Coastguard Workerif path.find('/') != 0:
63*387f9dfdSAndroid Build Coastguard Worker    path = "/dev/pts/" + path
64*387f9dfdSAndroid Build Coastguard Workertry:
65*387f9dfdSAndroid Build Coastguard Worker    pi = stat(path)
66*387f9dfdSAndroid Build Coastguard Workerexcept:
67*387f9dfdSAndroid Build Coastguard Worker    print("Unable to read device %s. Exiting." % path)
68*387f9dfdSAndroid Build Coastguard Worker    exit()
69*387f9dfdSAndroid Build Coastguard Worker
70*387f9dfdSAndroid Build Coastguard Worker# define BPF program
71*387f9dfdSAndroid Build Coastguard Workerbpf_text = """
72*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h>
73*387f9dfdSAndroid Build Coastguard Worker#include <linux/fs.h>
74*387f9dfdSAndroid Build Coastguard Worker#include <linux/uio.h>
75*387f9dfdSAndroid Build Coastguard Worker
76*387f9dfdSAndroid Build Coastguard Worker#define BUFSIZE USER_DATASIZE
77*387f9dfdSAndroid Build Coastguard Workerstruct data_t {
78*387f9dfdSAndroid Build Coastguard Worker    int count;
79*387f9dfdSAndroid Build Coastguard Worker    char buf[BUFSIZE];
80*387f9dfdSAndroid Build Coastguard Worker};
81*387f9dfdSAndroid Build Coastguard Worker
82*387f9dfdSAndroid Build Coastguard WorkerBPF_ARRAY(data_map, struct data_t, 1);
83*387f9dfdSAndroid Build Coastguard WorkerPERF_TABLE
84*387f9dfdSAndroid Build Coastguard Worker
85*387f9dfdSAndroid Build Coastguard Workerstatic int do_tty_write(void *ctx, const char __user *buf, size_t count)
86*387f9dfdSAndroid Build Coastguard Worker{
87*387f9dfdSAndroid Build Coastguard Worker    int zero = 0, i;
88*387f9dfdSAndroid Build Coastguard Worker    struct data_t *data;
89*387f9dfdSAndroid Build Coastguard Worker
90*387f9dfdSAndroid Build Coastguard Worker/* We can't read data to map data before v4.11 */
91*387f9dfdSAndroid Build Coastguard Worker#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
92*387f9dfdSAndroid Build Coastguard Worker    struct data_t _data = {};
93*387f9dfdSAndroid Build Coastguard Worker
94*387f9dfdSAndroid Build Coastguard Worker    data = &_data;
95*387f9dfdSAndroid Build Coastguard Worker#else
96*387f9dfdSAndroid Build Coastguard Worker    data = data_map.lookup(&zero);
97*387f9dfdSAndroid Build Coastguard Worker    if (!data)
98*387f9dfdSAndroid Build Coastguard Worker        return 0;
99*387f9dfdSAndroid Build Coastguard Worker#endif
100*387f9dfdSAndroid Build Coastguard Worker
101*387f9dfdSAndroid Build Coastguard Worker    #pragma unroll
102*387f9dfdSAndroid Build Coastguard Worker    for (i = 0; i < USER_DATACOUNT; i++) {
103*387f9dfdSAndroid Build Coastguard Worker        // bpf_probe_read_user() can only use a fixed size, so truncate to count
104*387f9dfdSAndroid Build Coastguard Worker        // in user space:
105*387f9dfdSAndroid Build Coastguard Worker        if (bpf_probe_read_user(&data->buf, BUFSIZE, (void *)buf))
106*387f9dfdSAndroid Build Coastguard Worker            return 0;
107*387f9dfdSAndroid Build Coastguard Worker        if (count > BUFSIZE)
108*387f9dfdSAndroid Build Coastguard Worker            data->count = BUFSIZE;
109*387f9dfdSAndroid Build Coastguard Worker        else
110*387f9dfdSAndroid Build Coastguard Worker            data->count = count;
111*387f9dfdSAndroid Build Coastguard Worker        PERF_OUTPUT_CTX
112*387f9dfdSAndroid Build Coastguard Worker        if (count < BUFSIZE)
113*387f9dfdSAndroid Build Coastguard Worker            return 0;
114*387f9dfdSAndroid Build Coastguard Worker        count -= BUFSIZE;
115*387f9dfdSAndroid Build Coastguard Worker        buf += BUFSIZE;
116*387f9dfdSAndroid Build Coastguard Worker    }
117*387f9dfdSAndroid Build Coastguard Worker
118*387f9dfdSAndroid Build Coastguard Worker    return 0;
119*387f9dfdSAndroid Build Coastguard Worker};
120*387f9dfdSAndroid Build Coastguard Worker
121*387f9dfdSAndroid Build Coastguard Worker/**
122*387f9dfdSAndroid Build Coastguard Worker * commit 9bb48c82aced (v5.11-rc4) tty: implement write_iter
123*387f9dfdSAndroid Build Coastguard Worker * changed arguments of tty_write function
124*387f9dfdSAndroid Build Coastguard Worker */
125*387f9dfdSAndroid Build Coastguard Worker#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 11)
126*387f9dfdSAndroid Build Coastguard Workerint kprobe__tty_write(struct pt_regs *ctx, struct file *file,
127*387f9dfdSAndroid Build Coastguard Worker    const char __user *buf, size_t count)
128*387f9dfdSAndroid Build Coastguard Worker{
129*387f9dfdSAndroid Build Coastguard Worker    if (file->f_inode->i_ino != PTS)
130*387f9dfdSAndroid Build Coastguard Worker        return 0;
131*387f9dfdSAndroid Build Coastguard Worker
132*387f9dfdSAndroid Build Coastguard Worker    return do_tty_write(ctx, buf, count);
133*387f9dfdSAndroid Build Coastguard Worker}
134*387f9dfdSAndroid Build Coastguard Worker#else
135*387f9dfdSAndroid Build Coastguard WorkerPROBE_TTY_WRITE
136*387f9dfdSAndroid Build Coastguard Worker{
137*387f9dfdSAndroid Build Coastguard Worker    const char __user *buf = NULL;
138*387f9dfdSAndroid Build Coastguard Worker    const struct kvec *kvec;
139*387f9dfdSAndroid Build Coastguard Worker    size_t count = 0;
140*387f9dfdSAndroid Build Coastguard Worker
141*387f9dfdSAndroid Build Coastguard Worker    if (iocb->ki_filp->f_inode->i_ino != PTS)
142*387f9dfdSAndroid Build Coastguard Worker        return 0;
143*387f9dfdSAndroid Build Coastguard Worker/**
144*387f9dfdSAndroid Build Coastguard Worker * commit 8cd54c1c8480 iov_iter: separate direction from flavour
145*387f9dfdSAndroid Build Coastguard Worker * `type` is represented by iter_type and data_source seperately
146*387f9dfdSAndroid Build Coastguard Worker */
147*387f9dfdSAndroid Build Coastguard Worker#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
148*387f9dfdSAndroid Build Coastguard Worker    if (from->type != (ITER_IOVEC + WRITE))
149*387f9dfdSAndroid Build Coastguard Worker        return 0;
150*387f9dfdSAndroid Build Coastguard Worker#else
151*387f9dfdSAndroid Build Coastguard Worker    if (ADD_FILTER_ITER_UBUF from->iter_type != ITER_IOVEC)
152*387f9dfdSAndroid Build Coastguard Worker        return 0;
153*387f9dfdSAndroid Build Coastguard Worker    if (from->data_source != WRITE)
154*387f9dfdSAndroid Build Coastguard Worker        return 0;
155*387f9dfdSAndroid Build Coastguard Worker#endif
156*387f9dfdSAndroid Build Coastguard Worker
157*387f9dfdSAndroid Build Coastguard Worker    /* Support 'type' and 'iter_type' field name */
158*387f9dfdSAndroid Build Coastguard Worker    switch (from->IOV_ITER_TYPE_NAME) {
159*387f9dfdSAndroid Build Coastguard Worker    /**
160*387f9dfdSAndroid Build Coastguard Worker     * <  5.14.0: case ITER_IOVEC + WRITE
161*387f9dfdSAndroid Build Coastguard Worker     * >= 5.14.0: case ITER_IOVEC
162*387f9dfdSAndroid Build Coastguard Worker     */
163*387f9dfdSAndroid Build Coastguard Worker    case CASE_ITER_IOVEC_NAME:
164*387f9dfdSAndroid Build Coastguard Worker        kvec  = from->kvec;
165*387f9dfdSAndroid Build Coastguard Worker        buf   = kvec->iov_base;
166*387f9dfdSAndroid Build Coastguard Worker        count = kvec->iov_len;
167*387f9dfdSAndroid Build Coastguard Worker        break;
168*387f9dfdSAndroid Build Coastguard Worker    CASE_ITER_UBUF_TEXT
169*387f9dfdSAndroid Build Coastguard Worker    /* TODO: Support more type */
170*387f9dfdSAndroid Build Coastguard Worker    default:
171*387f9dfdSAndroid Build Coastguard Worker        break;
172*387f9dfdSAndroid Build Coastguard Worker    }
173*387f9dfdSAndroid Build Coastguard Worker    return do_tty_write(ctx, buf, count);
174*387f9dfdSAndroid Build Coastguard Worker}
175*387f9dfdSAndroid Build Coastguard Worker#endif
176*387f9dfdSAndroid Build Coastguard Worker"""
177*387f9dfdSAndroid Build Coastguard Worker
178*387f9dfdSAndroid Build Coastguard Workerprobe_tty_write_kfunc = """
179*387f9dfdSAndroid Build Coastguard WorkerKFUNC_PROBE(tty_write, struct kiocb *iocb, struct iov_iter *from)
180*387f9dfdSAndroid Build Coastguard Worker"""
181*387f9dfdSAndroid Build Coastguard Worker
182*387f9dfdSAndroid Build Coastguard Workerprobe_tty_write_kprobe = """
183*387f9dfdSAndroid Build Coastguard Workerint kprobe__tty_write(struct pt_regs *ctx, struct kiocb *iocb,
184*387f9dfdSAndroid Build Coastguard Worker    struct iov_iter *from)
185*387f9dfdSAndroid Build Coastguard Worker"""
186*387f9dfdSAndroid Build Coastguard Worker
187*387f9dfdSAndroid Build Coastguard Workeris_support_kfunc = BPF.support_kfunc()
188*387f9dfdSAndroid Build Coastguard Workerif is_support_kfunc:
189*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('PROBE_TTY_WRITE', probe_tty_write_kfunc)
190*387f9dfdSAndroid Build Coastguard Workerelse:
191*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('PROBE_TTY_WRITE', probe_tty_write_kprobe)
192*387f9dfdSAndroid Build Coastguard Worker
193*387f9dfdSAndroid Build Coastguard Workerif BPF.kernel_struct_has_field(b'iov_iter', b'iter_type') == 1:
194*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('IOV_ITER_TYPE_NAME', 'iter_type')
195*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('CASE_ITER_IOVEC_NAME', 'ITER_IOVEC')
196*387f9dfdSAndroid Build Coastguard Workerelse:
197*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('IOV_ITER_TYPE_NAME', 'type')
198*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('CASE_ITER_IOVEC_NAME', 'ITER_IOVEC + WRITE')
199*387f9dfdSAndroid Build Coastguard Worker
200*387f9dfdSAndroid Build Coastguard Workercase_iter_ubuf_text = """
201*387f9dfdSAndroid Build Coastguard Worker    case ITER_UBUF:
202*387f9dfdSAndroid Build Coastguard Worker        buf   = from->ubuf;
203*387f9dfdSAndroid Build Coastguard Worker        count = from->count;
204*387f9dfdSAndroid Build Coastguard Worker        break;
205*387f9dfdSAndroid Build Coastguard Worker"""
206*387f9dfdSAndroid Build Coastguard Worker
207*387f9dfdSAndroid Build Coastguard Workerif BPF.kernel_struct_has_field(b'iov_iter', b'ubuf') == 1:
208*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('CASE_ITER_UBUF_TEXT', case_iter_ubuf_text)
209*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('ADD_FILTER_ITER_UBUF', 'from->iter_type != ITER_UBUF &&')
210*387f9dfdSAndroid Build Coastguard Workerelse:
211*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('CASE_ITER_UBUF_TEXT', '')
212*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('ADD_FILTER_ITER_UBUF', '')
213*387f9dfdSAndroid Build Coastguard Worker
214*387f9dfdSAndroid Build Coastguard Workerif BPF.kernel_struct_has_field(b'bpf_ringbuf', b'waitq') == 1:
215*387f9dfdSAndroid Build Coastguard Worker    PERF_MODE = "USE_BPF_RING_BUF"
216*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('PERF_TABLE',
217*387f9dfdSAndroid Build Coastguard Worker                            'BPF_RINGBUF_OUTPUT(events, 64);')
218*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('PERF_OUTPUT_CTX',
219*387f9dfdSAndroid Build Coastguard Worker                            'events.ringbuf_output(data, sizeof(*data), 0);')
220*387f9dfdSAndroid Build Coastguard Workerelse:
221*387f9dfdSAndroid Build Coastguard Worker    PERF_MODE = "USE_BPF_PERF_BUF"
222*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('PERF_TABLE', 'BPF_PERF_OUTPUT(events);')
223*387f9dfdSAndroid Build Coastguard Worker    bpf_text = bpf_text.replace('PERF_OUTPUT_CTX',
224*387f9dfdSAndroid Build Coastguard Worker                            'events.perf_submit(ctx, data, sizeof(*data));')
225*387f9dfdSAndroid Build Coastguard Worker
226*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('PTS', str(pi.st_ino))
227*387f9dfdSAndroid Build Coastguard Workerif debug or args.ebpf:
228*387f9dfdSAndroid Build Coastguard Worker    print(bpf_text)
229*387f9dfdSAndroid Build Coastguard Worker    if args.ebpf:
230*387f9dfdSAndroid Build Coastguard Worker        exit()
231*387f9dfdSAndroid Build Coastguard Worker
232*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('USER_DATASIZE', '%s' % args.datasize)
233*387f9dfdSAndroid Build Coastguard Workerbpf_text = bpf_text.replace('USER_DATACOUNT', '%s' % args.datacount)
234*387f9dfdSAndroid Build Coastguard Worker
235*387f9dfdSAndroid Build Coastguard Worker# initialize BPF
236*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text)
237*387f9dfdSAndroid Build Coastguard Worker
238*387f9dfdSAndroid Build Coastguard Workerif not args.noclear:
239*387f9dfdSAndroid Build Coastguard Worker    call("clear")
240*387f9dfdSAndroid Build Coastguard Worker
241*387f9dfdSAndroid Build Coastguard Worker# process event
242*387f9dfdSAndroid Build Coastguard Workerdef print_event(cpu, data, size):
243*387f9dfdSAndroid Build Coastguard Worker    event = b["events"].event(data)
244*387f9dfdSAndroid Build Coastguard Worker    print("%s" % event.buf[0:event.count].decode('utf-8', 'replace'), end="")
245*387f9dfdSAndroid Build Coastguard Worker    sys.stdout.flush()
246*387f9dfdSAndroid Build Coastguard Worker
247*387f9dfdSAndroid Build Coastguard Worker# loop with callback to print_event
248*387f9dfdSAndroid Build Coastguard Workerif PERF_MODE == "USE_BPF_RING_BUF":
249*387f9dfdSAndroid Build Coastguard Worker    b["events"].open_ring_buffer(print_event)
250*387f9dfdSAndroid Build Coastguard Workerelse:
251*387f9dfdSAndroid Build Coastguard Worker    b["events"].open_perf_buffer(print_event, page_cnt=64)
252*387f9dfdSAndroid Build Coastguard Worker
253*387f9dfdSAndroid Build Coastguard Workerwhile 1:
254*387f9dfdSAndroid Build Coastguard Worker    try:
255*387f9dfdSAndroid Build Coastguard Worker        if PERF_MODE == "USE_BPF_RING_BUF":
256*387f9dfdSAndroid Build Coastguard Worker            b.ring_buffer_poll()
257*387f9dfdSAndroid Build Coastguard Worker        else:
258*387f9dfdSAndroid Build Coastguard Worker            b.perf_buffer_poll()
259*387f9dfdSAndroid Build Coastguard Worker    except KeyboardInterrupt:
260*387f9dfdSAndroid Build Coastguard Worker        exit()
261