1#!/usr/bin/python 2# @lint-avoid-python-3-compatibility-imports 3# 4# readahead Show performance of read-ahead cache 5# For Linux, uses BCC, eBPF 6# 7# Copyright (c) 2020 Suchakra Sharma <mail@suchakra.in> 8# Licensed under the Apache License, Version 2.0 (the "License") 9# This was originally created for the BPF Performance Tools book 10# published by Addison Wesley. ISBN-13: 9780136554820 11# When copying or porting, include this comment. 12# 13# 20-Aug-2020 Suchakra Sharma Ported from bpftrace to BCC 14# 17-Sep-2021 Hengqi Chen Migrated to kfunc 15 16from __future__ import print_function 17from bcc import BPF 18from time import sleep 19import ctypes as ct 20import argparse 21 22# arguments 23examples = """examples: 24 ./readahead -d 20 # monitor for 20 seconds and generate stats 25""" 26 27parser = argparse.ArgumentParser( 28 description="Monitor performance of read ahead cache", 29 formatter_class=argparse.RawDescriptionHelpFormatter, 30 epilog=examples) 31parser.add_argument("-d", "--duration", type=int, 32 help="total duration to monitor for, in seconds") 33args = parser.parse_args() 34if not args.duration: 35 args.duration = 99999999 36 37# BPF program 38bpf_text = """ 39#include <uapi/linux/ptrace.h> 40#include <linux/mm_types.h> 41 42BPF_HASH(flag, u32, u8); // used to track if we are in do_page_cache_readahead() 43BPF_HASH(birth, struct page*, u64); // used to track timestamps of cache alloc'ed page 44BPF_ARRAY(pages); // increment/decrement readahead pages 45BPF_HISTOGRAM(dist); 46""" 47 48bpf_text_kprobe = """ 49int entry__do_page_cache_readahead(struct pt_regs *ctx) { 50 u32 pid; 51 u8 one = 1; 52 pid = bpf_get_current_pid_tgid(); 53 flag.update(&pid, &one); 54 return 0; 55} 56 57int exit__do_page_cache_readahead(struct pt_regs *ctx) { 58 u32 pid; 59 u8 zero = 0; 60 pid = bpf_get_current_pid_tgid(); 61 flag.update(&pid, &zero); 62 return 0; 63} 64 65int exit__page_cache_alloc(struct pt_regs *ctx) { 66 u32 pid; 67 u64 ts; 68 struct page *retval = (struct page*) PT_REGS_RC(ctx); 69 u32 zero = 0; // static key for accessing pages[0] 70 pid = bpf_get_current_pid_tgid(); 71 u8 *f = flag.lookup(&pid); 72 if (f != NULL && *f == 1) { 73 ts = bpf_ktime_get_ns(); 74 birth.update(&retval, &ts); 75 pages.atomic_increment(zero); 76 } 77 return 0; 78} 79 80int entry_mark_page_accessed(struct pt_regs *ctx) { 81 u64 ts, delta; 82 struct page *arg0 = (struct page *) PT_REGS_PARM1(ctx); 83 u32 zero = 0; // static key for accessing pages[0] 84 u64 *bts = birth.lookup(&arg0); 85 if (bts != NULL) { 86 delta = bpf_ktime_get_ns() - *bts; 87 dist.atomic_increment(bpf_log2l(delta/1000000)); 88 pages.atomic_increment(zero, -1); 89 birth.delete(&arg0); // remove the entry from hashmap 90 } 91 return 0; 92} 93""" 94 95bpf_text_kfunc = """ 96KFUNC_PROBE(RA_FUNC) 97{ 98 u32 pid = bpf_get_current_pid_tgid(); 99 u8 one = 1; 100 101 flag.update(&pid, &one); 102 return 0; 103} 104 105KRETFUNC_PROBE(RA_FUNC) 106{ 107 u32 pid = bpf_get_current_pid_tgid(); 108 u8 zero = 0; 109 110 flag.update(&pid, &zero); 111 return 0; 112} 113 114KRETFUNC_PROBE(__page_cache_alloc, gfp_t gfp, struct page *retval) 115{ 116 u64 ts; 117 u32 zero = 0; // static key for accessing pages[0] 118 u32 pid = bpf_get_current_pid_tgid(); 119 u8 *f = flag.lookup(&pid); 120 121 if (f != NULL && *f == 1) { 122 ts = bpf_ktime_get_ns(); 123 birth.update(&retval, &ts); 124 pages.atomic_increment(zero); 125 } 126 return 0; 127} 128 129KFUNC_PROBE(mark_page_accessed, struct page *arg0) 130{ 131 u64 ts, delta; 132 u32 zero = 0; // static key for accessing pages[0] 133 u64 *bts = birth.lookup(&arg0); 134 135 if (bts != NULL) { 136 delta = bpf_ktime_get_ns() - *bts; 137 dist.atomic_increment(bpf_log2l(delta/1000000)); 138 pages.atomic_increment(zero, -1); 139 birth.delete(&arg0); // remove the entry from hashmap 140 } 141 return 0; 142} 143""" 144 145if BPF.support_kfunc(): 146 if BPF.get_kprobe_functions(b"__do_page_cache_readahead"): 147 ra_func = "__do_page_cache_readahead" 148 else: 149 ra_func = "do_page_cache_ra" 150 bpf_text += bpf_text_kfunc.replace("RA_FUNC", ra_func) 151 b = BPF(text=bpf_text) 152else: 153 bpf_text += bpf_text_kprobe 154 b = BPF(text=bpf_text) 155 if BPF.get_kprobe_functions(b"__do_page_cache_readahead"): 156 ra_event = "__do_page_cache_readahead" 157 else: 158 ra_event = "do_page_cache_ra" 159 b.attach_kprobe(event=ra_event, fn_name="entry__do_page_cache_readahead") 160 b.attach_kretprobe(event=ra_event, fn_name="exit__do_page_cache_readahead") 161 b.attach_kretprobe(event="__page_cache_alloc", fn_name="exit__page_cache_alloc") 162 b.attach_kprobe(event="mark_page_accessed", fn_name="entry_mark_page_accessed") 163 164# header 165print("Tracing... Hit Ctrl-C to end.") 166 167# print 168def print_stats(): 169 print() 170 print("Read-ahead unused pages: %d" % (b["pages"][ct.c_ulong(0)].value)) 171 print("Histogram of read-ahead used page age (ms):") 172 print("") 173 b["dist"].print_log2_hist("age (ms)") 174 b["dist"].clear() 175 b["pages"].clear() 176 177while True: 178 try: 179 sleep(args.duration) 180 print_stats() 181 except KeyboardInterrupt: 182 print_stats() 183 break 184