1

I have a C program that parses the /proc//stat directory to calculate the average CPU utilization over a period of 5 seconds:

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

#define ITERATIONS 5

int main(int argc, char *argv[])
{
    if (argc != 2) {
        printf( "usage: %s <PID>\n", argv[0] );
        return(-1);
    }

    long double a[4], b[4];
    long double pidTime = 0.0;
    long int clk;
    int i;
    FILE *fp;
    char stat[1024];

    clk = sysconf(_SC_CLK_TCK);

    if (clk == -1) {
        printf("Could not determine clock ticks per second");
        return(-1);
    }

    char *pidPath = malloc(strlen("/proc/stat/")+strlen(argv[1])+1);
    if (pidPath == NULL) {
        printf("Could not allocate memory for str\n");
        return(-1);
    } else {
        strcpy(pidPath, "/proc/");
        strcat(pidPath, argv[1]);
        strcat(pidPath, "/stat");
    }

    for(i = 0; i < ITERATIONS; i++)
    {
        fp = fopen(pidPath,"r");
        if (fp == NULL) {
            perror(pidPath);
            return(-1);
        } else {
            fgets(stat, sizeof(stat), fp);
            sscanf(stat,"%*d %*s %*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %Lf %Lf %Lf %Lf %*ld %*ld %*ld %*ld %*llu",&a[0],&a[1],&a[2],&a[3]);
            fclose(fp);
            sleep(1);
        }

        fp = fopen(pidPath,"r");
        if (fp == NULL) {
            perror(pidPath);
            return(-1);
        } else {
            fgets(stat, sizeof(stat), fp);
           sscanf(stat,"%*d %*s %*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %Lf %Lf %Lf %Lf %*ld %*ld %*ld %*ld %*llu",&b[0],&b[1],&b[2],&b[3]);
            fclose(fp);
        }

        pidTime += (((b[0]+b[1]+b[2]+b[3]) - (a[0]+a[1]+a[2]+a[3])));
    }

    pidTime = (pidTime / (clk * ITERATIONS));
    printf("pidCPU=%Lf\n", pidTime);
    printf("%ld", clk);
    free(pidPath);
    return(0);
}

From what I understand the relevant fields in stat are:

  • int utime; /** user mode jiffies **/

  • int stime; /** kernel mode jiffies **/

  • int cutime; /** user mode jiffies with childs **/

  • int cstime; /** kernel mode jiffies with childs **/

For a single process, this works great, but when I have a process that forks, or is multithreaded, this breaks down. Do the cutime and cstime counters only work when the parent is waiting for the child processes? How can I calculate the total usage of the process tree rooted at PID?

anthozep
  • 333
  • 5
  • 16
  • Why not output your errors on `stderr` ? –  Aug 09 '16 at 15:55
  • @Ascam I should, I think I was just being lazy at this point. You're right though. – anthozep Aug 09 '16 at 16:07
  • 1
    Anyway, just following logic, since you're tracking a PID, you'll only be given infos about **this** PID. Each child process being a... **process**, you refer to them by their own ID. My final point is: yes, it seems normal that you're getting only some `wait` activity watching parents. –  Aug 09 '16 at 16:15

1 Answers1

1

Yes, the parent needs to wait for the CPU time of the children to be added in (see manual entry for getrusage link). Also see this answer for more details.

Community
  • 1
  • 1
Jerzy
  • 670
  • 6
  • 12
  • So is there a way to get the process tree from /proc? If so, I could potentially make this a recursive solution – anthozep Aug 09 '16 at 16:08
  • On some kernels, you may be able to get the children from `/proc/**pid**/task/**taskid**/children` file [see link](https://lkml.org/lkml/2011/12/28/59). Otherwise, you can do reverse lookup from the parent pid. The parent pid is listed in `/proc/*pid*/status` on line `PPid`. You can try from a shell with: `fgrep PPid /proc/[0-9]*/status 2>/dev/null | fgrep $$ | cut -d/ -f3` which should list subprocesses of your shell (`$$`). Note that the grep/cut will itself show up in this list, but these processes will be gone by the time your prompt returns. – Jerzy Aug 15 '16 at 09:37