blob: 90a55b05132efa2c89de5009e84022d03d978733 [file] [log] [blame]
Alexey Ivanov777e8022019-01-03 13:46:38 -08001#!/usr/bin/env python
unixtest57abe5b2016-01-31 10:47:03 +00002#
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
Marko Myllynen27e7aea2018-09-26 20:09:07 +030022import argparse
unixtest57abe5b2016-01-31 10:47:03 +000023import 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
unixtest57abe5b2016-01-31 10:47:03 +000043mpa = 0
44mbd = 0
45apcl = 0
46apd = 0
Joel Fernandes2b348702018-02-27 20:38:42 -080047total = 0
unixtest57abe5b2016-01-31 10:47:03 +000048misses = 0
Joel Fernandes2b348702018-02-27 20:38:42 -080049hits = 0
unixtest57abe5b2016-01-31 10:47:03 +000050debug = 0
51
unixtest57abe5b2016-01-31 10:47:03 +000052# arguments
Marko Myllynen27e7aea2018-09-26 20:09:07 +030053parser = 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)
unixtest57abe5b2016-01-31 10:47:03 +000068
Marko Myllynen27e7aea2018-09-26 20:09:07 +030069# define BPF program
unixtest57abe5b2016-01-31 10:47:03 +000070bpf_text = """
unixtest57abe5b2016-01-31 10:47:03 +000071#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 = {};
unixtest57abe5b2016-01-31 10:47:03 +000080 u64 ip;
81
Naveen N. Rao4afa96a2016-05-03 14:54:21 +053082 key.ip = PT_REGS_IP(ctx);
Javier Honduvilla Coto64bf9652018-08-01 06:50:19 +020083 counts.increment(key); // update counter
unixtest57abe5b2016-01-31 10:47:03 +000084 return 0;
85}
86
87"""
Marko Myllynen27e7aea2018-09-26 20:09:07 +030088
89if debug or args.ebpf:
90 print(bpf_text)
91 if args.ebpf:
92 exit()
93
94# load BPF program
unixtest57abe5b2016-01-31 10:47:03 +000095b = 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="")
Joel Fernandes2b348702018-02-27 20:38:42 -0800104print("%8s %8s %8s %8s %12s %10s" %
105 ("TOTAL", "MISSES", "HITS", "DIRTIES", "BUFFERS_MB", "CACHED_MB"))
unixtest57abe5b2016-01-31 10:47:03 +0000106
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
Marko Myllynen27e7aea2018-09-26 20:09:07 +0300122 counts = b["counts"]
unixtest57abe5b2016-01-31 10:47:03 +0000123 for k, v in sorted(counts.items(), key=lambda counts: counts[1].value):
124
Brenden Blanco42d60982017-04-24 14:31:28 -0700125 if re.match(b'mark_page_accessed', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700126 mpa = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000127
Brenden Blanco42d60982017-04-24 14:31:28 -0700128 if re.match(b'mark_buffer_dirty', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700129 mbd = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000130
Brenden Blanco42d60982017-04-24 14:31:28 -0700131 if re.match(b'add_to_page_cache_lru', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700132 apcl = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000133
Brenden Blanco42d60982017-04-24 14:31:28 -0700134 if re.match(b'account_page_dirtied', b.ksym(k.ip)) is not None:
chantraa2d669c2016-07-29 14:10:15 -0700135 apd = max(0, v.value)
unixtest57abe5b2016-01-31 10:47:03 +0000136
Joel Fernandes2b348702018-02-27 20:38:42 -0800137 # 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)
unixtest57abe5b2016-01-31 10:47:03 +0000141
Joel Fernandes2b348702018-02-27 20:38:42 -0800142 if total < 0:
143 total = 0
unixtest57abe5b2016-01-31 10:47:03 +0000144
Joel Fernandes2b348702018-02-27 20:38:42 -0800145 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
unixtest57abe5b2016-01-31 10:47:03 +0000156
157 if debug:
Joel Fernandes2b348702018-02-27 20:38:42 -0800158 print("%d %d %d %d %d %d %d\n" %
159 (mpa, mbd, apcl, apd, total, misses, hits))
unixtest57abe5b2016-01-31 10:47:03 +0000160
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
Marko Myllynen27e7aea2018-09-26 20:09:07 +0300168 if tstamp:
unixtest57abe5b2016-01-31 10:47:03 +0000169 print("%-8s " % strftime("%H:%M:%S"), end="")
Joel Fernandes2b348702018-02-27 20:38:42 -0800170 print("%8d %8d %8d %8d %12.0f %10.0f" %
171 (total, misses, hits, mbd, buff, cached))
unixtest57abe5b2016-01-31 10:47:03 +0000172
173 mpa = 0
174 mbd = 0
175 apcl = 0
176 apd = 0
Joel Fernandes2b348702018-02-27 20:38:42 -0800177 total = 0
unixtest57abe5b2016-01-31 10:47:03 +0000178 misses = 0
Joel Fernandes2b348702018-02-27 20:38:42 -0800179 hits = 0
unixtest57abe5b2016-01-31 10:47:03 +0000180 cached = 0
181 buff = 0
unixtest57abe5b2016-01-31 10:47:03 +0000182
183 if exiting:
184 print("Detaching...")
185 exit()