1#!/bin/sh
2
3# This script displays the per-cpu utilization values.
4# The utilization value is fetched from the cpufreq driver with the file:
5# /sys/devices/system/cpu/cpu%d/cpufreq/cpu_utilization
6# The utilization value in this file is the cpu utilization scaled to the max CPU frequency.
7# So, this script converts this value to the current utilization levels.
8#
9# USAGE: cpuutil [-i <interval>] [-n <samples>] [-m <mask>]
10#
11
12help()
13{
14	echo "USAGE: cpuutil [-i <interval>] [-n <samples>] [-m <mask>]"
15	printf "\n"
16	echo "		i - Specifies the interval between two sucessive polls in seconds"
17	echo "			Default: 1 second"
18	echo "		n - Specifies the total number of samples to be polled"
19	echo "			Default: No limit. User must terminate with ctrl-c"
20	echo "		m - Specifies the CPU mask. Must be prefixed with '0x'"
21	echo "			Default: All CPUs"
22	echo ""
23	echo "		E.g. cpuutil -i 10 -n 5 -m 0x2"
24	echo "			Display utilization of CPU1 every 10 seconds, 5 times"
25}
26
27usage()
28{
29	echo "Error: Invalid usage! Option: $1"
30	printf "\n"
31	help
32	exit 1
33}
34
35# The the total number of CPUs
36getMaxCpuCount()
37{
38	local val
39	val=`cat /proc/cpuinfo | grep processor | tail -1 | awk '{print $3}'`
40	echo $((val + 1))
41}
42
43# Get the CPU Mask for all CPUs
44getAllCpuMask()
45{
46	local val=""
47	local i=0
48	local max=$(getMaxCpuCount)
49	local shiftval
50
51	while [ $i -lt $max ];
52	do
53		shiftval=1
54		shiftval=$((shiftval << $i))
55		val=$((val | shiftval))
56		i=$((i + 1))
57	done
58
59	echo $val
60}
61
62# Get mask for specific CPU
63getCpuMask()
64{
65	local shiftval=1
66	echo $((shiftval << $1))
67}
68
69# Poll and display values
70pollValues()
71{
72	local i=0
73	local utilfile="/sys/devices/system/cpu/cpu%d/cpufreq/cpu_utilization"
74	local curfreqfile="/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_cur_freq"
75	local maxfreqfile="/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq"
76	local util
77	local curfreq
78	local maxfreq
79	local fname
80	local val
81	local tmp
82
83	printf "%8s" "."
84
85	while [ $i -lt $(getMaxCpuCount) ];do
86		if [ $(( cmask & $(getCpuMask $i) )) != 0 ]; then
87			fname=$(printf $utilfile $i)
88			util=`cat $fname`
89
90			fname=$(printf $curfreqfile $i)
91			curfreq=`cat $fname`
92
93			fname=$(printf $maxfreqfile $i)
94			maxfreq=`cat $fname`
95
96			val=$(( $((util * maxfreq)) / curfreq))
97			printf "%8d" $val
98			eval avg$i=\$\(\(\ avg$i\ \+\ val\ \)\)
99		fi
100		if [ $i = 0 ]; then
101			avgcount=$((avgcount + 1))
102		fi
103		i=$((i + 1))
104	done
105
106	printf "\n"
107
108	sleep $1
109}
110
111# Display the average values
112showAvg()
113{
114	local i=0
115	local tmp
116	local rem
117
118	printf "\n\n%8s" "Avg:"
119
120	while [ $i -lt $(getMaxCpuCount) ];do
121		if [ $((cmask & $(getCpuMask $i) )) != 0 ]; then
122			eval tmp=\$avg$i
123			rem=`expr $tmp % $avgcount`
124			if [ $rem -gt $((avgcount / 2)) ]; then
125				tmp=$((tmp + avgcount))
126			fi
127			tmp=$((tmp / avgcount))
128			printf "%8d" $tmp
129		fi
130		i=$((i + 1))
131	done
132
133	printf "\n"
134}
135
136# ctrl-c handler
137sigHandler()
138{
139	showAvg
140	exit 0
141}
142
143avgcount=0
144echo "/-------------------------------------------------------------------------------------\\"
145echo "| This script uses the values exported by the cpufreq driver via the sys interface.   |"
146echo "| And values exported by the cpufreq driver are scaled by the driver to               |"
147echo "| 'utilization at max-freq'. This script converts these values to current utilization |"
148echo "| levels using the current CPU frequency. So please expect some loss in accuracy due  |"
149echo "| to rounding off occuring twice.                                                     |"
150echo "\\-------------------------------------------------------------------------------------/"
151printf "\n"
152
153# Parse the command line arguments
154for i in "$@"; do
155	case $i in
156		-i)
157			if [ -n "$currarg" ]; then
158				usage $currarg
159			fi
160			currarg="-i"
161			;;
162		-n)
163			if [ -n "$currarg" ]; then
164				usage $currarg
165			fi
166			currarg="-n"
167			;;
168		-m)
169			if [ -n "$currarg" ]; then
170				usage $currarg
171			fi
172			currarg="-m"
173			;;
174		-h|--help)
175			if [ -n "$currarg" ]; then
176				usage $currarg
177			fi
178			help
179			exit 0
180			;;
181		*)
182			if [ "$currarg" = "-i" -o "$currarg" = "-n" ]; then
183				parameter=`echo $i | sed 's/[0-9]*//'`
184				if [ -n "$parameter" ]; then
185					usage $currarg
186				else
187					if [ "$currarg" = "-i" ]; then
188						pinterval=$i
189					else
190						maxsamples=$i
191					fi
192					currarg=""
193				fi
194			elif [ "$currarg" = "-m" ]; then
195				parameter=`echo $i | sed 's/0[xX][0-9]*//'`
196				if [ -n "$parameter" ]; then
197					usage $currarg
198				else
199					cmask=`echo $i | sed 's/0[xX]//'`
200					currarg=""
201				fi
202			else
203				usage $i
204			fi
205			;;
206	esac
207done
208
209# Set default values if no arguments are specified
210if [ -z $pinterval ]; then
211	pinterval=1
212elif [ $pinterval -gt 3600 ];then
213	echo "Limiting polling interval to 1 hour"
214	pinterval=3600
215fi
216if [ -z $maxsamples ]; then
217	maxsamples=0
218fi
219
220
221if [ -z "$cmask" ]; then
222	cmask=$(getAllCpuMask)
223else
224	allcpumask=$(getAllCpuMask)
225	maskresult=$((cmask & ~allcpumask))
226	if [ $maskresult != 0 ]; then
227		echo "Error: Invalid CPU Mask"
228		printf "Total CPUs is: %d, Mask for all CPUs is 0x%x\n" $(getMaxCpuCount) $(getAllCpuMask)
229		exit 1
230	fi
231fi
232
233# Display the actual values
234printf "\n"
235echo "Polling interval: $pinterval seconds"
236if [ $maxsamples = 0 ];then
237	echo "Max Samples: 0 (No limit set)"
238else
239	echo "Max Samples: $maxsamples"
240fi
241printf "CPU Mask: 0x%x\n" $cmask
242printf "\n"
243
244i=0
245printf "%8s" "CPU:"
246while [ $i -lt $(getMaxCpuCount) ]; do
247	if [ $((cmask & $(getCpuMask $i) )) != 0 ]; then
248		printf "%8d" $i
249	fi
250	eval avg$i=0
251	i=$((i + 1))
252done
253printf "\n"
254
255trap sigHandler INT
256
257if [ $maxsamples -eq 0 ];then
258	while [ 1 ]; do
259		pollValues $pinterval
260	done
261else
262	while [ $maxsamples -ne 1 ]; do
263		pollValues $pinterval
264		maxsamples=$((maxsamples - 1))
265	done
266
267	# We dont want to sleep after the last poll
268	pollValues 0
269	showAvg
270fi
271
272exit 0
273