Last active
February 13, 2025 18:29
-
-
Save hiranp/f83d50a00cd8b4bcc5c5d22ca06c3e4b to your computer and use it in GitHub Desktop.
cript to monitor and log space, memory, and CPU usage of a process
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Script to monitor and log space, memory, and CPU usage of a process | |
| # Usage: sudo ./monitor_process.sh <process_name> <log_file> | |
| # Enable error handling after argument check | |
| set -o errexit | |
| set -o nounset | |
| set -o pipefail | |
| # Function to log message to both console and file | |
| log() { | |
| echo "$1" | tee -a "$LOG_FILE" | |
| } | |
| # Function to show usage | |
| usage() { | |
| echo "Usage: $0 <process_name> [log_file]" | |
| echo " process_name: Name of the process to monitor" | |
| echo " log_file: Optional. Path to log file (default: ./<process_name>_monitor.log)" | |
| exit 1 | |
| } | |
| # Check for required arguments | |
| if [ $# -lt 1 ]; then | |
| usage | |
| fi | |
| # Configuration | |
| readonly PROCESS_NAME="$1" | |
| readonly LOG_FILE="${2:-"./${PROCESS_NAME}_monitor.log"}" | |
| readonly INTERVAL=5 # Monitoring interval in seconds | |
| readonly MAX_RUNTIME=1800 # Maximum runtime in seconds (30 minutes) | |
| readonly START_TIME=$(date +%s) | |
| DISK_MEASUREMENTS=() # Array to store disk usage measurements | |
| # Function to get process PID | |
| get_pid() { | |
| pgrep -o -f "$PROCESS_NAME" || true | |
| } | |
| # Function to find process read/write locations | |
| find_process_locations() { | |
| local pid=$1 | |
| local req_mode="${2:-w}" # Optional: "r" for read-only, "w" for write (default), "rw" for read-write | |
| local fd_dir="/proc/$pid/fd" | |
| # Check if PID is provided | |
| if [ -z "$pid" ]; then | |
| echo "Error: PID not provided" >&2 | |
| return 1 | |
| fi | |
| # Check if process exists | |
| if ! kill -0 "$pid" 2>/dev/null; then | |
| echo "Error: Process $pid does not exist" >&2 | |
| return 1 | |
| fi | |
| # Check if fd directory exists and is readable | |
| if [ ! -d "$fd_dir" ] || [ ! -r "$fd_dir" ]; then | |
| echo "Error: Cannot access $fd_dir" >&2 | |
| return 1 | |
| fi | |
| # Find and filter file descriptors opened for writing | |
| find "$fd_dir" -type l 2>/dev/null | while read -r fd; do | |
| local fdnum | |
| fdnum=$(basename "$fd") | |
| local info_file="/proc/$pid/fdinfo/$fdnum" | |
| # Check if fdinfo file exists | |
| [ -f "$info_file" ] || continue | |
| local flag | |
| flag=$(grep '^flags:' "$info_file" | awk '{print $2}') | |
| # Compute access mode: 0 = read-only, 1 = write-only, 2 = read-write | |
| local fdmode=$((flag & 3)) | |
| # Check against desired mode | |
| case "$req_mode" in | |
| r) | |
| [ "$fdmode" -eq 0 ] || continue | |
| ;; | |
| w) | |
| [ "$fdmode" -ne 0 ] || continue | |
| ;; | |
| rw) | |
| [ "$fdmode" -eq 2 ] || continue | |
| ;; | |
| *) | |
| # If an unsupported mode is provided, skip this FD | |
| continue | |
| ;; | |
| esac | |
| local target | |
| target=$(readlink "$fd" 2>/dev/null) || continue | |
| # Skip special files and directories | |
| case "$target" in | |
| /dev/* | pipe:* | socket:* | anon_inode:*) continue ;; | |
| esac | |
| # Only show regular files and directories that exist | |
| if [ -f "$target" ] || [ -d "$target" ]; then | |
| echo "$target" | |
| fi | |
| done | sort -u | |
| } | |
| # Function to get disk usage | |
| get_disk_usage() { | |
| local pid=$1 | |
| if [ -d "/proc/$pid/cwd" ]; then | |
| du -sh "/proc/$pid/cwd" 2>/dev/null | awk '{print $1}' || echo "N/A" | |
| else | |
| echo "N/A" | |
| fi | |
| } | |
| # Function to get memory usage | |
| get_memory_usage() { | |
| local pid=$1 | |
| pmap "$pid" 2>/dev/null | tail -n 1 | awk '/[0-9]K/{print $2}' || echo "N/A" | |
| } | |
| # Function to get CPU usage | |
| get_cpu_usage() { | |
| local pid=$1 | |
| ps -p "$pid" -o %cpu= 2>/dev/null | awk '{printf "%.1f", $1}' || echo "N/A" | |
| } | |
| # Function to calculate average disk usage rate | |
| calculate_average_disk_rate() { | |
| local -a measurements=("${@}") | |
| local count=${#measurements[@]} | |
| local total=0 | |
| for ((i = 1; i < count; i++)); do | |
| local diff=$((measurements[i] - measurements[i - 1])) | |
| total=$((total + diff)) | |
| done | |
| # Calculate average change per interval | |
| if [ $((count - 1)) -gt 0 ]; then | |
| echo $((total / (count - 1))) | |
| else | |
| echo 0 | |
| fi | |
| } | |
| # Initialize log file with headers | |
| { | |
| log "=== Process Monitoring Report ===" | |
| log "Started at: $(date)" | |
| log "Process: $PROCESS_NAME" | |
| log "Monitoring interval: ${INTERVAL}s" | |
| log "Maximum runtime: ${MAX_RUNTIME}s" | |
| log "----------------------------------------" | |
| } | |
| # Get initial measurements | |
| PID=$(get_pid) | |
| if [ -z "$PID" ]; then | |
| log "Error: Process $PROCESS_NAME not found" | |
| exit 1 | |
| fi | |
| # Store initial disk usage | |
| INITIAL_DISK_USAGE=$(get_disk_usage "$PID") | |
| DISK_MEASUREMENTS+=("$(echo "$INITIAL_DISK_USAGE" | numfmt --from=iec)") | |
| log "Initial disk usage: $INITIAL_DISK_USAGE" | |
| # Monitoring loop | |
| while true; do | |
| CURRENT_TIME=$(date +%s) | |
| RUNTIME=$((CURRENT_TIME - START_TIME)) | |
| # Exit if maximum runtime is reached | |
| if [ "$RUNTIME" -ge "$MAX_RUNTIME" ]; then | |
| FINAL_DISK_USAGE=$(get_disk_usage "$PID") | |
| DISK_MEASUREMENTS+=("$(echo "$FINAL_DISK_USAGE" | numfmt --from=iec)") | |
| USAGE_RATE=$(calculate_disk_usage_rate "$INITIAL_DISK_USAGE" "$FINAL_DISK_USAGE" "$RUNTIME") | |
| AVG_RATE=$(calculate_average_disk_rate "${DISK_MEASUREMENTS[@]}") | |
| { | |
| log "=== Monitoring Summary ===" | |
| log "Ended at: $(date)" | |
| log "Total runtime: $RUNTIME seconds" | |
| log "Initial disk usage: $INITIAL_DISK_USAGE" | |
| log "Final disk usage: $FINAL_DISK_USAGE" | |
| log "Disk usage rate: ${USAGE_RATE} MB/minute" | |
| log "Average rate of change: ${AVG_RATE} bytes/interval" | |
| } | |
| exit 0 | |
| fi | |
| # Get current PID | |
| PID=$(get_pid) | |
| if [ -z "$PID" ]; then | |
| log "$(date): Process $PROCESS_NAME not found" | |
| sleep "$INTERVAL" | |
| continue | |
| fi | |
| # Get current measurements and store disk usage | |
| MEM_USAGE=$(get_memory_usage "$PID") | |
| CPU_USAGE=$(get_cpu_usage "$PID") | |
| DISK_USAGE=$(get_disk_usage "$PID") | |
| DISK_MEASUREMENTS+=("$(echo "$DISK_USAGE" | numfmt --from=iec)") | |
| # Log the information | |
| log "$(date): PID=$PID, Memory=$MEM_USAGE, CPU=${CPU_USAGE}%, Disk=$DISK_USAGE" | |
| sleep "$INTERVAL" | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment