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