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 ['age', int], 63 ]) 64 65def regions_to_list(regions): 66 return [region_to_dict(r) 67 for r in list_for_each_entry( 68 'struct damon_region', regions.address_of_(), 'list')] 69 70def target_to_dict(target): 71 return to_dict(target, [ 72 ['pid', int], 73 ['nr_regions', int], 74 ['regions_list', regions_to_list], 75 ['obsolete', bool], 76 ]) 77 78def targets_to_list(targets): 79 return [target_to_dict(t) 80 for t in list_for_each_entry( 81 'struct damon_target', targets.address_of_(), 'list')] 82 83def damos_access_pattern_to_dict(pattern): 84 return to_dict(pattern, [ 85 ['min_sz_region', int], 86 ['max_sz_region', int], 87 ['min_nr_accesses', int], 88 ['max_nr_accesses', int], 89 ['min_age_region', int], 90 ['max_age_region', int], 91 ]) 92 93def damos_quota_goal_to_dict(goal): 94 return to_dict(goal, [ 95 ['metric', int], 96 ['target_value', int], 97 ['current_value', int], 98 ['last_psi_total', int], 99 ['nid', int], 100 ]) 101 102def damos_quota_goals_to_list(goals): 103 return [damos_quota_goal_to_dict(g) 104 for g in list_for_each_entry( 105 'struct damos_quota_goal', goals.address_of_(), 'list')] 106 107def damos_quota_to_dict(quota): 108 return to_dict(quota, [ 109 ['reset_interval', int], 110 ['ms', int], ['sz', int], 111 ['goals', damos_quota_goals_to_list], 112 ['goal_tuner', int], 113 ['esz', int], 114 ['fail_charge_num', int], 115 ['fail_charge_denom', int], 116 ['weight_sz', int], 117 ['weight_nr_accesses', int], 118 ['weight_age', int], 119 ]) 120 121def damos_watermarks_to_dict(watermarks): 122 return to_dict(watermarks, [ 123 ['metric', int], 124 ['interval', int], 125 ['high', int], ['mid', int], ['low', int], 126 ]) 127 128def damos_migrate_dests_to_dict(dests): 129 nr_dests = int(dests.nr_dests) 130 node_id_arr = [] 131 weight_arr = [] 132 for i in range(nr_dests): 133 node_id_arr.append(int(dests.node_id_arr[i])) 134 weight_arr.append(int(dests.weight_arr[i])) 135 return { 136 'node_id_arr': node_id_arr, 137 'weight_arr': weight_arr, 138 'nr_dests': nr_dests, 139 } 140 141def damos_filter_to_dict(damos_filter): 142 filter_type_keyword = { 143 0: 'anon', 144 1: 'active', 145 2: 'memcg', 146 3: 'young', 147 4: 'hugepage_size', 148 5: 'unmapped', 149 6: 'addr', 150 7: 'target' 151 } 152 dict_ = { 153 'type': filter_type_keyword[int(damos_filter.type)], 154 'matching': bool(damos_filter.matching), 155 'allow': bool(damos_filter.allow), 156 } 157 type_ = dict_['type'] 158 if type_ == 'memcg': 159 dict_['memcg_id'] = int(damos_filter.memcg_id) 160 elif type_ == 'addr': 161 dict_['addr_range'] = [int(damos_filter.addr_range.start), 162 int(damos_filter.addr_range.end)] 163 elif type_ == 'target': 164 dict_['target_idx'] = int(damos_filter.target_idx) 165 elif type_ == 'hugepage_size': 166 dict_['sz_range'] = [int(damos_filter.sz_range.min), 167 int(damos_filter.sz_range.max)] 168 return dict_ 169 170def scheme_to_dict(scheme): 171 dict_ = to_dict(scheme, [ 172 ['pattern', damos_access_pattern_to_dict], 173 ['action', int], 174 ['apply_interval_us', int], 175 ['quota', damos_quota_to_dict], 176 ['wmarks', damos_watermarks_to_dict], 177 ['target_nid', int], 178 ['migrate_dests', damos_migrate_dests_to_dict], 179 ]) 180 core_filters = [] 181 for f in list_for_each_entry( 182 'struct damos_filter', scheme.core_filters.address_of_(), 'list'): 183 core_filters.append(damos_filter_to_dict(f)) 184 dict_['core_filters'] = core_filters 185 ops_filters = [] 186 for f in list_for_each_entry( 187 'struct damos_filter', scheme.ops_filters.address_of_(), 'list'): 188 ops_filters.append(damos_filter_to_dict(f)) 189 dict_['ops_filters'] = ops_filters 190 191 return dict_ 192 193def schemes_to_list(schemes): 194 return [scheme_to_dict(s) 195 for s in list_for_each_entry( 196 'struct damos', schemes.address_of_(), 'list')] 197 198def damon_ctx_to_dict(ctx): 199 return to_dict(ctx, [ 200 ['ops', ops_to_dict], 201 ['attrs', attrs_to_dict], 202 ['adaptive_targets', targets_to_list], 203 ['schemes', schemes_to_list], 204 ['pause', bool], 205 ]) 206 207def main(): 208 if len(sys.argv) < 3: 209 print('Usage: %s <kdamond pid> <file>' % sys.argv[0]) 210 exit(1) 211 212 pid = int(sys.argv[1]) 213 file_to_store = sys.argv[2] 214 215 kthread_data = cast('struct kthread *', 216 find_task(prog, pid).worker_private).data 217 ctx = cast('struct damon_ctx *', kthread_data) 218 status = {'contexts': [damon_ctx_to_dict(ctx)]} 219 if file_to_store == 'stdout': 220 print(json.dumps(status, indent=4)) 221 else: 222 with open(file_to_store, 'w') as f: 223 json.dump(status, f, indent=4) 224 225if __name__ == '__main__': 226 main() 227