xref: /linux/tools/testing/selftests/damon/drgn_dump_damon_status.py (revision bba2c3615bd6cfee7456d1130f2e6b01b3f4e9ba)
1#!/usr/bin/env drgn
2# SPDX-License-Identifier: GPL-2.0
3
4'''
5Read DAMON context data and dump as a json string.
6'''
7import drgn
8from drgn import FaultError, NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof
9from drgn.helpers.common import *
10from drgn.helpers.linux import *
11
12import json
13import sys
14
15if "prog" not in globals():
16    try:
17        prog = drgn.get_default_prog()
18    except drgn.NoDefaultProgramError:
19        prog = drgn.program_from_kernel()
20        drgn.set_default_prog(prog)
21
22def to_dict(object, attr_name_converter):
23    d = {}
24    for attr_name, converter in attr_name_converter:
25        d[attr_name] = converter(getattr(object, attr_name))
26    return d
27
28def ops_to_dict(ops):
29    return to_dict(ops, [
30        ['id', int],
31        ])
32
33def intervals_goal_to_dict(goal):
34    return to_dict(goal, [
35        ['access_bp', int],
36        ['aggrs', int],
37        ['min_sample_us', int],
38        ['max_sample_us', int],
39        ])
40
41def attrs_to_dict(attrs):
42    return to_dict(attrs, [
43        ['sample_interval', int],
44        ['aggr_interval', int],
45        ['ops_update_interval', int],
46        ['intervals_goal', intervals_goal_to_dict],
47        ['min_nr_regions', int],
48        ['max_nr_regions', int],
49        ])
50
51def addr_range_to_dict(addr_range):
52    return to_dict(addr_range, [
53        ['start', int],
54        ['end', int],
55        ])
56
57def region_to_dict(region):
58    return to_dict(region, [
59        ['ar', addr_range_to_dict],
60        ['sampling_addr', int],
61        ['nr_accesses', int],
62        ['nr_accesses_bp', int],
63        ['age', int],
64        ])
65
66def regions_to_list(regions):
67    return [region_to_dict(r)
68            for r in list_for_each_entry(
69                'struct damon_region', regions.address_of_(), 'list')]
70
71def target_to_dict(target):
72    return to_dict(target, [
73        ['pid', int],
74        ['nr_regions', int],
75        ['regions_list', regions_to_list],
76        ['obsolete', bool],
77        ])
78
79def targets_to_list(targets):
80    return [target_to_dict(t)
81            for t in list_for_each_entry(
82                'struct damon_target', targets.address_of_(), 'list')]
83
84def damos_access_pattern_to_dict(pattern):
85    return to_dict(pattern, [
86        ['min_sz_region', int],
87        ['max_sz_region', int],
88        ['min_nr_accesses', int],
89        ['max_nr_accesses', int],
90        ['min_age_region', int],
91        ['max_age_region', int],
92        ])
93
94def damos_quota_goal_to_dict(goal):
95    return to_dict(goal, [
96        ['metric', int],
97        ['target_value', int],
98        ['current_value', int],
99        ['last_psi_total', int],
100        ['nid', int],
101        ])
102
103def damos_quota_goals_to_list(goals):
104    return [damos_quota_goal_to_dict(g)
105            for g in list_for_each_entry(
106                'struct damos_quota_goal', goals.address_of_(), 'list')]
107
108def damos_quota_to_dict(quota):
109    return to_dict(quota, [
110        ['reset_interval', int],
111        ['ms', int], ['sz', int],
112        ['goals', damos_quota_goals_to_list],
113        ['goal_tuner', int],
114        ['esz', int],
115        ['fail_charge_num', int],
116        ['fail_charge_denom', int],
117        ['weight_sz', int],
118        ['weight_nr_accesses', int],
119        ['weight_age', int],
120        ])
121
122def damos_watermarks_to_dict(watermarks):
123    return to_dict(watermarks, [
124        ['metric', int],
125        ['interval', int],
126        ['high', int], ['mid', int], ['low', int],
127        ])
128
129def damos_migrate_dests_to_dict(dests):
130    nr_dests = int(dests.nr_dests)
131    node_id_arr = []
132    weight_arr = []
133    for i in range(nr_dests):
134        node_id_arr.append(int(dests.node_id_arr[i]))
135        weight_arr.append(int(dests.weight_arr[i]))
136    return {
137            'node_id_arr': node_id_arr,
138            'weight_arr': weight_arr,
139            'nr_dests': nr_dests,
140            }
141
142def damos_filter_to_dict(damos_filter):
143    filter_type_keyword = {
144            0: 'anon',
145            1: 'active',
146            2: 'memcg',
147            3: 'young',
148            4: 'hugepage_size',
149            5: 'unmapped',
150            6: 'addr',
151            7: 'target'
152            }
153    dict_ = {
154            'type': filter_type_keyword[int(damos_filter.type)],
155            'matching': bool(damos_filter.matching),
156            'allow': bool(damos_filter.allow),
157            }
158    type_ = dict_['type']
159    if type_ == 'memcg':
160        dict_['memcg_id'] = int(damos_filter.memcg_id)
161    elif type_ == 'addr':
162        dict_['addr_range'] = [int(damos_filter.addr_range.start),
163                               int(damos_filter.addr_range.end)]
164    elif type_ == 'target':
165        dict_['target_idx'] = int(damos_filter.target_idx)
166    elif type_ == 'hugeapge_size':
167        dict_['sz_range'] = [int(damos_filter.sz_range.min),
168                             int(damos_filter.sz_range.max)]
169    return dict_
170
171def scheme_to_dict(scheme):
172    dict_ = to_dict(scheme, [
173        ['pattern', damos_access_pattern_to_dict],
174        ['action', int],
175        ['apply_interval_us', int],
176        ['quota', damos_quota_to_dict],
177        ['wmarks', damos_watermarks_to_dict],
178        ['target_nid', int],
179        ['migrate_dests', damos_migrate_dests_to_dict],
180        ])
181    core_filters = []
182    for f in list_for_each_entry(
183            'struct damos_filter', scheme.core_filters.address_of_(), 'list'):
184        core_filters.append(damos_filter_to_dict(f))
185    dict_['core_filters'] = core_filters
186    ops_filters = []
187    for f in list_for_each_entry(
188            'struct damos_filter', scheme.ops_filters.address_of_(), 'list'):
189        ops_filters.append(damos_filter_to_dict(f))
190    dict_['ops_filters'] = ops_filters
191
192    return dict_
193
194def schemes_to_list(schemes):
195    return [scheme_to_dict(s)
196            for s in list_for_each_entry(
197                'struct damos', schemes.address_of_(), 'list')]
198
199def damon_ctx_to_dict(ctx):
200    return to_dict(ctx, [
201        ['ops', ops_to_dict],
202        ['attrs', attrs_to_dict],
203        ['adaptive_targets', targets_to_list],
204        ['schemes', schemes_to_list],
205        ['pause', bool],
206        ])
207
208def main():
209    if len(sys.argv) < 3:
210        print('Usage: %s <kdamond pid> <file>' % sys.argv[0])
211        exit(1)
212
213    pid = int(sys.argv[1])
214    file_to_store = sys.argv[2]
215
216    kthread_data = cast('struct kthread *',
217                        find_task(prog, pid).worker_private).data
218    ctx = cast('struct damon_ctx *', kthread_data)
219    status = {'contexts': [damon_ctx_to_dict(ctx)]}
220    if file_to_store == 'stdout':
221        print(json.dumps(status, indent=4))
222    else:
223        with open(file_to_store, 'w') as f:
224            json.dump(status, f, indent=4)
225
226if __name__ == '__main__':
227    main()
228