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