home/.local/script/inotify-consumers

129 lines
5.0 KiB
Bash
Executable File

#!/bin/bash
# Get the procs sorted by the number of inotify watches
# @author Carl-Erik Kopseng
# @latest https://github.com/fatso83/dotfiles/blob/master/utils/scripts/inotify-consumers
# Discussion leading up to answer: https://unix.stackexchange.com/questions/15509/whos-consuming-my-inotify-resources
#
#
########################################## Notice ##########################################
### Since Fall 2022 you should prefer using the following C++ version ###
### https://github.com/mikesart/inotify-info ###
############################################################################################
#
#
# The fastest version of this script is here: https://github.com/fatso83/dotfiles/commit/inotify-consumers-v1-fastest
# Later PRs introduced significant slowdowns at the cost of better output, but it is insignificant on most machines
# See this for details: https://github.com/fatso83/dotfiles/pull/10#issuecomment-1122374716
main(){
printf "\n%${WLEN}s %${WLEN}s\n" "INOTIFY" "INSTANCES"
printf "%${WLEN}s %${WLEN}s\n" "WATCHES" "PER "
printf "%${WLEN}s %${WLEN}s %s\n" " COUNT " "PROCESS " "PID USER COMMAND"
printf -- "------------------------------------------------------------\n"
generateData
}
usage(){
cat << EOF
Usage: $0 [--help|--limits]
-l, --limits Will print the current related limits and how to change them
-h, --help Show this help
FYI: Check out Michael Sartain's C++ take on this script. The resulting native executable
is much faster, modern and feature rich. It can be found at
https://github.com/mikesart/inotify-info
Requires building, but is well worth the few seconds :)
EOF
}
limits(){
printf "\nCurrent limits\n-------------\n"
sysctl fs.inotify.max_user_instances fs.inotify.max_user_watches
cat <<- EOF
Changing settings permanently
-----------------------------
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p # re-read config
EOF
}
generateData() {
local -i PROC
local -i PID
local -i CNT
local -i INSTANCES
local -i TOT
local -i TOTINSTANCES
# read process list into cache
local PSLIST="$(ps ax -o pid,user=WIDE-COLUMN,command $COLSTRING)"
local INOTIFY="$(find /proc/[0-9]*/fdinfo -type f 2>/dev/null | xargs grep ^inotify 2>/dev/null)"
local INOTIFYCNT="$(echo "$INOTIFY" | cut -d "/" -s --output-delimiter=" " -f 3 |uniq -c | sed -e 's/:.*//')"
# unique instances per process is denoted by number of inotify FDs
local INOTIFYINSTANCES="$(echo "$INOTIFY" | cut -d "/" -s --output-delimiter=" " -f 3,5 | sed -e 's/:.*//'| uniq |awk '{print $1}' |uniq -c)"
local INOTIFYUSERINSTANCES="$(echo "$INOTIFY" | cut -d "/" -s --output-delimiter=" " -f 3,5 | sed -e 's/:.*//' | uniq |
while read PID FD; do echo $PID $FD $(grep -e "^ *${PID} " <<< "$PSLIST"|awk '{print $2}'); done | cut -d" " -f 3 | sort | uniq -c |sort -nr)"
set -e
cat <<< "$INOTIFYCNT" |
{
while read -rs CNT PROC; do # count watches of processes found
echo "${PROC},${CNT},$(echo "$INOTIFYINSTANCES" | grep " ${PROC}$" |awk '{print $1}')"
done
} |
grep -v ",0," | # remove entires without watches
sort -n -t "," -k 2,3 -r | # sort to begin with highest numbers
{ # group commands so that $TOT is visible in the printf
IFS=","
while read -rs PID CNT INSTANCES; do # show watches and corresponding process info
printf "%$(( WLEN - 2 ))d %$(( WLEN - 2 ))d %s\n" "$CNT" "$INSTANCES" "$(grep -e "^ *${PID} " <<< "$PSLIST")"
TOT=$(( TOT + CNT ))
TOTINSTANCES=$(( TOTINSTANCES + INSTANCES))
done
# These stats should be per-user as well, since inotify limits are per-user..
printf "\n%$(( WLEN - 2 ))d %s\n" "$TOT" "WATCHES TOTAL COUNT"
# the total across different users is somewhat meaningless, not printing for now.
# printf "\n%$(( WLEN - 2 ))d %s\n" "$TOTINSTANCES" "TOTAL INSTANCES COUNT"
}
echo ""
echo "INotify instances per user (e.g. limits specified by fs.inotify.max_user_instances): "
echo ""
(
echo "INSTANCES USER"
echo "----------- ------------------"
echo "$INOTIFYUSERINSTANCES"
) | column -t
echo ""
exit 0
}
# get terminal width
declare -i COLS=$(tput cols 2>/dev/null || echo 80)
declare -i WLEN=10
declare COLSTRING="--columns $(( COLS - WLEN ))" # get terminal width
if [ "$1" = "--limits" -o "$1" = "-l" ]; then
limits
exit 0
fi
if [ "$1" = "--help" -o "$1" = "-h" ]; then
usage
exit 0
fi
# added this line and moved some declarations to allow for the full display instead of a truncated version
if [ "$1" = "--full" -o "$1" = "-f" ]; then
unset COLSTRING
main
fi
if [ -n "$1" ]; then
printf "\nUnknown parameter '$1'\n" >&2
usage
exit 1
fi
main