1#!/usr/bin/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 ctypes as ct 27import re 28import time 29 30# arguments 31examples = """examples: 32 ./dcsnoop # trace failed dcache lookups 33 ./dcsnoop -a # trace all dcache lookups 34""" 35parser = argparse.ArgumentParser( 36 description="Trace directory entry cache (dcache) lookups", 37 formatter_class=argparse.RawDescriptionHelpFormatter, 38 epilog=examples) 39parser.add_argument("-a", "--all", action="store_true", 40 help="trace all lookups (default is fails only)") 41parser.add_argument("--ebpf", action="store_true", 42 help=argparse.SUPPRESS) 43args = parser.parse_args() 44 45# define BPF program 46bpf_text = """ 47#include <uapi/linux/ptrace.h> 48#include <linux/fs.h> 49#include <linux/sched.h> 50 51#define MAX_FILE_LEN 64 52 53enum lookup_type { 54 LOOKUP_MISS, 55 LOOKUP_REFERENCE, 56}; 57 58struct entry_t { 59 char name[MAX_FILE_LEN]; 60}; 61 62BPF_HASH(entrybypid, u32, struct entry_t); 63 64struct data_t { 65 u32 pid; 66 enum lookup_type type; 67 char comm[TASK_COMM_LEN]; 68 char filename[MAX_FILE_LEN]; 69}; 70 71BPF_PERF_OUTPUT(events); 72 73/* from fs/namei.c: */ 74struct nameidata { 75 struct path path; 76 struct qstr last; 77 // [...] 78}; 79 80static inline 81void submit_event(struct pt_regs *ctx, void *name, int type, u32 pid) 82{ 83 struct data_t data = { 84 .pid = pid, 85 .type = type, 86 }; 87 bpf_get_current_comm(&data.comm, sizeof(data.comm)); 88 bpf_probe_read(&data.filename, sizeof(data.filename), name); 89 events.perf_submit(ctx, &data, sizeof(data)); 90} 91 92int trace_fast(struct pt_regs *ctx, struct nameidata *nd, struct path *path) 93{ 94 u32 pid = bpf_get_current_pid_tgid(); 95 submit_event(ctx, (void *)nd->last.name, LOOKUP_REFERENCE, pid); 96 return 1; 97} 98 99int kprobe__d_lookup(struct pt_regs *ctx, const struct dentry *parent, 100 const struct qstr *name) 101{ 102 u32 pid = bpf_get_current_pid_tgid(); 103 struct entry_t entry = {}; 104 const char *fname = name->name; 105 if (fname) { 106 bpf_probe_read(&entry.name, sizeof(entry.name), (void *)fname); 107 } 108 entrybypid.update(&pid, &entry); 109 return 0; 110} 111 112int kretprobe__d_lookup(struct pt_regs *ctx) 113{ 114 u32 pid = bpf_get_current_pid_tgid(); 115 struct entry_t *ep; 116 ep = entrybypid.lookup(&pid); 117 if (ep == 0 || PT_REGS_RC(ctx) != 0) { 118 return 0; // missed entry or lookup didn't fail 119 } 120 submit_event(ctx, (void *)ep->name, LOOKUP_MISS, pid); 121 entrybypid.delete(&pid); 122 return 0; 123} 124""" 125 126TASK_COMM_LEN = 16 # linux/sched.h 127MAX_FILE_LEN = 64 # see inline C 128 129class Data(ct.Structure): 130 _fields_ = [ 131 ("pid", ct.c_uint), 132 ("type", ct.c_int), 133 ("comm", ct.c_char * TASK_COMM_LEN), 134 ("filename", ct.c_char * MAX_FILE_LEN), 135 ] 136 137if args.ebpf: 138 print(bpf_text) 139 exit() 140 141# initialize BPF 142b = BPF(text=bpf_text) 143if args.all: 144 b.attach_kprobe(event="lookup_fast", fn_name="trace_fast") 145 146mode_s = { 147 0: 'M', 148 1: 'R', 149} 150 151start_ts = time.time() 152 153def print_event(cpu, data, size): 154 event = ct.cast(data, ct.POINTER(Data)).contents 155 print("%-11.6f %-6d %-16s %1s %s" % ( 156 time.time() - start_ts, event.pid, 157 event.comm.decode('utf-8', 'replace'), mode_s[event.type], 158 event.filename.decode('utf-8', 'replace'))) 159 160# header 161print("%-11s %-6s %-16s %1s %s" % ("TIME(s)", "PID", "COMM", "T", "FILE")) 162 163b["events"].open_perf_buffer(print_event, page_cnt=64) 164while 1: 165 b.perf_buffer_poll() 166