Accurately Calculating CPU Utilization in Linux Using /Proc/Stat

Accurate calculation of CPU usage given in percentage in Linux?

According the htop source code, my assumptions looks like they are valid:

(see static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) function at LinuxProcessList.c)

// Guest time is already accounted in usertime
usertime = usertime - guest; # As you see here, it subtracts guest from user time
nicetime = nicetime - guestnice; # and guest_nice from nice time
// Fields existing on kernels >= 2.6
// (and RHEL's patched kernel 2.4...)
unsigned long long int idlealltime = idletime + ioWait; # ioWait is added in the idleTime
unsigned long long int systemalltime = systemtime + irq + softIrq;
unsigned long long int virtalltime = guest + guestnice;
unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime;

And so, from fields listed in the first line of /proc/stat: (see section 1.8 at documentation)

     user    nice   system  idle      iowait irq   softirq  steal  guest  guest_nice
cpu 74608 2520 24433 1117073 6176 4054 0 0 0 0

Algorithmically, we can calculate the CPU usage percentage like:

PrevIdle = previdle + previowait
Idle = idle + iowait

PrevNonIdle = prevuser + prevnice + prevsystem + previrq + prevsoftirq + prevsteal
NonIdle = user + nice + system + irq + softirq + steal

PrevTotal = PrevIdle + PrevNonIdle
Total = Idle + NonIdle

# differentiate: actual value minus the previous one
totald = Total - PrevTotal
idled = Idle - PrevIdle

CPU_Percentage = (totald - idled)/totald

Accurately Calculating CPU Utilization in Linux using /proc/stat

I think iowait/irq/softirq are not counted in one of the first 4 numbers. You can see the comment of irqtime_account_process_tick in kernel code for more detail:

(for Linux kernel 4.1.1)

2815  * Tick demultiplexing follows the order
2816 * - pending hardirq update <-- this is irq
2817 * - pending softirq update <-- this is softirq
2818 * - user_time
2819 * - idle_time <-- iowait is included in here, discuss below
2820 * - system time
2821 * - check for guest_time
2822 * - else account as system_time

For the idle time handling, see account_idle_time function:

2772 /*
2773 * Account for idle time.
2774 * @cputime: the cpu time spent in idle wait
2775 */
2776 void account_idle_time(cputime_t cputime)
2777 {
2778 u64 *cpustat = kcpustat_this_cpu->cpustat;
2779 struct rq *rq = this_rq();
2780
2781 if (atomic_read(&rq->nr_iowait) > 0)
2782 cpustat[CPUTIME_IOWAIT] += (__force u64) cputime;
2783 else
2784 cpustat[CPUTIME_IDLE] += (__force u64) cputime;
2785 }

If the cpu is idle AND there is some IO pending, it will count the time in CPUTIME_IOWAIT. Otherwise, it is count in CPUTIME_IDLE.

To conclude, I think the jiffies in irq/softirq should be counted as "busy" for cpu because it was actually handling some IRQ or soft IRQ. On the other hand, the jiffies in "iowait" should be counted as "idle" for cpu because it was not doing something but waiting for a pending IO to happen.

Current CPU core utilization via /proc/stat

The following program:

#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

struct cpuusage {
char name[20];
// Absolute values since last reboot.
unsigned long long idletime;
unsigned long long workingtime;
};

struct cpustat {
char name[20];
unsigned long long user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice;
};

struct cpuusage cpuusage_from_cpustat(struct cpustat s) {
struct cpuusage r;
strncpy(r.name, s.name, sizeof(r.name));
r.name[sizeof(r.name) - 1] = '\0';
r.idletime = s.idle + s.iowait;
r.workingtime = s.user + s.nice + s.system + s.irq + s.softirq;
return r;
}

void cpuusage_show_diff(struct cpuusage now, struct cpuusage prev) {
// the number of ticks that passed by since the last measurement
const unsigned long long workingtime = now.workingtime - prev.workingtime;
const unsigned long long alltime = workingtime + (now.idletime - prev.idletime);
// they are divided by themselves - so the unit does not matter.
printf("Usage: %.0Lf%%\n", (long double)workingtime / alltime * 100.0L);
}

int main() {
struct cpuusage prev = {0};
//
const int stat = open("/proc/stat", O_RDONLY);
assert(stat != -1);
fcntl(stat, F_SETFL, O_NONBLOCK);
while (1) {
// let's read everything in one call so it's nicely synced.
int r = lseek(stat, SEEK_SET, 0);
assert(r != -1);
char buffer[10001];
const ssize_t readed = read(stat, buffer, sizeof(buffer) - 1);
assert(readed != -1);
buffer[readed] = '\0';
// Read the values from the readed buffer/
FILE *f = fmemopen(buffer, readed, "r");
// Uch, so much borign typing.
struct cpustat c = {0};
while (fscanf(f, "%19s %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", c.name, &c.user, &c.nice,
&c.system, &c.idle, &c.iowait, &c.irq, &c.softirq, &c.steal, &c.guest,
&c.guest_nice) == 11) {
// Just an example for first cpu core.
if (strcmp(c.name, "cpu0") == 0) {
struct cpuusage now = cpuusage_from_cpustat(c);
cpuusage_show_diff(now, prev);
prev = now;
break;
}
}
fclose(f);
//
sleep(1);
}
}

Outputs the usage of the first core each second. I may be off with the calculations - consult this forum on which fields exactly to use from /dev/stat.

Reading /proc/stat values to get cpu usage throws DivideByZeroException

This is how I solved it.

public class HardwareInfoManager : IHardwareInfoManager
{
private IConfiguration Configuration;

private List<long> oldCpuStatistics;

private List<long> newCpuStatistics;

public HardwareInfoManager(IConfiguration Configuration)
{
this.Configuration = Configuration;
oldCpuStatistics = new List<long>();
newCpuStatistics = new List<long>();
}

public HardwareInfoDto GetHardWareInfo()
{
return new HardwareInfoDto()
{
TenantId = Configuration.GetValue<string>("TenantId"),
Hostname = GetHostName(),
Temperature = GetTemperature(),
MemoryStats = GetMemoryStats(),
CPUUsage = GetCPUUsage()
};
}

private string GetHostName()
{
string hostNameFilePath = "//etc//hostname";
if (File.Exists(hostNameFilePath))
{
return (File.ReadAllText(hostNameFilePath));
}
else
{
return "";
}

}

private decimal GetTemperature()
{
string temperatureFilePath = "//sys//class//thermal//thermal_zone0//temp";
if (File.Exists(temperatureFilePath))
{
decimal output = Convert.ToDecimal(File.ReadAllText(temperatureFilePath));
output /= 1000;
//string temperature = output.ToString() + "°C";
return output;
//var file= File.ReadAllLines();
}
else
{
return 0.00M;
}
}

private MemoryStatsDto GetMemoryStats()
{
MemoryStatsDto memoryStatsDto = new MemoryStatsDto();
string memoryStatsPath = "//proc//meminfo";
if (File.Exists(memoryStatsPath))
{
var file = File.ReadAllLines(memoryStatsPath);

//Skipping all lines we are not interested in
for (int i = 0; i < 3; i++)
{
int firstOccurenceOfDigit = 0;
var memoryLine = file[i];
//index of first number , start the string until the end and store it
for (int j = 0; j < memoryLine.Length; j++)
{
if (Char.IsNumber(memoryLine[j]))
{
firstOccurenceOfDigit = j;
break;
}
}

var memoryValue = memoryLine.Substring(firstOccurenceOfDigit);

switch (i)
{
case 0:
memoryStatsDto.MemoryTotal = memoryValue;
break;
case 1:
memoryStatsDto.MemoryFree = memoryValue;
break;
case 2:
memoryStatsDto.MemoryAvailable = memoryValue;
break;
default: break;
}
}

return memoryStatsDto;
}
else
{
memoryStatsDto.MemoryAvailable = "";
memoryStatsDto.MemoryFree = "";
memoryStatsDto.MemoryTotal = "";
return memoryStatsDto;
}
}

private decimal GetCPUUsage()
{
string cpuUsagePath = "//proc//stat";
StringBuilder sb = new StringBuilder();
if (File.Exists(cpuUsagePath) && oldCpuStatistics.IsNullOrEmpty())
{
oldCpuStatistics = SaveIntsFromFilePath(cpuUsagePath, oldCpuStatistics);
Thread.Sleep(10000);
GetCPUUsage();
}
if (File.Exists(cpuUsagePath) && !oldCpuStatistics.IsNullOrEmpty())
{
newCpuStatistics = SaveIntsFromFilePath(cpuUsagePath, newCpuStatistics);
var prevIdle = oldCpuStatistics[3] + oldCpuStatistics[4];
decimal idle = newCpuStatistics[3] + newCpuStatistics[4];

var prevNonIdle = oldCpuStatistics[0] + oldCpuStatistics[1] + oldCpuStatistics[2] + oldCpuStatistics[5] + oldCpuStatistics[6] + oldCpuStatistics[7];
decimal nonIdle = newCpuStatistics[0] + newCpuStatistics[1] + newCpuStatistics[2] + newCpuStatistics[5] + newCpuStatistics[6] + newCpuStatistics[7];

var prevTotal = prevIdle + prevNonIdle;
decimal total = idle + nonIdle;

var totalDifference = total - prevTotal;
var idleDifference = idle - prevIdle;

decimal cpuPercentage = 0;

Log.Logger.Information($"TotalDifference is {totalDifference}");
Log.Logger.Information($"IdleDifference is {idleDifference}");

cpuPercentage = (totalDifference - idleDifference) * 100M / (totalDifference);
cpuPercentage = Math.Round(cpuPercentage, 2);
return cpuPercentage;
}
else
{
return 0;
}
}

private List<long> SaveIntsFromFilePath(string path, List<long> longList)
{
var firstLineOfCPUFile = File.ReadAllLines(path).First();
StringBuilder sb = new StringBuilder();

for (int i = 0; i < firstLineOfCPUFile.Length; i++)
{
//take first index of a number until it reaches a whitespace, add to an int array
if (Char.IsNumber(firstLineOfCPUFile[i]))
{
sb.Append(firstLineOfCPUFile[i]);
//start with this index until it reaches whitespace
}
if (Char.IsWhiteSpace(firstLineOfCPUFile[i]) && i > 5)
{
longList.Add(long.Parse(sb.ToString()));
sb.Clear();
//start with this index until it reaches whitespace
}
}
sb.Clear();

for (int i = 0; i < longList.Count; i++)
{
Log.Logger.Information($"LongList index {i} value is {longList[i]}");
}
return longList;
}
}

Determining CPU utilization

You need to sample the values in /proc/stat at two times, and calculate the average utilisation over that time. (Instantaneous utilisation doesn't make a whole lot of sense - it'll always be 100% on a single core machine, since your utilsation-measuring code is running whenever it looks).

How do I get the total CPU usage of an application from /proc/pid/stat?

Preparation

To calculate CPU usage for a specific process you'll need the following:

  1. /proc/uptime
    • #1 uptime of the system (seconds)
  2. /proc/[PID]/stat
    • #14 utime - CPU time spent in user code, measured in clock ticks
    • #15 stime - CPU time spent in kernel code, measured in clock ticks
    • #16 cutime - Waited-for children's CPU time spent in user code (in clock ticks)
    • #17 cstime - Waited-for children's CPU time spent in kernel code (in clock ticks)
    • #22 starttime - Time when the process started, measured in clock ticks
  3. Hertz (number of clock ticks per second) of your system.
    • In most cases, getconf CLK_TCK can be used to return the number of clock ticks.
    • The sysconf(_SC_CLK_TCK) C function call may also be used to return the hertz value.


Calculation

First we determine the total time spent for the process:

total_time = utime + stime

We also have to decide whether we want to include the time from children processes. If we do, then we add those values to total_time:

total_time = total_time + cutime + cstime

Next we get the total elapsed time in seconds since the process started:

seconds = uptime - (starttime / Hertz)

Finally we calculate the CPU usage percentage:

cpu_usage = 100 * ((total_time / Hertz) / seconds)

See also

Top and ps not showing the same cpu result

How to get total cpu usage in Linux (c++)

Calculating CPU usage of a process in Linux



Related Topics



Leave a reply



Submit