0

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.

Somdip Dey
  • 3,346
  • 6
  • 28
  • 60
  • 1
    It's not C related that you're missing. What do you think will happen if you close and reopen a file? Will `fscanf` read a next line, or file position will reset? – Andrejs Cainikovs Feb 28 '19 at 15:13
  • If I close and open again then fscanf reads the same line from the same file the next time (at least in this case). But in fact I have to do that so that I can calculate the average utilisation over time. But what I am also trying to do is that first time when I read the file I need to read not just the first line but all the lines starting with “cpu” and so the same for the next time and use the rest of the logic to compute the average. – Somdip Dey Feb 28 '19 at 15:16
  • Correct. Then what's wrong with `f= fopen(); while(!feof(f)) { fscanf() }; fclose(f);`? – Andrejs Cainikovs Feb 28 '19 at 15:21
  • @AndrejsCainikovs thank you for your reply. but i am struggling to read the lines only starting with 'cpu' substring and do not need to traverse till the end of the file as mentioned in your code. – Somdip Dey Feb 28 '19 at 15:26
  • @AndrejsCainikovs [`while (!feof(f))` is *always* wrong](https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong), but the gist is that OP needs to loop to read all the lines of `/proc/stat`, not just read a single line then close the file, of course. – unwind Feb 28 '19 at 15:31
  • @SomdipDey Next question: how do you know if line contains `cpu` or not? Correct, you read first, and then check. What I'm trying to say, is that you need to read every line and check either via regex, or your own implemented function if new line is suitable for your processing. – Andrejs Cainikovs Feb 28 '19 at 15:34
  • @unwind Thanks for pointing this out. I'm aware of consequences of using that function, and even had upvoted that question sometime long time ago. However, I could not find a simpler way to provide a minimal one-liner for this particular scenario. – Andrejs Cainikovs Feb 28 '19 at 15:38
  • thanks fells. I have written the code, but its actually hardcoded. instead of reading till the end of file I am reading for a particular number of lines. Will be posting my answer very soon. But again, thank you @AndrejsCainikovs – Somdip Dey Feb 28 '19 at 15:40
  • @SomdipDey Particular number of lines? Not all machines have 16 CPU's listed in `/proc/stat`. Welcome! – Andrejs Cainikovs Feb 28 '19 at 15:43
  • yes, exactly. Although as you have mentioned that having regex to find the string pattern is better but when I am using regex it has a computation overhead of 30ms for each regex check. Since i need the program to run in real time devices hence, I had to hard code the number of lines to read instead. @AndrejsCainikovs – Somdip Dey Feb 28 '19 at 15:45
  • @AndrejsCainikovs for reference and if others find it useful. I have uploaded my answer code. thank you for the help once again. – Somdip Dey Feb 28 '19 at 15:53

2 Answers2

1

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.

Somdip Dey
  • 3,346
  • 6
  • 28
  • 60
0

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;
}

kylehuff
  • 5,177
  • 2
  • 34
  • 35