blob: 4ee99778800bcdc913cc27406d2d1bc70a7ae26c [file] [log] [blame]
sangeri-us b3c13752017-11-08 07:26:16 -06001#!/bin/bash
2
3# This program will calculate memory usage for each process and generate a
4# comma-separated value (CSV) output file named output.csv in the current
5# directory. The output will consist of 2 lines. The first is a comma-
6# separated list of process names. The second is a list of comma-separated
7# memory usage values (expressed in bytes). Here is an abbrieviated example of
8# the output:
9# python(9),/lib/systemd/systemd-journald,/usr/bin/python,/sbin/init,
10# phosphor-hwmon-readd(4),ipmid,phosphor-ledcontroller(4)
11# 57896960,11789312,4434944,2893824,1900544,1764352
12
13program_name=${0##*/}
14temp_file_path_1=/tmp/${program_name}_results_1
15temp_file_path_2=/tmp/${program_name}_results_2
16temp_file_path_3=/tmp/${program_name}_results_3
17
18temp_file_list="${temp_file_path_1} ${temp_file_path_2} ${temp_file_path_3}"
19csv_file_path="output.csv"
20
21# Description of argument(s):
22# pid The pid for which you desire statistics. If this is not specified,
23# statistics will be gathered for all active pids.
24
Patrick Williams90dfee32022-12-08 06:52:46 -060025function get_parms() {
sangeri-us b3c13752017-11-08 07:26:16 -060026
Patrick Williams90dfee32022-12-08 06:52:46 -060027 # Get program parms.
sangeri-us b3c13752017-11-08 07:26:16 -060028
Patrick Williams90dfee32022-12-08 06:52:46 -060029 pids="${1:-$(ls /proc | grep -v [A-Za-z])}" ; shift
sangeri-us b3c13752017-11-08 07:26:16 -060030
Patrick Williams90dfee32022-12-08 06:52:46 -060031 return 0
sangeri-us b3c13752017-11-08 07:26:16 -060032
33}
34
35
Patrick Williams90dfee32022-12-08 06:52:46 -060036function exit_function() {
sangeri-us b3c13752017-11-08 07:26:16 -060037
Patrick Williams90dfee32022-12-08 06:52:46 -060038 # Used to clean up tmp files.
sangeri-us b3c13752017-11-08 07:26:16 -060039
Patrick Williams90dfee32022-12-08 06:52:46 -060040 rm -f ${temp_file_list}
41 return
sangeri-us b3c13752017-11-08 07:26:16 -060042
43}
44
45
Patrick Williams90dfee32022-12-08 06:52:46 -060046function validate_parms() {
sangeri-us b3c13752017-11-08 07:26:16 -060047
Patrick Williams90dfee32022-12-08 06:52:46 -060048 # Validate program parameters.
sangeri-us b3c13752017-11-08 07:26:16 -060049
Patrick Williams90dfee32022-12-08 06:52:46 -060050 # Making sure only root can run our script.
51 if [ "${USER}" != "root" ] ; then
52 echo "This script must be run as root" 1>&2
53 return 1
54 fi
sangeri-us b3c13752017-11-08 07:26:16 -060055
Patrick Williams90dfee32022-12-08 06:52:46 -060056 trap "exit_function $signal \$?" EXIT
57 return 0
sangeri-us b3c13752017-11-08 07:26:16 -060058
59}
60
61
Patrick Williams90dfee32022-12-08 06:52:46 -060062function get_process_mem() {
sangeri-us b3c13752017-11-08 07:26:16 -060063
Patrick Williams90dfee32022-12-08 06:52:46 -060064 local pid="${1}" ; shift
65 # Count memory statistic for passed pid.
sangeri-us b3c13752017-11-08 07:26:16 -060066
Patrick Williams90dfee32022-12-08 06:52:46 -060067 # Description of argument(s):
68 # pid The process ID for which you desire statistics.
69 [ ! -f /proc/${pid}/status -o ! -f /proc/${pid}/smaps ] && return 0
sangeri-us b3c13752017-11-08 07:26:16 -060070
Patrick Williams90dfee32022-12-08 06:52:46 -060071 # pss_total Total proportional set size of a process.
72 # private_total Total number of clean and dirty private pages in the
73 # mapping.
74 # shared_total The difference between pss_total and private_total.
sangeri-us b3c13752017-11-08 07:26:16 -060075
Patrick Williams90dfee32022-12-08 06:52:46 -060076 local pss_total private_total shared_total sum name
77 pss_total=$(grep -e "^Pss:" /proc/${pid}/smaps | awk '{print $2}' | awk '{sum += $1} END {print sum}')
78 private_total=$(grep -e "^Private" /proc/${pid}/smaps | awk '{print $2}' | awk '{sum += $1} END {print sum}')
sangeri-us b3c13752017-11-08 07:26:16 -060079
Patrick Williams90dfee32022-12-08 06:52:46 -060080 [ -z "${pss_total}" -o -z "${private_total}" ] && return 0
81 (( shared_total=pss_total-private_total ))
82 name=$(cut -d "" -f 1 /proc/${pid}/cmdline)
83 (( sum=shared_total+private_total ))
84 echo -e "${private_total} + ${shared_total} = ${sum} ${name}"
sangeri-us b3c13752017-11-08 07:26:16 -060085
86}
87
88
Patrick Williams90dfee32022-12-08 06:52:46 -060089function mainf() {
sangeri-us b3c13752017-11-08 07:26:16 -060090
Patrick Williams90dfee32022-12-08 06:52:46 -060091 get_parms "$@" || return 1
sangeri-us b3c13752017-11-08 07:26:16 -060092
Patrick Williams90dfee32022-12-08 06:52:46 -060093 validate_parms || return 1
sangeri-us b3c13752017-11-08 07:26:16 -060094
Patrick Williams90dfee32022-12-08 06:52:46 -060095 # To ensure temp files not exist.
96 rm -f ${temp_file_list}
sangeri-us b3c13752017-11-08 07:26:16 -060097
Patrick Williams90dfee32022-12-08 06:52:46 -060098 for pid in ${pids} ; do
99 get_process_mem ${pid} >> ${temp_file_path_1}
100 done
sangeri-us b3c13752017-11-08 07:26:16 -0600101
Patrick Williams90dfee32022-12-08 06:52:46 -0600102 # This is used to sort results by memory usage.
103 sort -gr -k 5 ${temp_file_path_1} > ${temp_file_path_2}
sangeri-us b3c13752017-11-08 07:26:16 -0600104
Patrick Williams90dfee32022-12-08 06:52:46 -0600105 # Find duplicates in the process list output and combine them. In such
106 # cases, adjust the process name by including a (<count>) suffix. In the
107 # following example of output, 4 instances of "phosphor-hwmon-readd" have
108 # been combined.
109 # 974848 + 925696 = 1900544 phosphor-hwmon-readd(4)
110 for proc_name in $(awk '{print $6}' ${temp_file_path_2} | sort -u) ; do
111 count=$(awk -v src=${proc_name} '{if ($6==src) {print $6}}' ${temp_file_path_2} | wc -l)
112 [ "${count}" = "1" ] && count_string="" || count_string="(${count})"
113 vmsize_in_kb=$(awk -v src=${proc_name} '{if ($6==src) {print $1}}' ${temp_file_path_2} | awk '{sum += $1} END {print sum}')
114 vmrss_in_kb=$(awk -v src=${proc_name} '{if ($6==src) {print $3}}' ${temp_file_path_2} | awk '{sum += $1} END {print sum}')
115 total=$(awk '{print $5}' ${temp_file_path_2} | awk '{sum += $1} END {print sum}')
116 (( sum=vmrss_in_kb+vmsize_in_kb ))
117 echo -e "${vmsize_in_kb} + ${vmrss_in_kb} = ${sum} \t ${proc_name}${count_string}" >> ${temp_file_path_3}
118 done
sangeri-us b3c13752017-11-08 07:26:16 -0600119
Patrick Williams90dfee32022-12-08 06:52:46 -0600120 # Sort once more.
121 sort -gr -k 5 ${temp_file_path_3} > ${temp_file_path_1}
sangeri-us b3c13752017-11-08 07:26:16 -0600122
Patrick Williams90dfee32022-12-08 06:52:46 -0600123 # Read results from temp file and convert it to csv.
124 csv_line1=""
125 csv_line2=""
126 while read line ; do
127 while read private plus_operator shared equal_sign sum name ; do
128 (( sum == 0 )) && continue
129 csv_line1+=",${name}"
130 csv_line2+=",${sum}"
131 done <<<${line}
132 done < ${temp_file_path_1}
sangeri-us b3c13752017-11-08 07:26:16 -0600133
Patrick Williams90dfee32022-12-08 06:52:46 -0600134 # Strip leading commas.
135 csv_line1="${csv_line1#,}"
136 csv_line2="${csv_line2#,}"
137 { echo "${csv_line1}" ; echo "${csv_line2}" ; } >> ${csv_file_path}
sangeri-us b3c13752017-11-08 07:26:16 -0600138
Patrick Williams90dfee32022-12-08 06:52:46 -0600139 return 0
sangeri-us b3c13752017-11-08 07:26:16 -0600140
141}
142
143# Main
144
Patrick Williams90dfee32022-12-08 06:52:46 -0600145mainf "${@}"
146rc="${?}"
147exit "${rc}"
sangeri-us b3c13752017-11-08 07:26:16 -0600148