blob: a86481edd01988561f1ae89228eef6f7f2839362 [file] [log] [blame]
Alexey Ivanovcc01a9c2019-01-16 09:50:46 -08001#!/usr/bin/python
Brendan Gregg203b4c92016-10-18 12:10:24 -07002# @lint-avoid-python-3-compatibility-imports
3#
4# slabratetop Summarize kmem_cache_alloc() calls.
5# For Linux, uses BCC, eBPF.
6#
7# USAGE: slabratetop [-h] [-C] [-r MAXROWS] [interval] [count]
8#
9# This uses in-kernel BPF maps to store cache summaries for efficiency.
10#
11# SEE ALSO: slabtop(1), which shows the cache volumes.
12#
13# Copyright 2016 Netflix, Inc.
14# Licensed under the Apache License, Version 2.0 (the "License")
15#
16# 15-Oct-2016 Brendan Gregg Created this.
17
18from __future__ import print_function
19from bcc import BPF
Brenden Blanco42d60982017-04-24 14:31:28 -070020from bcc.utils import printb
Brendan Gregg203b4c92016-10-18 12:10:24 -070021from time import sleep, strftime
22import argparse
Brendan Gregg203b4c92016-10-18 12:10:24 -070023from subprocess import call
24
25# arguments
26examples = """examples:
27 ./slabratetop # kmem_cache_alloc() top, 1 second refresh
28 ./slabratetop -C # don't clear the screen
29 ./slabratetop 5 # 5 second summaries
30 ./slabratetop 5 10 # 5 second summaries, 10 times only
31"""
32parser = argparse.ArgumentParser(
33 description="Kernel SLAB/SLUB memory cache allocation rate top",
34 formatter_class=argparse.RawDescriptionHelpFormatter,
35 epilog=examples)
36parser.add_argument("-C", "--noclear", action="store_true",
37 help="don't clear the screen")
38parser.add_argument("-r", "--maxrows", default=20,
39 help="maximum rows to print, default 20")
40parser.add_argument("interval", nargs="?", default=1,
41 help="output interval, in seconds")
42parser.add_argument("count", nargs="?", default=99999999,
43 help="number of outputs")
Nathan Scottcf0792f2018-02-02 16:56:50 +110044parser.add_argument("--ebpf", action="store_true",
45 help=argparse.SUPPRESS)
Brendan Gregg203b4c92016-10-18 12:10:24 -070046args = parser.parse_args()
47interval = int(args.interval)
48countdown = int(args.count)
49maxrows = int(args.maxrows)
50clear = not int(args.noclear)
51debug = 0
52
53# linux stats
54loadavg = "/proc/loadavg"
55
Brendan Gregg203b4c92016-10-18 12:10:24 -070056# define BPF program
57bpf_text = """
58#include <uapi/linux/ptrace.h>
59#include <linux/mm.h>
Jerome Marchand33393d32021-02-18 11:33:20 +010060#include <linux/kasan.h>
Daniel Rank788303e2020-09-27 16:55:22 -070061
62// memcg_cache_params is a part of kmem_cache, but is not publicly exposed in
63// kernel versions 5.4 to 5.8. Define an empty struct for it here to allow the
64// bpf program to compile. It has been completely removed in kernel version
65// 5.9, but it does not hurt to have it here for versions 5.4 to 5.8.
66struct memcg_cache_params {};
67
Gary Linfa103452018-03-14 18:14:56 +080068#ifdef CONFIG_SLUB
Brendan Gregg203b4c92016-10-18 12:10:24 -070069#include <linux/slub_def.h>
Gary Linfa103452018-03-14 18:14:56 +080070#else
71#include <linux/slab_def.h>
72#endif
Brendan Gregg203b4c92016-10-18 12:10:24 -070073
74#define CACHE_NAME_SIZE 32
75
76// the key for the output summary
77struct info_t {
78 char name[CACHE_NAME_SIZE];
79};
80
81// the value of the output summary
82struct val_t {
83 u64 count;
84 u64 size;
85};
86
87BPF_HASH(counts, struct info_t, struct val_t);
88
89int kprobe__kmem_cache_alloc(struct pt_regs *ctx, struct kmem_cache *cachep)
90{
91 struct info_t info = {};
Paul Chaignonf86f7e82018-06-14 02:20:03 +020092 const char *name = cachep->name;
Sumanth Korikkar7f6066d2020-05-20 10:49:56 -050093 bpf_probe_read_kernel(&info.name, sizeof(info.name), name);
Brendan Gregg203b4c92016-10-18 12:10:24 -070094
95 struct val_t *valp, zero = {};
yonghong-song82f43022019-10-31 08:16:12 -070096 valp = counts.lookup_or_try_init(&info, &zero);
Philip Gladstoneba64f032019-09-20 01:12:01 -040097 if (valp) {
98 valp->count++;
99 valp->size += cachep->size;
100 }
Brendan Gregg203b4c92016-10-18 12:10:24 -0700101
102 return 0;
103}
104"""
Nathan Scottcf0792f2018-02-02 16:56:50 +1100105if debug or args.ebpf:
Brendan Gregg203b4c92016-10-18 12:10:24 -0700106 print(bpf_text)
Nathan Scottcf0792f2018-02-02 16:56:50 +1100107 if args.ebpf:
108 exit()
Brendan Gregg203b4c92016-10-18 12:10:24 -0700109
110# initialize BPF
111b = BPF(text=bpf_text)
112
113print('Tracing... Output every %d secs. Hit Ctrl-C to end' % interval)
114
115# output
116exiting = 0
117while 1:
118 try:
119 sleep(interval)
120 except KeyboardInterrupt:
121 exiting = 1
122
123 # header
124 if clear:
125 call("clear")
126 else:
127 print()
128 with open(loadavg) as stats:
129 print("%-8s loadavg: %s" % (strftime("%H:%M:%S"), stats.read()))
130 print("%-32s %6s %10s" % ("CACHE", "ALLOCS", "BYTES"))
131
132 # by-TID output
133 counts = b.get_table("counts")
134 line = 0
135 for k, v in reversed(sorted(counts.items(),
136 key=lambda counts: counts[1].size)):
Brenden Blanco42d60982017-04-24 14:31:28 -0700137 printb(b"%-32s %6d %10d" % (k.name, v.count, v.size))
Brendan Gregg203b4c92016-10-18 12:10:24 -0700138
139 line += 1
140 if line >= maxrows:
141 break
142 counts.clear()
143
144 countdown -= 1
145 if exiting or countdown == 0:
146 print("Detaching...")
147 exit()