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