Get CPU Utilization of all available CPUs individually in C

379 views Asked by At

When I use cat /proc/stat command on terminal I get the following output:

cpu  9268738 47123 3940054 3851366876 911347 0 164981 0 0 0 
cpu0 558436 2170 208965 240825151 54221 0 30439 0 0 0 
cpu1 699380 1976 382320 240476662 50707 0 7260 0 0 0 
cpu2 547485 2685 204733 240867376 56441 0 4410 0 0 0 
cpu3 541016 3581 202538 240872692 57657 0 3051 0 0 0 
cpu4 552305 2716 286470 240322626 70098 0 60308 0 0 0 
cpu5 490248 3598 211000 240891224 59970 0 2596 0 0 0 
cpu6 510708 1987 215605 240879645 57692 0 2546 0 0 0 
cpu7 528486 3053 220346 240866189 54916 0 2273 0 0 0 
cpu8 540615 2563 216076 240857715 53633 0 2161 0 0 0 
cpu9 862775 1794 413426 240049704 49504 0 1755 0 0 0 
cpu10 576740 5166 230907 240805093 51594 0 2084 0 0 0 
cpu11 611709 2192 268375 240408228 62183 0 37502 0 0 0 
cpu12 589948 3351 227945 240734505 59752 0 1992 0 0 0 
cpu13 552315 3205 217448 240834143 58786 0 2137 0 0 0 
cpu14 554752 3387 218348 240835453 56078 0 2222 0 0 0 
cpu15 551815 3693 215547 240840464 58106 0 2240 0 0 0 
...

From the aforementioned output I could notice that my computer has 16 distinct CPUs. And I am trying to write a C program to fetch the CPU utilization of all the CPUs cores available. But the issue is that I have the following code, which only enables me to fetch the overall CPU utilization but only reading the first line of the /proc/stat file:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    long double cpu0_a[4], cpu0_b[4], loadavg;
    FILE *fp;
    char dump[50];

    while(1)
    {
        fp = fopen("/proc/stat","r");
        fscanf(fp,"%*s %Lf %Lf %Lf %Lf",&cpu0_a[0],&cpu0_a[1],&cpu0_a[2],&cpu0_a[3]);
        fclose(fp);
        sleep(1);

        fp = fopen("/proc/stat","r");
        fscanf(fp,"%*s %Lf %Lf %Lf %Lf",&cpu0_b[0],&cpu0_b[1],&cpu0_b[2],&cpu0_b[3]);
        fclose(fp);

        loadavg = ((cpu0_b[0]+cpu0_b[1]+cpu0_b[2]) - (cpu0_a[0]+cpu0_a[1]+cpu0_a[2])) / ((cpu0_b[0]+cpu0_b[1]+cpu0_b[2]+cpu0_b[3]) - (cpu0_a[0]+cpu0_a[1]+cpu0_a[2]+cpu0_a[3]));
        printf("The current CPU utilization is : %Lf\n",loadavg);
    }

    return(0);
}

How can I read the /proc/stat file to print out current CPU utilization of all available CPUs individually instead of overall one?

P.S. I am new to C programming and coming from a C# background, so I might be missing something very basic in C. Thank you for your help in this.

2

There are 2 answers

0
Somdip Dey On

The following code does the work. Although with due respect, the code has hard-coded number of CPUs available on the system, and hence, has to be manually changed based on the system you are running. Although EOF could have been used in combination with regex to check for cpu utilization from /proc/stat file, but regex has a computational overhead (almost 30ms for the system I am using). This code is more tuned towards the use in real time devices and hence, avoids using regex for the reason mentioned above.

The code is as follows:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define NUM_CPU     16

int main(void)
{
    //This code only caters for first 8 CPUs available
    long double cpu_a[NUM_CPU+1][4], cpu_b[NUM_CPU+1][4], loadavg[NUM_CPU];
    FILE *fp;
    char dump[50];

    while(1)
    {
        int count = 0;
        fp = fopen("/proc/stat","r");
        while(count < NUM_CPU+2)
        {
          fscanf(fp,"%*s %Lf %Lf %Lf %Lf",&cpu_a[count][0],&cpu_a[count][1],&cpu_a[count][2],&cpu_a[count][3]);
          count++;
        }
        fclose(fp);
        usleep(200*1000); // 200 msec sleep and read the /proc/stat file again

        count = 0;
        fp = fopen("/proc/stat","r");
        while(count < NUM_CPU+2)
        {
          fscanf(fp,"%*s %Lf %Lf %Lf %Lf",&cpu_b[count][0],&cpu_b[count][1],&cpu_b[count][2],&cpu_b[count][3]);
          count++;
        }
        fclose(fp);

        for(int i = 1; i < NUM_CPU+2; i++)
        {
          loadavg[i-1] = ((cpu_b[i][0]+cpu_b[i][1]+cpu_b[i][2]) - (cpu_a[i][0]+cpu_a[i][1]+cpu_a[i][2])) / ((cpu_b[i][0]+cpu_b[i][1]+cpu_b[i][2]+cpu_b[i][3]) - (cpu_a[i][0]+cpu_a[i][1]+cpu_a[i][2]+cpu_a[i][3]));
          printf("The current CPU %d utilization is : %Lf\n", (i-1), loadavg[i-1]);
        }
        printf("\n\n");
    }

    return(0);
}

N.B. Here, in the program, CPU Utilization 1.0 = 100% CPU ulization.

0
kylehuff On

As you have already demonstrated in your follow-up answer, you need to iterate the lines of output.

But, to iterate over all CPUs present without knowing the total you could simply define a C struct, and instantiate it with an arbitrary (high) count.

I have adapted something similar I've done to your example, so the output is different, but should suffice as an example.

(Not tested very well, could have some obvious logic holes, but again, an example)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/*
outputs CPU utilization for up to MAX_CPU CPUs.

* To output only the individual CPUs and not the combined total, set SKIP_TOTAL to 1
* To output only the combined total, set SKIP_TOTAL to 0, and MAX_CPU to 0
*/

#define MAX_CPU     64  // arbitrary max; can be set to 0 to only output overall
#define SKIP_TOTAL  1   // skips first "cpu", set to 0 to include in output

struct CPUS {
  char id[4];
  long double user;
  long double nice;
  long double system;
  long double idle;
  long double idle_last;  // store previous idle value
  long double sum_last;   // store previous sum value
};

struct CPUS cpus[MAX_CPU + 1];

void calculate(int output) {
  long cpu_delta, cpu_idle, cpu_used, utilization;
  FILE *fp;

  int last_cpu = 0;
  int cpu_num = 0;
  int sum;

  fp = fopen("/proc/stat", "r");

  while (last_cpu == 0 && cpu_num <= MAX_CPU) {
    fscanf(
      fp, "%s %Lf %Lf %Lf %Lf%*[^\n]\n",
      (char *)&cpus[cpu_num].id, &cpus[cpu_num].user, &cpus[cpu_num].nice,
      &cpus[cpu_num].system, &cpus[cpu_num].idle
    );

    // check if the first colum (placed in the id field) contains "cpu", if
    // not, we are no longer processing CPU related lines
    if(strstr(cpus[cpu_num].id, "cpu") != NULL) {

      if (cpu_num == 0) {
        if (SKIP_TOTAL == 1) {
          cpu_num += 1;
          continue;
        } else {
          // overwrite "cpu" to "all"
          strcpy(cpus[cpu_num].id, "all");
        }
      }

      // sum all of the values
      sum = cpus[cpu_num].user + cpus[cpu_num].nice + \
        cpus[cpu_num].system + cpus[cpu_num].idle;

      // collect the difference between sum and the last sum
      cpu_delta = sum - cpus[cpu_num].sum_last;
      // collect idle time
      cpu_idle = cpus[cpu_num].idle - cpus[cpu_num].idle_last;
      // delta minus ide time
      cpu_used = cpu_delta - cpu_idle;
      // percentage of utilization
      utilization = (100 * cpu_used) / cpu_delta;

      if (output == 1) {
        printf("%s:\t%li%%\n", cpus[cpu_num].id, utilization);
      }

      // store the current sum and idle time for calculation on next iteration
      cpus[cpu_num].sum_last = sum;
      cpus[cpu_num].idle_last = cpus[cpu_num].idle;
    } else {
      // no more CPUs to enumarte; exit while loop
      last_cpu = 1;
    }
    cpu_num += 1;
  }
  fclose(fp);
}

int main(void) {
  calculate(0);     // first pass to collect baseline (no output)
  usleep(200*1000); // wait
  calculate(1);     // collect again and output

  return 0;
}