| # SPDX-License-Identifier: GPL-2.0 |
| |
| import os |
| |
| import _damo_fmt_str |
| import _damon |
| |
| def cum_mem_pct_of(percentiles, idle_sec): |
| if idle_sec < percentiles[0]: |
| return 0.0 |
| for cum_mem_pct, iter_idle_second in enumerate(percentiles): |
| if iter_idle_second == idle_sec: |
| return cum_mem_pct |
| if iter_idle_second > idle_sec: |
| # assume linear distribution |
| before_idle_sec = percentiles[cum_mem_pct - 1] |
| before_to_target_idle_sec = idle_sec - before_idle_sec |
| before_to_current = iter_idle_second - before_idle_sec |
| the_ratio = before_to_target_idle_sec / before_to_current |
| return cum_mem_pct - 1 + the_ratio |
| return 100.0 |
| |
| def mem_sz_of_idle_time_range(percentiles, idle_sec_range): |
| a = cum_mem_pct_of(percentiles, idle_sec_range[1]) |
| b = cum_mem_pct_of(percentiles, idle_sec_range[0]) |
| return a - b |
| |
| def do_pr_idle_time_mem_sz(percentiles, gran, raw_number): |
| # percentiles is 101 values list, each meaning p0 to p100 in seconds. |
| # gran is the granularity of idle time to memory size |
| min_idle_sec = percentiles[0] |
| max_idle_sec = percentiles[-1] |
| if max_idle_sec == min_idle_sec: |
| idle_time_interval = 1 |
| else: |
| idle_time_interval = (max_idle_sec - min_idle_sec) / gran |
| |
| idle_sec_range = [min_idle_sec - 1, min_idle_sec + idle_time_interval] |
| rows = [] |
| max_time_column_len = 0 |
| while idle_sec_range[0] < max_idle_sec: |
| rows.append([ |
| _damo_fmt_str.format_time_sec(idle_sec_range[1], raw_number), |
| _damo_fmt_str.format_percent( |
| mem_sz_of_idle_time_range(percentiles, idle_sec_range), |
| raw_number)]) |
| |
| time_col_len = len(rows[-1][0]) |
| if time_col_len > max_time_column_len: |
| max_time_column_len = time_col_len |
| |
| idle_sec_range[0] += idle_time_interval |
| idle_sec_range[1] += idle_time_interval |
| for row in rows: |
| print('%s%s%s' % ( |
| row[0], ' ' * (max_time_column_len + 4 - len(row[0])), row[1])) |
| |
| def pr_idle_time_mem_sz(nr_lines, raw_number, input_idle_time_percentiles): |
| if input_idle_time_percentiles is not None: |
| do_pr_idle_time_mem_sz( |
| input_idle_time_percentiles, nr_lines, raw_number) |
| return |
| param_dir = '/sys/module/damon_stat/parameters' |
| with open(os.path.join(param_dir, 'memory_idle_ms_percentiles'), 'r') as f: |
| idle_sec_percentiles = [int(x) / 1000 for x in f.read().split(',')] |
| do_pr_idle_time_mem_sz(idle_sec_percentiles, nr_lines, raw_number) |
| |
| def pr_idle_time_percentiles(range_vals, raw_number, |
| input_idle_time_percentiles): |
| if input_idle_time_percentiles is not None: |
| idle_sec_percentiles = input_idle_time_percentiles |
| else: |
| param_dir = '/sys/module/damon_stat/parameters' |
| with open(os.path.join(param_dir, |
| 'memory_idle_ms_percentiles'), 'r') as f: |
| idle_sec_percentiles = [int(x) / 1000 for x in f.read().split(',')] |
| |
| start, end, interval = range_vals |
| percentile = start |
| while percentile < end: |
| print('%3d' % percentile, |
| _damo_fmt_str.format_time_sec( |
| idle_sec_percentiles[percentile], raw_number)) |
| percentile += interval |
| |
| def partial_to_full_idle_time_percentiles(partial_percentiles): |
| # partial_percentiles is a list of lists having two entries. The two |
| # entries are percentile number and the value. |
| full_percentile_vals = [] |
| |
| # fill up middle percentiles first |
| first_known_perceentile = partial_percentiles[0][0] |
| last_known_perceentile = partial_percentiles[-1][0] |
| for percentile, val in partial_percentiles: |
| if len(full_percentile_vals) == 0: |
| full_percentile_vals.append(val) |
| last_percentile = percentile |
| continue |
| last_val = full_percentile_vals[-1] |
| val_diff = val - last_val |
| percentile_diff = percentile - last_percentile |
| val_diff_per_percentile = val_diff / percentile_diff |
| iter_percentile = last_percentile + 1 |
| while iter_percentile < percentile: |
| full_percentile_vals.append(full_percentile_vals[-1] + |
| val_diff_per_percentile) |
| iter_percentile += 1 |
| full_percentile_vals.append(val) |
| last_percentile = percentile |
| |
| # fill up first missing percentiles |
| first_known_val_diff_per_percentile = \ |
| full_percentile_vals[1] - full_percentile_vals[0] |
| for percentile in range(first_known_perceentile - 1, -1, -1): |
| full_percentile_vals.insert( |
| 0, full_percentile_vals[0] - |
| first_known_val_diff_per_percentile) |
| |
| # fill up last missing percentiles |
| last_known_val_diff_per_percentile = \ |
| full_percentile_vals[-1] - full_percentile_vals[-2] |
| for percentile in range(last_known_perceentile + 1, 101, 1): |
| full_percentile_vals.append( |
| full_percentile_vals[-1] + last_known_val_diff_per_percentile) |
| |
| return full_percentile_vals |
| |
| def input_idle_time_percentiles_to_full_percentiles(input_percentiles): |
| if len(input_percentiles) == 1 and os.path.isfile(input_percentiles[0]): |
| with open(input_percentiles[0], 'r') as f: |
| input_percentiles = f.read().split() |
| len_input = len(input_percentiles) |
| if len_input < 2 or len_input % 2 != 0: |
| return None, 'input length invalid' |
| partial_percentiles = [] |
| for i in range(0, len_input, 2): |
| # val should be in seconds |
| percentile, val = input_percentiles[i], input_percentiles[i + 1] |
| try: |
| percentile = int(percentile) |
| val = float(val) |
| except Exception as e: |
| return None, 'casting fail (%s)' % e |
| partial_percentiles.append([percentile, val]) |
| return partial_to_full_idle_time_percentiles(partial_percentiles), None |
| |
| def handle_read(to_read, args, input_idle_time_percentiles, param_dir): |
| if to_read == 'idle_time_mem_sz': |
| pr_idle_time_mem_sz(args.idle_time_mem_sz_lines, args.raw_number, |
| input_idle_time_percentiles) |
| elif to_read == 'idle_time_percentiles': |
| pr_idle_time_percentiles(args.idle_time_percentiles_range, |
| args.raw_number, |
| input_idle_time_percentiles) |
| elif to_read is not None: |
| file_path = os.path.join(param_dir, to_read) |
| if not os.path.isfile(file_path): |
| print('Wrong parameter (%s). Please use one among below:' % |
| to_read) |
| for param_file in os.listdir(param_dir): |
| print('- %s' % param_file) |
| print('- idle_time_mem_sz') |
| print('- idle_time_percentiles') |
| exit(1) |
| with open(os.path.join(param_dir, to_read), 'r') as f: |
| print(f.read().strip()) |
| else: |
| for param in os.listdir(param_dir): |
| with open(os.path.join(param_dir, param), 'r') as f: |
| print('%s: %s' % (param, f.read().strip())) |
| |
| def handle_read_write(args): |
| module_name = args.module_name |
| param_dir = '/sys/module/damon_%s/parameters' % module_name |
| if args.action == 'read': |
| if args.input_idle_time_percentiles is not None: |
| input_idle_time_percentiles, err = \ |
| input_idle_time_percentiles_to_full_percentiles( |
| args.input_idle_time_percentiles) |
| if err is not None: |
| print('wrong --input_idle_time_percentiles (%s)' % err) |
| exit(1) |
| else: |
| input_idle_time_percentiles = None |
| for to_read in args.to_read: |
| handle_read(to_read, args, input_idle_time_percentiles, param_dir) |
| elif args.action == 'write': |
| if len(args.parameter_value) % 2 != 0: |
| print('wrong paramter_value') |
| exit(1) |
| |
| for i in range(0, len(args.parameter_value), 2): |
| param_name = args.parameter_value[i] |
| param_val = args.parameter_value[i + 1] |
| with open(os.path.join(param_dir, param_name), 'w') as f: |
| f.write(param_val) |
| |
| def main(args): |
| _damon.ensure_root_permission() |
| handle_read_write(args) |
| |
| def set_argparser(parser): |
| subparsers = parser.add_subparsers( |
| title='action', dest='action', metavar='<action>') |
| subparsers.required = True |
| |
| parser_read = subparsers.add_parser('read', help='read parameters') |
| parser_read.add_argument( |
| 'to_read', metavar='<parameter name or display keywords>', |
| nargs='*', |
| help='what to read.') |
| parser_read.add_argument( |
| '--idle_time_mem_sz_lines', metavar='<int>', default=10, type=int, |
| help='number of lines for idle time to memory size output') |
| parser_read.add_argument( |
| '--idle_time_percentiles_range', metavar='<int>', |
| default=[0, 101, 10], type=int, nargs=3, |
| help='idle time percentiles print range (start, end, interval)') |
| parser_read.add_argument( |
| '--input_idle_time_percentiles', nargs='+', |
| metavar='<percentile> <value>', |
| help='idle time percentiles to visualize') |
| parser_read.add_argument('--raw_number', action='store_true', |
| help='print number in raw form') |
| |
| parser_write = subparsers.add_parser('write', help='write parameters') |
| parser_write.add_argument( |
| 'parameter_value', metavar=('<parameter name> <value>'), nargs='+', |
| help='name of the parameter and the value to write') |
| return parser |