• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2# @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
20from bcc.utils import printb
21from time import sleep, strftime
22import argparse
23from 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")
44parser.add_argument("--ebpf", action="store_true",
45    help=argparse.SUPPRESS)
46args = 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
56# define BPF program
57bpf_text = """
58#include <uapi/linux/ptrace.h>
59#include <linux/mm.h>
60#include <linux/kasan.h>
61
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
68#ifdef CONFIG_SLUB
69#include <linux/slub_def.h>
70#else
71#include <linux/slab_def.h>
72#endif
73
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 = {};
92    const char *name = cachep->name;
93    bpf_probe_read_kernel(&info.name, sizeof(info.name), name);
94
95    struct val_t *valp, zero = {};
96    valp = counts.lookup_or_try_init(&info, &zero);
97    if (valp) {
98        valp->count++;
99        valp->size += cachep->size;
100    }
101
102    return 0;
103}
104"""
105if debug or args.ebpf:
106    print(bpf_text)
107    if args.ebpf:
108        exit()
109
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)):
137        printb(b"%-32s %6d %10d" % (k.name, v.count, v.size))
138
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()
148