1#!/usr/bin/env python 2# 3# cachestat Count cache kernel function calls. 4# For Linux, uses BCC, eBPF. See .c file. 5# 6# USAGE: cachestat 7# Taken from funccount by Brendan Gregg 8# This is a rewrite of cachestat from perf to bcc 9# https://github.com/brendangregg/perf-tools/blob/master/fs/cachestat 10# 11# Copyright (c) 2016 Allan McAleavy. 12# Copyright (c) 2015 Brendan Gregg. 13# Licensed under the Apache License, Version 2.0 (the "License") 14# 15# 09-Sep-2015 Brendan Gregg Created this. 16# 06-Nov-2015 Allan McAleavy 17# 13-Jan-2016 Allan McAleavy run pep8 against program 18 19from __future__ import print_function 20from bcc import BPF 21from time import sleep, strftime 22import argparse 23import signal 24import re 25from sys import argv 26 27# signal handler 28def signal_ignore(signal, frame): 29 print() 30 31# Function to gather data from /proc/meminfo 32# return dictionary for quicker lookup of both values 33def get_meminfo(): 34 result = dict() 35 36 for line in open('/proc/meminfo'): 37 k = line.split(':', 3) 38 v = k[1].split() 39 result[k[0]] = int(v[0]) 40 return result 41 42# set global variables 43mpa = 0 44mbd = 0 45apcl = 0 46apd = 0 47total = 0 48misses = 0 49hits = 0 50debug = 0 51 52# arguments 53parser = argparse.ArgumentParser( 54 description="Count cache kernel function calls", 55 formatter_class=argparse.RawDescriptionHelpFormatter) 56parser.add_argument("-T", "--timestamp", action="store_true", 57 help="include timestamp on output") 58parser.add_argument("interval", nargs="?", default=5, 59 help="output interval, in seconds") 60parser.add_argument("count", nargs="?", default=-1, 61 help="number of outputs") 62parser.add_argument("--ebpf", action="store_true", 63 help=argparse.SUPPRESS) 64args = parser.parse_args() 65count = int(args.count) 66tstamp = args.timestamp 67interval = int(args.interval) 68 69# define BPF program 70bpf_text = """ 71#include <uapi/linux/ptrace.h> 72struct key_t { 73 u64 ip; 74}; 75 76BPF_HASH(counts, struct key_t); 77 78int do_count(struct pt_regs *ctx) { 79 struct key_t key = {}; 80 u64 ip; 81 82 key.ip = PT_REGS_IP(ctx); 83 counts.increment(key); // update counter 84 return 0; 85} 86 87""" 88 89if debug or args.ebpf: 90 print(bpf_text) 91 if args.ebpf: 92 exit() 93 94# load BPF program 95b = BPF(text=bpf_text) 96b.attach_kprobe(event="add_to_page_cache_lru", fn_name="do_count") 97b.attach_kprobe(event="mark_page_accessed", fn_name="do_count") 98b.attach_kprobe(event="account_page_dirtied", fn_name="do_count") 99b.attach_kprobe(event="mark_buffer_dirty", fn_name="do_count") 100 101# header 102if tstamp: 103 print("%-8s " % "TIME", end="") 104print("%8s %8s %8s %8s %12s %10s" % 105 ("TOTAL", "MISSES", "HITS", "DIRTIES", "BUFFERS_MB", "CACHED_MB")) 106 107loop = 0 108exiting = 0 109while 1: 110 if count > 0: 111 loop += 1 112 if loop > count: 113 exit() 114 115 try: 116 sleep(interval) 117 except KeyboardInterrupt: 118 exiting = 1 119 # as cleanup can take many seconds, trap Ctrl-C: 120 signal.signal(signal.SIGINT, signal_ignore) 121 122 counts = b["counts"] 123 for k, v in sorted(counts.items(), key=lambda counts: counts[1].value): 124 125 if re.match(b'mark_page_accessed', b.ksym(k.ip)) is not None: 126 mpa = max(0, v.value) 127 128 if re.match(b'mark_buffer_dirty', b.ksym(k.ip)) is not None: 129 mbd = max(0, v.value) 130 131 if re.match(b'add_to_page_cache_lru', b.ksym(k.ip)) is not None: 132 apcl = max(0, v.value) 133 134 if re.match(b'account_page_dirtied', b.ksym(k.ip)) is not None: 135 apd = max(0, v.value) 136 137 # total = total cache accesses without counting dirties 138 # misses = total of add to lru because of read misses 139 total = (mpa - mbd) 140 misses = (apcl - apd) 141 142 if total < 0: 143 total = 0 144 145 if misses < 0: 146 misses = 0 147 148 hits = total - misses 149 150 # If hits are < 0, then its possible misses are overestimated 151 # due to possibly page cache read ahead adding more pages than 152 # needed. In this case just assume misses as total and reset hits. 153 if hits < 0: 154 misses = total 155 hits = 0 156 157 if debug: 158 print("%d %d %d %d %d %d %d\n" % 159 (mpa, mbd, apcl, apd, total, misses, hits)) 160 161 counts.clear() 162 163 # Get memory info 164 mem = get_meminfo() 165 cached = int(mem["Cached"]) / 1024 166 buff = int(mem["Buffers"]) / 1024 167 168 if tstamp: 169 print("%-8s " % strftime("%H:%M:%S"), end="") 170 print("%8d %8d %8d %8d %12.0f %10.0f" % 171 (total, misses, hits, mbd, buff, cached)) 172 173 mpa = 0 174 mbd = 0 175 apcl = 0 176 apd = 0 177 total = 0 178 misses = 0 179 hits = 0 180 cached = 0 181 buff = 0 182 183 if exiting: 184 print("Detaching...") 185 exit() 186