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# readahead Show performance of read-ahead cache 5*387f9dfdSAndroid Build Coastguard Worker# For Linux, uses BCC, eBPF 6*387f9dfdSAndroid Build Coastguard Worker# 7*387f9dfdSAndroid Build Coastguard Worker# Copyright (c) 2020 Suchakra Sharma <[email protected]> 8*387f9dfdSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License") 9*387f9dfdSAndroid Build Coastguard Worker# This was originally created for the BPF Performance Tools book 10*387f9dfdSAndroid Build Coastguard Worker# published by Addison Wesley. ISBN-13: 9780136554820 11*387f9dfdSAndroid Build Coastguard Worker# When copying or porting, include this comment. 12*387f9dfdSAndroid Build Coastguard Worker# 13*387f9dfdSAndroid Build Coastguard Worker# 20-Aug-2020 Suchakra Sharma Ported from bpftrace to BCC 14*387f9dfdSAndroid Build Coastguard Worker# 17-Sep-2021 Hengqi Chen Migrated to kfunc 15*387f9dfdSAndroid Build Coastguard Worker# 30-Jan-2023 Rong Tao Support more kfunc/kprobe, introduce folio 16*387f9dfdSAndroid Build Coastguard Worker 17*387f9dfdSAndroid Build Coastguard Workerfrom __future__ import print_function 18*387f9dfdSAndroid Build Coastguard Workerfrom bcc import BPF 19*387f9dfdSAndroid Build Coastguard Workerfrom time import sleep 20*387f9dfdSAndroid Build Coastguard Workerimport ctypes as ct 21*387f9dfdSAndroid Build Coastguard Workerimport argparse 22*387f9dfdSAndroid Build Coastguard Worker 23*387f9dfdSAndroid Build Coastguard Worker# arguments 24*387f9dfdSAndroid Build Coastguard Workerexamples = """examples: 25*387f9dfdSAndroid Build Coastguard Worker ./readahead -d 20 # monitor for 20 seconds and generate stats 26*387f9dfdSAndroid Build Coastguard Worker""" 27*387f9dfdSAndroid Build Coastguard Worker 28*387f9dfdSAndroid Build Coastguard Workerparser = argparse.ArgumentParser( 29*387f9dfdSAndroid Build Coastguard Worker description="Monitor performance of read ahead cache", 30*387f9dfdSAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter, 31*387f9dfdSAndroid Build Coastguard Worker epilog=examples) 32*387f9dfdSAndroid Build Coastguard Workerparser.add_argument("-d", "--duration", type=int, 33*387f9dfdSAndroid Build Coastguard Worker help="total duration to monitor for, in seconds") 34*387f9dfdSAndroid Build Coastguard Workerargs = parser.parse_args() 35*387f9dfdSAndroid Build Coastguard Workerif not args.duration: 36*387f9dfdSAndroid Build Coastguard Worker args.duration = 99999999 37*387f9dfdSAndroid Build Coastguard Worker 38*387f9dfdSAndroid Build Coastguard Worker# BPF program 39*387f9dfdSAndroid Build Coastguard Workerbpf_text = """ 40*387f9dfdSAndroid Build Coastguard Worker#include <uapi/linux/ptrace.h> 41*387f9dfdSAndroid Build Coastguard Worker#include <linux/mm_types.h> 42*387f9dfdSAndroid Build Coastguard Worker#include <linux/mm.h> 43*387f9dfdSAndroid Build Coastguard Worker 44*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(flag, u32, u8); // used to track if we are in do_page_cache_readahead() 45*387f9dfdSAndroid Build Coastguard WorkerBPF_HASH(birth, struct page*, u64); // used to track timestamps of cache alloc'ed page 46*387f9dfdSAndroid Build Coastguard WorkerBPF_ARRAY(pages); // increment/decrement readahead pages 47*387f9dfdSAndroid Build Coastguard WorkerBPF_HISTOGRAM(dist); 48*387f9dfdSAndroid Build Coastguard Worker""" 49*387f9dfdSAndroid Build Coastguard Worker 50*387f9dfdSAndroid Build Coastguard Workerbpf_text_kprobe = """ 51*387f9dfdSAndroid Build Coastguard Workerint entry__do_page_cache_readahead(struct pt_regs *ctx) { 52*387f9dfdSAndroid Build Coastguard Worker u32 pid; 53*387f9dfdSAndroid Build Coastguard Worker u8 one = 1; 54*387f9dfdSAndroid Build Coastguard Worker pid = bpf_get_current_pid_tgid(); 55*387f9dfdSAndroid Build Coastguard Worker flag.update(&pid, &one); 56*387f9dfdSAndroid Build Coastguard Worker return 0; 57*387f9dfdSAndroid Build Coastguard Worker} 58*387f9dfdSAndroid Build Coastguard Worker 59*387f9dfdSAndroid Build Coastguard Workerint exit__do_page_cache_readahead(struct pt_regs *ctx) { 60*387f9dfdSAndroid Build Coastguard Worker u32 pid; 61*387f9dfdSAndroid Build Coastguard Worker u8 zero = 0; 62*387f9dfdSAndroid Build Coastguard Worker pid = bpf_get_current_pid_tgid(); 63*387f9dfdSAndroid Build Coastguard Worker flag.update(&pid, &zero); 64*387f9dfdSAndroid Build Coastguard Worker return 0; 65*387f9dfdSAndroid Build Coastguard Worker} 66*387f9dfdSAndroid Build Coastguard Worker 67*387f9dfdSAndroid Build Coastguard Workerint exit__page_cache_alloc(struct pt_regs *ctx) { 68*387f9dfdSAndroid Build Coastguard Worker u32 pid; 69*387f9dfdSAndroid Build Coastguard Worker u64 ts; 70*387f9dfdSAndroid Build Coastguard Worker struct page *retval = (struct page*) GET_RETVAL_PAGE; 71*387f9dfdSAndroid Build Coastguard Worker u32 zero = 0; // static key for accessing pages[0] 72*387f9dfdSAndroid Build Coastguard Worker pid = bpf_get_current_pid_tgid(); 73*387f9dfdSAndroid Build Coastguard Worker u8 *f = flag.lookup(&pid); 74*387f9dfdSAndroid Build Coastguard Worker if (f != NULL && *f == 1) { 75*387f9dfdSAndroid Build Coastguard Worker ts = bpf_ktime_get_ns(); 76*387f9dfdSAndroid Build Coastguard Worker birth.update(&retval, &ts); 77*387f9dfdSAndroid Build Coastguard Worker pages.atomic_increment(zero); 78*387f9dfdSAndroid Build Coastguard Worker } 79*387f9dfdSAndroid Build Coastguard Worker return 0; 80*387f9dfdSAndroid Build Coastguard Worker} 81*387f9dfdSAndroid Build Coastguard Worker 82*387f9dfdSAndroid Build Coastguard Workerint entry_mark_page_accessed(struct pt_regs *ctx) { 83*387f9dfdSAndroid Build Coastguard Worker u64 ts, delta; 84*387f9dfdSAndroid Build Coastguard Worker struct page *arg0 = (struct page *) PT_REGS_PARM1(ctx); 85*387f9dfdSAndroid Build Coastguard Worker u32 zero = 0; // static key for accessing pages[0] 86*387f9dfdSAndroid Build Coastguard Worker u64 *bts = birth.lookup(&arg0); 87*387f9dfdSAndroid Build Coastguard Worker if (bts != NULL) { 88*387f9dfdSAndroid Build Coastguard Worker delta = bpf_ktime_get_ns() - *bts; 89*387f9dfdSAndroid Build Coastguard Worker dist.atomic_increment(bpf_log2l(delta/1000000)); 90*387f9dfdSAndroid Build Coastguard Worker pages.atomic_increment(zero, -1); 91*387f9dfdSAndroid Build Coastguard Worker birth.delete(&arg0); // remove the entry from hashmap 92*387f9dfdSAndroid Build Coastguard Worker } 93*387f9dfdSAndroid Build Coastguard Worker return 0; 94*387f9dfdSAndroid Build Coastguard Worker} 95*387f9dfdSAndroid Build Coastguard Worker""" 96*387f9dfdSAndroid Build Coastguard Worker 97*387f9dfdSAndroid Build Coastguard Workerbpf_text_kfunc = """ 98*387f9dfdSAndroid Build Coastguard WorkerKFUNC_PROBE(RA_FUNC) 99*387f9dfdSAndroid Build Coastguard Worker{ 100*387f9dfdSAndroid Build Coastguard Worker u32 pid = bpf_get_current_pid_tgid(); 101*387f9dfdSAndroid Build Coastguard Worker u8 one = 1; 102*387f9dfdSAndroid Build Coastguard Worker 103*387f9dfdSAndroid Build Coastguard Worker flag.update(&pid, &one); 104*387f9dfdSAndroid Build Coastguard Worker return 0; 105*387f9dfdSAndroid Build Coastguard Worker} 106*387f9dfdSAndroid Build Coastguard Worker 107*387f9dfdSAndroid Build Coastguard WorkerKRETFUNC_PROBE(RA_FUNC) 108*387f9dfdSAndroid Build Coastguard Worker{ 109*387f9dfdSAndroid Build Coastguard Worker u32 pid = bpf_get_current_pid_tgid(); 110*387f9dfdSAndroid Build Coastguard Worker u8 zero = 0; 111*387f9dfdSAndroid Build Coastguard Worker 112*387f9dfdSAndroid Build Coastguard Worker flag.update(&pid, &zero); 113*387f9dfdSAndroid Build Coastguard Worker return 0; 114*387f9dfdSAndroid Build Coastguard Worker} 115*387f9dfdSAndroid Build Coastguard Worker 116*387f9dfdSAndroid Build Coastguard WorkerKFUNC_PROBE(mark_page_accessed, struct page *arg0) 117*387f9dfdSAndroid Build Coastguard Worker{ 118*387f9dfdSAndroid Build Coastguard Worker u64 ts, delta; 119*387f9dfdSAndroid Build Coastguard Worker u32 zero = 0; // static key for accessing pages[0] 120*387f9dfdSAndroid Build Coastguard Worker u64 *bts = birth.lookup(&arg0); 121*387f9dfdSAndroid Build Coastguard Worker 122*387f9dfdSAndroid Build Coastguard Worker if (bts != NULL) { 123*387f9dfdSAndroid Build Coastguard Worker delta = bpf_ktime_get_ns() - *bts; 124*387f9dfdSAndroid Build Coastguard Worker dist.atomic_increment(bpf_log2l(delta/1000000)); 125*387f9dfdSAndroid Build Coastguard Worker pages.atomic_increment(zero, -1); 126*387f9dfdSAndroid Build Coastguard Worker birth.delete(&arg0); // remove the entry from hashmap 127*387f9dfdSAndroid Build Coastguard Worker } 128*387f9dfdSAndroid Build Coastguard Worker return 0; 129*387f9dfdSAndroid Build Coastguard Worker} 130*387f9dfdSAndroid Build Coastguard Worker""" 131*387f9dfdSAndroid Build Coastguard Worker 132*387f9dfdSAndroid Build Coastguard Workerbpf_text_kfunc_cache_alloc_ret_page = """ 133*387f9dfdSAndroid Build Coastguard WorkerKRETFUNC_PROBE(__page_cache_alloc, gfp_t gfp, struct page *retval) 134*387f9dfdSAndroid Build Coastguard Worker{ 135*387f9dfdSAndroid Build Coastguard Worker u64 ts; 136*387f9dfdSAndroid Build Coastguard Worker u32 zero = 0; // static key for accessing pages[0] 137*387f9dfdSAndroid Build Coastguard Worker u32 pid = bpf_get_current_pid_tgid(); 138*387f9dfdSAndroid Build Coastguard Worker u8 *f = flag.lookup(&pid); 139*387f9dfdSAndroid Build Coastguard Worker 140*387f9dfdSAndroid Build Coastguard Worker if (f != NULL && *f == 1) { 141*387f9dfdSAndroid Build Coastguard Worker ts = bpf_ktime_get_ns(); 142*387f9dfdSAndroid Build Coastguard Worker birth.update(&retval, &ts); 143*387f9dfdSAndroid Build Coastguard Worker pages.atomic_increment(zero); 144*387f9dfdSAndroid Build Coastguard Worker } 145*387f9dfdSAndroid Build Coastguard Worker return 0; 146*387f9dfdSAndroid Build Coastguard Worker} 147*387f9dfdSAndroid Build Coastguard Worker""" 148*387f9dfdSAndroid Build Coastguard Worker 149*387f9dfdSAndroid Build Coastguard Workerbpf_text_kfunc_cache_alloc_ret_folio = """ 150*387f9dfdSAndroid Build Coastguard WorkerKRETFUNC_PROBE(filemap_alloc_folio, gfp_t gfp, unsigned int order, 151*387f9dfdSAndroid Build Coastguard Worker struct folio *retval) 152*387f9dfdSAndroid Build Coastguard Worker{ 153*387f9dfdSAndroid Build Coastguard Worker u64 ts; 154*387f9dfdSAndroid Build Coastguard Worker u32 zero = 0; // static key for accessing pages[0] 155*387f9dfdSAndroid Build Coastguard Worker u32 pid = bpf_get_current_pid_tgid(); 156*387f9dfdSAndroid Build Coastguard Worker u8 *f = flag.lookup(&pid); 157*387f9dfdSAndroid Build Coastguard Worker struct page *page = folio_page(retval, 0); 158*387f9dfdSAndroid Build Coastguard Worker 159*387f9dfdSAndroid Build Coastguard Worker if (f != NULL && *f == 1) { 160*387f9dfdSAndroid Build Coastguard Worker ts = bpf_ktime_get_ns(); 161*387f9dfdSAndroid Build Coastguard Worker birth.update(&page, &ts); 162*387f9dfdSAndroid Build Coastguard Worker pages.atomic_increment(zero); 163*387f9dfdSAndroid Build Coastguard Worker } 164*387f9dfdSAndroid Build Coastguard Worker return 0; 165*387f9dfdSAndroid Build Coastguard Worker} 166*387f9dfdSAndroid Build Coastguard Worker""" 167*387f9dfdSAndroid Build Coastguard Worker 168*387f9dfdSAndroid Build Coastguard Workerif BPF.support_kfunc(): 169*387f9dfdSAndroid Build Coastguard Worker if BPF.get_kprobe_functions(b"__do_page_cache_readahead"): 170*387f9dfdSAndroid Build Coastguard Worker ra_func = "__do_page_cache_readahead" 171*387f9dfdSAndroid Build Coastguard Worker elif BPF.get_kprobe_functions(b"do_page_cache_ra"): 172*387f9dfdSAndroid Build Coastguard Worker ra_func = "do_page_cache_ra" 173*387f9dfdSAndroid Build Coastguard Worker elif BPF.get_kprobe_functions(b"page_cache_ra_order"): 174*387f9dfdSAndroid Build Coastguard Worker ra_func = "page_cache_ra_order" 175*387f9dfdSAndroid Build Coastguard Worker else: 176*387f9dfdSAndroid Build Coastguard Worker print("Not found any kfunc.") 177*387f9dfdSAndroid Build Coastguard Worker exit() 178*387f9dfdSAndroid Build Coastguard Worker bpf_text += bpf_text_kfunc.replace("RA_FUNC", ra_func) 179*387f9dfdSAndroid Build Coastguard Worker if BPF.get_kprobe_functions(b"__page_cache_alloc"): 180*387f9dfdSAndroid Build Coastguard Worker bpf_text += bpf_text_kfunc_cache_alloc_ret_page 181*387f9dfdSAndroid Build Coastguard Worker else: 182*387f9dfdSAndroid Build Coastguard Worker bpf_text += bpf_text_kfunc_cache_alloc_ret_folio 183*387f9dfdSAndroid Build Coastguard Worker b = BPF(text=bpf_text) 184*387f9dfdSAndroid Build Coastguard Workerelse: 185*387f9dfdSAndroid Build Coastguard Worker bpf_text += bpf_text_kprobe 186*387f9dfdSAndroid Build Coastguard Worker if BPF.get_kprobe_functions(b"__do_page_cache_readahead"): 187*387f9dfdSAndroid Build Coastguard Worker ra_event = "__do_page_cache_readahead" 188*387f9dfdSAndroid Build Coastguard Worker elif BPF.get_kprobe_functions(b"do_page_cache_ra"): 189*387f9dfdSAndroid Build Coastguard Worker ra_event = "do_page_cache_ra" 190*387f9dfdSAndroid Build Coastguard Worker elif BPF.get_kprobe_functions(b"page_cache_ra_order"): 191*387f9dfdSAndroid Build Coastguard Worker ra_event = "page_cache_ra_order" 192*387f9dfdSAndroid Build Coastguard Worker else: 193*387f9dfdSAndroid Build Coastguard Worker print("Not found any kprobe.") 194*387f9dfdSAndroid Build Coastguard Worker exit() 195*387f9dfdSAndroid Build Coastguard Worker if BPF.get_kprobe_functions(b"__page_cache_alloc"): 196*387f9dfdSAndroid Build Coastguard Worker cache_func = "__page_cache_alloc" 197*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('GET_RETVAL_PAGE', 'PT_REGS_RC(ctx)') 198*387f9dfdSAndroid Build Coastguard Worker else: 199*387f9dfdSAndroid Build Coastguard Worker cache_func = "filemap_alloc_folio" 200*387f9dfdSAndroid Build Coastguard Worker bpf_text = bpf_text.replace('GET_RETVAL_PAGE', 'folio_page((struct folio *)PT_REGS_RC(ctx), 0)') 201*387f9dfdSAndroid Build Coastguard Worker b = BPF(text=bpf_text) 202*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event=ra_event, fn_name="entry__do_page_cache_readahead") 203*387f9dfdSAndroid Build Coastguard Worker b.attach_kretprobe(event=ra_event, fn_name="exit__do_page_cache_readahead") 204*387f9dfdSAndroid Build Coastguard Worker b.attach_kretprobe(event=cache_func, fn_name="exit__page_cache_alloc") 205*387f9dfdSAndroid Build Coastguard Worker b.attach_kprobe(event="mark_page_accessed", fn_name="entry_mark_page_accessed") 206*387f9dfdSAndroid Build Coastguard Worker 207*387f9dfdSAndroid Build Coastguard Worker# header 208*387f9dfdSAndroid Build Coastguard Workerprint("Tracing... Hit Ctrl-C to end.") 209*387f9dfdSAndroid Build Coastguard Worker 210*387f9dfdSAndroid Build Coastguard Worker# print 211*387f9dfdSAndroid Build Coastguard Workerdef print_stats(): 212*387f9dfdSAndroid Build Coastguard Worker print() 213*387f9dfdSAndroid Build Coastguard Worker print("Read-ahead unused pages: %d" % (b["pages"][ct.c_ulong(0)].value)) 214*387f9dfdSAndroid Build Coastguard Worker print("Histogram of read-ahead used page age (ms):") 215*387f9dfdSAndroid Build Coastguard Worker print("") 216*387f9dfdSAndroid Build Coastguard Worker b["dist"].print_log2_hist("age (ms)") 217*387f9dfdSAndroid Build Coastguard Worker b["dist"].clear() 218*387f9dfdSAndroid Build Coastguard Worker b["pages"].clear() 219*387f9dfdSAndroid Build Coastguard Worker 220*387f9dfdSAndroid Build Coastguard Workerwhile True: 221*387f9dfdSAndroid Build Coastguard Worker try: 222*387f9dfdSAndroid Build Coastguard Worker sleep(args.duration) 223*387f9dfdSAndroid Build Coastguard Worker print_stats() 224*387f9dfdSAndroid Build Coastguard Worker except KeyboardInterrupt: 225*387f9dfdSAndroid Build Coastguard Worker print_stats() 226*387f9dfdSAndroid Build Coastguard Worker break 227