Dev Tip - Tracking Process Resource Use

Version 7

    by Darryl Gove-Oracle

    About the Author
    Darryl GoveDarryl Gove is a senior principal software engineer in the Oracle Solaris Studio team, working on optimizing applications and benchmarks for current and future processors.  He is also the author of the books Multicore Application Programming, Solaris Application Programming, and The Developer's Edge.  Find his blog at http://blogs.oracle.com/d.
    See Also
    - Oracle Solaris Studio
    - SPARC Systems
    - Studio Forums
    - Tech Articles for Developers
    Follow OTN
    YouTube OTN Homepage

    Introduction

    It is sometimes useful to understand how a process is using resources – like CPU time or memory. Potentially it is useful to understand other actions like messages sent, or context switches. Fortunately all Solaris provides a wealth of information about process resource use.

    Finding Out Resource Use

    There are two major ways that we can obtain resource use information. The first is through the function call getrusage(). We can use this call to get information about the user and system time consumed by the process. The following code prints out the user and system time when a process exits:

    #include <stdio.h> #include <sys/resource.h>  #pragma fini (report) static void report() {   struct rusage r;   getrusage(RUSAGE_SELF, &r);   printf(" Usr %8.2fs\n", r.ru_utime.tv_sec*1.0 + r.ru_utime.tv_usec*0.000001);   printf(" Sys %8.2fs\n", r.ru_stime.tv_sec*1.0 + r.ru_stime.tv_usec*0.000001); }

     

    In the code we use pragma fini to declare a routine that gets called at exit time. We also declare the routine itself as being static. Static variables and routines are local to the object file, this means that the routine “report()” declared here will not get confused with any other routines called “report()” that might occur in the executable or the libraries that it uses. This code can be compiled and run as:

    $ cc -G -Kpic -o libmem1.so mem1.c $ LD_PRELOAD=./libmem1.so sleep 1  Usr     0.00s  Sys     0.00s

     

    However, getrusage() does not provide information about memory consumption. If we want that information we need to explore the /procfilesystem. The file /proc/self/psinfocontains information about the Size and RSS of an application. The following code shows how /proc can be used to print application size as well as CPU utilisation.

    #include <stdio.h> #include <sys/resource.h>  #include <unistd.h> #include <fcntl.h> #include <procfs.h>  static size_t getRSSsize() {   struct psinfo psinfo;   int fd;   fd = open( "/proc/self/psinfo", O_RDONLY);   if (fd == -1) { return 0; }   read( fd, &psinfo, sizeof(psinfo) );   close( fd );   return (size_t)psinfo.pr_rssize ; }  static size_t getsize() {   struct psinfo psinfo;   int fd;   fd = open( "/proc/self/psinfo", O_RDONLY);   if (fd == -1) { return 0; }   read( fd, &psinfo, sizeof(psinfo) );   close( fd );   return (size_t)psinfo.pr_size ; }  #pragma fini (report) static void report() {   struct rusage r;   getrusage(RUSAGE_SELF, &r);   printf(" Usr %8.2fs\n", r.ru_utime.tv_sec*1.0 + r.ru_utime.tv_usec*0.000001);      printf(" Sys %8.2fs\n", r.ru_stime.tv_sec*1.0 + r.ru_stime.tv_usec*0.000001);       printf(" Siz %8i KB\n", getsize());   printf(" RSS %8i KB\n", getRSSsize()); }

     

    The output from this expanded code looks like:

    $ cc -G -Kpic -o libmem2.so mem2.c $ LD_PRELOAD=./libmem2.so sleep 1  Usr     0.00s  Sys     0.01s  Siz     2696 KB  RSS     1808 KB

     

    Tracking Resource Usage

    It can be useful to find out the resource usage of an application when it exits. However, it is often more useful to see how resource usage is growing over time. To do this we need to install a timer so that we can report how resource utilisation at regular intervals. This article describes how to write a signal handler that responds to a timer. The basic code looks like:

    #include <signal.h> #include <string.h> #include <stdio.h> #include <unistd.h>  // Setup signal handler  static struct sigaction sig_action; static struct itimerval timeout={0};  // Set timer for 1 second timeout static void set_timer() {   timeout.it_value.tv_sec=1;   setitimer(ITIMER_PROF, &timeout, 0); }  // Handler timer signal static void handle_prof_signal(int sig_no, siginfo_t* info, void *context) {   write(STDERR_FILENO,"Tick\n",5);   set_timer(); }  // Set up handler when program loaded #pragma init(init_handler) static void init_handler() {   memset(&sig_action, 0, sizeof(sig_action));   sig_action.sa_sigaction = handle_prof_signal;   sig_action.sa_flags = SA_RESTART | SA_SIGINFO;   sigemptyset(&sig_action.sa_mask);   sigaction(SIGPROF, &sig_action, 0);   set_timer(); }  void main() {   while(1) {} }

     

    This code sets up a signal hander for one second intervals. It uses pragma init to install the handler before the main executable runs – this technique is useful for when we convert this into a library. The code uses write() which is a safe call to make in a signal hander. In the program main() just executes an infinite loop so that we can see the output from the timer signal.

     

    Reporting Resource Use At Intervals and At Exit

    The following code combines the timer code with the resource code to print out resource utilisation at five second intervals as well as at process exit:

    #include <signal.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <sys/resource.h> #include <unistd.h> #include <fcntl.h> #include <procfs.h>  // Report resource information  // get RSS size static size_t getRSSsize() {   struct psinfo psinfo;   int fd;   fd = open( "/proc/self/psinfo", O_RDONLY);   if (fd == -1) { return 0; }   read( fd, &psinfo, sizeof(psinfo) );   close( fd );   return (size_t)psinfo.pr_rssize ; }  // get size static size_t getsize() {   struct psinfo psinfo;   int fd;   fd = open( "/proc/self/psinfo", O_RDONLY);   if (fd == -1) { return 0; }   read( fd, &psinfo, sizeof(psinfo) );   close( fd );   return (size_t)psinfo.pr_size ; }  // Report resources on exit as well as using timer #pragma fini(report_resources) static void report_resources() {   struct rusage data;   char s[80];   int len;   getrusage(RUSAGE_SELF, &data);   len=snprintf(s,80," %8.2f User(s)", data.ru_utime.tv_sec*1.0 + data.ru_utime.tv_usec*0.000001);   write(STDERR_FILENO,s,len);   len=snprintf(s,80," %8.2f Sys(s)", data.ru_stime.tv_sec*1.0 + data.ru_stime.tv_usec*0.000001);   write(STDERR_FILENO,s,len);   len=snprintf(s,80," %8li RSS(KB)", getRSSsize() );   write(STDERR_FILENO,s,len);   len=snprintf(s,80," %8li Size(KB)\n", getsize() );   write(STDERR_FILENO,s,len); }  // Setup signal handler  static struct sigaction sig_action; static struct itimerval timeout={0};  // Set timer for five second interval static void set_timer() {   timeout.it_value.tv_sec=5;   setitimer(ITIMER_PROF, &timeout, 0); }  // Handler timer signal static void handle_prof_signal(int sig_no, siginfo_t* info, void *context) {   report_resources();   set_timer(); }  // Set up handler when program loaded #pragma init(init_handler) static void init_handler() {   memset(&sig_action, 0, sizeof(sig_action));   sig_action.sa_sigaction = handle_prof_signal;   sig_action.sa_flags = SA_RESTART | SA_SIGINFO;   sigemptyset(&sig_action.sa_mask);   sigaction(SIGPROF, &sig_action, 0);   set_timer();   printf("Exec %s\n",getexecname()); }

     

    This code also uses the Solaris call getexecname()to report the name of the executable being tracked.