1#!/usr/bin/env python 2# @lint-avoid-python-3-compatibility-imports 3# 4# dcsnoop Trace directory entry cache (dcache) lookups. 5# For Linux, uses BCC, eBPF. Embedded C. 6# 7# USAGE: dcsnoop [-h] [-a] 8# 9# By default, this traces every failed dcache lookup, and shows the process 10# performing the lookup and the filename requested. A -a option can be used 11# to show all lookups, not just failed ones. 12# 13# This uses kernel dynamic tracing of the d_lookup() function, and will need 14# to be modified to match kernel changes. 15# 16# Also see dcstat(8), for per-second summaries. 17# 18# Copyright 2016 Netflix, Inc. 19# Licensed under the Apache License, Version 2.0 (the "License") 20# 21# 09-Feb-2016 Brendan Gregg Created this. 22 23from __future__ import print_function 24from bcc import BPF 25import argparse 26import re 27import time 28 29# arguments 30examples = """examples: 31 ./dcsnoop # trace failed dcache lookups 32 ./dcsnoop -a # trace all dcache lookups 33""" 34parser = argparse.ArgumentParser( 35 description="Trace directory entry cache (dcache) lookups", 36 formatter_class=argparse.RawDescriptionHelpFormatter, 37 epilog=examples) 38parser.add_argument("-a", "--all", action="store_true", 39 help="trace all lookups (default is fails only)") 40parser.add_argument("--ebpf", action="store_true", 41 help=argparse.SUPPRESS) 42args = parser.parse_args() 43 44# define BPF program 45bpf_text = """ 46#include <uapi/linux/ptrace.h> 47#include <linux/fs.h> 48#include <linux/sched.h> 49 50#define MAX_FILE_LEN 64 51 52enum lookup_type { 53 LOOKUP_MISS, 54 LOOKUP_REFERENCE, 55}; 56 57struct entry_t { 58 char name[MAX_FILE_LEN]; 59}; 60 61BPF_HASH(entrybypid, u32, struct entry_t); 62 63struct data_t { 64 u32 pid; 65 enum lookup_type type; 66 char comm[TASK_COMM_LEN]; 67 char filename[MAX_FILE_LEN]; 68}; 69 70BPF_PERF_OUTPUT(events); 71 72/* from fs/namei.c: */ 73struct nameidata { 74 struct path path; 75 struct qstr last; 76 // [...] 77}; 78 79static inline 80void submit_event(struct pt_regs *ctx, void *name, int type, u32 pid) 81{ 82 struct data_t data = { 83 .pid = pid, 84 .type = type, 85 }; 86 bpf_get_current_comm(&data.comm, sizeof(data.comm)); 87 bpf_probe_read_kernel(&data.filename, sizeof(data.filename), name); 88 events.perf_submit(ctx, &data, sizeof(data)); 89} 90 91int trace_fast(struct pt_regs *ctx, struct nameidata *nd, struct path *path) 92{ 93 u32 pid = bpf_get_current_pid_tgid() >> 32; 94 submit_event(ctx, (void *)nd->last.name, LOOKUP_REFERENCE, pid); 95 return 1; 96} 97 98int kprobe__d_lookup(struct pt_regs *ctx, const struct dentry *parent, 99 const struct qstr *name) 100{ 101 u32 tid = bpf_get_current_pid_tgid(); 102 struct entry_t entry = {}; 103 const char *fname = name->name; 104 if (fname) { 105 bpf_probe_read_kernel(&entry.name, sizeof(entry.name), (void *)fname); 106 } 107 entrybypid.update(&tid, &entry); 108 return 0; 109} 110 111int kretprobe__d_lookup(struct pt_regs *ctx) 112{ 113 u64 pid_tgid = bpf_get_current_pid_tgid(); 114 u32 pid = pid_tgid >> 32; 115 u32 tid = (u32)pid_tgid; 116 struct entry_t *ep; 117 118 ep = entrybypid.lookup(&tid); 119 if (ep == 0) { 120 return 0; // missed entry 121 } 122 if (PT_REGS_RC(ctx) != 0) { 123 entrybypid.delete(&tid); 124 return 0; // lookup didn't fail 125 } 126 127 submit_event(ctx, (void *)ep->name, LOOKUP_MISS, pid); 128 entrybypid.delete(&tid); 129 return 0; 130} 131""" 132 133if args.ebpf: 134 print(bpf_text) 135 exit() 136 137# initialize BPF 138b = BPF(text=bpf_text) 139if args.all: 140 b.attach_kprobe(event_re="^lookup_fast$|^lookup_fast.constprop.*.\d$", fn_name="trace_fast") 141 142mode_s = { 143 0: 'M', 144 1: 'R', 145} 146 147start_ts = time.time() 148 149def print_event(cpu, data, size): 150 event = b["events"].event(data) 151 print("%-11.6f %-7d %-16s %1s %s" % ( 152 time.time() - start_ts, event.pid, 153 event.comm.decode('utf-8', 'replace'), mode_s[event.type], 154 event.filename.decode('utf-8', 'replace'))) 155 156# header 157print("%-11s %-7s %-16s %1s %s" % ("TIME(s)", "PID", "COMM", "T", "FILE")) 158 159b["events"].open_perf_buffer(print_event, page_cnt=64) 160while 1: 161 try: 162 b.perf_buffer_poll() 163 except KeyboardInterrupt: 164 exit() 165