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 ]) 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 ['esz', int], 113 ['weight_sz', int], 114 ['weight_nr_accesses', int], 115 ['weight_age', int], 116 ]) 117 118def damos_watermarks_to_dict(watermarks): 119 return to_dict(watermarks, [ 120 ['metric', int], 121 ['interval', int], 122 ['high', int], ['mid', int], ['low', int], 123 ]) 124 125def damos_migrate_dests_to_dict(dests): 126 nr_dests = int(dests.nr_dests) 127 node_id_arr = [] 128 weight_arr = [] 129 for i in range(nr_dests): 130 node_id_arr.append(int(dests.node_id_arr[i])) 131 weight_arr.append(int(dests.weight_arr[i])) 132 return { 133 'node_id_arr': node_id_arr, 134 'weight_arr': weight_arr, 135 'nr_dests': nr_dests, 136 } 137 138def damos_filter_to_dict(damos_filter): 139 filter_type_keyword = { 140 0: 'anon', 141 1: 'active', 142 2: 'memcg', 143 3: 'young', 144 4: 'hugepage_size', 145 5: 'unmapped', 146 6: 'addr', 147 7: 'target' 148 } 149 dict_ = { 150 'type': filter_type_keyword[int(damos_filter.type)], 151 'matching': bool(damos_filter.matching), 152 'allow': bool(damos_filter.allow), 153 } 154 type_ = dict_['type'] 155 if type_ == 'memcg': 156 dict_['memcg_id'] = int(damos_filter.memcg_id) 157 elif type_ == 'addr': 158 dict_['addr_range'] = [int(damos_filter.addr_range.start), 159 int(damos_filter.addr_range.end)] 160 elif type_ == 'target': 161 dict_['target_idx'] = int(damos_filter.target_idx) 162 elif type_ == 'hugeapge_size': 163 dict_['sz_range'] = [int(damos_filter.sz_range.min), 164 int(damos_filter.sz_range.max)] 165 return dict_ 166 167def scheme_to_dict(scheme): 168 dict_ = to_dict(scheme, [ 169 ['pattern', damos_access_pattern_to_dict], 170 ['action', int], 171 ['apply_interval_us', int], 172 ['quota', damos_quota_to_dict], 173 ['wmarks', damos_watermarks_to_dict], 174 ['target_nid', int], 175 ['migrate_dests', damos_migrate_dests_to_dict], 176 ]) 177 filters = [] 178 for f in list_for_each_entry( 179 'struct damos_filter', scheme.filters.address_of_(), 'list'): 180 filters.append(damos_filter_to_dict(f)) 181 dict_['filters'] = filters 182 ops_filters = [] 183 for f in list_for_each_entry( 184 'struct damos_filter', scheme.ops_filters.address_of_(), 'list'): 185 ops_filters.append(damos_filter_to_dict(f)) 186 dict_['ops_filters'] = ops_filters 187 188 return dict_ 189 190def schemes_to_list(schemes): 191 return [scheme_to_dict(s) 192 for s in list_for_each_entry( 193 'struct damos', schemes.address_of_(), 'list')] 194 195def damon_ctx_to_dict(ctx): 196 return to_dict(ctx, [ 197 ['ops', ops_to_dict], 198 ['attrs', attrs_to_dict], 199 ['adaptive_targets', targets_to_list], 200 ['schemes', schemes_to_list], 201 ]) 202 203def main(): 204 if len(sys.argv) < 3: 205 print('Usage: %s <kdamond pid> <file>' % sys.argv[0]) 206 exit(1) 207 208 pid = int(sys.argv[1]) 209 file_to_store = sys.argv[2] 210 211 kthread_data = cast('struct kthread *', 212 find_task(prog, pid).worker_private).data 213 ctx = cast('struct damon_ctx *', kthread_data) 214 status = {'contexts': [damon_ctx_to_dict(ctx)]} 215 if file_to_store == 'stdout': 216 print(json.dumps(status, indent=4)) 217 else: 218 with open(file_to_store, 'w') as f: 219 json.dump(status, f, indent=4) 220 221if __name__ == '__main__': 222 main() 223