blob: 573c697b6998b30aec95853e8f98dc9a7131bc67 [file] [log] [blame]
unixtest57abe5b2016-01-31 10:47:03 +00001#!/usr/bin/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 signal
23import re
24from sys import argv
25
26# signal handler
27def signal_ignore(signal, frame):
28 print()
29
30# Function to gather data from /proc/meminfo
31# return dictionary for quicker lookup of both values
32def get_meminfo():
33 result = dict()
34
35 for line in open('/proc/meminfo'):
36 k = line.split(':', 3)
37 v = k[1].split()
38 result[k[0]] = int(v[0])
39 return result
40
41# set global variables
unixtest57abe5b2016-01-31 10:47:03 +000042mpa = 0
43mbd = 0
44apcl = 0
45apd = 0
Joel Fernandes2b348702018-02-27 20:38:42 -080046total = 0
unixtest57abe5b2016-01-31 10:47:03 +000047misses = 0
Joel Fernandes2b348702018-02-27 20:38:42 -080048hits = 0
unixtest57abe5b2016-01-31 10:47:03 +000049debug = 0
50
51# args
52def usage():
53 print("USAGE: %s [-T] [ interval [count] ]" % argv[0])
54 exit()
55
56# arguments
57interval = 5
58count = -1
59tstamp = 0
60
61if len(argv) > 1:
62 if str(argv[1]) == '-T':
63 tstamp = 1
64
65if len(argv) > 1 and tstamp == 0:
66 try:
67 if int(argv[1]) > 0:
68 interval = int(argv[1])
69 if len(argv) > 2:
70 if int(argv[2]) > 0:
71 count = int(argv[2])
72 except:
73 usage()
74
75elif len(argv) > 2 and tstamp == 1:
76 try:
77 if int(argv[2]) > 0:
78 interval = int(argv[2])
79 if len(argv) >= 4:
80 if int(argv[3]) > 0:
81 count = int(argv[3])
82 except:
83 usage()
84
85# load BPF program
86bpf_text = """
87
88#include <uapi/linux/ptrace.h>
89struct key_t {
90 u64 ip;
91};
92
93BPF_HASH(counts, struct key_t);
94
95int do_count(struct pt_regs *ctx) {
96 struct key_t key = {};
unixtest57abe5b2016-01-31 10:47:03 +000097 u64 ip;
98
Naveen N. Rao4afa96a2016-05-03 14:54:21 +053099 key.ip = PT_REGS_IP(ctx);
Javier Honduvilla Coto64bf9652018-08-01 06:50:19 +0200100 counts.increment(key); // update counter
unixtest57abe5b2016-01-31 10:47:03 +0000101 return 0;
102}
103
104"""
105b = BPF(text=bpf_text)
106b.attach_kprobe(event="add_to_page_cache_lru", fn_name="do_count")
107b.attach_kprobe(event="mark_page_accessed", fn_name="do_count")
108b.attach_kprobe(event="account_page_dirtied", fn_name="do_count")
109b.attach_kprobe(event="mark_buffer_dirty", fn_name="do_count")
110
111# header
112if tstamp:
113 print("%-8s " % "TIME", end="")
Joel Fernandes2b348702018-02-27 20:38:42 -0800114print("%8s %8s %8s %8s %12s %10s" %
115 ("TOTAL", "MISSES", "HITS", "DIRTIES", "BUFFERS_MB", "CACHED_MB"))
unixtest57abe5b2016-01-31 10:47:03 +0000116
117loop = 0
118exiting = 0
119while 1:
120 if count > 0:
121 loop += 1
122 if loop > count:
123 exit()
124
125 try:
126 sleep(interval)
127 except KeyboardInterrupt:
128 exiting = 1
129 # as cleanup can take many seconds, trap Ctrl-C:
130 signal.signal(signal.SIGINT, signal_ignore)
131
132 counts = b.get_table("counts")
133 for k, v in sorted(counts.items(), key=lambda counts: counts[1].value):
134
Brenden Blanco42d60982017-04-24 14:31:28 -0700135 if re.match(b'mark_page_accessed', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700136 mpa = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000137
Brenden Blanco42d60982017-04-24 14:31:28 -0700138 if re.match(b'mark_buffer_dirty', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700139 mbd = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000140
Brenden Blanco42d60982017-04-24 14:31:28 -0700141 if re.match(b'add_to_page_cache_lru', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700142 apcl = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000143
Brenden Blanco42d60982017-04-24 14:31:28 -0700144 if re.match(b'account_page_dirtied', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700145 apd = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000146
Joel Fernandes2b348702018-02-27 20:38:42 -0800147 # total = total cache accesses without counting dirties
148 # misses = total of add to lru because of read misses
149 total = (mpa - mbd)
150 misses = (apcl - apd)
unixtest57abe5b2016-01-31 10:47:03 +0000151
Joel Fernandes2b348702018-02-27 20:38:42 -0800152 if total < 0:
153 total = 0
unixtest57abe5b2016-01-31 10:47:03 +0000154
Joel Fernandes2b348702018-02-27 20:38:42 -0800155 if misses < 0:
156 misses = 0
157
158 hits = total - misses
159
160 # If hits are < 0, then its possible misses are overestimated
161 # due to possibly page cache read ahead adding more pages than
162 # needed. In this case just assume misses as total and reset hits.
163 if hits < 0:
164 misses = total
165 hits = 0
unixtest57abe5b2016-01-31 10:47:03 +0000166
167 if debug:
Joel Fernandes2b348702018-02-27 20:38:42 -0800168 print("%d %d %d %d %d %d %d\n" %
169 (mpa, mbd, apcl, apd, total, misses, hits))
unixtest57abe5b2016-01-31 10:47:03 +0000170
171 counts.clear()
172
173 # Get memory info
174 mem = get_meminfo()
175 cached = int(mem["Cached"]) / 1024
176 buff = int(mem["Buffers"]) / 1024
177
178 if tstamp == 1:
179 print("%-8s " % strftime("%H:%M:%S"), end="")
Joel Fernandes2b348702018-02-27 20:38:42 -0800180 print("%8d %8d %8d %8d %12.0f %10.0f" %
181 (total, misses, hits, mbd, buff, cached))
unixtest57abe5b2016-01-31 10:47:03 +0000182
183 mpa = 0
184 mbd = 0
185 apcl = 0
186 apd = 0
Joel Fernandes2b348702018-02-27 20:38:42 -0800187 total = 0
unixtest57abe5b2016-01-31 10:47:03 +0000188 misses = 0
Joel Fernandes2b348702018-02-27 20:38:42 -0800189 hits = 0
unixtest57abe5b2016-01-31 10:47:03 +0000190 cached = 0
191 buff = 0
unixtest57abe5b2016-01-31 10:47:03 +0000192
193 if exiting:
194 print("Detaching...")
195 exit()