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