# To calculate the total CPU usage as a percentage

Posted on

Problem

I have been developing the below method to calculate the CPU usage of a Linux system at a point in time, as a percentage of the total number of cores available in the system.

``````#!/bin/bash

# Get number of cores from proc cpuinfo
CORECOUNT=`grep -c ^processor /proc/cpuinfo`
# Use top, skip the first 7 rows, count the sum of the values in column 9 - the CPU column, do some simple rounding at the end.
CPUUSAGE=`top -bn 1 | awk 'NR>7{s+=\$9} END {print s/'\$CORECOUNT'}' | awk '{print int(\$1+0.5)}'`
printf "\$CPUUSAGEn"
``````

Would this be considered a reasonably accurate way of doing this? Any suggestions to increase efficiency/accuracy would be greatly appreciated.

Solution

There are a few problems you have here. The first, is the concept of getting the usage at a point in time, which is not possible. But, you can get it for a span of time. You are using `top -bn 1` which will get the usage for the span of a second.

Since you are accessing the `/proc` system to get the number of CPU’s, you may as well use the right `/proc` file for what you want:

`cat /proc/stat`:

`````` ~ \$ cat /proc/stat
cpu  177206 161827 1274669 3582291272 144435 154779 0 0 0 0
cpu0 32170 48533 914773 894903107 30650 69784 0 0 0 0
cpu1 52790 41088 180295 895685563 76035 14410 0 0 0 0
cpu2 18745 40955 49422 895959636 11295 2234 0 0 0 0
cpu3 73500 31249 130178 895742965 26454 68350 0 0 0 0
intr 339571688 48 10 0 0 0 0 2 0 1 0 0 0 152 0 0 8750527 0 322394 40428015 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
......
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 660707018
btime 1401123121
processes 196075
procs_running 1
procs_blocked 0
softirq 322202763 0 136092603 1792784 40737199 4701209 0 6 133008518 51951 5818493
``````

With the above file, I would calculate it manually…. (also using the documentation here: http://www.mjmwired.net/kernel/Documentation/filesystems/proc.txt#1212 )

I have taken the liberty of programming it myself…. since the recommendation is sucha deviation from your solution, it makes sense:

``````#!/bin/bash

#CORECOUNT=\$(grep cpu /proc/stat | grep -v 'cpu ' | wc -l)
#echo \$CORECOUNT cores not needed for calculation though

DELAY=\${1:-1}

function getstat() {
grep 'cpu ' /proc/stat | sed -e 's/  */x/g' -e 's/^cpux//'
}

function extract() {
echo \$1 | cut -d 'x' -f \$2
}

function change() {
local e=\$(extract \$ENDSTAT \$1)
local b=\$(extract \$STARTSTAT \$1)
local diff=\$(( \$e - \$b ))
echo \$diff
}

#Record the start statistics

STARTSTAT=\$(getstat)

sleep \$DELAY

#Record the end statistics

ENDSTAT=\$(getstat)

#http://www.mjmwired.net/kernel/Documentation/filesystems/proc.txt#1236
#echo "From \$STARTSTAT"
#echo "TO   \$ENDSTAT"
#     usr    nice   sys     idle       iowait irq    guest
#From 177834 168085 1276260 3584351494 144468 154895 0 0 0 0
#TO   177834 168085 1276261 3584351895 144468 154895 0 0 0 0

USR=\$(change 1)
SYS=\$(change 3)
IDLE=\$(change 4)
IOW=\$(change 5)

#echo USR \$USR SYS \$SYS IDLE \$IDLE IOW \$IOW

ACTIVE=\$(( \$USR + \$SYS + \$IOW ))
TOTAL=\$((\$ACTIVE + \$IDLE))
PCT=\$(( \$ACTIVE * 100 / \$TOTAL ))

echo "BUSY \$ACTIVE TOTAL \$TOTAL \$PCT %"
``````

Let’s assume that column 9 in the output of the `top` command is a good measure of the CPU usage. I don’t really know, but let’s assume.

Your script could be written cleaner and better:

1. Don’t use ``...`` style process substitution, use `\$(...)` instead
2. Usually, there’s no point piping the output of one `awk` command to another. You can do what you did in a single `awk`, which is better, because you save one unnecessary process
3. Better to pass variables to `awk` using the `-v` parameter than trying to embed in the command line
4. Don’t use `printf` when an `echo` is enough and simpler
5. Avoid extremely long lines. The mouse wheel scrolls up-down fine, left-right is a PITA. You could break that long comment to 2 lines.

Suggested implementation:

``````#!/bin/bash
# Get number of cores
CORECOUNT=\$(grep -c ^processor /proc/cpuinfo)
# Use top, skip the first 7 rows, count the sum of the values
#   in column 9 - the CPU column, do some simple rounding at the end
CPUUSAGE=\$(top -bn 1 | awk -v n=\$CORECOUNT 'NR > 7 { s += \$9 } END { print int(s / n + .5); }')
echo \$CPUUSAGE
``````

Btw, do you really need `\$CPUUSAGE`? If not, just omit the variable, change the last line:

``````top -bn 1 | awk -v n=\$CORECOUNT 'NR > 7 { s += \$9 } END { print int(s / n + .5); }'
``````

(I used `n` as the Awk variable name mainly to make my answer fit on the page without scrolling ðŸ˜‰ Feel free to name it `corecount` or as you like.)