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