| #!/usr/bin/env drgn |
| # SPDX-License-Identifier: GPL-2.0 |
| |
| ''' |
| Read DAMON context data and dump as a json string. |
| ''' |
| import drgn |
| from drgn import FaultError, NULL, Object, cast, container_of, execscript, offsetof, reinterpret, sizeof |
| from drgn.helpers.common import * |
| from drgn.helpers.linux import * |
| |
| import json |
| import sys |
| |
| if "prog" not in globals(): |
| try: |
| prog = drgn.get_default_prog() |
| except drgn.NoDefaultProgramError: |
| prog = drgn.program_from_kernel() |
| drgn.set_default_prog(prog) |
| |
| def to_dict(object, attr_name_converter): |
| d = {} |
| for attr_name, converter in attr_name_converter: |
| d[attr_name] = converter(getattr(object, attr_name)) |
| return d |
| |
| def ops_to_dict(ops): |
| return to_dict(ops, [ |
| ['id', int], |
| ]) |
| |
| def intervals_goal_to_dict(goal): |
| return to_dict(goal, [ |
| ['access_bp', int], |
| ['aggrs', int], |
| ['min_sample_us', int], |
| ['max_sample_us', int], |
| ]) |
| |
| def attrs_to_dict(attrs): |
| return to_dict(attrs, [ |
| ['sample_interval', int], |
| ['aggr_interval', int], |
| ['ops_update_interval', int], |
| ['intervals_goal', intervals_goal_to_dict], |
| ['min_nr_regions', int], |
| ['max_nr_regions', int], |
| ]) |
| |
| def addr_range_to_dict(addr_range): |
| return to_dict(addr_range, [ |
| ['start', int], |
| ['end', int], |
| ]) |
| |
| def region_to_dict(region): |
| return to_dict(region, [ |
| ['ar', addr_range_to_dict], |
| ['sampling_addr', int], |
| ['nr_accesses', int], |
| ['nr_accesses_bp', int], |
| ['age', int], |
| ]) |
| |
| def regions_to_list(regions): |
| return [region_to_dict(r) |
| for r in list_for_each_entry( |
| 'struct damon_region', regions.address_of_(), 'list')] |
| |
| def target_to_dict(target): |
| return to_dict(target, [ |
| ['pid', int], |
| ['nr_regions', int], |
| ['regions_list', regions_to_list], |
| ]) |
| |
| def targets_to_list(targets): |
| return [target_to_dict(t) |
| for t in list_for_each_entry( |
| 'struct damon_target', targets.address_of_(), 'list')] |
| |
| def damos_access_pattern_to_dict(pattern): |
| return to_dict(pattern, [ |
| ['min_sz_region', int], |
| ['max_sz_region', int], |
| ['min_nr_accesses', int], |
| ['max_nr_accesses', int], |
| ['min_age_region', int], |
| ['max_age_region', int], |
| ]) |
| |
| def damos_quota_goal_to_dict(goal): |
| return to_dict(goal, [ |
| ['metric', int], |
| ['target_value', int], |
| ['current_value', int], |
| ['last_psi_total', int], |
| ['nid', int], |
| ]) |
| |
| def damos_quota_goals_to_list(goals): |
| return [damos_quota_goal_to_dict(g) |
| for g in list_for_each_entry( |
| 'struct damos_quota_goal', goals.address_of_(), 'list')] |
| |
| def damos_quota_to_dict(quota): |
| return to_dict(quota, [ |
| ['reset_interval', int], |
| ['ms', int], ['sz', int], |
| ['goals', damos_quota_goals_to_list], |
| ['esz', int], |
| ['weight_sz', int], |
| ['weight_nr_accesses', int], |
| ['weight_age', int], |
| ]) |
| |
| def damos_watermarks_to_dict(watermarks): |
| return to_dict(watermarks, [ |
| ['metric', int], |
| ['interval', int], |
| ['high', int], ['mid', int], ['low', int], |
| ]) |
| |
| def damos_migrate_dests_to_dict(dests): |
| nr_dests = int(dests.nr_dests) |
| node_id_arr = [] |
| weight_arr = [] |
| for i in range(nr_dests): |
| node_id_arr.append(int(dests.node_id_arr[i])) |
| weight_arr.append(int(dests.weight_arr[i])) |
| return { |
| 'node_id_arr': node_id_arr, |
| 'weight_arr': weight_arr, |
| 'nr_dests': nr_dests, |
| } |
| |
| def damos_filter_to_dict(damos_filter): |
| filter_type_keyword = { |
| 0: 'anon', |
| 1: 'active', |
| 2: 'memcg', |
| 3: 'young', |
| 4: 'hugepage_size', |
| 5: 'unmapped', |
| 6: 'addr', |
| 7: 'target' |
| } |
| dict_ = { |
| 'type': filter_type_keyword[int(damos_filter.type)], |
| 'matching': bool(damos_filter.matching), |
| 'allow': bool(damos_filter.allow), |
| } |
| type_ = dict_['type'] |
| if type_ == 'memcg': |
| dict_['memcg_id'] = int(damos_filter.memcg_id) |
| elif type_ == 'addr': |
| dict_['addr_range'] = [int(damos_filter.addr_range.start), |
| int(damos_filter.addr_range.end)] |
| elif type_ == 'target': |
| dict_['target_idx'] = int(damos_filter.target_idx) |
| elif type_ == 'hugeapge_size': |
| dict_['sz_range'] = [int(damos_filter.sz_range.min), |
| int(damos_filter.sz_range.max)] |
| return dict_ |
| |
| def scheme_to_dict(scheme): |
| dict_ = to_dict(scheme, [ |
| ['pattern', damos_access_pattern_to_dict], |
| ['action', int], |
| ['apply_interval_us', int], |
| ['quota', damos_quota_to_dict], |
| ['wmarks', damos_watermarks_to_dict], |
| ['target_nid', int], |
| ['migrate_dests', damos_migrate_dests_to_dict], |
| ]) |
| filters = [] |
| for f in list_for_each_entry( |
| 'struct damos_filter', scheme.filters.address_of_(), 'list'): |
| filters.append(damos_filter_to_dict(f)) |
| dict_['filters'] = filters |
| ops_filters = [] |
| for f in list_for_each_entry( |
| 'struct damos_filter', scheme.ops_filters.address_of_(), 'list'): |
| ops_filters.append(damos_filter_to_dict(f)) |
| dict_['ops_filters'] = ops_filters |
| |
| return dict_ |
| |
| def schemes_to_list(schemes): |
| return [scheme_to_dict(s) |
| for s in list_for_each_entry( |
| 'struct damos', schemes.address_of_(), 'list')] |
| |
| def damon_ctx_to_dict(ctx): |
| return to_dict(ctx, [ |
| ['ops', ops_to_dict], |
| ['attrs', attrs_to_dict], |
| ['adaptive_targets', targets_to_list], |
| ['schemes', schemes_to_list], |
| ]) |
| |
| def main(): |
| if len(sys.argv) < 3: |
| print('Usage: %s <kdamond pid> <file>' % sys.argv[0]) |
| exit(1) |
| |
| pid = int(sys.argv[1]) |
| file_to_store = sys.argv[2] |
| |
| kthread_data = cast('struct kthread *', |
| find_task(prog, pid).worker_private).data |
| ctx = cast('struct damon_ctx *', kthread_data) |
| status = {'contexts': [damon_ctx_to_dict(ctx)]} |
| if file_to_store == 'stdout': |
| print(json.dumps(status, indent=4)) |
| else: |
| with open(file_to_store, 'w') as f: |
| json.dump(status, f, indent=4) |
| |
| if __name__ == '__main__': |
| main() |