xref: /aosp_15_r20/external/bcc/tools/dcsnoop.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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