xref: /OK3568_Linux_fs/kernel/tools/cgroup/memcg_slabinfo.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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