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