1*4882a593Smuzhiyun#!/usr/bin/env drgn 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# Copyright (C) 2020 Roman Gushchin <guro@fb.com> 4*4882a593Smuzhiyun# Copyright (C) 2020 Facebook 5*4882a593Smuzhiyun 6*4882a593Smuzhiyunfrom os import stat 7*4882a593Smuzhiyunimport argparse 8*4882a593Smuzhiyunimport sys 9*4882a593Smuzhiyun 10*4882a593Smuzhiyunfrom drgn.helpers.linux import list_for_each_entry, list_empty 11*4882a593Smuzhiyunfrom drgn.helpers.linux import for_each_page 12*4882a593Smuzhiyunfrom drgn.helpers.linux.cpumask import for_each_online_cpu 13*4882a593Smuzhiyunfrom drgn.helpers.linux.percpu import per_cpu_ptr 14*4882a593Smuzhiyunfrom drgn import container_of, FaultError, Object 15*4882a593Smuzhiyun 16*4882a593Smuzhiyun 17*4882a593SmuzhiyunDESC = """ 18*4882a593SmuzhiyunThis is a drgn script to provide slab statistics for memory cgroups. 19*4882a593SmuzhiyunIt supports cgroup v2 and v1 and can emulate memory.kmem.slabinfo 20*4882a593Smuzhiyuninterface of cgroup v1. 21*4882a593SmuzhiyunFor drgn, visit https://github.com/osandov/drgn. 22*4882a593Smuzhiyun""" 23*4882a593Smuzhiyun 24*4882a593Smuzhiyun 25*4882a593SmuzhiyunMEMCGS = {} 26*4882a593Smuzhiyun 27*4882a593SmuzhiyunOO_SHIFT = 16 28*4882a593SmuzhiyunOO_MASK = ((1 << OO_SHIFT) - 1) 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun 31*4882a593Smuzhiyundef err(s): 32*4882a593Smuzhiyun print('slabinfo.py: error: %s' % s, file=sys.stderr, flush=True) 33*4882a593Smuzhiyun sys.exit(1) 34*4882a593Smuzhiyun 35*4882a593Smuzhiyun 36*4882a593Smuzhiyundef find_memcg_ids(css=prog['root_mem_cgroup'].css, prefix=''): 37*4882a593Smuzhiyun if not list_empty(css.children.address_of_()): 38*4882a593Smuzhiyun for css in list_for_each_entry('struct cgroup_subsys_state', 39*4882a593Smuzhiyun css.children.address_of_(), 40*4882a593Smuzhiyun 'sibling'): 41*4882a593Smuzhiyun name = prefix + '/' + css.cgroup.kn.name.string_().decode('utf-8') 42*4882a593Smuzhiyun memcg = container_of(css, 'struct mem_cgroup', 'css') 43*4882a593Smuzhiyun MEMCGS[css.cgroup.kn.id.value_()] = memcg 44*4882a593Smuzhiyun find_memcg_ids(css, name) 45*4882a593Smuzhiyun 46*4882a593Smuzhiyun 47*4882a593Smuzhiyundef is_root_cache(s): 48*4882a593Smuzhiyun try: 49*4882a593Smuzhiyun return False if s.memcg_params.root_cache else True 50*4882a593Smuzhiyun except AttributeError: 51*4882a593Smuzhiyun return True 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun 54*4882a593Smuzhiyundef cache_name(s): 55*4882a593Smuzhiyun if is_root_cache(s): 56*4882a593Smuzhiyun return s.name.string_().decode('utf-8') 57*4882a593Smuzhiyun else: 58*4882a593Smuzhiyun return s.memcg_params.root_cache.name.string_().decode('utf-8') 59*4882a593Smuzhiyun 60*4882a593Smuzhiyun 61*4882a593Smuzhiyun# SLUB 62*4882a593Smuzhiyun 63*4882a593Smuzhiyundef oo_order(s): 64*4882a593Smuzhiyun return s.oo.x >> OO_SHIFT 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun 67*4882a593Smuzhiyundef oo_objects(s): 68*4882a593Smuzhiyun return s.oo.x & OO_MASK 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun 71*4882a593Smuzhiyundef count_partial(n, fn): 72*4882a593Smuzhiyun nr_pages = 0 73*4882a593Smuzhiyun for page in list_for_each_entry('struct page', n.partial.address_of_(), 74*4882a593Smuzhiyun 'lru'): 75*4882a593Smuzhiyun nr_pages += fn(page) 76*4882a593Smuzhiyun return nr_pages 77*4882a593Smuzhiyun 78*4882a593Smuzhiyun 79*4882a593Smuzhiyundef count_free(page): 80*4882a593Smuzhiyun return page.objects - page.inuse 81*4882a593Smuzhiyun 82*4882a593Smuzhiyun 83*4882a593Smuzhiyundef slub_get_slabinfo(s, cfg): 84*4882a593Smuzhiyun nr_slabs = 0 85*4882a593Smuzhiyun nr_objs = 0 86*4882a593Smuzhiyun nr_free = 0 87*4882a593Smuzhiyun 88*4882a593Smuzhiyun for node in range(cfg['nr_nodes']): 89*4882a593Smuzhiyun n = s.node[node] 90*4882a593Smuzhiyun nr_slabs += n.nr_slabs.counter.value_() 91*4882a593Smuzhiyun nr_objs += n.total_objects.counter.value_() 92*4882a593Smuzhiyun nr_free += count_partial(n, count_free) 93*4882a593Smuzhiyun 94*4882a593Smuzhiyun return {'active_objs': nr_objs - nr_free, 95*4882a593Smuzhiyun 'num_objs': nr_objs, 96*4882a593Smuzhiyun 'active_slabs': nr_slabs, 97*4882a593Smuzhiyun 'num_slabs': nr_slabs, 98*4882a593Smuzhiyun 'objects_per_slab': oo_objects(s), 99*4882a593Smuzhiyun 'cache_order': oo_order(s), 100*4882a593Smuzhiyun 'limit': 0, 101*4882a593Smuzhiyun 'batchcount': 0, 102*4882a593Smuzhiyun 'shared': 0, 103*4882a593Smuzhiyun 'shared_avail': 0} 104*4882a593Smuzhiyun 105*4882a593Smuzhiyun 106*4882a593Smuzhiyundef cache_show(s, cfg, objs): 107*4882a593Smuzhiyun if cfg['allocator'] == 'SLUB': 108*4882a593Smuzhiyun sinfo = slub_get_slabinfo(s, cfg) 109*4882a593Smuzhiyun else: 110*4882a593Smuzhiyun err('SLAB isn\'t supported yet') 111*4882a593Smuzhiyun 112*4882a593Smuzhiyun if cfg['shared_slab_pages']: 113*4882a593Smuzhiyun sinfo['active_objs'] = objs 114*4882a593Smuzhiyun sinfo['num_objs'] = objs 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun print('%-17s %6lu %6lu %6u %4u %4d' 117*4882a593Smuzhiyun ' : tunables %4u %4u %4u' 118*4882a593Smuzhiyun ' : slabdata %6lu %6lu %6lu' % ( 119*4882a593Smuzhiyun cache_name(s), sinfo['active_objs'], sinfo['num_objs'], 120*4882a593Smuzhiyun s.size, sinfo['objects_per_slab'], 1 << sinfo['cache_order'], 121*4882a593Smuzhiyun sinfo['limit'], sinfo['batchcount'], sinfo['shared'], 122*4882a593Smuzhiyun sinfo['active_slabs'], sinfo['num_slabs'], 123*4882a593Smuzhiyun sinfo['shared_avail'])) 124*4882a593Smuzhiyun 125*4882a593Smuzhiyun 126*4882a593Smuzhiyundef detect_kernel_config(): 127*4882a593Smuzhiyun cfg = {} 128*4882a593Smuzhiyun 129*4882a593Smuzhiyun cfg['nr_nodes'] = prog['nr_online_nodes'].value_() 130*4882a593Smuzhiyun 131*4882a593Smuzhiyun if prog.type('struct kmem_cache').members[1].name == 'flags': 132*4882a593Smuzhiyun cfg['allocator'] = 'SLUB' 133*4882a593Smuzhiyun elif prog.type('struct kmem_cache').members[1].name == 'batchcount': 134*4882a593Smuzhiyun cfg['allocator'] = 'SLAB' 135*4882a593Smuzhiyun else: 136*4882a593Smuzhiyun err('Can\'t determine the slab allocator') 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun cfg['shared_slab_pages'] = False 139*4882a593Smuzhiyun try: 140*4882a593Smuzhiyun if prog.type('struct obj_cgroup'): 141*4882a593Smuzhiyun cfg['shared_slab_pages'] = True 142*4882a593Smuzhiyun except: 143*4882a593Smuzhiyun pass 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun return cfg 146*4882a593Smuzhiyun 147*4882a593Smuzhiyun 148*4882a593Smuzhiyundef for_each_slab_page(prog): 149*4882a593Smuzhiyun PGSlab = 1 << prog.constant('PG_slab') 150*4882a593Smuzhiyun PGHead = 1 << prog.constant('PG_head') 151*4882a593Smuzhiyun 152*4882a593Smuzhiyun for page in for_each_page(prog): 153*4882a593Smuzhiyun try: 154*4882a593Smuzhiyun if page.flags.value_() & PGSlab: 155*4882a593Smuzhiyun yield page 156*4882a593Smuzhiyun except FaultError: 157*4882a593Smuzhiyun pass 158*4882a593Smuzhiyun 159*4882a593Smuzhiyun 160*4882a593Smuzhiyundef main(): 161*4882a593Smuzhiyun parser = argparse.ArgumentParser(description=DESC, 162*4882a593Smuzhiyun formatter_class= 163*4882a593Smuzhiyun argparse.RawTextHelpFormatter) 164*4882a593Smuzhiyun parser.add_argument('cgroup', metavar='CGROUP', 165*4882a593Smuzhiyun help='Target memory cgroup') 166*4882a593Smuzhiyun args = parser.parse_args() 167*4882a593Smuzhiyun 168*4882a593Smuzhiyun try: 169*4882a593Smuzhiyun cgroup_id = stat(args.cgroup).st_ino 170*4882a593Smuzhiyun find_memcg_ids() 171*4882a593Smuzhiyun memcg = MEMCGS[cgroup_id] 172*4882a593Smuzhiyun except KeyError: 173*4882a593Smuzhiyun err('Can\'t find the memory cgroup') 174*4882a593Smuzhiyun 175*4882a593Smuzhiyun cfg = detect_kernel_config() 176*4882a593Smuzhiyun 177*4882a593Smuzhiyun print('# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab>' 178*4882a593Smuzhiyun ' : tunables <limit> <batchcount> <sharedfactor>' 179*4882a593Smuzhiyun ' : slabdata <active_slabs> <num_slabs> <sharedavail>') 180*4882a593Smuzhiyun 181*4882a593Smuzhiyun if cfg['shared_slab_pages']: 182*4882a593Smuzhiyun obj_cgroups = set() 183*4882a593Smuzhiyun stats = {} 184*4882a593Smuzhiyun caches = {} 185*4882a593Smuzhiyun 186*4882a593Smuzhiyun # find memcg pointers belonging to the specified cgroup 187*4882a593Smuzhiyun obj_cgroups.add(memcg.objcg.value_()) 188*4882a593Smuzhiyun for ptr in list_for_each_entry('struct obj_cgroup', 189*4882a593Smuzhiyun memcg.objcg_list.address_of_(), 190*4882a593Smuzhiyun 'list'): 191*4882a593Smuzhiyun obj_cgroups.add(ptr.value_()) 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun # look over all slab pages, belonging to non-root memcgs 194*4882a593Smuzhiyun # and look for objects belonging to the given memory cgroup 195*4882a593Smuzhiyun for page in for_each_slab_page(prog): 196*4882a593Smuzhiyun objcg_vec_raw = page.memcg_data.value_() 197*4882a593Smuzhiyun if objcg_vec_raw == 0: 198*4882a593Smuzhiyun continue 199*4882a593Smuzhiyun cache = page.slab_cache 200*4882a593Smuzhiyun if not cache: 201*4882a593Smuzhiyun continue 202*4882a593Smuzhiyun addr = cache.value_() 203*4882a593Smuzhiyun caches[addr] = cache 204*4882a593Smuzhiyun # clear the lowest bit to get the true obj_cgroups 205*4882a593Smuzhiyun objcg_vec = Object(prog, 'struct obj_cgroup **', 206*4882a593Smuzhiyun value=objcg_vec_raw & ~1) 207*4882a593Smuzhiyun 208*4882a593Smuzhiyun if addr not in stats: 209*4882a593Smuzhiyun stats[addr] = 0 210*4882a593Smuzhiyun 211*4882a593Smuzhiyun for i in range(oo_objects(cache)): 212*4882a593Smuzhiyun if objcg_vec[i].value_() in obj_cgroups: 213*4882a593Smuzhiyun stats[addr] += 1 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun for addr in caches: 216*4882a593Smuzhiyun if stats[addr] > 0: 217*4882a593Smuzhiyun cache_show(caches[addr], cfg, stats[addr]) 218*4882a593Smuzhiyun 219*4882a593Smuzhiyun else: 220*4882a593Smuzhiyun for s in list_for_each_entry('struct kmem_cache', 221*4882a593Smuzhiyun memcg.kmem_caches.address_of_(), 222*4882a593Smuzhiyun 'memcg_params.kmem_caches_node'): 223*4882a593Smuzhiyun cache_show(s, cfg, None) 224*4882a593Smuzhiyun 225*4882a593Smuzhiyun 226*4882a593Smuzhiyunmain() 227