diff --git a/tools/config_tool.py b/tools/config_tool.py index f2e746b..9265e9e 100755 --- a/tools/config_tool.py +++ b/tools/config_tool.py @@ -7,6 +7,7 @@ from memory_process_profiler import memory_process_profiler from file_descriptor_process_profiler import file_descriptor_process_profiler from thread_process_profiler import thread_process_profiler +from global_system_processes_profiler import global_system_processes_profiler # Menu ANSI Colors BLACK = '\033[30m' @@ -102,8 +103,9 @@ def display_menu(): print("1. Create General Process Profiler") print("2. Create Memory Process Profiler") print("3. Create File Descriptor Profiler") - print("4. Create Thread Descriptor Profiler") - print("5. Exit") + print("4. Create Thread Profiler") + print("5. Create Global System Processes Profiler") + print("6. Exit") watch_list_choice = input(f"\n{BRIGHT_CYAN}Enter your choice (1-5):{RESET}\n") if watch_list_choice == '1': clear_screen() @@ -122,6 +124,10 @@ def display_menu(): thread_process_profiler() break if watch_list_choice == '5': + clear_screen() + global_system_processes_profiler() + break + if watch_list_choice == '6': break print(f"\n{BLACK}{BACKGROUND_BRIGHT_MAGENTA}Invalid choice. Please try again.{RESET}") elif top_choice == '4': diff --git a/tools/global_system_processes_profiler.py b/tools/global_system_processes_profiler.py new file mode 100755 index 0000000..592ae79 --- /dev/null +++ b/tools/global_system_processes_profiler.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 + +import os +import textwrap + +# Base Directory +base_dir = os.path.dirname(os.path.abspath(__file__)) +os.chdir(base_dir) + +# Menu ANSI Colors +BLACK = '\033[30m' +GREEN = '\033[32m' +BRIGHT_GREEN = '\033[92m' +BRIGHT_CYAN = '\033[96m' +BRIGHT_YELLOW = '\033[93m' +BACKGROUND_BRIGHT_MAGENTA = '\033[105m' +RESET = '\033[0m' + +# Reset Screen +def clear_screen(): + os.system('cls' if os.name == 'nt' else 'clear') + +# Write configuration file to watch_list directory +def write_to_file(filename, template): + try: + with open(os.path.join(os.pardir, "watch_list", filename), 'w', encoding="utf-8") as file: + file.write(template) + clear_screen() + print(f"\n{GREEN}'{filename}' configuration created.{RESET}") + except Exception as e: + print(f"{BLACK}{BACKGROUND_BRIGHT_MAGENTA}\nAn error occurred: {e}{RESET}") + +# Global System Processes Profiler Menu +def global_system_processes_profiler(): + print(f"\n{BRIGHT_GREEN}GLOBAL SYSTEM PROCESSES PROFILER SETTINGS:{RESET}") + filename = "system-processes" + while True: + try: + interval = int(input(f"\n{BRIGHT_CYAN}Enter the monitoring interval {GREEN}(in seconds, recommend 1800 (30min)){RESET}:{RESET}\n")) + break + except ValueError: + print(f"{BLACK}{BACKGROUND_BRIGHT_MAGENTA}\nInvalid input. Please enter a number of seconds.{RESET}") + + template = f""" + # Global System Processes Profiler + import os + import sys + import time + import logging + import threading + import psutil + import operator + import subprocess + + global_system_processes_profiler_file = (f"{{str(os.getcwd())}}/logs/glbl-sys-procs-profiler.log") + error_file = (f"{{str(os.getcwd())}}/logs/error.log") + + # Global File Descriptor Counter + + def get_top_processes(num_top=5): + processes = [] + for proc in psutil.process_iter(['pid', 'name', 'num_threads', 'num_fds', 'memory_info', 'cpu_percent']): + try: + processes.append(proc.info) + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + pass + + top_threads = sorted(processes, key=lambda p: p['num_threads'], reverse=True)[:num_top] + top_fds = sorted(processes, key=lambda p: p['num_fds'], reverse=True)[:num_top] + top_memory = sorted(processes, key=lambda p: p['memory_info'].rss, reverse=True)[:num_top] + top_cpu = sorted(processes, key=lambda p: p['cpu_percent'], reverse=True)[:num_top] + + return top_threads, top_fds, top_memory, top_cpu + + def monitor(): + while True: + try: + top_threads, top_fds, top_memory, top_cpu = get_top_processes() + with open(global_system_processes_profiler_file, "a", encoding="utf-8") as f: + f.write(f"########### Top 5 Memory (RSS) Consumers - {{time.ctime()}} ###########\\n") + for proc in top_memory: + f.write(f"{{time.ctime()}} - Top Memory (RSS) Consumer - Name: {{proc['name']}}, PID: {{proc['pid']}}, Memory (RSS): {{proc['memory_info'].rss}} bytes\\n") + f.write(f"########### Top 5 CPU Consumers - {{time.ctime()}} ###########\\n") + for proc in top_cpu: + f.write(f"{{time.ctime()}} - Top CPU Consumer - Name: {{proc['name']}}, PID: {{proc['pid']}}, CPU: {{proc['cpu_percent']}}%\\n") + f.write(f"########### Top 5 Thread Consumers - {{time.ctime()}} ###########\\n") + for proc in top_threads: + f.write(f"{{time.ctime()}} - Top Thread Consumer - Name: {{proc['name']}}, PID: {{proc['pid']}}, Threads: {{proc['num_threads']}}\\n") + f.write(f"########### Top 5 File Descriptor Consumers - {{time.ctime()}} ###########\\n") + for proc in top_fds: + f.write(f"{{time.ctime()}} - Top File Descriptor Consumer - Name: {{proc['name']}}, PID: {{proc['pid']}}, File Descriptors: {{proc['num_fds']}}\\n") + time.sleep({interval}) + except Exception as e: + print(f"An error occurred in Process Watch configuration file {filename}: {{e}}") + with open(error_file, "a", encoding="utf-8") as file: + file.write(str(e) + (f" in {filename}") + "\\n") + raise sys.exit(1) + + def worker(): + monitor_thread = threading.Thread(target=monitor) + monitor_thread.start() + + worker() + """ + # Write the template into a config + write_to_file(os.path.abspath(f"../watch_list/{filename}_glbl.py"), textwrap.dedent(template)) \ No newline at end of file diff --git a/tools/memory_process_profiler.py b/tools/memory_process_profiler.py index e8c8c8b..73bd322 100755 --- a/tools/memory_process_profiler.py +++ b/tools/memory_process_profiler.py @@ -32,7 +32,7 @@ def write_to_file(filename, template): # Memory Process Profiler Menu def memory_process_profiler(): - print(f"\n{BRIGHT_GREEN}MEMORY PROCESS PROFILER SETTINGS:{RESET}") + print(f"\n{BRIGHT_GREEN}MEMORY (RSS) PROCESS PROFILER SETTINGS:{RESET}") filename = input(f"\n{BRIGHT_CYAN}Enter the name of the configuration file:{RESET}\n") sanitized_filename = filename.replace(".", "-") process_name = input(f"\n{BRIGHT_CYAN}Enter the process name to monitor {GREEN}(use name in ps ouput){RESET}:{RESET}\n") @@ -51,7 +51,7 @@ def memory_process_profiler(): action = input(f"\n{BRIGHT_CYAN}Enter the action to take upon the memory threshold being met {GREEN}(leave blank for no action){RESET}:{RESET}\n") or "echo" template = f""" - # Memory Process Profiler + # Memory (RSS) Process Profiler import os import sys import time @@ -96,10 +96,10 @@ def monitor(): process_memory = int(process_memory_set.pop()) if int(process_memory) >= {memory_threshold}: with open(memory_process_profiler_file, "a", encoding="utf-8") as f: - f.write(f"{{time.ctime()}} - Memory Alert - Above Threshold {memory_threshold} MB - Process: [ {process_name} ], PID: {{int(find_pid_by_name('{process_name}'))}}, Memory: {{find_memory_usage_by_pid(int(find_pid_by_name('{process_name}')))}} MB\\n") + f.write(f"{{time.ctime()}} - Memory (RSS) Alert - Above Threshold {memory_threshold} MB - Process: [ {process_name} ], PID: {{int(find_pid_by_name('{process_name}'))}}, Memory: {{find_memory_usage_by_pid(int(find_pid_by_name('{process_name}')))}} MB\\n") process_action = subprocess.run(['{action}'], capture_output=True, text=True) time.sleep(5) - f.write(f"{{time.ctime()}} - Memory Alert - After Remediation - Action Taken: [ {action} ], Process: [ {process_name} ], PID: {{int(find_pid_by_name('{process_name}'))}}, Memory: {{find_memory_usage_by_pid(int(find_pid_by_name('{process_name}')))}} MB\\n") + f.write(f"{{time.ctime()}} - Memory (RSS) Alert - After Remediation - Action Taken: [ {action} ], Process: [ {process_name} ], PID: {{int(find_pid_by_name('{process_name}'))}}, Memory: {{find_memory_usage_by_pid(int(find_pid_by_name('{process_name}')))}} MB\\n") time.sleep({interval}) except Exception as e: print(f"An error occurred in Process Watch configuration file {sanitized_filename}: {{e}}")