blob: 2009a19df97d9598b8d86502236c8203ff0e4df5 [file] [log] [blame]
Alexey Ivanov777e8022019-01-03 13:46:38 -08001#!/usr/bin/env python
Brendan Gregg5bfadab2016-02-10 01:36:51 -08002# @lint-avoid-python-3-compatibility-imports
3#
4# dcstat Directory entry cache (dcache) stats.
5# For Linux, uses BCC, eBPF.
6#
7# USAGE: dcstat [interval [count]]
8#
9# This uses kernel dynamic tracing of kernel functions, lookup_fast() and
10# d_lookup(), which will need to be modified to match kernel changes. See
11# code comments.
12#
13# Copyright 2016 Netflix, Inc.
14# Licensed under the Apache License, Version 2.0 (the "License")
15#
16# 09-Feb-2016 Brendan Gregg Created this.
17
18from __future__ import print_function
19from bcc import BPF
20from ctypes import c_int
21from time import sleep, strftime
22from sys import argv
23
24def usage():
25 print("USAGE: %s [interval [count]]" % argv[0])
26 exit()
27
28# arguments
29interval = 1
30count = -1
31if len(argv) > 1:
32 try:
33 interval = int(argv[1])
34 if interval == 0:
35 raise
36 if len(argv) > 2:
37 count = int(argv[2])
38 except: # also catches -h, --help
39 usage()
40
41# define BPF program
42bpf_text = """
43#include <uapi/linux/ptrace.h>
44
45enum stats {
46 S_REFS = 1,
47 S_SLOW,
48 S_MISS,
49 S_MAXSTAT
50};
51
Brenden Blancod51870b2017-08-16 11:29:23 -070052BPF_ARRAY(stats, u64, S_MAXSTAT);
Brendan Gregg5bfadab2016-02-10 01:36:51 -080053
54/*
55 * How this is instrumented, and how to interpret the statistics, is very much
56 * tied to the current kernel implementation (this was written on Linux 4.4).
57 * This will need maintenance to keep working as the implementation changes. To
58 * aid future adventurers, this is is what the current code does, and why.
59 *
60 * First problem: the current implementation takes a path and then does a
61 * lookup of each component. So how do we count a reference? Once for the path
62 * lookup, or once for every component lookup? I've chosen the latter
63 * since it seems to map more closely to actual dcache lookups (via
64 * __d_lookup_rcu()). It's counted via calls to lookup_fast().
65 *
66 * The implementation tries different, progressively slower, approaches to
Edward Bettsfdf9b082017-10-10 21:13:28 +010067 * lookup a file. At what point do we call it a dcache miss? I've chosen when
Brendan Gregg5bfadab2016-02-10 01:36:51 -080068 * a d_lookup() (which is called during lookup_slow()) returns zero.
69 *
70 * I've also included a "SLOW" statistic to show how often the fast lookup
71 * failed. Whether this exists or is interesting is an implementation detail,
72 * and the "SLOW" statistic may be removed in future versions.
73 */
74void count_fast(struct pt_regs *ctx) {
75 int key = S_REFS;
76 u64 *leaf = stats.lookup(&key);
77 if (leaf) (*leaf)++;
78}
79
80void count_lookup(struct pt_regs *ctx) {
81 int key = S_SLOW;
82 u64 *leaf = stats.lookup(&key);
83 if (leaf) (*leaf)++;
Naveen N. Rao4afa96a2016-05-03 14:54:21 +053084 if (PT_REGS_RC(ctx) == 0) {
Brendan Gregg5bfadab2016-02-10 01:36:51 -080085 key = S_MISS;
86 leaf = stats.lookup(&key);
87 if (leaf) (*leaf)++;
88 }
89}
90"""
91
92# load BPF program
93b = BPF(text=bpf_text)
94b.attach_kprobe(event="lookup_fast", fn_name="count_fast")
95b.attach_kretprobe(event="d_lookup", fn_name="count_lookup")
96
97# stat column labels and indexes
98stats = {
99 "REFS": 1,
100 "SLOW": 2,
101 "MISS": 3
102}
103
104# header
105print("%-8s " % "TIME", end="")
Brenden Blancoc94ab7a2016-03-11 15:34:29 -0800106for stype, idx in sorted(stats.items(), key=lambda k_v: (k_v[1], k_v[0])):
Brendan Gregg5bfadab2016-02-10 01:36:51 -0800107 print(" %8s" % (stype + "/s"), end="")
108print(" %8s" % "HIT%")
109
110# output
111i = 0
112while (1):
113 if count > 0:
114 i += 1
115 if i > count:
116 exit()
117 try:
118 sleep(interval)
119 except KeyboardInterrupt:
120 pass
121 exit()
122
123 print("%-8s: " % strftime("%H:%M:%S"), end="")
124
125 # print each statistic as a column
Brenden Blancoc94ab7a2016-03-11 15:34:29 -0800126 for stype, idx in sorted(stats.items(), key=lambda k_v: (k_v[1], k_v[0])):
Brendan Gregg5bfadab2016-02-10 01:36:51 -0800127 try:
128 val = b["stats"][c_int(idx)].value / interval
129 print(" %8d" % val, end="")
130 except:
131 print(" %8d" % 0, end="")
132
133 # print hit ratio percentage
134 try:
135 ref = b["stats"][c_int(stats["REFS"])].value
136 miss = b["stats"][c_int(stats["MISS"])].value
137 hit = ref - miss
138 pct = float(100) * hit / ref
139 print(" %8.2f" % pct)
140 except:
141 print(" %7s%%" % "-")
142
143 b["stats"].clear()