Bug 90499

Summary: getrusage does not capture resources consumed by threads
Product: [Retired] Red Hat Linux Reporter: Dave Wagoner <wagdalule>
Component: kernelAssignee: Arjan van de Ven <arjanv>
Status: CLOSED WONTFIX QA Contact: Brian Brock <bbrock>
Severity: medium Docs Contact:
Priority: medium    
Version: 7.3Keywords: FutureFeature
Target Milestone: ---   
Target Release: ---   
Hardware: i686   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Enhancement
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2004-09-30 15:40:53 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Dave Wagoner 2003-05-08 21:27:16 UTC
Description of problem:

When threads are launched using pthread_create() and CPU time is consumed, the 
amount of CPU time (as well as other resources) is not obtainable with getrusage
(). The RedHat implementation for threading is to launch a distinct process as 
opposed to true threading within the process. Since this is the case, one would 
think that invoking getrusage and specifying children processes should capture 
the resource usage. 

Is there another way to obtain getrusage like data or will getrusage be updated 
to provide the expected output? 


Version-Release number of selected component (if applicable):


How reproducible:
Consistently.

Steps to Reproduce:
1.Write a short C program that burns CPU in threads (example included in 
the "additional info section")
2.Add getrusage calls; compile, link, run
3.Note that CPU time for both "parent" and threads (implemented as children) 
does not capture the CPU consumed by the threads.
  
    
Actual results:
Output consistently shows no CPU consumed when work done by "threads"

Expected results:
Output that consistently shows the amount CPU consumed by threads.

Additional info:

Demo program; BE SURE TO LINK WITH -lpthread 

============================================================================


/****************************************************************
 *
 * demo of getrusage to test Redhat & Solaris semantics 
 *    
 *****************************************************************/

/*
 * Note: Must compile & Link as: 
 *      Solaris:  cc -o getrusage getrusage.c -lthread 
 *      Redhat:   cc -o getrusage getrusage.c -lpthread
 *
 *      Paranoid: cc -o getrusage getrusage.c -lthread -lpthread 
 *
 *      Linking with the threads library is the key; it will run if 
 *      compiled without the additional library, but it will not run
 *      correctly.
 *
 */
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>

/* cc thisfile.c -lthread -lpthread */
#define __REENTRANT             /* basic 3-lines for threads */
#include <pthread.h>
#ifdef SOLARIS
#include <thread.h>
#endif

extern int errno;

#define TERMS           1e8
#define TERMS_T         1e8
#define NUM_THREADS     4

/* use macros for accessing rusage arrays - adds clarity */
#define START 0
#define END   1

/*
 * prototypes
 */
float delta_rusage_times(struct rusage *r0, struct rusage *r1);
void  do_series(); 
void *do_series_t(void *);              /* thread routine */

/*********************************************************************
 * main()
 *********************************************************************/
main()
{
   /* beginning and ending rusage numbers for parent & children */
   struct rusage rusage_parent[2];
   struct rusage rusage_children[2];
   unsigned int pid;
   int          stat;
   long int     thread_arg = 119;
   int          j;                      /* loop counter */

   pthread_t  thread[4];

   getrusage(RUSAGE_SELF,     &rusage_parent[START]);
   do_series();
   getrusage(RUSAGE_SELF,     &rusage_parent[END]);

   getrusage(RUSAGE_CHILDREN, &rusage_children[START]);
   if ( !(pid=fork()) ) {
      do_series();
      exit(0);
   } else {
      if (pid < 0) {
         fprintf(stderr,"fork failed; returns %d, errno=%d\n", pid, errno);
         exit(-1);
      }
      wait(&stat);
   }
   getrusage(RUSAGE_CHILDREN, &rusage_children[END]);

   printf("Parent   CPU time consumed: %10.6f seconds\n", 
      delta_rusage_times(&rusage_parent[START], &rusage_parent[END]));
   printf("Children CPU time consumed: %10.6f seconds\n", 
      delta_rusage_times(&rusage_children[START], &rusage_children[END]));

   /*
    * Now try with threads
    */
   getrusage(RUSAGE_SELF,     &rusage_parent[START]);
   getrusage(RUSAGE_CHILDREN, &rusage_children[START]);

   /* launch threads */
   for (j=0; j<NUM_THREADS; j++) {
      stat = pthread_create(&thread[j],  NULL, do_series_t, (void *)thread_arg);
      printf("pthread_create() returns %d, errno=%d\n",stat, errno);
   }

   /* wait for threads to terminate */
   for (j=0; j<NUM_THREADS; j++ ) {
      pthread_join( thread[j], NULL );
   }

   getrusage(RUSAGE_SELF,     &rusage_parent[END]);
   getrusage(RUSAGE_CHILDREN, &rusage_children[END]);

   printf("Parent   CPU time consumed: %10.6f seconds\n", 
      delta_rusage_times(&rusage_parent[START], &rusage_parent[END]));
   printf("Children CPU time consumed: %10.6f seconds\n", 
      delta_rusage_times(&rusage_children[START], &rusage_children[END]));
}                                               /* main() */

/*********************************************************************
 * delta_rusage_times() - 
 *********************************************************************/
float delta_rusage_times(struct rusage *r0, struct rusage *r1)
{
   float r0_time = r0->ru_utime.tv_sec + r0->ru_utime.tv_usec/1000000.0 +
                   r0->ru_stime.tv_sec + r0->ru_stime.tv_usec/1000000.0;

   float r1_time = r1->ru_utime.tv_sec + r1->ru_utime.tv_usec/1000000.0 +
                   r1->ru_stime.tv_sec + r1->ru_stime.tv_usec/1000000.0;

   return(r1_time - r0_time);
}                                               /* delta_rusage_times() */

/*********************************************************************
 * do_series() - burn some CPU in a process
 *********************************************************************/
void  do_series( )
{
   double accum = 0.0;                          /* accumulate results */
   long int i;                                  /* looping variable */

   for ( i=1 ; i<=TERMS ; i++ ) {
      accum += 1.0 /(double)i;
   }

   printf("do_series(%ld) = %lf\n", (long int)TERMS, accum);
}                                               /* do_series() */

/*********************************************************************
 * do_series_t() - burn some CPU in a thread
 *********************************************************************/
void  *do_series_t( void *thread_arg) 
{
   double accum = 0.0;                          /* accumulate results */
   long int i;                                  /* looping variable */

   for ( i=1 ; i<=TERMS_T ; i++ ) {
      accum += 1.0 /(double)i;
   }

   printf("do_series_t(%ld) = %lf\n", (long int)TERMS_T, accum);

/* printf("do_series_t() receives arg of %ld\n",(long int)thread_arg); */
}                                               /* do_series() */
 accum);



/* printf("do_series_t() receives arg of %ld\n",(long int)thread_arg); */

}                                               /* do_series() */

Comment 1 Bugzilla owner 2004-09-30 15:40:53 UTC
Thanks for the bug report. However, Red Hat no longer maintains this version of
the product. Please upgrade to the latest version and open a new bug if the problem
persists.

The Fedora Legacy project (http://fedoralegacy.org/) maintains some older releases, 
and if you believe this bug is interesting to them, please report the problem in
the bug tracker at: http://bugzilla.fedora.us/