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# filetop file reads and writes by process. 5*387f9dfdSAndroid Build Coastguard Worker# For Linux, uses BCC, eBPF. 6*387f9dfdSAndroid Build Coastguard Worker# 7*387f9dfdSAndroid Build Coastguard Worker# USAGE: filetop.py [-h] [-C] [-r MAXROWS] [interval] [count] 8*387f9dfdSAndroid Build Coastguard Worker# 9*387f9dfdSAndroid Build Coastguard Worker# This uses in-kernel eBPF maps to store per process summaries for efficiency. 10*387f9dfdSAndroid Build Coastguard Worker# 11*387f9dfdSAndroid Build Coastguard Worker# Copyright 2016 Netflix, Inc. 12*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License") 13*387f9dfdSAndroid Build Coastguard Worker# 14*387f9dfdSAndroid Build Coastguard Worker# 06-Feb-2016 Brendan Gregg Created this. 15*387f9dfdSAndroid Build Coastguard Worker 16*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function 17*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF 18*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep, strftime 19*387f9dfdSAndroid Build Coastguard Workerimport argparse 20*387f9dfdSAndroid Build Coastguard Workerfrom subprocess import call 21*387f9dfdSAndroid Build Coastguard Worker 22*387f9dfdSAndroid Build Coastguard Worker# arguments 23*387f9dfdSAndroid Build Coastguard Workerexamples = """examples: 24*387f9dfdSAndroid Build Coastguard Worker ./filetop # file I/O top, 1 second refresh 25*387f9dfdSAndroid Build Coastguard Worker ./filetop -C # don't clear the screen 26*387f9dfdSAndroid Build Coastguard Worker ./filetop -p 181 # PID 181 only 27*387f9dfdSAndroid Build Coastguard Worker ./filetop 5 # 5 second summaries 28*387f9dfdSAndroid Build Coastguard Worker ./filetop 5 10 # 5 second summaries, 10 times only 29*387f9dfdSAndroid Build Coastguard Worker""" 30*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser( 31*387f9dfdSAndroid Build Coastguard Worker description="File reads and writes by process", 32*387f9dfdSAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter, 33*387f9dfdSAndroid Build Coastguard Worker epilog=examples) 34*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-a", "--all-files", action="store_true", 35*387f9dfdSAndroid Build Coastguard Worker help="include non-regular file types (sockets, FIFOs, etc)") 36*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-C", "--noclear", action="store_true", 37*387f9dfdSAndroid Build Coastguard Worker help="don't clear the screen") 38*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-r", "--maxrows", default=20, 39*387f9dfdSAndroid Build Coastguard Worker help="maximum rows to print, default 20") 40*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-s", "--sort", default="all", 41*387f9dfdSAndroid Build Coastguard Worker choices=["all", "reads", "writes", "rbytes", "wbytes"], 42*387f9dfdSAndroid Build Coastguard Worker help="sort column, default all") 43*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-p", "--pid", type=int, metavar="PID", dest="tgid", 44*387f9dfdSAndroid Build Coastguard Worker help="trace this PID only") 45*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("interval", nargs="?", default=1, 46*387f9dfdSAndroid Build Coastguard Worker help="output interval, in seconds") 47*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("count", nargs="?", default=99999999, 48*387f9dfdSAndroid Build Coastguard Worker help="number of outputs") 49*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true", 50*387f9dfdSAndroid Build Coastguard Worker help=argparse.SUPPRESS) 51*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args() 52*387f9dfdSAndroid Build Coastguard Workerinterval = int(args.interval) 53*387f9dfdSAndroid Build Coastguard Workercountdown = int(args.count) 54*387f9dfdSAndroid Build Coastguard Workermaxrows = int(args.maxrows) 55*387f9dfdSAndroid Build Coastguard Workerclear = not int(args.noclear) 56*387f9dfdSAndroid Build Coastguard Workerdebug = 0 57*387f9dfdSAndroid Build Coastguard Worker 58*387f9dfdSAndroid Build Coastguard Worker# linux stats 59*387f9dfdSAndroid Build Coastguard Workerloadavg = "/proc/loadavg" 60*387f9dfdSAndroid Build Coastguard Worker 61*387f9dfdSAndroid Build Coastguard Worker# define BPF program 62*387f9dfdSAndroid Build Coastguard Workerbpf_text = """ 63*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h> 64*387f9dfdSAndroid Build Coastguard Worker#include <linux/blkdev.h> 65*387f9dfdSAndroid Build Coastguard Worker 66*387f9dfdSAndroid Build Coastguard Worker// the key for the output summary 67*387f9dfdSAndroid Build Coastguard Workerstruct info_t { 68*387f9dfdSAndroid Build Coastguard Worker unsigned long inode; 69*387f9dfdSAndroid Build Coastguard Worker dev_t dev; 70*387f9dfdSAndroid Build Coastguard Worker dev_t rdev; 71*387f9dfdSAndroid Build Coastguard Worker u32 pid; 72*387f9dfdSAndroid Build Coastguard Worker u32 name_len; 73*387f9dfdSAndroid Build Coastguard Worker char comm[TASK_COMM_LEN]; 74*387f9dfdSAndroid Build Coastguard Worker // de->d_name.name may point to de->d_iname so limit len accordingly 75*387f9dfdSAndroid Build Coastguard Worker char name[DNAME_INLINE_LEN]; 76*387f9dfdSAndroid Build Coastguard Worker char type; 77*387f9dfdSAndroid Build Coastguard Worker}; 78*387f9dfdSAndroid Build Coastguard Worker 79*387f9dfdSAndroid Build Coastguard Worker// the value of the output summary 80*387f9dfdSAndroid Build Coastguard Workerstruct val_t { 81*387f9dfdSAndroid Build Coastguard Worker u64 reads; 82*387f9dfdSAndroid Build Coastguard Worker u64 writes; 83*387f9dfdSAndroid Build Coastguard Worker u64 rbytes; 84*387f9dfdSAndroid Build Coastguard Worker u64 wbytes; 85*387f9dfdSAndroid Build Coastguard Worker}; 86*387f9dfdSAndroid Build Coastguard Worker 87*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(counts, struct info_t, struct val_t); 88*387f9dfdSAndroid Build Coastguard Worker 89*387f9dfdSAndroid Build Coastguard Workerstatic int do_entry(struct pt_regs *ctx, struct file *file, 90*387f9dfdSAndroid Build Coastguard Worker char __user *buf, size_t count, int is_read) 91*387f9dfdSAndroid Build Coastguard Worker{ 92*387f9dfdSAndroid Build Coastguard Worker u32 tgid = bpf_get_current_pid_tgid() >> 32; 93*387f9dfdSAndroid Build Coastguard Worker if (TGID_FILTER) 94*387f9dfdSAndroid Build Coastguard Worker return 0; 95*387f9dfdSAndroid Build Coastguard Worker 96*387f9dfdSAndroid Build Coastguard Worker u32 pid = bpf_get_current_pid_tgid(); 97*387f9dfdSAndroid Build Coastguard Worker 98*387f9dfdSAndroid Build Coastguard Worker // skip I/O lacking a filename 99*387f9dfdSAndroid Build Coastguard Worker struct dentry *de = file->f_path.dentry; 100*387f9dfdSAndroid Build Coastguard Worker int mode = file->f_inode->i_mode; 101*387f9dfdSAndroid Build Coastguard Worker struct qstr d_name = de->d_name; 102*387f9dfdSAndroid Build Coastguard Worker if (d_name.len == 0 || TYPE_FILTER) 103*387f9dfdSAndroid Build Coastguard Worker return 0; 104*387f9dfdSAndroid Build Coastguard Worker 105*387f9dfdSAndroid Build Coastguard Worker // store counts and sizes by pid & file 106*387f9dfdSAndroid Build Coastguard Worker struct info_t info = { 107*387f9dfdSAndroid Build Coastguard Worker .pid = pid, 108*387f9dfdSAndroid Build Coastguard Worker .inode = file->f_inode->i_ino, 109*387f9dfdSAndroid Build Coastguard Worker .dev = file->f_inode->i_sb->s_dev, 110*387f9dfdSAndroid Build Coastguard Worker .rdev = file->f_inode->i_rdev, 111*387f9dfdSAndroid Build Coastguard Worker }; 112*387f9dfdSAndroid Build Coastguard Worker bpf_get_current_comm(&info.comm, sizeof(info.comm)); 113*387f9dfdSAndroid Build Coastguard Worker info.name_len = d_name.len; 114*387f9dfdSAndroid Build Coastguard Worker bpf_probe_read_kernel(&info.name, sizeof(info.name), d_name.name); 115*387f9dfdSAndroid Build Coastguard Worker if (S_ISREG(mode)) { 116*387f9dfdSAndroid Build Coastguard Worker info.type = 'R'; 117*387f9dfdSAndroid Build Coastguard Worker } else if (S_ISSOCK(mode)) { 118*387f9dfdSAndroid Build Coastguard Worker info.type = 'S'; 119*387f9dfdSAndroid Build Coastguard Worker } else { 120*387f9dfdSAndroid Build Coastguard Worker info.type = 'O'; 121*387f9dfdSAndroid Build Coastguard Worker } 122*387f9dfdSAndroid Build Coastguard Worker 123*387f9dfdSAndroid Build Coastguard Worker struct val_t *valp, zero = {}; 124*387f9dfdSAndroid Build Coastguard Worker valp = counts.lookup_or_try_init(&info, &zero); 125*387f9dfdSAndroid Build Coastguard Worker if (valp) { 126*387f9dfdSAndroid Build Coastguard Worker if (is_read) { 127*387f9dfdSAndroid Build Coastguard Worker valp->reads++; 128*387f9dfdSAndroid Build Coastguard Worker valp->rbytes += count; 129*387f9dfdSAndroid Build Coastguard Worker } else { 130*387f9dfdSAndroid Build Coastguard Worker valp->writes++; 131*387f9dfdSAndroid Build Coastguard Worker valp->wbytes += count; 132*387f9dfdSAndroid Build Coastguard Worker } 133*387f9dfdSAndroid Build Coastguard Worker } 134*387f9dfdSAndroid Build Coastguard Worker 135*387f9dfdSAndroid Build Coastguard Worker return 0; 136*387f9dfdSAndroid Build Coastguard Worker} 137*387f9dfdSAndroid Build Coastguard Worker 138*387f9dfdSAndroid Build Coastguard Workerint trace_read_entry(struct pt_regs *ctx, struct file *file, 139*387f9dfdSAndroid Build Coastguard Worker char __user *buf, size_t count) 140*387f9dfdSAndroid Build Coastguard Worker{ 141*387f9dfdSAndroid Build Coastguard Worker return do_entry(ctx, file, buf, count, 1); 142*387f9dfdSAndroid Build Coastguard Worker} 143*387f9dfdSAndroid Build Coastguard Worker 144*387f9dfdSAndroid Build Coastguard Workerint trace_write_entry(struct pt_regs *ctx, struct file *file, 145*387f9dfdSAndroid Build Coastguard Worker char __user *buf, size_t count) 146*387f9dfdSAndroid Build Coastguard Worker{ 147*387f9dfdSAndroid Build Coastguard Worker return do_entry(ctx, file, buf, count, 0); 148*387f9dfdSAndroid Build Coastguard Worker} 149*387f9dfdSAndroid Build Coastguard Worker 150*387f9dfdSAndroid Build Coastguard Worker""" 151*387f9dfdSAndroid Build Coastguard Workerif args.tgid: 152*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('TGID_FILTER', 'tgid != %d' % args.tgid) 153*387f9dfdSAndroid Build Coastguard Workerelse: 154*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('TGID_FILTER', '0') 155*387f9dfdSAndroid Build Coastguard Workerif args.all_files: 156*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('TYPE_FILTER', '0') 157*387f9dfdSAndroid Build Coastguard Workerelse: 158*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('TYPE_FILTER', '!S_ISREG(mode)') 159*387f9dfdSAndroid Build Coastguard Worker 160*387f9dfdSAndroid Build Coastguard Workerif debug or args.ebpf: 161*387f9dfdSAndroid Build Coastguard Worker print(bpf_text) 162*387f9dfdSAndroid Build Coastguard Worker if args.ebpf: 163*387f9dfdSAndroid Build Coastguard Worker exit() 164*387f9dfdSAndroid Build Coastguard Worker 165*387f9dfdSAndroid Build Coastguard Worker# initialize BPF 166*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text) 167*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event="vfs_read", fn_name="trace_read_entry") 168*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event="vfs_write", fn_name="trace_write_entry") 169*387f9dfdSAndroid Build Coastguard Worker 170*387f9dfdSAndroid Build Coastguard WorkerDNAME_INLINE_LEN = 32 # linux/dcache.h 171*387f9dfdSAndroid Build Coastguard Worker 172*387f9dfdSAndroid Build Coastguard Workerprint('Tracing... Output every %d secs. Hit Ctrl-C to end' % interval) 173*387f9dfdSAndroid Build Coastguard Worker 174*387f9dfdSAndroid Build Coastguard Workerdef sort_fn(counts): 175*387f9dfdSAndroid Build Coastguard Worker if args.sort == "all": 176*387f9dfdSAndroid Build Coastguard Worker return (counts[1].rbytes + counts[1].wbytes + counts[1].reads + counts[1].writes) 177*387f9dfdSAndroid Build Coastguard Worker else: 178*387f9dfdSAndroid Build Coastguard Worker return getattr(counts[1], args.sort) 179*387f9dfdSAndroid Build Coastguard Worker 180*387f9dfdSAndroid Build Coastguard Worker# output 181*387f9dfdSAndroid Build Coastguard Workerexiting = 0 182*387f9dfdSAndroid Build Coastguard Workerwhile 1: 183*387f9dfdSAndroid Build Coastguard Worker try: 184*387f9dfdSAndroid Build Coastguard Worker sleep(interval) 185*387f9dfdSAndroid Build Coastguard Worker except KeyboardInterrupt: 186*387f9dfdSAndroid Build Coastguard Worker exiting = 1 187*387f9dfdSAndroid Build Coastguard Worker 188*387f9dfdSAndroid Build Coastguard Worker # header 189*387f9dfdSAndroid Build Coastguard Worker if clear: 190*387f9dfdSAndroid Build Coastguard Worker call("clear") 191*387f9dfdSAndroid Build Coastguard Worker else: 192*387f9dfdSAndroid Build Coastguard Worker print() 193*387f9dfdSAndroid Build Coastguard Worker with open(loadavg) as stats: 194*387f9dfdSAndroid Build Coastguard Worker print("%-8s loadavg: %s" % (strftime("%H:%M:%S"), stats.read())) 195*387f9dfdSAndroid Build Coastguard Worker print("%-7s %-16s %-6s %-6s %-7s %-7s %1s %s" % ("TID", "COMM", 196*387f9dfdSAndroid Build Coastguard Worker "READS", "WRITES", "R_Kb", "W_Kb", "T", "FILE")) 197*387f9dfdSAndroid Build Coastguard Worker 198*387f9dfdSAndroid Build Coastguard Worker # by-TID output 199*387f9dfdSAndroid Build Coastguard Worker counts = b.get_table("counts") 200*387f9dfdSAndroid Build Coastguard Worker line = 0 201*387f9dfdSAndroid Build Coastguard Worker for k, v in reversed(sorted(counts.items(), 202*387f9dfdSAndroid Build Coastguard Worker key=sort_fn)): 203*387f9dfdSAndroid Build Coastguard Worker name = k.name.decode('utf-8', 'replace') 204*387f9dfdSAndroid Build Coastguard Worker if k.name_len > DNAME_INLINE_LEN: 205*387f9dfdSAndroid Build Coastguard Worker name = name[:-3] + "..." 206*387f9dfdSAndroid Build Coastguard Worker 207*387f9dfdSAndroid Build Coastguard Worker # print line 208*387f9dfdSAndroid Build Coastguard Worker print("%-7d %-16s %-6d %-6d %-7d %-7d %1s %s" % (k.pid, 209*387f9dfdSAndroid Build Coastguard Worker k.comm.decode('utf-8', 'replace'), v.reads, v.writes, 210*387f9dfdSAndroid Build Coastguard Worker v.rbytes / 1024, v.wbytes / 1024, 211*387f9dfdSAndroid Build Coastguard Worker k.type.decode('utf-8', 'replace'), name)) 212*387f9dfdSAndroid Build Coastguard Worker 213*387f9dfdSAndroid Build Coastguard Worker line += 1 214*387f9dfdSAndroid Build Coastguard Worker if line >= maxrows: 215*387f9dfdSAndroid Build Coastguard Worker break 216*387f9dfdSAndroid Build Coastguard Worker counts.clear() 217*387f9dfdSAndroid Build Coastguard Worker 218*387f9dfdSAndroid Build Coastguard Worker countdown -= 1 219*387f9dfdSAndroid Build Coastguard Worker if exiting or countdown == 0: 220*387f9dfdSAndroid Build Coastguard Worker print("Detaching...") 221*387f9dfdSAndroid Build Coastguard Worker exit() 222