0

I am trying to read the CPU usage of a current process based on PID. I am using the following code to fetch CPU usage:

#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <listdir.h>
struct pstat {
    long unsigned int utime_ticks;
    long int cutime_ticks;
    long unsigned int stime_ticks;
    long int cstime_ticks;
    long unsigned int vsize; // virtual memory size in bytes
    long unsigned int rss; //Resident  Set  Size in bytes

    long unsigned int cpu_total_time;
};

/*
 * read /proc data into the passed struct pstat
 * returns 0 on success, -1 on error
*/
int get_usage(const pid_t pid, struct pstat* result) {
    //convert  pid to string
    char pid_s[20];
    snprintf(pid_s, sizeof(pid_s), "%d", pid);
    char stat_filepath[30] = "/proc/"; strncat(stat_filepath, pid_s,
            sizeof(stat_filepath) - strlen(stat_filepath) -1);
    strncat(stat_filepath, "/stat", sizeof(stat_filepath) -
            strlen(stat_filepath) -1);

    FILE *fpstat = fopen(stat_filepath, "r");
    if (fpstat == NULL) {
        perror("FOPEN ERROR ");
        return -1;
    }

    FILE *fstat = fopen("/proc/stat", "r");
    if (fstat == NULL) {
        perror("FOPEN ERROR ");
        fclose(fstat);
        return -1;
    }

    //read values from /proc/pid/stat
    bzero(result, sizeof(struct pstat));
    long int rss;
    if (fscanf(fpstat, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
                "%lu %ld %ld %*d %*d %*d %*d %*u %lu %ld",
                &result->utime_ticks, &result->stime_ticks,
                &result->cutime_ticks, &result->cstime_ticks, &result->vsize,
                &rss) == EOF) {
        fclose(fpstat);
        return -1;
    }
    fclose(fpstat);
    result->rss = rss * getpagesize();

    //read+calc cpu total time from /proc/stat
    long unsigned int cpu_time[10];
    bzero(cpu_time, sizeof(cpu_time));
    if (fscanf(fstat, "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
                &cpu_time[0], &cpu_time[1], &cpu_time[2], &cpu_time[3],
                &cpu_time[4], &cpu_time[5], &cpu_time[6], &cpu_time[7],
                &cpu_time[8], &cpu_time[9]) == EOF) {
        fclose(fstat);
        return -1;
    }

    fclose(fstat);

    for(int i=0; i < 10;i++)
        result->cpu_total_time += cpu_time[i];

    return 0;
}

Got the above code from this github project: https://github.com/fho/code_snippets/blob/master/c/getusage.c and is an answer of this question: https://stackoverflow.com/a/4410209/9951420

Now when I am trying to call the get_usage() in my function as follows:

int my_cpu_usage_func(int pid){
  struct pstat* result;
  double cpu = 0.0;
  int success = get_usage(pid, result);
  if(success == 0) printf("CPU usage read successfully\n");
  if(success == -1) printf("Couldn't read CPU usage\n");
  cpu = result->cpu_total_time;
  printf("CPU usage of %i %f", pid, cpu);
}

When I am compiling my_cpu_usage_func(int) I am first getting the following warning:

extmodule.c:131:23: note: initialize the variable 'result' to silence this warning struct pstat* result; ^ = NULL

And then when I am executing the same function I am getting the following error:

FOPEN ERROR : No such file or directory

Can anyone help in this? N.B. I am new to C programming, so any help will be awesome.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
TheCoder
  • 87
  • 2
  • 11
  • 1
    You dont have to consult /proc, there is a systemcall `getrusage()` And: it is actually cheaper: only one systemcall, instead of (at least) three. – wildplasser Jun 17 '18 at 22:30
  • 1
    BTW:`strncat(stat_filepath, "/stat", sizeof(stat_filepath) - strlen(stat_filepath) -1);` is dangerous; it relies on the final byte beeing NUL (which it isn't) And: if you are *really* new in C coding: this is good coding (except for the obvious errors) – wildplasser Jun 17 '18 at 22:40
  • @wildplasser thanks for the reply. Could you direct me to an example of how I can get the cpu percentage used by a specific pid?? I couldnt happen to get a working example. – TheCoder Jun 17 '18 at 22:50
  • Percentage is hard. (because it depends on the others, which could be many, and varying...) It is also relatively useless. But: you could count latency instead. Time is easy to measure. – wildplasser Jun 17 '18 at 23:47
  • 1
    Assuming this is Linux, you probably should add the Linux tag. – Andrew Henle Jun 18 '18 at 09:43
  • the posted code is `#include` ing the 'home grown' header file: `listing.h` but you have not included the contents of that file. – user3629249 Jun 18 '18 at 14:47
  • regarding: `FILE *fstat = fopen("/proc/stat", "r"); if (fstat == NULL) { perror("FOPEN ERROR "); fclose(fstat);` If the file failed to open, then don't try to close it. However, I suspect you really wanted to close `fpstat` instead – user3629249 Jun 18 '18 at 14:50
  • regarding `if(success == -1) printf("Couldn't read CPU usage\n"); cpu = result->cpu_total_time;` How can you expect to be summing a value if the 'getting' of that value failed? – user3629249 Jun 18 '18 at 14:57
  • regarding: `extmodule.c:131:23:` You only posted 80 lines of code, so, the code you posted (80 lines) does not have a line 131! – user3629249 Jun 18 '18 at 15:00
  • 2
    regarding the calls to `fscanf()`, the code would be much better off to check against the number of input format specifiers actually input/converter in the format parameter than against EOF – user3629249 Jun 18 '18 at 15:03
  • rather than trying to 'roll your own' CPU usage calculation, Strongly suggest using `clock()` or `clock_gettime()` Read the associated MAN pages for the details – user3629249 Jun 18 '18 at 15:11

1 Answers1

1

There are a few trivial errors in the program; I Annotated the fixes with // <<-- [And I added a main() function]


#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#pragma include <listdir.h> // <<-- non-existent header

struct pstat {
    long unsigned int utime_ticks;
    long int cutime_ticks;
    long unsigned int stime_ticks;
    long int cstime_ticks;
    long unsigned int vsize; // virtual memory size in bytes
    long unsigned int rss; //Resident  Set  Size in bytes

    long unsigned int cpu_total_time;
};

/*
 * read /proc data into the passed struct pstat
 * returns 0 on success, -1 on error
*/
int get_usage(const pid_t pid, struct pstat* result) {
    char stat_filepath[80] ;
    int ii;
        // use snprintf() , not str[n]cat() to compose strings
    ii = snprintf(stat_filepath, sizeof stat_filepath, "/proc/%d/stat", pid );
    if (ii >= sizeof stat_filepath) return -1;

    FILE *fpstat = fopen(stat_filepath, "r");
    if (fpstat == NULL) {
        perror("FOPEN ERROR "); // <<-- should be printed by caller
        return -1;
    }

    FILE *fstat = fopen("/proc/stat", "r");
    if (fstat == NULL) {
        perror("FOPEN ERROR "); // <<-- could be printed by caller
        fclose(fstat);
        return -1;
    }

    //read values from /proc/pid/stat
    memset(result, 0 , sizeof *result ); // bzero is a BSD-ism
    long int rss;
    if (fscanf(fpstat, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
                "%lu %ld %ld %*d %*d %*d %*d %*u %lu %ld",
                &result->utime_ticks, &result->stime_ticks,
                &result->cutime_ticks, &result->cstime_ticks, &result->vsize,
                &rss) == EOF) {
        fclose(fpstat);
        return -1;
    }
    fclose(fpstat);
    result->rss = rss * getpagesize();

    //read+calc cpu total time from /proc/stat
    long unsigned int cpu_time[10];
    memset(cpu_time,0, sizeof cpu_time);
    if (fscanf(fstat, "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
                &cpu_time[0], &cpu_time[1], &cpu_time[2], &cpu_time[3],
                &cpu_time[4], &cpu_time[5], &cpu_time[6], &cpu_time[7],
                &cpu_time[8], &cpu_time[9]) == EOF) {
        fclose(fstat);
        return -1;
    }

    fclose(fstat);

    for(ii=0; ii < 10;ii++)
        result->cpu_total_time += cpu_time[ii];

    return 0;
}
int my_cpu_usage_func(int pid){
  struct pstat result; // <<-- this is a structure, not a pointer
  double cpu = 0.0;
  int success ;

  success = get_usage(pid, &result); // <<-- but the function expects a pointer, so give it one
  if (success == -1) printf("Couldn't read CPU usage\n");
  else if(success == 0) {
        printf("CPU usage read successfully\n");
        cpu = result.cpu_total_time;
        printf("CPU usage of %d %f\n", pid, cpu);
        }
  return success;
}

int main(void)
{
my_cpu_usage_func(getpid());

return 0;
}
joop
  • 4,330
  • 1
  • 15
  • 26