diff --git a/CHANGELOG.md b/CHANGELOG.md index d8334e3..42cbd1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ - [x] `oom-sort` - [x] `psi-top` - [x] `psi-monitor` + - [x] `i-memhog` - [x] Improve poll rate algorithm - [x] Fixed Makefile for installation on CentOS 7 (remove gzip `-k` option). - [x] Added `max_post_sigterm_victim_lifetime` option: send SIGKILL to the victim if it doesn't respond to SIGTERM for a certain time @@ -42,6 +43,7 @@ - [x] Removed self-defense options from the config, use systemd unit scheduling instead - [x] Added the ability to send any signal instead of SIGTERM for processes with certain names - [x] Added initial support for `PSI` + - [x] Recheck memory levels after finding a victim - [x] Improved user input validation - [x] Improved documentation - [x] Handle signals diff --git a/Makefile b/Makefile index a715e77..eaf8c2e 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ install: install -m0755 ./oom-sort $(DESTDIR)/$(PREFIX)/usr/bin/oom-sort install -m0755 ./psi-top $(DESTDIR)/$(PREFIX)/usr/bin/psi-top install -m0755 ./psi-monitor $(DESTDIR)/$(PREFIX)/usr/bin/psi-monitor + install -m0755 ./i-memhog $(DESTDIR)/$(PREFIX)/usr/bin/i-memhog install -d $(DESTDIR)/$(PREFIX)/etc/nohang -git describe --tags --long --dirty > ./version @@ -41,6 +42,7 @@ uninstall: rm -fv $(PREFIX)/usr/bin/oom-sort rm -fv $(PREFIX)/usr/bin/psi-top rm -fv $(PREFIX)/usr/bin/psi-monitor + rm -fv $(PREFIX)/usr/bin/i-memhog rm -fv $(PREFIX)/usr/share/man/man1/nohang.1.gz rm -fv $(PREFIX)/usr/share/man/man1/oom-sort.1.gz rm -fv $(PREFIX)/lib/systemd/system/nohang.service diff --git a/README.md b/README.md index 0c9bb3a..21feea6 100644 --- a/README.md +++ b/README.md @@ -533,6 +533,10 @@ some 0.29 7.58 14.58 | full 0.28 6.92 13.24 ``` +### i-memhog + +`i-memhog` is an interactive memory hog for testing purposes. + ## Contribution Use cases, feature requests and any questions are [welcome](https://github.com/hakavlad/nohang/issues). diff --git a/i-memhog b/i-memhog new file mode 100755 index 0000000..198461a --- /dev/null +++ b/i-memhog @@ -0,0 +1,315 @@ +#!/usr/bin/env python3 + + +from signal import signal, SIGTERM +from time import sleep +from sys import exit +import os + + +# чек общей доступной, для lim2avail +def total_mem_available(): + + with open('/proc/meminfo') as file: + mem_list = file.readlines() + + mem_available = meminfo_num(mem_list, mem_available_index) + swap_free = meminfo_num(mem_list, swap_free_index) + + return round((swap_free + mem_available) / 1024) # MiB + + +# добитие байтами рандома +def terminal(): + ex = [] + while True: + try: + ex.append(os.urandom(1)) + except MemoryError: + continue + + +def meminfo_num(mem_list, index): + return int(mem_list[index].split(':')[1].split(' ')[-2]) + + +# выдача основных показателей meminfo, KiB +def mem_check_main(): + + with open('/proc/meminfo') as file: + mem_list = file.readlines() + + mem_available = meminfo_num(mem_list, mem_available_index) + swap_total = meminfo_num(mem_list, swap_total_index) + swap_free = meminfo_num(mem_list, swap_free_index) + + return mem_available, swap_total, swap_free + + +def signal_handler(signum, frame): + print('Got signal {}'.format(signum)) + # sleep(1) + # exit() + + +def meminfo(): + + # получаем сырой mem_list + with open('/proc/meminfo') as file: + mem_list = file.readlines() + + # получаем список названий позиций: MemTotal etc + mem_list_names = [] + for s in mem_list: + mem_list_names.append(s.split(':')[0]) + + # ищем MemAvailable, обрабатываем исключение + try: + mem_available_index = mem_list_names.index('MemAvailable') + except ValueError: + print("Your Linux kernel is too old (3.14+ requied), bye!") + # исключение для ядер < 3.14, не определяющих MemAvailable + exit() + + # ищем позиции SwapTotl и SwapFree + swap_total_index = mem_list_names.index('SwapTotal') + swap_free_index = mem_list_names.index('SwapFree') + + buffers_index = mem_list_names.index('Buffers') + cached_index = mem_list_names.index('Cached') + active_index = mem_list_names.index('Active') + inactive_index = mem_list_names.index('Inactive') + shmem_index = mem_list_names.index('Shmem') + + # ищем значение MemTotal в KiB + mem_total = int(mem_list[0].split(':')[1].split(' ')[-2]) + + return mem_total, mem_available_index, swap_total_index, swap_free_index, buffers_index, cached_index, active_index, inactive_index, shmem_index + + +meminfo_tuple = meminfo() + +mem_total = meminfo_tuple[0] +mem_available_index = meminfo_tuple[1] +swap_total_index = meminfo_tuple[2] +swap_free_index = meminfo_tuple[3] + +buffers_index = meminfo_tuple[4] +cached_index = meminfo_tuple[5] +active_index = meminfo_tuple[6] +inactive_index = meminfo_tuple[7] +shmem_index = meminfo_tuple[8] + + +# печать показателей на этапах работы +def print_mem(): + + mem_tup = mem_check_main() + + mem_available = mem_tup[0] + swap_total = mem_tup[1] + swap_free = mem_tup[2] + + print( + 'MemAvailable: ', + round( + mem_available / + 1024 / + 1024, + 3), + 'GiB,', + round( + mem_available / + 1024), + 'MiB,', + round( + mem_available / + mem_total * + 100, + 1), + '%') + + if swap_total != 0: + print( + 'SwapFree: ', + round( + swap_free / + 1024 / + 1024, + 3), + 'GiB,', + round( + swap_free / + 1024), + 'MiB,', + round( + swap_free / + swap_total * + 100, + 1), + '%') + print('Total Free: ', + round((mem_available + swap_free) / 1024 / 1024, + 3), + 'GiB,', + round((mem_available + swap_free) / 1024), + 'MiB,', + round((mem_available + swap_free) / (mem_total + swap_total) * 100, + 1), + '%') + else: + print( + 'Swap disabled' + ) + + +# бесконечный жор +def inf(): + + print( + 'Вводите целые неотрицательные числа. Чем больше, тем быстрее потребление памяти.\n1000 same обеспечивает потребление на уровне полтора гиг в секунду,\nurandom работает на скорости максимум 170 M/s' + ) + same = input("same: ") + urandom = input("urandom: ") + + expanding_list = [] + + print( + 'Процесс неограниченного потребления пошёл... Press Ctrl + C for exit' + ) + + while True: + try: + expanding_list.append(os.urandom(int(urandom))) + expanding_list.append('#' * int(same)) + except MemoryError: + print('MemoryError, start побайтовая добивалка!') + terminal() + + +def selfterm(): + os.kill(os.getpid(), signal.SIGTERM) + + +# жор числп гиг +def lim(): + + expanding_list = [] + + n = input('На сколько гигабайт уменьшить доступную память?\n: ') + + print('Погнали тратить ' + n + ' гиг...') + + i = 0 + + while True: + + i += 1 + + try: + expanding_list.append(os.urandom(int(100))) + expanding_list.append('#' * int(300)) + except MemoryError: + print('MemoryError!') + break + if i > 2020202 * int(n): + print('DONE') + break + + return expanding_list + + +# жор до остатка мегабайт +def lim2avail(): + + expanding_list = [] + + n = input( + 'Сколько мегабайт общей доступной памяти (MemAvailable + SwapFree) оставить?\nВведите целое положительное число: ' + ) + + # проверка на целое положительное + if n.isdigit(): + n = int(n) + else: + print( + 'Вы ввели не целое положительное число' + ) + return 0 + + if n == 0: + print( + 'Вы ввели не целое положительное число' + ) + return 0 + + print( + 'Погнали уменьшать доступную память до уровня ниже ' + + str(n) + + ' MiB...') + + while True: + try: + expanding_list.append(os.urandom(5000)) + expanding_list.append('#' * 5000) + except MemoryError: + print('MemoryError!') + break + if total_mem_available() <= n: + print('DONE') + break + + return expanding_list + + +print('WARNING: эта прога способна потратить память и повесить систему, будьте осторожны.') +print('При ее работе следите за показателями памяти.') + + +print('Ignore SIGTERM? (y|n)') + +sss = input(': ') + +if sss == 'y': + signal(SIGTERM, signal_handler) + print('The SIGTERM signal will be ignored') +else: + print('The SIGTERM signal will NOT be ignored') + + +ex_list = [] + +try: + while True: + + print() + print_mem() + print() + print('Выберите вариант из списка ниже') + print('8 или i или I - запустить бесконечное потребление, предложив выбрать скорость потребления и энтропию') + print('7 или l или L - запустить ограниченное потребление заданного числа гигов') + print('6 или a или A - жрать память пока количество доступной памяти не опустится ниже заданного') + print('0 или с или С - очистить накопления при их наличии') + print('q или любой другой символ - выход (можно просто нажать Enter)') + + li = input(': ') + + if li is 'l' or li is 'L' or li is '7': + x = lim() + ex_list.append(x) + elif li is 'i' or li is 'I' or li is '8': + inf() + elif li is 'c' or li is 'C' or li is '0': + ex_list = [] + x = 0 + y = 0 + elif li is '6' or li is 'a' or li is 'A': + y = lim2avail() + ex_list.append(y) + else: + exit() + +except KeyboardInterrupt: + print() + print_mem() + selfterm()