1b3c13754Ssangeri-us #!/bin/bash 2b3c13754Ssangeri-us 3b3c13754Ssangeri-us # This program will calculate memory usage for each process and generate a 4b3c13754Ssangeri-us # comma-separated value (CSV) output file named output.csv in the current 5b3c13754Ssangeri-us # directory. The output will consist of 2 lines. The first is a comma- 6b3c13754Ssangeri-us # separated list of process names. The second is a list of comma-separated 7b3c13754Ssangeri-us # memory usage values (expressed in bytes). Here is an abbrieviated example of 8b3c13754Ssangeri-us # the output: 9b3c13754Ssangeri-us # python(9),/lib/systemd/systemd-journald,/usr/bin/python,/sbin/init, 10b3c13754Ssangeri-us # phosphor-hwmon-readd(4),ipmid,phosphor-ledcontroller(4) 11b3c13754Ssangeri-us # 57896960,11789312,4434944,2893824,1900544,1764352 12b3c13754Ssangeri-us 13b3c13754Ssangeri-us program_name=${0##*/} 14b3c13754Ssangeri-us temp_file_path_1=/tmp/${program_name}_results_1 15b3c13754Ssangeri-us temp_file_path_2=/tmp/${program_name}_results_2 16b3c13754Ssangeri-us temp_file_path_3=/tmp/${program_name}_results_3 17b3c13754Ssangeri-us 18b3c13754Ssangeri-us temp_file_list="${temp_file_path_1} ${temp_file_path_2} ${temp_file_path_3}" 19b3c13754Ssangeri-us csv_file_path="output.csv" 20b3c13754Ssangeri-us 21b3c13754Ssangeri-us # Description of argument(s): 22b3c13754Ssangeri-us # pid The pid for which you desire statistics. If this is not specified, 23b3c13754Ssangeri-us # statistics will be gathered for all active pids. 24b3c13754Ssangeri-us 25*90dfee36SPatrick Williamsfunction get_parms() { 26b3c13754Ssangeri-us 27b3c13754Ssangeri-us # Get program parms. 28b3c13754Ssangeri-us 29b3c13754Ssangeri-us pids="${1:-$(ls /proc | grep -v [A-Za-z])}" ; shift 30b3c13754Ssangeri-us 31b3c13754Ssangeri-us return 0 32b3c13754Ssangeri-us 33b3c13754Ssangeri-us } 34b3c13754Ssangeri-us 35b3c13754Ssangeri-us 36*90dfee36SPatrick Williamsfunction exit_function() { 37b3c13754Ssangeri-us 38b3c13754Ssangeri-us # Used to clean up tmp files. 39b3c13754Ssangeri-us 40b3c13754Ssangeri-us rm -f ${temp_file_list} 41b3c13754Ssangeri-us return 42b3c13754Ssangeri-us 43b3c13754Ssangeri-us } 44b3c13754Ssangeri-us 45b3c13754Ssangeri-us 46*90dfee36SPatrick Williamsfunction validate_parms() { 47b3c13754Ssangeri-us 48b3c13754Ssangeri-us # Validate program parameters. 49b3c13754Ssangeri-us 50b3c13754Ssangeri-us # Making sure only root can run our script. 51b3c13754Ssangeri-us if [ "${USER}" != "root" ] ; then 52b3c13754Ssangeri-us echo "This script must be run as root" 1>&2 53b3c13754Ssangeri-us return 1 54b3c13754Ssangeri-us fi 55b3c13754Ssangeri-us 56b3c13754Ssangeri-us trap "exit_function $signal \$?" EXIT 57b3c13754Ssangeri-us return 0 58b3c13754Ssangeri-us 59b3c13754Ssangeri-us } 60b3c13754Ssangeri-us 61b3c13754Ssangeri-us 62*90dfee36SPatrick Williamsfunction get_process_mem() { 63b3c13754Ssangeri-us 64b3c13754Ssangeri-us local pid="${1}" ; shift 65b3c13754Ssangeri-us # Count memory statistic for passed pid. 66b3c13754Ssangeri-us 67b3c13754Ssangeri-us # Description of argument(s): 68b3c13754Ssangeri-us # pid The process ID for which you desire statistics. 69b3c13754Ssangeri-us [ ! -f /proc/${pid}/status -o ! -f /proc/${pid}/smaps ] && return 0 70b3c13754Ssangeri-us 71b3c13754Ssangeri-us # pss_total Total proportional set size of a process. 72b3c13754Ssangeri-us # private_total Total number of clean and dirty private pages in the 73b3c13754Ssangeri-us # mapping. 74b3c13754Ssangeri-us # shared_total The difference between pss_total and private_total. 75b3c13754Ssangeri-us 76b3c13754Ssangeri-us local pss_total private_total shared_total sum name 77b3c13754Ssangeri-us pss_total=$(grep -e "^Pss:" /proc/${pid}/smaps | awk '{print $2}' | awk '{sum += $1} END {print sum}') 78b3c13754Ssangeri-us private_total=$(grep -e "^Private" /proc/${pid}/smaps | awk '{print $2}' | awk '{sum += $1} END {print sum}') 79b3c13754Ssangeri-us 80b3c13754Ssangeri-us [ -z "${pss_total}" -o -z "${private_total}" ] && return 0 81b3c13754Ssangeri-us (( shared_total=pss_total-private_total )) 82b3c13754Ssangeri-us name=$(cut -d "" -f 1 /proc/${pid}/cmdline) 83b3c13754Ssangeri-us (( sum=shared_total+private_total )) 84b3c13754Ssangeri-us echo -e "${private_total} + ${shared_total} = ${sum} ${name}" 85b3c13754Ssangeri-us 86b3c13754Ssangeri-us } 87b3c13754Ssangeri-us 88b3c13754Ssangeri-us 89*90dfee36SPatrick Williamsfunction mainf() { 90b3c13754Ssangeri-us 91b3c13754Ssangeri-us get_parms "$@" || return 1 92b3c13754Ssangeri-us 93b3c13754Ssangeri-us validate_parms || return 1 94b3c13754Ssangeri-us 95b3c13754Ssangeri-us # To ensure temp files not exist. 96b3c13754Ssangeri-us rm -f ${temp_file_list} 97b3c13754Ssangeri-us 98b3c13754Ssangeri-us for pid in ${pids} ; do 99b3c13754Ssangeri-us get_process_mem ${pid} >> ${temp_file_path_1} 100b3c13754Ssangeri-us done 101b3c13754Ssangeri-us 102b3c13754Ssangeri-us # This is used to sort results by memory usage. 103b3c13754Ssangeri-us sort -gr -k 5 ${temp_file_path_1} > ${temp_file_path_2} 104b3c13754Ssangeri-us 105b3c13754Ssangeri-us # Find duplicates in the process list output and combine them. In such 106b3c13754Ssangeri-us # cases, adjust the process name by including a (<count>) suffix. In the 107b3c13754Ssangeri-us # following example of output, 4 instances of "phosphor-hwmon-readd" have 108b3c13754Ssangeri-us # been combined. 109b3c13754Ssangeri-us # 974848 + 925696 = 1900544 phosphor-hwmon-readd(4) 110b3c13754Ssangeri-us for proc_name in $(awk '{print $6}' ${temp_file_path_2} | sort -u) ; do 111b3c13754Ssangeri-us count=$(awk -v src=${proc_name} '{if ($6==src) {print $6}}' ${temp_file_path_2} | wc -l) 112b3c13754Ssangeri-us [ "${count}" = "1" ] && count_string="" || count_string="(${count})" 113b3c13754Ssangeri-us vmsize_in_kb=$(awk -v src=${proc_name} '{if ($6==src) {print $1}}' ${temp_file_path_2} | awk '{sum += $1} END {print sum}') 114b3c13754Ssangeri-us vmrss_in_kb=$(awk -v src=${proc_name} '{if ($6==src) {print $3}}' ${temp_file_path_2} | awk '{sum += $1} END {print sum}') 115b3c13754Ssangeri-us total=$(awk '{print $5}' ${temp_file_path_2} | awk '{sum += $1} END {print sum}') 116b3c13754Ssangeri-us (( sum=vmrss_in_kb+vmsize_in_kb )) 117b3c13754Ssangeri-us echo -e "${vmsize_in_kb} + ${vmrss_in_kb} = ${sum} \t ${proc_name}${count_string}" >> ${temp_file_path_3} 118b3c13754Ssangeri-us done 119b3c13754Ssangeri-us 120b3c13754Ssangeri-us # Sort once more. 121b3c13754Ssangeri-us sort -gr -k 5 ${temp_file_path_3} > ${temp_file_path_1} 122b3c13754Ssangeri-us 123b3c13754Ssangeri-us # Read results from temp file and convert it to csv. 124b3c13754Ssangeri-us csv_line1="" 125b3c13754Ssangeri-us csv_line2="" 126b3c13754Ssangeri-us while read line ; do 127b3c13754Ssangeri-us while read private plus_operator shared equal_sign sum name ; do 128b3c13754Ssangeri-us (( sum == 0 )) && continue 129b3c13754Ssangeri-us csv_line1+=",${name}" 130b3c13754Ssangeri-us csv_line2+=",${sum}" 131b3c13754Ssangeri-us done <<<${line} 132b3c13754Ssangeri-us done < ${temp_file_path_1} 133b3c13754Ssangeri-us 134b3c13754Ssangeri-us # Strip leading commas. 135b3c13754Ssangeri-us csv_line1="${csv_line1#,}" 136b3c13754Ssangeri-us csv_line2="${csv_line2#,}" 137b3c13754Ssangeri-us { echo "${csv_line1}" ; echo "${csv_line2}" ; } >> ${csv_file_path} 138b3c13754Ssangeri-us 139b3c13754Ssangeri-us return 0 140b3c13754Ssangeri-us 141b3c13754Ssangeri-us } 142b3c13754Ssangeri-us 143b3c13754Ssangeri-us # Main 144b3c13754Ssangeri-us 145b3c13754Ssangeri-us mainf "${@}" 146b3c13754Ssangeri-us rc="${?}" 147b3c13754Ssangeri-us exit "${rc}" 148b3c13754Ssangeri-us 149