162306a36Sopenharmony_ci#!/usr/bin/env drgn 262306a36Sopenharmony_ci# 362306a36Sopenharmony_ci# Copyright (C) 2020 Roman Gushchin <guro@fb.com> 462306a36Sopenharmony_ci# Copyright (C) 2020 Facebook 562306a36Sopenharmony_ci 662306a36Sopenharmony_cifrom os import stat 762306a36Sopenharmony_ciimport argparse 862306a36Sopenharmony_ciimport sys 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cifrom drgn.helpers.linux import list_for_each_entry, list_empty 1162306a36Sopenharmony_cifrom drgn.helpers.linux import for_each_page 1262306a36Sopenharmony_cifrom drgn.helpers.linux.cpumask import for_each_online_cpu 1362306a36Sopenharmony_cifrom drgn.helpers.linux.percpu import per_cpu_ptr 1462306a36Sopenharmony_cifrom drgn import container_of, FaultError, Object, cast 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciDESC = """ 1862306a36Sopenharmony_ciThis is a drgn script to provide slab statistics for memory cgroups. 1962306a36Sopenharmony_ciIt supports cgroup v2 and v1 and can emulate memory.kmem.slabinfo 2062306a36Sopenharmony_ciinterface of cgroup v1. 2162306a36Sopenharmony_ciFor drgn, visit https://github.com/osandov/drgn. 2262306a36Sopenharmony_ci""" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciMEMCGS = {} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciOO_SHIFT = 16 2862306a36Sopenharmony_ciOO_MASK = ((1 << OO_SHIFT) - 1) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cidef err(s): 3262306a36Sopenharmony_ci print('slabinfo.py: error: %s' % s, file=sys.stderr, flush=True) 3362306a36Sopenharmony_ci sys.exit(1) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cidef find_memcg_ids(css=prog['root_mem_cgroup'].css, prefix=''): 3762306a36Sopenharmony_ci if not list_empty(css.children.address_of_()): 3862306a36Sopenharmony_ci for css in list_for_each_entry('struct cgroup_subsys_state', 3962306a36Sopenharmony_ci css.children.address_of_(), 4062306a36Sopenharmony_ci 'sibling'): 4162306a36Sopenharmony_ci name = prefix + '/' + css.cgroup.kn.name.string_().decode('utf-8') 4262306a36Sopenharmony_ci memcg = container_of(css, 'struct mem_cgroup', 'css') 4362306a36Sopenharmony_ci MEMCGS[css.cgroup.kn.id.value_()] = memcg 4462306a36Sopenharmony_ci find_memcg_ids(css, name) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cidef is_root_cache(s): 4862306a36Sopenharmony_ci try: 4962306a36Sopenharmony_ci return False if s.memcg_params.root_cache else True 5062306a36Sopenharmony_ci except AttributeError: 5162306a36Sopenharmony_ci return True 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cidef cache_name(s): 5562306a36Sopenharmony_ci if is_root_cache(s): 5662306a36Sopenharmony_ci return s.name.string_().decode('utf-8') 5762306a36Sopenharmony_ci else: 5862306a36Sopenharmony_ci return s.memcg_params.root_cache.name.string_().decode('utf-8') 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci# SLUB 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cidef oo_order(s): 6462306a36Sopenharmony_ci return s.oo.x >> OO_SHIFT 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cidef oo_objects(s): 6862306a36Sopenharmony_ci return s.oo.x & OO_MASK 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cidef count_partial(n, fn): 7262306a36Sopenharmony_ci nr_objs = 0 7362306a36Sopenharmony_ci for slab in list_for_each_entry('struct slab', n.partial.address_of_(), 7462306a36Sopenharmony_ci 'slab_list'): 7562306a36Sopenharmony_ci nr_objs += fn(slab) 7662306a36Sopenharmony_ci return nr_objs 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cidef count_free(slab): 8062306a36Sopenharmony_ci return slab.objects - slab.inuse 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cidef slub_get_slabinfo(s, cfg): 8462306a36Sopenharmony_ci nr_slabs = 0 8562306a36Sopenharmony_ci nr_objs = 0 8662306a36Sopenharmony_ci nr_free = 0 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci for node in range(cfg['nr_nodes']): 8962306a36Sopenharmony_ci n = s.node[node] 9062306a36Sopenharmony_ci nr_slabs += n.nr_slabs.counter.value_() 9162306a36Sopenharmony_ci nr_objs += n.total_objects.counter.value_() 9262306a36Sopenharmony_ci nr_free += count_partial(n, count_free) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return {'active_objs': nr_objs - nr_free, 9562306a36Sopenharmony_ci 'num_objs': nr_objs, 9662306a36Sopenharmony_ci 'active_slabs': nr_slabs, 9762306a36Sopenharmony_ci 'num_slabs': nr_slabs, 9862306a36Sopenharmony_ci 'objects_per_slab': oo_objects(s), 9962306a36Sopenharmony_ci 'cache_order': oo_order(s), 10062306a36Sopenharmony_ci 'limit': 0, 10162306a36Sopenharmony_ci 'batchcount': 0, 10262306a36Sopenharmony_ci 'shared': 0, 10362306a36Sopenharmony_ci 'shared_avail': 0} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cidef cache_show(s, cfg, objs): 10762306a36Sopenharmony_ci if cfg['allocator'] == 'SLUB': 10862306a36Sopenharmony_ci sinfo = slub_get_slabinfo(s, cfg) 10962306a36Sopenharmony_ci else: 11062306a36Sopenharmony_ci err('SLAB isn\'t supported yet') 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if cfg['shared_slab_pages']: 11362306a36Sopenharmony_ci sinfo['active_objs'] = objs 11462306a36Sopenharmony_ci sinfo['num_objs'] = objs 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci print('%-17s %6lu %6lu %6u %4u %4d' 11762306a36Sopenharmony_ci ' : tunables %4u %4u %4u' 11862306a36Sopenharmony_ci ' : slabdata %6lu %6lu %6lu' % ( 11962306a36Sopenharmony_ci cache_name(s), sinfo['active_objs'], sinfo['num_objs'], 12062306a36Sopenharmony_ci s.size, sinfo['objects_per_slab'], 1 << sinfo['cache_order'], 12162306a36Sopenharmony_ci sinfo['limit'], sinfo['batchcount'], sinfo['shared'], 12262306a36Sopenharmony_ci sinfo['active_slabs'], sinfo['num_slabs'], 12362306a36Sopenharmony_ci sinfo['shared_avail'])) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cidef detect_kernel_config(): 12762306a36Sopenharmony_ci cfg = {} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci cfg['nr_nodes'] = prog['nr_online_nodes'].value_() 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if prog.type('struct kmem_cache').members[1].name == 'flags': 13262306a36Sopenharmony_ci cfg['allocator'] = 'SLUB' 13362306a36Sopenharmony_ci elif prog.type('struct kmem_cache').members[1].name == 'batchcount': 13462306a36Sopenharmony_ci cfg['allocator'] = 'SLAB' 13562306a36Sopenharmony_ci else: 13662306a36Sopenharmony_ci err('Can\'t determine the slab allocator') 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci cfg['shared_slab_pages'] = False 13962306a36Sopenharmony_ci try: 14062306a36Sopenharmony_ci if prog.type('struct obj_cgroup'): 14162306a36Sopenharmony_ci cfg['shared_slab_pages'] = True 14262306a36Sopenharmony_ci except: 14362306a36Sopenharmony_ci pass 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return cfg 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cidef for_each_slab(prog): 14962306a36Sopenharmony_ci PGSlab = 1 << prog.constant('PG_slab') 15062306a36Sopenharmony_ci PGHead = 1 << prog.constant('PG_head') 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci for page in for_each_page(prog): 15362306a36Sopenharmony_ci try: 15462306a36Sopenharmony_ci if page.flags.value_() & PGSlab: 15562306a36Sopenharmony_ci yield cast('struct slab *', page) 15662306a36Sopenharmony_ci except FaultError: 15762306a36Sopenharmony_ci pass 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cidef main(): 16162306a36Sopenharmony_ci parser = argparse.ArgumentParser(description=DESC, 16262306a36Sopenharmony_ci formatter_class= 16362306a36Sopenharmony_ci argparse.RawTextHelpFormatter) 16462306a36Sopenharmony_ci parser.add_argument('cgroup', metavar='CGROUP', 16562306a36Sopenharmony_ci help='Target memory cgroup') 16662306a36Sopenharmony_ci args = parser.parse_args() 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci try: 16962306a36Sopenharmony_ci cgroup_id = stat(args.cgroup).st_ino 17062306a36Sopenharmony_ci find_memcg_ids() 17162306a36Sopenharmony_ci memcg = MEMCGS[cgroup_id] 17262306a36Sopenharmony_ci except KeyError: 17362306a36Sopenharmony_ci err('Can\'t find the memory cgroup') 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci cfg = detect_kernel_config() 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci print('# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab>' 17862306a36Sopenharmony_ci ' : tunables <limit> <batchcount> <sharedfactor>' 17962306a36Sopenharmony_ci ' : slabdata <active_slabs> <num_slabs> <sharedavail>') 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if cfg['shared_slab_pages']: 18262306a36Sopenharmony_ci obj_cgroups = set() 18362306a36Sopenharmony_ci stats = {} 18462306a36Sopenharmony_ci caches = {} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci # find memcg pointers belonging to the specified cgroup 18762306a36Sopenharmony_ci obj_cgroups.add(memcg.objcg.value_()) 18862306a36Sopenharmony_ci for ptr in list_for_each_entry('struct obj_cgroup', 18962306a36Sopenharmony_ci memcg.objcg_list.address_of_(), 19062306a36Sopenharmony_ci 'list'): 19162306a36Sopenharmony_ci obj_cgroups.add(ptr.value_()) 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci # look over all slab folios and look for objects belonging 19462306a36Sopenharmony_ci # to the given memory cgroup 19562306a36Sopenharmony_ci for slab in for_each_slab(prog): 19662306a36Sopenharmony_ci objcg_vec_raw = slab.memcg_data.value_() 19762306a36Sopenharmony_ci if objcg_vec_raw == 0: 19862306a36Sopenharmony_ci continue 19962306a36Sopenharmony_ci cache = slab.slab_cache 20062306a36Sopenharmony_ci if not cache: 20162306a36Sopenharmony_ci continue 20262306a36Sopenharmony_ci addr = cache.value_() 20362306a36Sopenharmony_ci caches[addr] = cache 20462306a36Sopenharmony_ci # clear the lowest bit to get the true obj_cgroups 20562306a36Sopenharmony_ci objcg_vec = Object(prog, 'struct obj_cgroup **', 20662306a36Sopenharmony_ci value=objcg_vec_raw & ~1) 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if addr not in stats: 20962306a36Sopenharmony_ci stats[addr] = 0 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci for i in range(oo_objects(cache)): 21262306a36Sopenharmony_ci if objcg_vec[i].value_() in obj_cgroups: 21362306a36Sopenharmony_ci stats[addr] += 1 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci for addr in caches: 21662306a36Sopenharmony_ci if stats[addr] > 0: 21762306a36Sopenharmony_ci cache_show(caches[addr], cfg, stats[addr]) 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci else: 22062306a36Sopenharmony_ci for s in list_for_each_entry('struct kmem_cache', 22162306a36Sopenharmony_ci memcg.kmem_caches.address_of_(), 22262306a36Sopenharmony_ci 'memcg_params.kmem_caches_node'): 22362306a36Sopenharmony_ci cache_show(s, cfg, None) 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cimain() 227