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