blob: 4cfb44ebd89d45f2fe22c066448e4375653a5065 [file] [log] [blame]
# 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