xref: /aosp_15_r20/external/bcc/tools/dcstat.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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# dcstat   Directory entry cache (dcache) stats.
5*387f9dfdSAndroid Build Coastguard Worker#          For Linux, uses BCC, eBPF.
6*387f9dfdSAndroid Build Coastguard Worker#
7*387f9dfdSAndroid Build Coastguard Worker# USAGE: dcstat [interval [count]]
8*387f9dfdSAndroid Build Coastguard Worker#
9*387f9dfdSAndroid Build Coastguard Worker# This uses kernel dynamic tracing of kernel functions, lookup_fast() and
10*387f9dfdSAndroid Build Coastguard Worker# d_lookup(), which will need to be modified to match kernel changes. See
11*387f9dfdSAndroid Build Coastguard Worker# code comments.
12*387f9dfdSAndroid Build Coastguard Worker#
13*387f9dfdSAndroid Build Coastguard Worker# Copyright 2016 Netflix, Inc.
14*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License")
15*387f9dfdSAndroid Build Coastguard Worker#
16*387f9dfdSAndroid Build Coastguard Worker# 09-Feb-2016   Brendan Gregg   Created this.
17*387f9dfdSAndroid Build Coastguard Worker
18*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function
19*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF
20*387f9dfdSAndroid Build Coastguard Workerfrom ctypes import c_int
21*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep, strftime
22*387f9dfdSAndroid Build Coastguard Workerfrom sys import argv
23*387f9dfdSAndroid Build Coastguard Worker
24*387f9dfdSAndroid Build Coastguard Workerdef usage():
25*387f9dfdSAndroid Build Coastguard Worker    print("USAGE: %s [interval [count]]" % argv[0])
26*387f9dfdSAndroid Build Coastguard Worker    exit()
27*387f9dfdSAndroid Build Coastguard Worker
28*387f9dfdSAndroid Build Coastguard Worker# arguments
29*387f9dfdSAndroid Build Coastguard Workerinterval = 1
30*387f9dfdSAndroid Build Coastguard Workercount = -1
31*387f9dfdSAndroid Build Coastguard Workerif len(argv) > 1:
32*387f9dfdSAndroid Build Coastguard Worker    try:
33*387f9dfdSAndroid Build Coastguard Worker        interval = int(argv[1])
34*387f9dfdSAndroid Build Coastguard Worker        if interval == 0:
35*387f9dfdSAndroid Build Coastguard Worker            raise
36*387f9dfdSAndroid Build Coastguard Worker        if len(argv) > 2:
37*387f9dfdSAndroid Build Coastguard Worker            count = int(argv[2])
38*387f9dfdSAndroid Build Coastguard Worker    except:  # also catches -h, --help
39*387f9dfdSAndroid Build Coastguard Worker        usage()
40*387f9dfdSAndroid Build Coastguard Worker
41*387f9dfdSAndroid Build Coastguard Worker# define BPF program
42*387f9dfdSAndroid Build Coastguard Workerbpf_text = """
43*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h>
44*387f9dfdSAndroid Build Coastguard Worker
45*387f9dfdSAndroid Build Coastguard Workerenum stats {
46*387f9dfdSAndroid Build Coastguard Worker    S_REFS = 1,
47*387f9dfdSAndroid Build Coastguard Worker    S_SLOW,
48*387f9dfdSAndroid Build Coastguard Worker    S_MISS,
49*387f9dfdSAndroid Build Coastguard Worker    S_MAXSTAT
50*387f9dfdSAndroid Build Coastguard Worker};
51*387f9dfdSAndroid Build Coastguard Worker
52*387f9dfdSAndroid Build Coastguard WorkerBPF_ARRAY(stats, u64, S_MAXSTAT);
53*387f9dfdSAndroid Build Coastguard Worker
54*387f9dfdSAndroid Build Coastguard Worker/*
55*387f9dfdSAndroid Build Coastguard Worker * How this is instrumented, and how to interpret the statistics, is very much
56*387f9dfdSAndroid Build Coastguard Worker * tied to the current kernel implementation (this was written on Linux 4.4).
57*387f9dfdSAndroid Build Coastguard Worker * This will need maintenance to keep working as the implementation changes. To
58*387f9dfdSAndroid Build Coastguard Worker * aid future adventurers, this is is what the current code does, and why.
59*387f9dfdSAndroid Build Coastguard Worker *
60*387f9dfdSAndroid Build Coastguard Worker * First problem: the current implementation takes a path and then does a
61*387f9dfdSAndroid Build Coastguard Worker * lookup of each component. So how do we count a reference? Once for the path
62*387f9dfdSAndroid Build Coastguard Worker * lookup, or once for every component lookup? I've chosen the latter
63*387f9dfdSAndroid Build Coastguard Worker * since it seems to map more closely to actual dcache lookups (via
64*387f9dfdSAndroid Build Coastguard Worker * __d_lookup_rcu()). It's counted via calls to lookup_fast().
65*387f9dfdSAndroid Build Coastguard Worker *
66*387f9dfdSAndroid Build Coastguard Worker * The implementation tries different, progressively slower, approaches to
67*387f9dfdSAndroid Build Coastguard Worker * lookup a file. At what point do we call it a dcache miss? I've chosen when
68*387f9dfdSAndroid Build Coastguard Worker * a d_lookup() (which is called during lookup_slow()) returns zero.
69*387f9dfdSAndroid Build Coastguard Worker *
70*387f9dfdSAndroid Build Coastguard Worker * I've also included a "SLOW" statistic to show how often the fast lookup
71*387f9dfdSAndroid Build Coastguard Worker * failed. Whether this exists or is interesting is an implementation detail,
72*387f9dfdSAndroid Build Coastguard Worker * and the "SLOW" statistic may be removed in future versions.
73*387f9dfdSAndroid Build Coastguard Worker */
74*387f9dfdSAndroid Build Coastguard Workervoid count_fast(struct pt_regs *ctx) {
75*387f9dfdSAndroid Build Coastguard Worker    int key = S_REFS;
76*387f9dfdSAndroid Build Coastguard Worker    stats.atomic_increment(key);
77*387f9dfdSAndroid Build Coastguard Worker}
78*387f9dfdSAndroid Build Coastguard Worker
79*387f9dfdSAndroid Build Coastguard Workervoid count_lookup(struct pt_regs *ctx) {
80*387f9dfdSAndroid Build Coastguard Worker    int key = S_SLOW;
81*387f9dfdSAndroid Build Coastguard Worker    stats.atomic_increment(key);
82*387f9dfdSAndroid Build Coastguard Worker    if (PT_REGS_RC(ctx) == 0) {
83*387f9dfdSAndroid Build Coastguard Worker        key = S_MISS;
84*387f9dfdSAndroid Build Coastguard Worker        stats.atomic_increment(key);
85*387f9dfdSAndroid Build Coastguard Worker    }
86*387f9dfdSAndroid Build Coastguard Worker}
87*387f9dfdSAndroid Build Coastguard Worker"""
88*387f9dfdSAndroid Build Coastguard Worker
89*387f9dfdSAndroid Build Coastguard Worker# load BPF program
90*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text)
91*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event_re="^lookup_fast$|^lookup_fast.constprop.*.\d$", fn_name="count_fast")
92*387f9dfdSAndroid Build Coastguard Workerb.attach_kretprobe(event="d_lookup", fn_name="count_lookup")
93*387f9dfdSAndroid Build Coastguard Worker
94*387f9dfdSAndroid Build Coastguard Worker# stat column labels and indexes
95*387f9dfdSAndroid Build Coastguard Workerstats = {
96*387f9dfdSAndroid Build Coastguard Worker    "REFS": 1,
97*387f9dfdSAndroid Build Coastguard Worker    "SLOW": 2,
98*387f9dfdSAndroid Build Coastguard Worker    "MISS": 3
99*387f9dfdSAndroid Build Coastguard Worker}
100*387f9dfdSAndroid Build Coastguard Worker
101*387f9dfdSAndroid Build Coastguard Worker# header
102*387f9dfdSAndroid Build Coastguard Workerprint("%-8s  " % "TIME", end="")
103*387f9dfdSAndroid Build Coastguard Workerfor stype, idx in sorted(stats.items(), key=lambda k_v: (k_v[1], k_v[0])):
104*387f9dfdSAndroid Build Coastguard Worker    print(" %8s" % (stype + "/s"), end="")
105*387f9dfdSAndroid Build Coastguard Workerprint(" %8s" % "HIT%")
106*387f9dfdSAndroid Build Coastguard Worker
107*387f9dfdSAndroid Build Coastguard Worker# output
108*387f9dfdSAndroid Build Coastguard Workeri = 0
109*387f9dfdSAndroid Build Coastguard Workerwhile (1):
110*387f9dfdSAndroid Build Coastguard Worker    if count > 0:
111*387f9dfdSAndroid Build Coastguard Worker        i += 1
112*387f9dfdSAndroid Build Coastguard Worker        if i > count:
113*387f9dfdSAndroid Build Coastguard Worker            exit()
114*387f9dfdSAndroid Build Coastguard Worker    try:
115*387f9dfdSAndroid Build Coastguard Worker        sleep(interval)
116*387f9dfdSAndroid Build Coastguard Worker    except KeyboardInterrupt:
117*387f9dfdSAndroid Build Coastguard Worker        exit()
118*387f9dfdSAndroid Build Coastguard Worker
119*387f9dfdSAndroid Build Coastguard Worker    print("%-8s: " % strftime("%H:%M:%S"), end="")
120*387f9dfdSAndroid Build Coastguard Worker
121*387f9dfdSAndroid Build Coastguard Worker    # print each statistic as a column
122*387f9dfdSAndroid Build Coastguard Worker    for stype, idx in sorted(stats.items(), key=lambda k_v: (k_v[1], k_v[0])):
123*387f9dfdSAndroid Build Coastguard Worker        try:
124*387f9dfdSAndroid Build Coastguard Worker            val = b["stats"][c_int(idx)].value / interval
125*387f9dfdSAndroid Build Coastguard Worker            print(" %8d" % val, end="")
126*387f9dfdSAndroid Build Coastguard Worker        except:
127*387f9dfdSAndroid Build Coastguard Worker            print(" %8d" % 0, end="")
128*387f9dfdSAndroid Build Coastguard Worker
129*387f9dfdSAndroid Build Coastguard Worker    # print hit ratio percentage
130*387f9dfdSAndroid Build Coastguard Worker    try:
131*387f9dfdSAndroid Build Coastguard Worker        ref = b["stats"][c_int(stats["REFS"])].value
132*387f9dfdSAndroid Build Coastguard Worker        miss = b["stats"][c_int(stats["MISS"])].value
133*387f9dfdSAndroid Build Coastguard Worker        hit = ref - miss
134*387f9dfdSAndroid Build Coastguard Worker        pct = float(100) * hit / ref
135*387f9dfdSAndroid Build Coastguard Worker        print(" %8.2f" % pct)
136*387f9dfdSAndroid Build Coastguard Worker    except:
137*387f9dfdSAndroid Build Coastguard Worker        print(" %7s%%" % "-")
138*387f9dfdSAndroid Build Coastguard Worker
139*387f9dfdSAndroid Build Coastguard Worker    b["stats"].clear()
140