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