xref: /openbmc/openbmc-test-automation/bin/plug_ins/Mem_tracker/memcheck.sh (revision 90dfee368240b5149f0ce42a8ebaeb5aa596fedb)
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