1*387f9dfdSAndroid Build Coastguard Worker#!/usr/bin/env python 2*387f9dfdSAndroid Build Coastguard Worker# 3*387f9dfdSAndroid Build Coastguard Worker# cachestat Count cache kernel function calls. 4*387f9dfdSAndroid Build Coastguard Worker# For Linux, uses BCC, eBPF. See .c file. 5*387f9dfdSAndroid Build Coastguard Worker# 6*387f9dfdSAndroid Build Coastguard Worker# USAGE: cachestat 7*387f9dfdSAndroid Build Coastguard Worker# Taken from funccount by Brendan Gregg 8*387f9dfdSAndroid Build Coastguard Worker# This is a rewrite of cachestat from perf to bcc 9*387f9dfdSAndroid Build Coastguard Worker# https://github.com/brendangregg/perf-tools/blob/master/fs/cachestat 10*387f9dfdSAndroid Build Coastguard Worker# 11*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) 2016 Allan McAleavy. 12*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) 2015 Brendan Gregg. 13*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License") 14*387f9dfdSAndroid Build Coastguard Worker# 15*387f9dfdSAndroid Build Coastguard Worker# 09-Sep-2015 Brendan Gregg Created this. 16*387f9dfdSAndroid Build Coastguard Worker# 06-Nov-2015 Allan McAleavy 17*387f9dfdSAndroid Build Coastguard Worker# 13-Jan-2016 Allan McAleavy run pep8 against program 18*387f9dfdSAndroid Build Coastguard Worker# 02-Feb-2019 Brendan Gregg Column shuffle, bring back %ratio 19*387f9dfdSAndroid Build Coastguard Worker# 15-Feb-2023 Rong Tao Add writeback_dirty_{folio,page} tracepoints 20*387f9dfdSAndroid Build Coastguard Worker 21*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function 22*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF 23*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep, strftime 24*387f9dfdSAndroid Build Coastguard Workerimport argparse 25*387f9dfdSAndroid Build Coastguard Workerimport signal 26*387f9dfdSAndroid Build Coastguard Workerimport re 27*387f9dfdSAndroid Build Coastguard Workerfrom sys import argv 28*387f9dfdSAndroid Build Coastguard Worker 29*387f9dfdSAndroid Build Coastguard Worker# signal handler 30*387f9dfdSAndroid Build Coastguard Workerdef signal_ignore(signal, frame): 31*387f9dfdSAndroid Build Coastguard Worker print() 32*387f9dfdSAndroid Build Coastguard Worker 33*387f9dfdSAndroid Build Coastguard Worker# Function to gather data from /proc/meminfo 34*387f9dfdSAndroid Build Coastguard Worker# return dictionary for quicker lookup of both values 35*387f9dfdSAndroid Build Coastguard Workerdef get_meminfo(): 36*387f9dfdSAndroid Build Coastguard Worker result = dict() 37*387f9dfdSAndroid Build Coastguard Worker 38*387f9dfdSAndroid Build Coastguard Worker for line in open('/proc/meminfo'): 39*387f9dfdSAndroid Build Coastguard Worker k = line.split(':', 3) 40*387f9dfdSAndroid Build Coastguard Worker v = k[1].split() 41*387f9dfdSAndroid Build Coastguard Worker result[k[0]] = int(v[0]) 42*387f9dfdSAndroid Build Coastguard Worker return result 43*387f9dfdSAndroid Build Coastguard Worker 44*387f9dfdSAndroid Build Coastguard Worker# set global variables 45*387f9dfdSAndroid Build Coastguard Workermpa = 0 46*387f9dfdSAndroid Build Coastguard Workermbd = 0 47*387f9dfdSAndroid Build Coastguard Workerapcl = 0 48*387f9dfdSAndroid Build Coastguard Workerapd = 0 49*387f9dfdSAndroid Build Coastguard Workertotal = 0 50*387f9dfdSAndroid Build Coastguard Workermisses = 0 51*387f9dfdSAndroid Build Coastguard Workerhits = 0 52*387f9dfdSAndroid Build Coastguard Workerdebug = 0 53*387f9dfdSAndroid Build Coastguard Worker 54*387f9dfdSAndroid Build Coastguard Worker# arguments 55*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser( 56*387f9dfdSAndroid Build Coastguard Worker description="Count cache kernel function calls", 57*387f9dfdSAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter) 58*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-T", "--timestamp", action="store_true", 59*387f9dfdSAndroid Build Coastguard Worker help="include timestamp on output") 60*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("interval", nargs="?", default=1, 61*387f9dfdSAndroid Build Coastguard Worker help="output interval, in seconds") 62*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("count", nargs="?", default=-1, 63*387f9dfdSAndroid Build Coastguard Worker help="number of outputs") 64*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("--ebpf", action="store_true", 65*387f9dfdSAndroid Build Coastguard Worker help=argparse.SUPPRESS) 66*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args() 67*387f9dfdSAndroid Build Coastguard Workercount = int(args.count) 68*387f9dfdSAndroid Build Coastguard Workertstamp = args.timestamp 69*387f9dfdSAndroid Build Coastguard Workerinterval = int(args.interval) 70*387f9dfdSAndroid Build Coastguard Worker 71*387f9dfdSAndroid Build Coastguard Worker# define BPF program 72*387f9dfdSAndroid Build Coastguard Workerbpf_text = """ 73*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h> 74*387f9dfdSAndroid Build Coastguard Workerstruct key_t { 75*387f9dfdSAndroid Build Coastguard Worker // NF_{APCL,MPA,MBD,APD} 76*387f9dfdSAndroid Build Coastguard Worker u32 nf; 77*387f9dfdSAndroid Build Coastguard Worker}; 78*387f9dfdSAndroid Build Coastguard Worker 79*387f9dfdSAndroid Build Coastguard Workerenum { 80*387f9dfdSAndroid Build Coastguard Worker NF_APCL, 81*387f9dfdSAndroid Build Coastguard Worker NF_MPA, 82*387f9dfdSAndroid Build Coastguard Worker NF_MBD, 83*387f9dfdSAndroid Build Coastguard Worker NF_APD, 84*387f9dfdSAndroid Build Coastguard Worker}; 85*387f9dfdSAndroid Build Coastguard Worker 86*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(counts, struct key_t); 87*387f9dfdSAndroid Build Coastguard Worker 88*387f9dfdSAndroid Build Coastguard Workerstatic int __do_count(void *ctx, u32 nf) { 89*387f9dfdSAndroid Build Coastguard Worker struct key_t key = {}; 90*387f9dfdSAndroid Build Coastguard Worker u64 ip; 91*387f9dfdSAndroid Build Coastguard Worker 92*387f9dfdSAndroid Build Coastguard Worker key.nf = nf; 93*387f9dfdSAndroid Build Coastguard Worker counts.atomic_increment(key); // update counter 94*387f9dfdSAndroid Build Coastguard Worker return 0; 95*387f9dfdSAndroid Build Coastguard Worker} 96*387f9dfdSAndroid Build Coastguard Worker 97*387f9dfdSAndroid Build Coastguard Workerint do_count_apcl(struct pt_regs *ctx) { 98*387f9dfdSAndroid Build Coastguard Worker return __do_count(ctx, NF_APCL); 99*387f9dfdSAndroid Build Coastguard Worker} 100*387f9dfdSAndroid Build Coastguard Workerint do_count_mpa(struct pt_regs *ctx) { 101*387f9dfdSAndroid Build Coastguard Worker return __do_count(ctx, NF_MPA); 102*387f9dfdSAndroid Build Coastguard Worker} 103*387f9dfdSAndroid Build Coastguard Workerint do_count_mbd(struct pt_regs *ctx) { 104*387f9dfdSAndroid Build Coastguard Worker return __do_count(ctx, NF_MBD); 105*387f9dfdSAndroid Build Coastguard Worker} 106*387f9dfdSAndroid Build Coastguard Workerint do_count_apd(struct pt_regs *ctx) { 107*387f9dfdSAndroid Build Coastguard Worker return __do_count(ctx, NF_APD); 108*387f9dfdSAndroid Build Coastguard Worker} 109*387f9dfdSAndroid Build Coastguard Workerint do_count_apd_tp(void *ctx) { 110*387f9dfdSAndroid Build Coastguard Worker return __do_count(ctx, NF_APD); 111*387f9dfdSAndroid Build Coastguard Worker} 112*387f9dfdSAndroid Build Coastguard Worker""" 113*387f9dfdSAndroid Build Coastguard Worker 114*387f9dfdSAndroid Build Coastguard Workerif debug or args.ebpf: 115*387f9dfdSAndroid Build Coastguard Worker print(bpf_text) 116*387f9dfdSAndroid Build Coastguard Worker if args.ebpf: 117*387f9dfdSAndroid Build Coastguard Worker exit() 118*387f9dfdSAndroid Build Coastguard Worker 119*387f9dfdSAndroid Build Coastguard Worker# load BPF program 120*387f9dfdSAndroid Build Coastguard Workerb = BPF(text=bpf_text) 121*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event="add_to_page_cache_lru", fn_name="do_count_apcl") 122*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event="mark_page_accessed", fn_name="do_count_mpa") 123*387f9dfdSAndroid Build Coastguard Worker 124*387f9dfdSAndroid Build Coastguard Worker# Function account_page_dirtied() is changed to folio_account_dirtied() in 5.15. 125*387f9dfdSAndroid Build Coastguard Worker# Both folio_account_dirtied() and account_page_dirtied() are 126*387f9dfdSAndroid Build Coastguard Worker# static functions and they may be gone during compilation and this may 127*387f9dfdSAndroid Build Coastguard Worker# introduce some inaccuracy, use tracepoint writeback_dirty_{page,folio}, 128*387f9dfdSAndroid Build Coastguard Worker# instead when attaching kprobe fails, and report the running 129*387f9dfdSAndroid Build Coastguard Worker# error in time. 130*387f9dfdSAndroid Build Coastguard Workerif BPF.get_kprobe_functions(b'folio_account_dirtied'): 131*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event="folio_account_dirtied", fn_name="do_count_apd") 132*387f9dfdSAndroid Build Coastguard Workerelif BPF.get_kprobe_functions(b'account_page_dirtied'): 133*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event="account_page_dirtied", fn_name="do_count_apd") 134*387f9dfdSAndroid Build Coastguard Workerelif BPF.tracepoint_exists("writeback", "writeback_dirty_folio"): 135*387f9dfdSAndroid Build Coastguard Worker b.attach_tracepoint(tp="writeback:writeback_dirty_folio", fn_name="do_count_apd_tp") 136*387f9dfdSAndroid Build Coastguard Workerelif BPF.tracepoint_exists("writeback", "writeback_dirty_page"): 137*387f9dfdSAndroid Build Coastguard Worker b.attach_tracepoint(tp="writeback:writeback_dirty_page", fn_name="do_count_apd_tp") 138*387f9dfdSAndroid Build Coastguard Workerelse: 139*387f9dfdSAndroid Build Coastguard Worker raise Exception("Failed to attach kprobe %s or %s or any tracepoint" % 140*387f9dfdSAndroid Build Coastguard Worker ("folio_account_dirtied", "account_page_dirtied")) 141*387f9dfdSAndroid Build Coastguard Workerb.attach_kprobe(event="mark_buffer_dirty", fn_name="do_count_mbd") 142*387f9dfdSAndroid Build Coastguard Worker 143*387f9dfdSAndroid Build Coastguard Worker# header 144*387f9dfdSAndroid Build Coastguard Workerif tstamp: 145*387f9dfdSAndroid Build Coastguard Worker print("%-8s " % "TIME", end="") 146*387f9dfdSAndroid Build Coastguard Workerprint("%8s %8s %8s %8s %12s %10s" % 147*387f9dfdSAndroid Build Coastguard Worker ("HITS", "MISSES", "DIRTIES", "HITRATIO", "BUFFERS_MB", "CACHED_MB")) 148*387f9dfdSAndroid Build Coastguard Worker 149*387f9dfdSAndroid Build Coastguard Workerloop = 0 150*387f9dfdSAndroid Build Coastguard Workerexiting = 0 151*387f9dfdSAndroid Build Coastguard Workerwhile 1: 152*387f9dfdSAndroid Build Coastguard Worker if count > 0: 153*387f9dfdSAndroid Build Coastguard Worker loop += 1 154*387f9dfdSAndroid Build Coastguard Worker if loop > count: 155*387f9dfdSAndroid Build Coastguard Worker exit() 156*387f9dfdSAndroid Build Coastguard Worker 157*387f9dfdSAndroid Build Coastguard Worker try: 158*387f9dfdSAndroid Build Coastguard Worker sleep(interval) 159*387f9dfdSAndroid Build Coastguard Worker except KeyboardInterrupt: 160*387f9dfdSAndroid Build Coastguard Worker exiting = 1 161*387f9dfdSAndroid Build Coastguard Worker # as cleanup can take many seconds, trap Ctrl-C: 162*387f9dfdSAndroid Build Coastguard Worker signal.signal(signal.SIGINT, signal_ignore) 163*387f9dfdSAndroid Build Coastguard Worker 164*387f9dfdSAndroid Build Coastguard Worker counts = b["counts"] 165*387f9dfdSAndroid Build Coastguard Worker for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): 166*387f9dfdSAndroid Build Coastguard Worker # partial string matches in case of .isra (necessary?) 167*387f9dfdSAndroid Build Coastguard Worker if k.nf == 0: # NF_APCL 168*387f9dfdSAndroid Build Coastguard Worker apcl = max(0, v.value) 169*387f9dfdSAndroid Build Coastguard Worker if k.nf == 1: # NF_MPA 170*387f9dfdSAndroid Build Coastguard Worker mpa = max(0, v.value) 171*387f9dfdSAndroid Build Coastguard Worker if k.nf == 2: # NF_MBD 172*387f9dfdSAndroid Build Coastguard Worker mbd = max(0, v.value) 173*387f9dfdSAndroid Build Coastguard Worker if k.nf == 3: # NF_APD 174*387f9dfdSAndroid Build Coastguard Worker apd = max(0, v.value) 175*387f9dfdSAndroid Build Coastguard Worker 176*387f9dfdSAndroid Build Coastguard Worker # total = total cache accesses without counting dirties 177*387f9dfdSAndroid Build Coastguard Worker # misses = total of add to lru because of read misses 178*387f9dfdSAndroid Build Coastguard Worker total = mpa - mbd 179*387f9dfdSAndroid Build Coastguard Worker misses = apcl - apd 180*387f9dfdSAndroid Build Coastguard Worker if misses < 0: 181*387f9dfdSAndroid Build Coastguard Worker misses = 0 182*387f9dfdSAndroid Build Coastguard Worker if total < 0: 183*387f9dfdSAndroid Build Coastguard Worker total = 0 184*387f9dfdSAndroid Build Coastguard Worker hits = total - misses 185*387f9dfdSAndroid Build Coastguard Worker 186*387f9dfdSAndroid Build Coastguard Worker # If hits are < 0, then its possible misses are overestimated 187*387f9dfdSAndroid Build Coastguard Worker # due to possibly page cache read ahead adding more pages than 188*387f9dfdSAndroid Build Coastguard Worker # needed. In this case just assume misses as total and reset hits. 189*387f9dfdSAndroid Build Coastguard Worker if hits < 0: 190*387f9dfdSAndroid Build Coastguard Worker misses = total 191*387f9dfdSAndroid Build Coastguard Worker hits = 0 192*387f9dfdSAndroid Build Coastguard Worker ratio = 0 193*387f9dfdSAndroid Build Coastguard Worker if total > 0: 194*387f9dfdSAndroid Build Coastguard Worker ratio = float(hits) / total 195*387f9dfdSAndroid Build Coastguard Worker 196*387f9dfdSAndroid Build Coastguard Worker if debug: 197*387f9dfdSAndroid Build Coastguard Worker print("%d %d %d %d %d %d %d\n" % 198*387f9dfdSAndroid Build Coastguard Worker (mpa, mbd, apcl, apd, total, misses, hits)) 199*387f9dfdSAndroid Build Coastguard Worker 200*387f9dfdSAndroid Build Coastguard Worker counts.clear() 201*387f9dfdSAndroid Build Coastguard Worker 202*387f9dfdSAndroid Build Coastguard Worker # Get memory info 203*387f9dfdSAndroid Build Coastguard Worker mem = get_meminfo() 204*387f9dfdSAndroid Build Coastguard Worker cached = int(mem["Cached"]) / 1024 205*387f9dfdSAndroid Build Coastguard Worker buff = int(mem["Buffers"]) / 1024 206*387f9dfdSAndroid Build Coastguard Worker 207*387f9dfdSAndroid Build Coastguard Worker if tstamp: 208*387f9dfdSAndroid Build Coastguard Worker print("%-8s " % strftime("%H:%M:%S"), end="") 209*387f9dfdSAndroid Build Coastguard Worker print("%8d %8d %8d %7.2f%% %12.0f %10.0f" % 210*387f9dfdSAndroid Build Coastguard Worker (hits, misses, mbd, 100 * ratio, buff, cached)) 211*387f9dfdSAndroid Build Coastguard Worker 212*387f9dfdSAndroid Build Coastguard Worker mpa = mbd = apcl = apd = total = misses = hits = cached = buff = 0 213*387f9dfdSAndroid Build Coastguard Worker 214*387f9dfdSAndroid Build Coastguard Worker if exiting: 215*387f9dfdSAndroid Build Coastguard Worker print("Detaching...") 216*387f9dfdSAndroid Build Coastguard Worker exit() 217