files relocation

This commit is contained in:
Alexey Avramov
2020-05-23 15:34:51 +09:00
parent 28cf1340a6
commit 0e59278233
19 changed files with 22 additions and 22 deletions

3997
src/nohang Executable file

File diff suppressed because it is too large Load Diff

244
src/oom-sort Executable file
View File

@@ -0,0 +1,244 @@
#!/usr/bin/env python3
"""
sort processes by oom_score
"""
from operator import itemgetter
from os import listdir
from argparse import ArgumentParser
def pid_to_oom_score(pid):
with open('/proc/{}/oom_score'.format(pid), 'rb', buffering=0) as f:
return int(f.read())
def pid_to_oom_score_adj(pid):
with open('/proc/{}/oom_score_adj'.format(pid), 'rb', buffering=0) as f:
return int(f.read())
def pid_to_cmdline(pid):
with open('/proc/{}/cmdline'.format(pid), 'rb', buffering=0) as f:
return f.read().decode('utf-8', 'ignore').replace(
'\x00', ' ').rstrip()
def pid_to_status_units(pid):
with open('/proc/{}/status'.format(pid), 'rb', buffering=0) as f:
f_list = f.read().decode('utf-8', 'ignore').split('\n')
for i in range(len(f_list)):
if i == 1:
name = f_list[0].split('\t')[1]
if i == uid_index:
uid = f_list[i].split('\t')[2]
if i == vm_rss_index:
vm_rss = f_list[i].split('\t')[1][:-3]
if i == vm_swap_index:
vm_swap = f_list[i].split('\t')[1][:-3]
return name, uid, vm_rss, vm_swap
def get_max_pid_len():
with open('/proc/sys/kernel/pid_max') as f:
for line in f:
return len(line.strip())
sort_dict = {
'PID': 0,
'oom_score': 1,
'oom_score_adj': 2,
'cmdline': 3,
'Name': 4,
'UID': 5,
'VmRSS': 6,
'VmSwap': 7
}
parser = ArgumentParser()
parser.add_argument(
'--num',
'-n',
help="""max number of lines; default: 99999""",
default=99999,
type=str
)
parser.add_argument(
'--len',
'-l',
help="""max cmdline length; default: 99999""",
default=99999,
type=int
)
parser.add_argument(
'--sort',
'-s',
help="""sort by unit; default: oom_score""",
default='oom_score',
type=str
)
args = parser.parse_args()
display_cmdline = args.len
num_lines = args.num
sort_by = args.sort
if sort_by not in sort_dict:
print('Invalid -s/--sort value. Valid values are:\nPID\noom_scor'
'e [default value]\noom-sore_adj\nUID\nName\ncmdline\nVmR'
'SS\nVmSwap')
exit()
# find VmRSS, VmSwap and UID positions in /proc/*/status for further
# searching positions of UID, VmRSS and VmSwap in each process
with open('/proc/self/status') as file:
status_list = file.readlines()
status_names = []
for s in status_list:
status_names.append(s.split(':')[0])
uid_index = status_names.index('Uid')
vm_rss_index = status_names.index('VmRSS')
vm_swap_index = status_names.index('VmSwap')
# get sorted list with pid, oom_score, oom_score_adj, cmdline
# get status units: name, uid, rss, swap
oom_list = []
for pid in listdir('/proc'):
# skip non-numeric entries and PID 1
if pid.isdigit() is False or pid == '1':
continue
try:
oom_score = pid_to_oom_score(pid)
oom_score_adj = pid_to_oom_score_adj(pid)
cmdline = pid_to_cmdline(pid)
if cmdline == '':
continue
name, uid, vm_rss, vm_swap = pid_to_status_units(pid)
except FileNotFoundError:
continue
except ProcessLookupError:
continue
except Exception as e:
print(e)
exit(1)
oom_list.append((
int(pid), int(oom_score), int(oom_score_adj), cmdline,
name, int(uid), int(vm_rss), int(vm_swap)))
# list sorted by oom_score
oom_list_sorted = sorted(
oom_list, key=itemgetter(int(sort_dict[sort_by])), reverse=True)
# find width of columns
max_pid_len = get_max_pid_len()
max_uid_len = len(str(sorted(
oom_list, key=itemgetter(5), reverse=True)[0][5]))
max_vm_rss_len = len(str(round(
sorted(oom_list, key=itemgetter(6), reverse=True)[0][6] / 1024)))
if max_vm_rss_len < 5:
max_vm_rss_len = 5
# print output
if display_cmdline == 0:
print(
'oom_score oom_score_adj{}UID{}PID Name {}VmRSS VmSwap'.format(
' ' * (max_uid_len - 2),
' ' * (max_pid_len - 2),
' ' * max_vm_rss_len
)
)
print(
'--------- ------------- {} {} --------------- {}-- --------'.format(
'-' * max_uid_len,
'-' * max_pid_len,
'-' * max_vm_rss_len
)
)
else:
print(
'oom_score oom_score_adj{}UID{}PID Name {}VmRSS VmSwa'
'p cmdline'.format(
' ' * (max_uid_len - 2),
' ' * (max_pid_len - 2),
' ' * max_vm_rss_len
)
)
print(
'--------- ------------- {} {} --------------- {}-- ------'
'-- -------'.format(
'-' * max_uid_len,
'-' * max_pid_len,
'-' * max_vm_rss_len
)
)
# print processes stats sorted by sort_dict[sort_by]
for i in oom_list_sorted[:int(num_lines)]:
pid = i[0]
oom_score = i[1]
oom_score_adj = i[2]
cmdline = i[3]
name = i[4]
uid = i[5]
vm_rss = i[6]
vm_swap = i[7]
print(
'{} {} {} {} {} {} M {} M {}'.format(
str(oom_score).rjust(9),
str(oom_score_adj).rjust(13),
str(uid).rjust(max_uid_len),
str(pid).rjust(max_pid_len),
name.ljust(15),
str(round(vm_rss / 1024.0)).rjust(max_vm_rss_len, ' '),
str(round(vm_swap / 1024.0)).rjust(6, ' '),
cmdline[:display_cmdline]
)
)

202
src/psi-top Executable file
View File

@@ -0,0 +1,202 @@
#!/usr/bin/env python3
import os
from argparse import ArgumentParser
def psi_path_to_metrics(psi_path):
"""
"""
with open(psi_path) as f:
psi_list = f.readlines()
some_list, full_list = psi_list[0].split(' '), psi_list[1].split(' ')
some_avg10 = some_list[1].split('=')[1]
some_avg60 = some_list[2].split('=')[1]
some_avg300 = some_list[3].split('=')[1]
full_avg10 = full_list[1].split('=')[1]
full_avg60 = full_list[2].split('=')[1]
full_avg300 = full_list[3].split('=')[1]
return (some_avg10, some_avg60, some_avg300,
full_avg10, full_avg60, full_avg300)
def psi_path_to_metrics_cpu(psi_path):
"""
"""
with open(psi_path) as f:
psi_list = f.readlines()
some_list = psi_list[0].rstrip().split(' ')
some_avg10 = some_list[1].split('=')[1]
some_avg60 = some_list[2].split('=')[1]
some_avg300 = some_list[3].split('=')[1]
return (some_avg10, some_avg60, some_avg300)
def cgroup2_root():
"""
"""
with open(mounts) as f:
for line in f:
if cgroup2_separator in line:
return line.partition(cgroup2_separator)[0].partition(' ')[2]
def get_psi_mem_files(cgroup2_path, met):
"""
"""
path_list = []
for root, dirs, files in os.walk(cgroup2_path):
for file in files:
path = os.path.join(root, file)
if path.endswith('/{}.pressure'.format(met)):
path_list.append(path)
return path_list
def psi_path_to_cgroup2(path):
"""
"""
if path.endswith('/cpu.pressure'):
return path.partition(cgroup2_mountpoint)[
2].partition('/cpu.pressure')[0]
if path.endswith('/io.pressure'):
return path.partition(cgroup2_mountpoint)[
2].partition('/io.pressure')[0]
if path.endswith('/memory.pressure'):
return path.partition(cgroup2_mountpoint)[
2].partition('/memory.pressure')[0]
parser = ArgumentParser()
parser.add_argument(
'-m',
'--metrics',
help="""metrics (memory, io or cpu)""",
default='memory',
type=str
)
args = parser.parse_args()
met = args.metrics
if not (met == 'memory' or met == 'io' or met == 'cpu'):
print('Invalid metrics:', met)
exit(1)
psi_path = '/proc/pressure/{}'.format(met)
mounts = '/proc/mounts'
cgroup2_separator = ' cgroup2 rw,'
cgroup2_mountpoint = cgroup2_root()
if cgroup2_mountpoint is None:
print('ERROR: cgroup_v2 hierarchy is not mounted')
exit(1)
try:
psi_path_to_metrics('/proc/pressure/memory')
except Exception as e:
print('ERROR: {}'.format(e))
print('PSI metrics are not provided by the kernel. Exit.')
exit(1)
if cgroup2_mountpoint is not None:
y = get_psi_mem_files(cgroup2_mountpoint, met)
path_list = get_psi_mem_files(cgroup2_mountpoint, met)
head_mem_io = '''PSI metrics: {}
cgroup_v2 mountpoint: {}
---------------------------------------------------------
some | full |
-------------------- | -------------------- | -----------
avg10 avg60 avg300 | avg10 avg60 avg300 | cgroup_v2
------ ------ ------ | ------ ------ ------ | -----------'''.format(
met, cgroup2_mountpoint)
head_cpu = '''PSI metrics: {}
cgroup_v2 mountpoint: {}
----------------------------------
some |
-------------------- | -----------
avg10 avg60 avg300 | cgroup_v2
------ ------ ------ | -----------'''.format(
met, cgroup2_mountpoint)
if met == 'cpu':
print(head_cpu)
else:
print(head_mem_io)
if met == 'cpu':
some_avg10, some_avg60, some_avg300 = psi_path_to_metrics_cpu(psi_path)
print('{} {} {} | {}'.format(
some_avg10.rjust(6),
some_avg60.rjust(6),
some_avg300.rjust(6),
'SYSTEM_WIDE'))
else:
(some_avg10, some_avg60, some_avg300, full_avg10, full_avg60, full_avg300
) = psi_path_to_metrics(psi_path)
print('{} {} {} | {} {} {} | {}'.format(
some_avg10.rjust(6),
some_avg60.rjust(6),
some_avg300.rjust(6),
full_avg10.rjust(6),
full_avg60.rjust(6),
full_avg300.rjust(6), 'SYSTEM_WIDE'))
for psi_path in path_list:
if met == 'cpu':
some_avg10, some_avg60, some_avg300 = psi_path_to_metrics_cpu(psi_path)
print('{} {} {} | {}'.format(
some_avg10.rjust(6),
some_avg60.rjust(6),
some_avg300.rjust(6),
psi_path_to_cgroup2(psi_path)))
else:
(some_avg10, some_avg60, some_avg300,
full_avg10, full_avg60, full_avg300) = psi_path_to_metrics(psi_path)
print('{} {} {} | {} {} {} | {}'.format(
some_avg10.rjust(6),
some_avg60.rjust(6),
some_avg300.rjust(6),
full_avg10.rjust(6),
full_avg60.rjust(6),
full_avg300.rjust(6), psi_path_to_cgroup2(psi_path)))

478
src/psi2log Executable file
View File

@@ -0,0 +1,478 @@
#!/usr/bin/env python3
"""psi2log"""
import os
from time import sleep, monotonic
from ctypes import CDLL
from sys import stdout, exit
from argparse import ArgumentParser
from signal import signal, SIGTERM, SIGINT, SIGQUIT, SIGHUP
def form(num):
"""
"""
s = str(num).split('.')
return '{}.{:0<2}'.format(s[0], s[1])
def signal_handler(signum, frame):
"""
"""
def signal_handler_inner(signum, frame):
pass
for i in sig_list:
signal(i, signal_handler_inner)
log('')
lpd = len(peaks_dict)
if lpd != 15:
exit()
log('=================================')
log('Peak values: avg10 avg60 avg300')
log('----------- ------ ------ ------')
log('some cpu {:>6} {:>6} {:>6}'.format(
form(peaks_dict['c_some_avg10']),
form(peaks_dict['c_some_avg60']),
form(peaks_dict['c_some_avg300']),
))
log('----------- ------ ------ ------')
log('some memory {:>6} {:>6} {:>6}'.format(
form(peaks_dict['m_some_avg10']),
form(peaks_dict['m_some_avg60']),
form(peaks_dict['m_some_avg300']),
))
log('full memory {:>6} {:>6} {:>6}'.format(
form(peaks_dict['m_full_avg10']),
form(peaks_dict['m_full_avg60']),
form(peaks_dict['m_full_avg300']),
))
log('----------- ------ ------ ------')
log('some io {:>6} {:>6} {:>6}'.format(
form(peaks_dict['i_some_avg10']),
form(peaks_dict['i_some_avg60']),
form(peaks_dict['i_some_avg300']),
))
log('full io {:>6} {:>6} {:>6}'.format(
form(peaks_dict['i_full_avg10']),
form(peaks_dict['i_full_avg60']),
form(peaks_dict['i_full_avg300']),
))
exit()
def cgroup2_root():
"""
"""
with open(mounts) as f:
for line in f:
if cgroup2_separator in line:
return line.partition(cgroup2_separator)[0].partition(' ')[2]
def mlockall():
"""
"""
MCL_CURRENT = 1
MCL_FUTURE = 2
MCL_ONFAULT = 4
CDLL('libc.so.6').mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT)
def psi_file_mem_to_metrics(psi_path):
"""
"""
with open(psi_path) as f:
psi_list = f.readlines()
some_list, full_list = psi_list[0].split(' '), psi_list[1].split(' ')
some_avg10 = some_list[1].split('=')[1]
some_avg60 = some_list[2].split('=')[1]
some_avg300 = some_list[3].split('=')[1]
full_avg10 = full_list[1].split('=')[1]
full_avg60 = full_list[2].split('=')[1]
full_avg300 = full_list[3].split('=')[1]
return (some_avg10, some_avg60, some_avg300,
full_avg10, full_avg60, full_avg300)
def psi_file_cpu_to_metrics(psi_path):
"""
"""
with open(psi_path) as f:
psi_list = f.readlines()
some_list = psi_list[0].split(' ')
some_avg10 = some_list[1].split('=')[1]
some_avg60 = some_list[2].split('=')[1]
some_avg300 = some_list[3].split('=')[1]
return (some_avg10, some_avg60, some_avg300)
def psi_file_mem_to_total(psi_path):
"""
"""
with open(psi_path) as f:
psi_list = f.readlines()
some_list, full_list = psi_list[0].split(' '), psi_list[1].split(' ')
some_total = some_list[4].split('=')[1]
full_total = full_list[4].split('=')[1]
return int(some_total), int(full_total)
def psi_file_cpu_to_total(psi_path):
"""
"""
with open(psi_path) as f:
psi_list = f.readlines()
some_list = psi_list[0].split(' ')
some_total = some_list[4].split('=')[1]
return int(some_total)
def print_head_1():
log('=================================================================='
'================================================')
log(' some cpu pressure || some memory pressure | full memory pressur'
'e || some io pressure | full io pressure')
log('-------------------- || -------------------- | -------------------'
'- || -------------------- | --------------------')
log(' avg10 avg60 avg300 || avg10 avg60 avg300 | avg10 avg60 avg30'
'0 || avg10 avg60 avg300 | avg10 avg60 avg300')
log('------ ------ ------ || ------ ------ ------ | ------ ------ -----'
'- || ------ ------ ------ | ------ ------ ------')
def print_head_2():
log('============================================')
log(' cpu | memory | io |')
log('----- | ----------- | ----------- |')
log(' some | some full | some full | interval')
log('----- | ----- ----- | ----- ----- | --------')
def log(*msg):
"""
"""
print(*msg)
if separate_log:
logging.info(*msg)
parser = ArgumentParser()
parser.add_argument(
'-t',
'--target',
help="""target (cgroup_v2 or SYTSTEM_WIDE)""",
default='SYSTEM_WIDE',
type=str
)
parser.add_argument(
'-i',
'--interval',
help="""interval in sec""",
default=2,
type=float
)
parser.add_argument(
'-l',
'--log',
help="""path to log file""",
default=None,
type=str
)
parser.add_argument(
'-m',
'--mode',
help="""mode (1 or 2)""",
default='1',
type=str
)
args = parser.parse_args()
target = args.target
interval = args.interval
log_file = args.log
mode = args.mode
if log_file is None:
separate_log = False
else:
separate_log = True
import logging
sig_list = [SIGTERM, SIGINT, SIGQUIT, SIGHUP]
for i in sig_list:
signal(i, signal_handler)
if separate_log:
logging.basicConfig(
filename=log_file,
level=logging.INFO,
format="%(asctime)s: %(message)s")
log('Starting psi2log')
log('target: {}'.format(target))
log('interval: {} sec'.format(interval))
if log_file is not None:
log('log file: {}'.format(log_file))
log('mode: {}'.format(mode))
try:
psi_file_mem_to_metrics('/proc/pressure/memory')
except Exception as e:
print('ERROR: {}'.format(e))
print('PSI metrics are not provided by the kernel. Exit.')
exit(1)
if target == 'SYSTEM_WIDE':
cpu_file = "/proc/pressure/cpu"
memory_file = "/proc/pressure/memory"
io_file = "/proc/pressure/io"
else:
mounts = '/proc/mounts'
cgroup2_separator = ' cgroup2 rw,'
cgroup2_mountpoint = cgroup2_root()
if cgroup2_mountpoint is None:
log('ERROR: unified cgroup hierarchy is not mounted, exit')
exit(1)
else:
log('cgroup_v2 mountpoint: {}'.format(cgroup2_mountpoint))
cpu_file = cgroup2_mountpoint + target + "/cpu.pressure"
memory_file = cgroup2_mountpoint + target + "/memory.pressure"
io_file = cgroup2_mountpoint + target + "/io.pressure"
peaks_dict = dict()
mlockall()
if mode == '2':
print_head_2()
total_cs0 = psi_file_cpu_to_total(cpu_file)
total_ms0, total_mf0 = psi_file_mem_to_total(memory_file)
total_is0, total_if0 = psi_file_mem_to_total(io_file)
monotonic0 = monotonic()
sleep(interval)
while True:
total_cs1 = psi_file_cpu_to_total(cpu_file)
total_ms1, total_mf1 = psi_file_mem_to_total(memory_file)
total_is1, total_if1 = psi_file_mem_to_total(io_file)
monotonic1 = monotonic()
dm = monotonic1 - monotonic0
monotonic0 = monotonic1
dtotal_cs = total_cs1 - total_cs0
avg_cs = dtotal_cs / dm / 10000
total_cs0 = total_cs1
dtotal_ms = total_ms1 - total_ms0
avg_ms = dtotal_ms / dm / 10000
total_ms0 = total_ms1
dtotal_mf = total_mf1 - total_mf0
avg_mf = dtotal_mf / dm / 10000
total_mf0 = total_mf1
dtotal_is = total_is1 - total_is0
avg_is = dtotal_is / dm / 10000
total_is0 = total_is1
dtotal_if = total_if1 - total_if0
avg_if = dtotal_if / dm / 10000
total_if0 = total_if1
log('{:>5} | {:>5} {:>5} | {:>5} {:>5} | {}'.format(
round(avg_cs, 1),
round(avg_ms, 1),
round(avg_mf, 1),
round(avg_is, 1),
round(avg_if, 1),
round(dm, 3)
))
stdout.flush()
sleep(interval)
if mode != '1':
log('ERROR: invalid mode. Exit.')
exit(1)
print_head_1()
while True:
if not os.path.exists(cpu_file):
log('ERROR: cpu pressure file does not exist: {}'.format(cpu_file))
sleep(interval)
continue
if not os.path.exists(memory_file):
log('ERROR: memory pressure file does not exist: {}'.format(
memory_file))
sleep(interval)
continue
if not os.path.exists(io_file):
log('ERROR: io pressure file does not exist: {}'.format(cpu_file))
sleep(interval)
continue
(c_some_avg10, c_some_avg60, c_some_avg300
) = psi_file_cpu_to_metrics(cpu_file)
(m_some_avg10, m_some_avg60, m_some_avg300,
m_full_avg10, m_full_avg60, m_full_avg300
) = psi_file_mem_to_metrics(memory_file)
(i_some_avg10, i_some_avg60, i_some_avg300,
i_full_avg10, i_full_avg60, i_full_avg300
) = psi_file_mem_to_metrics(io_file)
log('{:>6} {:>6} {:>6} || {:>6} {:>6} {:>6} | {:>6} {:>6} {:>6} || {:>6}'
' {:>6} {:>6} | {:>6} {:>6} {:>6}'.format(
c_some_avg10, c_some_avg60, c_some_avg300,
m_some_avg10, m_some_avg60, m_some_avg300,
m_full_avg10, m_full_avg60, m_full_avg300,
i_some_avg10, i_some_avg60, i_some_avg300,
i_full_avg10, i_full_avg60, i_full_avg300
))
c_some_avg10 = float(c_some_avg10)
if ('c_some_avg10' not in peaks_dict or
peaks_dict['c_some_avg10'] < c_some_avg10):
peaks_dict['c_some_avg10'] = c_some_avg10
c_some_avg60 = float(c_some_avg60)
if ('c_some_avg60' not in peaks_dict or
peaks_dict['c_some_avg60'] < c_some_avg60):
peaks_dict['c_some_avg60'] = c_some_avg60
c_some_avg300 = float(c_some_avg300)
if ('c_some_avg300' not in peaks_dict or
peaks_dict['c_some_avg300'] < c_some_avg300):
peaks_dict['c_some_avg300'] = c_some_avg300
#######################################################################
m_some_avg10 = float(m_some_avg10)
if ('m_some_avg10' not in peaks_dict or
peaks_dict['m_some_avg10'] < m_some_avg10):
peaks_dict['m_some_avg10'] = m_some_avg10
m_some_avg60 = float(m_some_avg60)
if ('m_some_avg60' not in peaks_dict or
peaks_dict['m_some_avg60'] < m_some_avg60):
peaks_dict['m_some_avg60'] = m_some_avg60
m_some_avg300 = float(m_some_avg300)
if ('m_some_avg300' not in peaks_dict or
peaks_dict['m_some_avg300'] < m_some_avg300):
peaks_dict['m_some_avg300'] = m_some_avg300
m_full_avg10 = float(m_full_avg10)
if ('m_full_avg10' not in peaks_dict or
peaks_dict['m_full_avg10'] < m_full_avg10):
peaks_dict['m_full_avg10'] = m_full_avg10
m_full_avg60 = float(m_full_avg60)
if ('m_full_avg60' not in peaks_dict or
peaks_dict['m_full_avg60'] < m_full_avg60):
peaks_dict['m_full_avg60'] = m_full_avg60
m_full_avg300 = float(m_full_avg300)
if ('m_full_avg300' not in peaks_dict or
peaks_dict['m_full_avg300'] < m_full_avg300):
peaks_dict['m_full_avg300'] = m_full_avg300
#######################################################################
i_some_avg10 = float(i_some_avg10)
if ('i_some_avg10' not in peaks_dict or
peaks_dict['i_some_avg10'] < i_some_avg10):
peaks_dict['i_some_avg10'] = i_some_avg10
i_some_avg60 = float(i_some_avg60)
if ('i_some_avg60' not in peaks_dict or
peaks_dict['i_some_avg60'] < i_some_avg60):
peaks_dict['i_some_avg60'] = i_some_avg60
i_some_avg300 = float(i_some_avg300)
if ('i_some_avg300' not in peaks_dict or
peaks_dict['i_some_avg300'] < i_some_avg300):
peaks_dict['i_some_avg300'] = i_some_avg300
i_full_avg10 = float(i_full_avg10)
if ('i_full_avg10' not in peaks_dict or
peaks_dict['i_full_avg10'] < i_full_avg10):
peaks_dict['i_full_avg10'] = i_full_avg10
i_full_avg60 = float(i_full_avg60)
if ('i_full_avg60' not in peaks_dict or
peaks_dict['i_full_avg60'] < i_full_avg60):
peaks_dict['i_full_avg60'] = i_full_avg60
i_full_avg300 = float(i_full_avg300)
if ('i_full_avg300' not in peaks_dict or
peaks_dict['i_full_avg300'] < i_full_avg300):
peaks_dict['i_full_avg300'] = i_full_avg300
stdout.flush()
sleep(interval)