2

I am trying how to use getrusage function in c in linux environment and was wondering if I am on a right track.

I wrote a small program to make sure I understand how getrusage works before applying to my project. I want to get the user/kernel times for both parent and child processes separately.

void stupidFunction();
void childSort();


int main(void)
{

  pid_t parent_pid=getpid();

  struct rusage parent_before_function_usage;
  getrusage(RUSAGE_SELF,&parent_before_function_usage);

  time_t  parent_before_function_user_usage_sec=parent_before_function_usage.ru_utime.tv_sec;
  time_t  parent_before_function_user_usage_microsec=parent_before_function_usage.ru_utime.tv_usec;
  time_t  parent_before_function_cpu_usage_sec =parent_before_function_usage.ru_stime.tv_sec;
  time_t  parent_before_function_cpu_usage_microsecsec =parent_before_function_usage.ru_stime.tv_usec;

  stupidFunction();

  pid_t pid;



  if((pid = fork()) <0)
  {
    fprintf(stderr,"Failed to create a fork process\n");
  }
  else if (pid == 0)
  {
    childSort();
  }



  int status;



  waitpid(-1,&status,0);


  printf("in parent\n");

  struct rusage parent_after_function_usage;
  getrusage(RUSAGE_SELF,&parent_after_function_usage);

  time_t parent_after_function_user_usage_sec=parent_after_function_usage.ru_utime.tv_sec;
  time_t parent_after_function_user_usage_microsec=parent_after_function_usage.ru_utime.tv_usec;
  time_t parent_after_function_cpu_usage_sec =parent_after_function_usage.ru_stime.tv_sec;
  time_t parent_after_function_cpu_usage_microsecsec =parent_after_function_usage.ru_stime.tv_usec;

  time_t parent_real_user_usage_sec=parent_after_function_user_usage_sec - parent_before_function_user_usage_sec;
  time_t parent_real_user_usage_microsec= parent_after_function_user_usage_microsec - parent_before_function_user_usage_microsec;
  time_t parent_real_cpu_usage_sec=parent_after_function_cpu_usage_sec - parent_before_function_cpu_usage_sec;
  time_t parent_real_cpu_usage_microsec = parent_after_function_cpu_usage_microsecsec - parent_before_function_cpu_usage_microsecsec;

  printf("User mode CPU time for parent: %d seconds, %d microseconds\n",parent_real_user_usage_sec,parent_real_user_usage_microsec);
  printf("Kern mode CPU time for parent: %d seconds, %d microseconds\n",parent_real_cpu_usage_sec,parent_real_cpu_usage_microsec);


  struct rusage child_function_usage;
  getrusage(RUSAGE_CHILDREN,&child_function_usage);



  time_t all_children_user_usage_sec=child_function_usage.ru_utime.tv_sec;
  time_t all_children_user_usage_microsec=child_function_usage.ru_utime.tv_usec;
  time_t all_children_cpu_usage_sec =child_function_usage.ru_stime.tv_sec;
  time_t all_children_cpu_usage_microsec =child_function_usage.ru_stime.tv_usec;

  printf("User mode CPU time for all children: %d seconds, %d microseconds\n",all_children_user_usage_sec,all_children_user_usage_microsec);
  printf("Kern mode CPU time for all children: %d seconds, %d microseconds\n",all_children_cpu_usage_sec,all_children_cpu_usage_microsec);


  return 0;
}



void stupidFunction()
{

  int i=0;

  while(i<900000000)
  {
    //  printf("%d\n",i);
    i+=1;
  }

}


void childSort()
{
  printf("in childSort\n");

  int fd[2];
  fd[0]=open("file1", O_RDONLY,0777);
  fd[1]=open("file2", O_WRONLY,0777);

  dup2(fd[0],0);
  dup2(fd[1],1);

  char* execArgs[2];
  execArgs[0]="sort";
  execArgs[1]=NULL;

  execvp(execArgs[0],execArgs);
}

Please provide some feedback about the correctness of the code and also, if instead of one children, I have many, will this code return the usage for all children combined ?

HaveNoDisplayName
  • 8,291
  • 106
  • 37
  • 47
Saik
  • 993
  • 1
  • 16
  • 40

1 Answers1

3

The semantics RUSAGE_CHILDREN are pretty clearly explained in the man page:

RUSAGE_CHILDREN

Return resource usage statistics for all children of the calling process that have terminated and been waited for. These statistics will include the resources used by grandchildren, and further removed descendants, if all of the intervening descendants waited on their terminated children.

Since your child process has terminated and you have waited for it, its statistics should be included in the data from your getrusage(RUSAGE_CHILDREN, ...) call. If you put the call before the waitpid, they wouldn't be included.

Note it says clearly that this includes all children, even if there is more than one, and further descendants, provided they have terminated and been waited for.

I do see some bugs in your program that could explain any odd behavior you might be seeing.

First, the type of the tv_usec member of struct timeval is not time_t but suseconds_t. Assigning .tv_usec to a variable of type time_t might overflow it, in principle.

Next, your _sec and _microsec variables are of type time_t, but you print them with the %d format specifier to printf(), which is intended for int. If time_t is of a type larger than int (which is the case on 64-bit Linux systems) then this will not work. The same goes when you change your _microsec variables to be of the correct type suseconds_t.

Now we don't necessarily know much about the types of time_t and suseconds_t. POSIX says only that time_t can be either an integer or floating-point type, and that suseconds_t is a signed integer type that can represent numbers from 0 to 1000000.

On all Linux platforms, as far as I know, time_t is a signed integer type, so I believe we could safely do

 time_t sec = ... ;
 time_t microsec = ...;
 printf("Time is %jd seconds and %jd microseconds\n", (intmax_t)sec, (intmax_t)microsec);

This won't necessarily be portable to all Unix systems, but I think it will work on most of them.

Also, opening files with mode 0777 is a bad practice, even for testing.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • Thanks for explanation, just one more question. Is there an easy way to get the times for individual children if there is more than one ? I was thinking to pretty much run getrusage after every waitpid and to start subtracting in order to get the times for each chindren separatley. Also I believe the type of tv_usec is long int and not suseconds_t, unless they are both the same type. – Saik Jan 31 '16 at 03:53
  • @Saik: Why yes there is. Instead of `waitpid`, use `wait3` or `wait4`, which fill in a `struct rusage` with resource usage for that specific child and its descendants. (If you want to know about the child separately from its descendants separately, you'll need to wait for someone to implement `wait6`). – Nate Eldredge Jan 31 '16 at 04:02
  • @Saik: [POSIX says](http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/time.h.html) the type of the second member of `struct timeval` shall be `suseconds_t`. It does appear to be the same as `long int` on my system. – Nate Eldredge Jan 31 '16 at 04:05
  • Make sense, I just remembered that GNU C libarary said it was long int, but I guess suseconds_t is a typedef of long int. By the way, do you know what time_t is a typedef of ? is it long int again ? – Saik Jan 31 '16 at 04:34
  • @NateEldredge Can you reference any source for the claim that wait4 includes resource usage of descendants? Does this mean it works like RUSAGE_CHILDREN? – Mitar Oct 15 '19 at 23:09
  • @Mitar: I don't know a place in the documentation where this is explained explicitly. You can read [the Linux source code](https://github.com/torvalds/linux/blob/3b1f00aceb7a67bf079a5a64aa5c6baf78a8f442/kernel/exit.c#L1088) and see that it internally calls `getrusage(RUSAGE_BOTH)` which [yields](https://github.com/torvalds/linux/blob/04cbfba6208592999d7bfe6609ec01dc3fde73f5/kernel/sys.c#L1740) the sum of the deceased process's `RUSAGE_SELF` and `RUSAGE_CHILDREN`. – Nate Eldredge Oct 16 '19 at 02:35