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