Back to index

glibc  2.9
memusage.c
Go to the documentation of this file.
00001 /* Profile heap and stack memory usage of running program.
00002    Copyright (C) 1998-2002, 2004, 2005, 2006 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <atomic.h>
00022 #include <dlfcn.h>
00023 #include <errno.h>
00024 #include <fcntl.h>
00025 #include <inttypes.h>
00026 #include <signal.h>
00027 #include <stdarg.h>
00028 #include <stdbool.h>
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032 #include <unistd.h>
00033 #include <sys/mman.h>
00034 #include <sys/time.h>
00035 
00036 #include <memusage.h>
00037 
00038 /* Pointer to the real functions.  These are determined used `dlsym'
00039    when really needed.  */
00040 static void *(*mallocp) (size_t);
00041 static void *(*reallocp) (void *, size_t);
00042 static void *(*callocp) (size_t, size_t);
00043 static void (*freep) (void *);
00044 
00045 static void *(*mmapp) (void *, size_t, int, int, int, off_t);
00046 static void *(*mmap64p) (void *, size_t, int, int, int, off64_t);
00047 static int (*munmapp) (void *, size_t);
00048 static void *(*mremapp) (void *, size_t, size_t, int, void *);
00049 
00050 enum
00051 {
00052   idx_malloc = 0,
00053   idx_realloc,
00054   idx_calloc,
00055   idx_free,
00056   idx_mmap_r,
00057   idx_mmap_w,
00058   idx_mmap_a,
00059   idx_mremap,
00060   idx_munmap,
00061   idx_last
00062 };
00063 
00064 
00065 struct header
00066 {
00067   size_t length;
00068   size_t magic;
00069 };
00070 
00071 #define MAGIC 0xfeedbeaf
00072 
00073 
00074 static memusage_cntr_t calls[idx_last];
00075 static memusage_cntr_t failed[idx_last];
00076 static memusage_size_t total[idx_last];
00077 static memusage_size_t grand_total;
00078 static memusage_cntr_t histogram[65536 / 16];
00079 static memusage_cntr_t large;
00080 static memusage_cntr_t calls_total;
00081 static memusage_cntr_t inplace;
00082 static memusage_cntr_t decreasing;
00083 static memusage_cntr_t realloc_free;
00084 static memusage_cntr_t inplace_mremap;
00085 static memusage_cntr_t decreasing_mremap;
00086 static memusage_size_t current_heap;
00087 static memusage_size_t peak_use[3];
00088 static __thread uintptr_t start_sp;
00089 
00090 /* A few macros to make the source more readable.  */
00091 #define peak_heap    peak_use[0]
00092 #define peak_stack   peak_use[1]
00093 #define peak_total   peak_use[2]
00094 
00095 #define DEFAULT_BUFFER_SIZE 1024
00096 static size_t buffer_size;
00097 
00098 static int fd = -1;
00099 
00100 static bool not_me;
00101 static int initialized;
00102 static bool trace_mmap;
00103 extern const char *__progname;
00104 
00105 struct entry
00106 {
00107   uint64_t heap;
00108   uint64_t stack;
00109   uint32_t time_low;
00110   uint32_t time_high;
00111 };
00112 
00113 static struct entry buffer[2 * DEFAULT_BUFFER_SIZE];
00114 static uatomic32_t buffer_cnt;
00115 static struct entry first;
00116 
00117 
00118 /* Update the global data after a successful function call.  */
00119 static void
00120 update_data (struct header *result, size_t len, size_t old_len)
00121 {
00122   if (result != NULL)
00123     {
00124       /* Record the information we need and mark the block using a
00125          magic number.  */
00126       result->length = len;
00127       result->magic = MAGIC;
00128     }
00129 
00130   /* Compute current heap usage and compare it with the maximum value.  */
00131   memusage_size_t heap
00132     = catomic_exchange_and_add (&current_heap, len - old_len) + len - old_len;
00133   catomic_max (&peak_heap, heap);
00134 
00135   /* Compute current stack usage and compare it with the maximum
00136      value.  The base stack pointer might not be set if this is not
00137      the main thread and it is the first call to any of these
00138      functions.  */
00139   if (__builtin_expect (!start_sp, 0))
00140     start_sp = GETSP ();
00141 
00142   uintptr_t sp = GETSP ();
00143 #ifdef STACK_GROWS_UPWARD
00144   /* This can happen in threads where we didn't catch the thread's
00145      stack early enough.  */
00146   if (__builtin_expect (sp < start_sp, 0))
00147     start_sp = sp;
00148   size_t current_stack = sp - start_sp;
00149 #else
00150   /* This can happen in threads where we didn't catch the thread's
00151      stack early enough.  */
00152   if (__builtin_expect (sp > start_sp, 0))
00153     start_sp = sp;
00154   size_t current_stack = start_sp - sp;
00155 #endif
00156   catomic_max (&peak_stack, current_stack);
00157 
00158   /* Add up heap and stack usage and compare it with the maximum value.  */
00159   catomic_max (&peak_total, heap + current_stack);
00160 
00161   /* Store the value only if we are writing to a file.  */
00162   if (fd != -1)
00163     {
00164       uatomic32_t idx = catomic_exchange_and_add (&buffer_cnt, 1);
00165       if (idx >= 2 * buffer_size)
00166        {
00167          /* We try to reset the counter to the correct range.  If
00168             this fails because of another thread increasing the
00169             counter it does not matter since that thread will take
00170             care of the correction.  */
00171          unsigned int reset = idx - 2 * buffer_size;
00172          catomic_compare_and_exchange_val_acq (&buffer_size, reset, idx);
00173          idx = reset;
00174        }
00175 
00176       buffer[idx].heap = current_heap;
00177       buffer[idx].stack = current_stack;
00178       GETTIME (buffer[idx].time_low, buffer[idx].time_high);
00179 
00180       /* Write out buffer if it is full.  */
00181       if (idx + 1 == buffer_size)
00182        write (fd, buffer, buffer_size * sizeof (struct entry));
00183       else if (idx + 1 == 2 * buffer_size)
00184        write (fd, &buffer[buffer_size], buffer_size * sizeof (struct entry));
00185     }
00186 }
00187 
00188 
00189 /* Interrupt handler.  */
00190 static void
00191 int_handler (int signo)
00192 {
00193   /* Nothing gets allocated.  Just record the stack pointer position.  */
00194   update_data (NULL, 0, 0);
00195 }
00196 
00197 
00198 /* Find out whether this is the program we are supposed to profile.
00199    For this the name in the variable `__progname' must match the one
00200    given in the environment variable MEMUSAGE_PROG_NAME.  If the variable
00201    is not present every program assumes it should be profiling.
00202 
00203    If this is the program open a file descriptor to the output file.
00204    We will write to it whenever the buffer overflows.  The name of the
00205    output file is determined by the environment variable MEMUSAGE_OUTPUT.
00206 
00207    If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical
00208    value determines the size of the internal buffer.  The number gives
00209    the number of elements in the buffer.  By setting the number to one
00210    one effectively selects unbuffered operation.
00211 
00212    If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
00213    which at the highest possible frequency records the stack pointer.  */
00214 static void
00215 me (void)
00216 {
00217   const char *env = getenv ("MEMUSAGE_PROG_NAME");
00218   size_t prog_len = strlen (__progname);
00219 
00220   initialized = -1;
00221   mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
00222   reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
00223   callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
00224   freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
00225 
00226   mmapp = (void *(*) (void *, size_t, int, int, int, off_t)) dlsym (RTLD_NEXT,
00227                                                             "mmap");
00228   mmap64p =
00229     (void *(*) (void *, size_t, int, int, int, off64_t)) dlsym (RTLD_NEXT,
00230                                                         "mmap64");
00231   mremapp = (void *(*) (void *, size_t, size_t, int, void *)) dlsym (RTLD_NEXT,
00232                                                              "mremap");
00233   munmapp = (int (*) (void *, size_t)) dlsym (RTLD_NEXT, "munmap");
00234   initialized = 1;
00235 
00236   if (env != NULL)
00237     {
00238       /* Check for program name.  */
00239       size_t len = strlen (env);
00240       if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
00241          || (prog_len != len && __progname[prog_len - len - 1] != '/'))
00242        not_me = true;
00243     }
00244 
00245   /* Only open the file if it's really us.  */
00246   if (!not_me && fd == -1)
00247     {
00248       const char *outname;
00249 
00250       if (!start_sp)
00251        start_sp = GETSP ();
00252 
00253       outname = getenv ("MEMUSAGE_OUTPUT");
00254       if (outname != NULL && outname[0] != '\0'
00255          && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
00256        {
00257          fd = creat64 (outname, 0666);
00258 
00259          if (fd == -1)
00260            /* Don't do anything in future calls if we cannot write to
00261               the output file.  */
00262            not_me = true;
00263          else
00264            {
00265              /* Write the first entry.  */
00266              first.heap = 0;
00267              first.stack = 0;
00268              GETTIME (first.time_low, first.time_high);
00269              /* Write it two times since we need the starting and end time. */
00270              write (fd, &first, sizeof (first));
00271              write (fd, &first, sizeof (first));
00272 
00273              /* Determine the buffer size.  We use the default if the
00274                environment variable is not present.  */
00275              buffer_size = DEFAULT_BUFFER_SIZE;
00276              if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
00277               {
00278                 buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
00279                 if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
00280                   buffer_size = DEFAULT_BUFFER_SIZE;
00281               }
00282 
00283              /* Possibly enable timer-based stack pointer retrieval.  */
00284              if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
00285               {
00286                 struct sigaction act;
00287 
00288                 act.sa_handler = (sighandler_t) &int_handler;
00289                 act.sa_flags = SA_RESTART;
00290                 sigfillset (&act.sa_mask);
00291 
00292                 if (sigaction (SIGPROF, &act, NULL) >= 0)
00293                   {
00294                     struct itimerval timer;
00295 
00296                     timer.it_value.tv_sec = 0;
00297                     timer.it_value.tv_usec = 1;
00298                     timer.it_interval = timer.it_value;
00299                     setitimer (ITIMER_PROF, &timer, NULL);
00300                   }
00301               }
00302            }
00303        }
00304 
00305       if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL)
00306        trace_mmap = true;
00307     }
00308 }
00309 
00310 
00311 /* Record the initial stack position.  */
00312 static void
00313 __attribute__ ((constructor))
00314 init (void)
00315 {
00316   start_sp = GETSP ();
00317   if (! initialized)
00318     me ();
00319 }
00320 
00321 
00322 /* `malloc' replacement.  We keep track of the memory usage if this is the
00323    correct program.  */
00324 void *
00325 malloc (size_t len)
00326 {
00327   struct header *result = NULL;
00328 
00329   /* Determine real implementation if not already happened.  */
00330   if (__builtin_expect (initialized <= 0, 0))
00331     {
00332       if (initialized == -1)
00333        return NULL;
00334       me ();
00335     }
00336 
00337   /* If this is not the correct program just use the normal function.  */
00338   if (not_me)
00339     return (*mallocp) (len);
00340 
00341   /* Keep track of number of calls.  */
00342   catomic_increment (&calls[idx_malloc]);
00343   /* Keep track of total memory consumption for `malloc'.  */
00344   catomic_add (&total[idx_malloc], len);
00345   /* Keep track of total memory requirement.  */
00346   catomic_add (&grand_total, len);
00347   /* Remember the size of the request.  */
00348   if (len < 65536)
00349     catomic_increment (&histogram[len / 16]);
00350   else
00351     catomic_increment (&large);
00352   /* Total number of calls of any of the functions.  */
00353   catomic_increment (&calls_total);
00354 
00355   /* Do the real work.  */
00356   result = (struct header *) (*mallocp) (len + sizeof (struct header));
00357   if (result == NULL)
00358     {
00359       catomic_increment (&failed[idx_malloc]);
00360       return NULL;
00361     }
00362 
00363   /* Update the allocation data and write out the records if necessary.  */
00364   update_data (result, len, 0);
00365 
00366   /* Return the pointer to the user buffer.  */
00367   return (void *) (result + 1);
00368 }
00369 
00370 
00371 /* `realloc' replacement.  We keep track of the memory usage if this is the
00372    correct program.  */
00373 void *
00374 realloc (void *old, size_t len)
00375 {
00376   struct header *result = NULL;
00377   struct header *real;
00378   size_t old_len;
00379 
00380   /* Determine real implementation if not already happened.  */
00381   if (__builtin_expect (initialized <= 0, 0))
00382     {
00383       if (initialized == -1)
00384        return NULL;
00385       me ();
00386     }
00387 
00388   /* If this is not the correct program just use the normal function.  */
00389   if (not_me)
00390     return (*reallocp) (old, len);
00391 
00392   if (old == NULL)
00393     {
00394       /* This is really a `malloc' call.  */
00395       real = NULL;
00396       old_len = 0;
00397     }
00398   else
00399     {
00400       real = ((struct header *) old) - 1;
00401       if (real->magic != MAGIC)
00402        /* This is no memory allocated here.  */
00403        return (*reallocp) (old, len);
00404       old_len = real->length;
00405     }
00406 
00407   /* Keep track of number of calls.  */
00408   catomic_increment (&calls[idx_realloc]);
00409   if (len > old_len)
00410     {
00411       /* Keep track of total memory consumption for `realloc'.  */
00412       catomic_add (&total[idx_realloc], len - old_len);
00413       /* Keep track of total memory requirement.  */
00414       catomic_add (&grand_total, len - old_len);
00415     }
00416 
00417   if (len == 0 && old != NULL)
00418     {
00419       /* Special case.  */
00420       catomic_increment (&realloc_free);
00421       /* Keep track of total memory freed using `free'.  */
00422       catomic_add (&total[idx_free], real->length);
00423 
00424       /* Update the allocation data and write out the records if necessary.  */
00425       update_data (NULL, 0, old_len);
00426 
00427       /* Do the real work.  */
00428       (*freep) (real);
00429 
00430       return NULL;
00431     }
00432 
00433   /* Remember the size of the request.  */
00434   if (len < 65536)
00435     catomic_increment (&histogram[len / 16]);
00436   else
00437     catomic_increment (&large);
00438   /* Total number of calls of any of the functions.  */
00439   catomic_increment (&calls_total);
00440 
00441   /* Do the real work.  */
00442   result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
00443   if (result == NULL)
00444     {
00445       catomic_increment (&failed[idx_realloc]);
00446       return NULL;
00447     }
00448 
00449   /* Record whether the reduction/increase happened in place.  */
00450   if (real == result)
00451     catomic_increment (&inplace);
00452   /* Was the buffer increased?  */
00453   if (old_len > len)
00454     catomic_increment (&decreasing);
00455 
00456   /* Update the allocation data and write out the records if necessary.  */
00457   update_data (result, len, old_len);
00458 
00459   /* Return the pointer to the user buffer.  */
00460   return (void *) (result + 1);
00461 }
00462 
00463 
00464 /* `calloc' replacement.  We keep track of the memory usage if this is the
00465    correct program.  */
00466 void *
00467 calloc (size_t n, size_t len)
00468 {
00469   struct header *result;
00470   size_t size = n * len;
00471 
00472   /* Determine real implementation if not already happened.  */
00473   if (__builtin_expect (initialized <= 0, 0))
00474     {
00475       if (initialized == -1)
00476        return NULL;
00477       me ();
00478     }
00479 
00480   /* If this is not the correct program just use the normal function.  */
00481   if (not_me)
00482     return (*callocp) (n, len);
00483 
00484   /* Keep track of number of calls.  */
00485   catomic_increment (&calls[idx_calloc]);
00486   /* Keep track of total memory consumption for `calloc'.  */
00487   catomic_add (&total[idx_calloc], size);
00488   /* Keep track of total memory requirement.  */
00489   catomic_add (&grand_total, size);
00490   /* Remember the size of the request.  */
00491   if (size < 65536)
00492     catomic_increment (&histogram[size / 16]);
00493   else
00494     catomic_increment (&large);
00495   /* Total number of calls of any of the functions.  */
00496   ++calls_total;
00497 
00498   /* Do the real work.  */
00499   result = (struct header *) (*mallocp) (size + sizeof (struct header));
00500   if (result == NULL)
00501     {
00502       catomic_increment (&failed[idx_calloc]);
00503       return NULL;
00504     }
00505 
00506   /* Update the allocation data and write out the records if necessary.  */
00507   update_data (result, size, 0);
00508 
00509   /* Do what `calloc' would have done and return the buffer to the caller.  */
00510   return memset (result + 1, '\0', size);
00511 }
00512 
00513 
00514 /* `free' replacement.  We keep track of the memory usage if this is the
00515    correct program.  */
00516 void
00517 free (void *ptr)
00518 {
00519   struct header *real;
00520 
00521   /* Determine real implementation if not already happened.  */
00522   if (__builtin_expect (initialized <= 0, 0))
00523     {
00524       if (initialized == -1)
00525        return;
00526       me ();
00527     }
00528 
00529   /* If this is not the correct program just use the normal function.  */
00530   if (not_me)
00531     {
00532       (*freep) (ptr);
00533       return;
00534     }
00535 
00536   /* `free (NULL)' has no effect.  */
00537   if (ptr == NULL)
00538     {
00539       catomic_increment (&calls[idx_free]);
00540       return;
00541     }
00542 
00543   /* Determine the pointer to the header.  */
00544   real = ((struct header *) ptr) - 1;
00545   if (real->magic != MAGIC)
00546     {
00547       /* This block wasn't allocated here.  */
00548       (*freep) (ptr);
00549       return;
00550     }
00551 
00552   /* Keep track of number of calls.  */
00553   catomic_increment (&calls[idx_free]);
00554   /* Keep track of total memory freed using `free'.  */
00555   catomic_add (&total[idx_free], real->length);
00556 
00557   /* Update the allocation data and write out the records if necessary.  */
00558   update_data (NULL, 0, real->length);
00559 
00560   /* Do the real work.  */
00561   (*freep) (real);
00562 }
00563 
00564 
00565 /* `mmap' replacement.  We do not have to keep track of the sizesince
00566    `munmap' will get it as a parameter.  */
00567 void *
00568 mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
00569 {
00570   void *result = NULL;
00571 
00572   /* Determine real implementation if not already happened.  */
00573   if (__builtin_expect (initialized <= 0, 0))
00574     {
00575       if (initialized == -1)
00576        return NULL;
00577       me ();
00578     }
00579 
00580   /* Always get a block.  We don't need extra memory.  */
00581   result = (*mmapp) (start, len, prot, flags, fd, offset);
00582 
00583   if (!not_me && trace_mmap)
00584     {
00585       int idx = (flags & MAP_ANON
00586                ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
00587 
00588       /* Keep track of number of calls.  */
00589       catomic_increment (&calls[idx]);
00590       /* Keep track of total memory consumption for `malloc'.  */
00591       catomic_add (&total[idx], len);
00592       /* Keep track of total memory requirement.  */
00593       catomic_add (&grand_total, len);
00594       /* Remember the size of the request.  */
00595       if (len < 65536)
00596        catomic_increment (&histogram[len / 16]);
00597       else
00598        catomic_increment (&large);
00599       /* Total number of calls of any of the functions.  */
00600       catomic_increment (&calls_total);
00601 
00602       /* Check for failures.  */
00603       if (result == NULL)
00604        catomic_increment (&failed[idx]);
00605       else if (idx == idx_mmap_w)
00606        /* Update the allocation data and write out the records if
00607           necessary.  Note the first parameter is NULL which means
00608           the size is not tracked.  */
00609        update_data (NULL, len, 0);
00610     }
00611 
00612   /* Return the pointer to the user buffer.  */
00613   return result;
00614 }
00615 
00616 
00617 /* `mmap' replacement.  We do not have to keep track of the sizesince
00618    `munmap' will get it as a parameter.  */
00619 void *
00620 mmap64 (void *start, size_t len, int prot, int flags, int fd, off64_t offset)
00621 {
00622   void *result = NULL;
00623 
00624   /* Determine real implementation if not already happened.  */
00625   if (__builtin_expect (initialized <= 0, 0))
00626     {
00627       if (initialized == -1)
00628        return NULL;
00629       me ();
00630     }
00631 
00632   /* Always get a block.  We don't need extra memory.  */
00633   result = (*mmap64p) (start, len, prot, flags, fd, offset);
00634 
00635   if (!not_me && trace_mmap)
00636     {
00637       int idx = (flags & MAP_ANON
00638                ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
00639 
00640       /* Keep track of number of calls.  */
00641       catomic_increment (&calls[idx]);
00642       /* Keep track of total memory consumption for `malloc'.  */
00643       catomic_add (&total[idx], len);
00644       /* Keep track of total memory requirement.  */
00645       catomic_add (&grand_total, len);
00646       /* Remember the size of the request.  */
00647       if (len < 65536)
00648        catomic_increment (&histogram[len / 16]);
00649       else
00650        catomic_increment (&large);
00651       /* Total number of calls of any of the functions.  */
00652       catomic_increment (&calls_total);
00653 
00654       /* Check for failures.  */
00655       if (result == NULL)
00656        catomic_increment (&failed[idx]);
00657       else if (idx == idx_mmap_w)
00658        /* Update the allocation data and write out the records if
00659           necessary.  Note the first parameter is NULL which means
00660           the size is not tracked.  */
00661        update_data (NULL, len, 0);
00662     }
00663 
00664   /* Return the pointer to the user buffer.  */
00665   return result;
00666 }
00667 
00668 
00669 /* `mmap' replacement.  We do not have to keep track of the sizesince
00670    `munmap' will get it as a parameter.  */
00671 void *
00672 mremap (void *start, size_t old_len, size_t len, int flags,  ...)
00673 {
00674   void *result = NULL;
00675   va_list ap;
00676 
00677   va_start (ap, flags);
00678   void *newaddr = (flags & MREMAP_FIXED) ? va_arg (ap, void *) : NULL;
00679   va_end (ap);
00680 
00681   /* Determine real implementation if not already happened.  */
00682   if (__builtin_expect (initialized <= 0, 0))
00683     {
00684       if (initialized == -1)
00685        return NULL;
00686       me ();
00687     }
00688 
00689   /* Always get a block.  We don't need extra memory.  */
00690   result = (*mremapp) (start, old_len, len, flags, newaddr);
00691 
00692   if (!not_me && trace_mmap)
00693     {
00694       /* Keep track of number of calls.  */
00695       catomic_increment (&calls[idx_mremap]);
00696       if (len > old_len)
00697        {
00698          /* Keep track of total memory consumption for `malloc'.  */
00699          catomic_add (&total[idx_mremap], len - old_len);
00700          /* Keep track of total memory requirement.  */
00701          catomic_add (&grand_total, len - old_len);
00702        }
00703       /* Remember the size of the request.  */
00704       if (len < 65536)
00705        catomic_increment (&histogram[len / 16]);
00706       else
00707        catomic_increment (&large);
00708       /* Total number of calls of any of the functions.  */
00709       catomic_increment (&calls_total);
00710 
00711       /* Check for failures.  */
00712       if (result == NULL)
00713        catomic_increment (&failed[idx_mremap]);
00714       else
00715        {
00716          /* Record whether the reduction/increase happened in place.  */
00717          if (start == result)
00718            catomic_increment (&inplace_mremap);
00719          /* Was the buffer increased?  */
00720          if (old_len > len)
00721            catomic_increment (&decreasing_mremap);
00722 
00723          /* Update the allocation data and write out the records if
00724             necessary.  Note the first parameter is NULL which means
00725             the size is not tracked.  */
00726          update_data (NULL, len, old_len);
00727        }
00728     }
00729 
00730   /* Return the pointer to the user buffer.  */
00731   return result;
00732 }
00733 
00734 
00735 /* `munmap' replacement.  */
00736 int
00737 munmap (void *start, size_t len)
00738 {
00739   int result;
00740 
00741   /* Determine real implementation if not already happened.  */
00742   if (__builtin_expect (initialized <= 0, 0))
00743     {
00744       if (initialized == -1)
00745        return -1;
00746       me ();
00747     }
00748 
00749   /* Do the real work.  */
00750   result = (*munmapp) (start, len);
00751 
00752   if (!not_me && trace_mmap)
00753     {
00754       /* Keep track of number of calls.  */
00755       catomic_increment (&calls[idx_munmap]);
00756 
00757       if (__builtin_expect (result == 0, 1))
00758        {
00759          /* Keep track of total memory freed using `free'.  */
00760          catomic_add (&total[idx_munmap], len);
00761 
00762          /* Update the allocation data and write out the records if
00763             necessary.  */
00764          update_data (NULL, 0, len);
00765        }
00766       else
00767        catomic_increment (&failed[idx_munmap]);
00768     }
00769 
00770   return result;
00771 }
00772 
00773 
00774 /* Write some statistics to standard error.  */
00775 static void
00776 __attribute__ ((destructor))
00777 dest (void)
00778 {
00779   int percent, cnt;
00780   unsigned long int maxcalls;
00781 
00782   /* If we haven't done anything here just return.  */
00783   if (not_me)
00784     return;
00785   /* If we should call any of the memory functions don't do any profiling.  */
00786   not_me = true;
00787 
00788   /* Finish the output file.  */
00789   if (fd != -1)
00790     {
00791       /* Write the partially filled buffer.  */
00792       if (buffer_cnt > buffer_size)
00793        write (fd, buffer + buffer_size,
00794               (buffer_cnt - buffer_size) * sizeof (struct entry));
00795       else
00796        write (fd, buffer, buffer_cnt * sizeof (struct entry));
00797 
00798       /* Go back to the beginning of the file.  We allocated two records
00799         here when we opened the file.  */
00800       lseek (fd, 0, SEEK_SET);
00801       /* Write out a record containing the total size.  */
00802       first.stack = peak_total;
00803       write (fd, &first, sizeof (struct entry));
00804       /* Write out another record containing the maximum for heap and
00805          stack.  */
00806       first.heap = peak_heap;
00807       first.stack = peak_stack;
00808       GETTIME (first.time_low, first.time_high);
00809       write (fd, &first, sizeof (struct entry));
00810 
00811       /* Close the file.  */
00812       close (fd);
00813       fd = -1;
00814     }
00815 
00816   /* Write a colorful statistic.  */
00817   fprintf (stderr, "\n\
00818 \e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\
00819 \e[04;34m         total calls   total memory   failed calls\e[0m\n\
00820 \e[00;34m malloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
00821 \e[00;34mrealloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m  (nomove:%ld, dec:%ld, free:%ld)\n\
00822 \e[00;34m calloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
00823 \e[00;34m   free|\e[0m %10lu   %12llu\n",
00824           (unsigned long long int) grand_total, (unsigned long int) peak_heap,
00825           (unsigned long int) peak_stack,
00826           (unsigned long int) calls[idx_malloc],
00827           (unsigned long long int) total[idx_malloc],
00828           failed[idx_malloc] ? "\e[01;41m" : "",
00829           (unsigned long int) failed[idx_malloc],
00830           (unsigned long int) calls[idx_realloc],
00831           (unsigned long long int) total[idx_realloc],
00832           failed[idx_realloc] ? "\e[01;41m" : "",
00833           (unsigned long int) failed[idx_realloc],
00834           (unsigned long int) inplace,
00835           (unsigned long int) decreasing,
00836           (unsigned long int) realloc_free,
00837           (unsigned long int) calls[idx_calloc],
00838           (unsigned long long int) total[idx_calloc],
00839           failed[idx_calloc] ? "\e[01;41m" : "",
00840           (unsigned long int) failed[idx_calloc],
00841           (unsigned long int) calls[idx_free],
00842           (unsigned long long int) total[idx_free]);
00843 
00844   if (trace_mmap)
00845     fprintf (stderr, "\
00846 \e[00;34mmmap(r)|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
00847 \e[00;34mmmap(w)|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
00848 \e[00;34mmmap(a)|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
00849 \e[00;34m mremap|\e[0m %10lu   %12llu   %s%12lu\e[00;00m  (nomove: %ld, dec:%ld)\n\
00850 \e[00;34m munmap|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n",
00851             (unsigned long int) calls[idx_mmap_r],
00852             (unsigned long long int) total[idx_mmap_r],
00853             failed[idx_mmap_r] ? "\e[01;41m" : "",
00854             (unsigned long int) failed[idx_mmap_r],
00855             (unsigned long int) calls[idx_mmap_w],
00856             (unsigned long long int) total[idx_mmap_w],
00857             failed[idx_mmap_w] ? "\e[01;41m" : "",
00858             (unsigned long int) failed[idx_mmap_w],
00859             (unsigned long int) calls[idx_mmap_a],
00860             (unsigned long long int) total[idx_mmap_a],
00861             failed[idx_mmap_a] ? "\e[01;41m" : "",
00862             (unsigned long int) failed[idx_mmap_a],
00863             (unsigned long int) calls[idx_mremap],
00864             (unsigned long long int) total[idx_mremap],
00865             failed[idx_mremap] ? "\e[01;41m" : "",
00866             (unsigned long int) failed[idx_mremap],
00867             (unsigned long int) inplace_mremap,
00868             (unsigned long int) decreasing_mremap,
00869             (unsigned long int) calls[idx_munmap],
00870             (unsigned long long int) total[idx_munmap],
00871             failed[idx_munmap] ? "\e[01;41m" : "",
00872             (unsigned long int) failed[idx_munmap]);
00873 
00874   /* Write out a histoogram of the sizes of the allocations.  */
00875   fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
00876 
00877   /* Determine the maximum of all calls for each size range.  */
00878   maxcalls = large;
00879   for (cnt = 0; cnt < 65536; cnt += 16)
00880     if (histogram[cnt / 16] > maxcalls)
00881       maxcalls = histogram[cnt / 16];
00882 
00883   for (cnt = 0; cnt < 65536; cnt += 16)
00884     /* Only write out the nonzero entries.  */
00885     if (histogram[cnt / 16] != 0)
00886       {
00887        percent = (histogram[cnt / 16] * 100) / calls_total;
00888        fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15,
00889                (unsigned long int) histogram[cnt / 16]);
00890        if (percent == 0)
00891          fputs (" <1% \e[41;37m", stderr);
00892        else
00893          fprintf (stderr, "%3d%% \e[41;37m", percent);
00894 
00895        /* Draw a bar with a length corresponding to the current
00896            percentage.  */
00897        percent = (histogram[cnt / 16] * 50) / maxcalls;
00898        while (percent-- > 0)
00899          fputc ('=', stderr);
00900         fputs ("\e[0;0m\n", stderr);
00901       }
00902 
00903   if (large != 0)
00904     {
00905       percent = (large * 100) / calls_total;
00906       fprintf (stderr, "   large   %12lu ", (unsigned long int) large);
00907       if (percent == 0)
00908        fputs (" <1% \e[41;37m", stderr);
00909       else
00910        fprintf (stderr, "%3d%% \e[41;37m", percent);
00911       percent = (large * 50) / maxcalls;
00912       while (percent-- > 0)
00913         fputc ('=', stderr);
00914       fputs ("\e[0;0m\n", stderr);
00915     }
00916 
00917   /* Any following malloc/free etc. calls should generate statistics again,
00918      because otherwise freeing something that has been malloced before
00919      this destructor (including struct header in front of it) wouldn't
00920      be properly freed.  */
00921   not_me = false;
00922 }