blob: 913fce4a9c0813ab0450def21b2b172298ff7d8b [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 = {};
97 u64 zero = 0, *val;
98 u64 ip;
99
Naveen N. Rao4afa96a2016-05-03 14:54:21 +0530100 key.ip = PT_REGS_IP(ctx);
unixtest57abe5b2016-01-31 10:47:03 +0000101 val = counts.lookup_or_init(&key, &zero); // update counter
102 (*val)++;
103 return 0;
104}
105
106"""
107b = BPF(text=bpf_text)
108b.attach_kprobe(event="add_to_page_cache_lru", fn_name="do_count")
109b.attach_kprobe(event="mark_page_accessed", fn_name="do_count")
110b.attach_kprobe(event="account_page_dirtied", fn_name="do_count")
111b.attach_kprobe(event="mark_buffer_dirty", fn_name="do_count")
112
113# header
114if tstamp:
115 print("%-8s " % "TIME", end="")
Joel Fernandes2b348702018-02-27 20:38:42 -0800116print("%8s %8s %8s %8s %12s %10s" %
117 ("TOTAL", "MISSES", "HITS", "DIRTIES", "BUFFERS_MB", "CACHED_MB"))
unixtest57abe5b2016-01-31 10:47:03 +0000118
119loop = 0
120exiting = 0
121while 1:
122 if count > 0:
123 loop += 1
124 if loop > count:
125 exit()
126
127 try:
128 sleep(interval)
129 except KeyboardInterrupt:
130 exiting = 1
131 # as cleanup can take many seconds, trap Ctrl-C:
132 signal.signal(signal.SIGINT, signal_ignore)
133
134 counts = b.get_table("counts")
135 for k, v in sorted(counts.items(), key=lambda counts: counts[1].value):
136
Brenden Blanco42d60982017-04-24 14:31:28 -0700137 if re.match(b'mark_page_accessed', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700138 mpa = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000139
Brenden Blanco42d60982017-04-24 14:31:28 -0700140 if re.match(b'mark_buffer_dirty', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700141 mbd = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000142
Brenden Blanco42d60982017-04-24 14:31:28 -0700143 if re.match(b'add_to_page_cache_lru', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700144 apcl = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000145
Brenden Blanco42d60982017-04-24 14:31:28 -0700146 if re.match(b'account_page_dirtied', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700147 apd = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000148
Joel Fernandes2b348702018-02-27 20:38:42 -0800149 # total = total cache accesses without counting dirties
150 # misses = total of add to lru because of read misses
151 total = (mpa - mbd)
152 misses = (apcl - apd)
unixtest57abe5b2016-01-31 10:47:03 +0000153
Joel Fernandes2b348702018-02-27 20:38:42 -0800154 if total < 0:
155 total = 0
unixtest57abe5b2016-01-31 10:47:03 +0000156
Joel Fernandes2b348702018-02-27 20:38:42 -0800157 if misses < 0:
158 misses = 0
159
160 hits = total - misses
161
162 # If hits are < 0, then its possible misses are overestimated
163 # due to possibly page cache read ahead adding more pages than
164 # needed. In this case just assume misses as total and reset hits.
165 if hits < 0:
166 misses = total
167 hits = 0
unixtest57abe5b2016-01-31 10:47:03 +0000168
169 if debug:
Joel Fernandes2b348702018-02-27 20:38:42 -0800170 print("%d %d %d %d %d %d %d\n" %
171 (mpa, mbd, apcl, apd, total, misses, hits))
unixtest57abe5b2016-01-31 10:47:03 +0000172
173 counts.clear()
174
175 # Get memory info
176 mem = get_meminfo()
177 cached = int(mem["Cached"]) / 1024
178 buff = int(mem["Buffers"]) / 1024
179
180 if tstamp == 1:
181 print("%-8s " % strftime("%H:%M:%S"), end="")
Joel Fernandes2b348702018-02-27 20:38:42 -0800182 print("%8d %8d %8d %8d %12.0f %10.0f" %
183 (total, misses, hits, mbd, buff, cached))
unixtest57abe5b2016-01-31 10:47:03 +0000184
185 mpa = 0
186 mbd = 0
187 apcl = 0
188 apd = 0
Joel Fernandes2b348702018-02-27 20:38:42 -0800189 total = 0
unixtest57abe5b2016-01-31 10:47:03 +0000190 misses = 0
Joel Fernandes2b348702018-02-27 20:38:42 -0800191 hits = 0
unixtest57abe5b2016-01-31 10:47:03 +0000192 cached = 0
193 buff = 0
unixtest57abe5b2016-01-31 10:47:03 +0000194
195 if exiting:
196 print("Detaching...")
197 exit()