(linenum→info "unix/slp.c:2238")

glibc/2.7/malloc/memusage.c

    1: /* Profile heap and stack memory usage of running program.
    2:    Copyright (C) 1998-2002, 2004, 2005, 2006 Free Software Foundation, Inc.
    3:    This file is part of the GNU C Library.
    4:    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
    5: 
    6:    The GNU C Library is free software; you can redistribute it and/or
    7:    modify it under the terms of the GNU Lesser General Public
    8:    License as published by the Free Software Foundation; either
    9:    version 2.1 of the License, or (at your option) any later version.
   10: 
   11:    The GNU C Library is distributed in the hope that it will be useful,
   12:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   13:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14:    Lesser General Public License for more details.
   15: 
   16:    You should have received a copy of the GNU Lesser General Public
   17:    License along with the GNU C Library; if not, write to the Free
   18:    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   19:    02111-1307 USA.  */
   20: 
   21: #include <atomic.h>
   22: #include <dlfcn.h>
   23: #include <errno.h>
   24: #include <fcntl.h>
   25: #include <inttypes.h>
   26: #include <signal.h>
   27: #include <stdarg.h>
   28: #include <stdbool.h>
   29: #include <stdio.h>
   30: #include <stdlib.h>
   31: #include <string.h>
   32: #include <unistd.h>
   33: #include <sys/mman.h>
   34: #include <sys/time.h>
   35: 
   36: #include <memusage.h>
   37: 
   38: /* Pointer to the real functions.  These are determined used `dlsym'
   39:    when really needed.  */
   40: static void *(*mallocp) (size_t);
   41: static void *(*reallocp) (void *, size_t);
   42: static void *(*callocp) (size_t, size_t);
   43: static void (*freep) (void *);
   44: 
   45: static void *(*mmapp) (void *, size_t, int, int, int, off_t);
   46: static void *(*mmap64p) (void *, size_t, int, int, int, off64_t);
   47: static int (*munmapp) (void *, size_t);
   48: static void *(*mremapp) (void *, size_t, size_t, int, void *);
   49: 
   50: enum
   51: {
   52:   idx_malloc = 0,
   53:   idx_realloc,
   54:   idx_calloc,
   55:   idx_free,
   56:   idx_mmap_r,
   57:   idx_mmap_w,
   58:   idx_mmap_a,
   59:   idx_mremap,
   60:   idx_munmap,
   61:   idx_last
   62: };
   63: 
   64: 
   65: struct header
   66: {
   67:   size_t length;
   68:   size_t magic;
   69: };
   70: 
   71: #define MAGIC 0xfeedbeaf
   72: 
   73: 
   74: static memusage_cntr_t calls[idx_last];
   75: static memusage_cntr_t failed[idx_last];
   76: static memusage_size_t total[idx_last];
   77: static memusage_size_t grand_total;
   78: static memusage_cntr_t histogram[65536 / 16];
   79: static memusage_cntr_t large;
   80: static memusage_cntr_t calls_total;
   81: static memusage_cntr_t inplace;
   82: static memusage_cntr_t decreasing;
   83: static memusage_cntr_t realloc_free;
   84: static memusage_cntr_t inplace_mremap;
   85: static memusage_cntr_t decreasing_mremap;
   86: static memusage_size_t current_heap;
   87: static memusage_size_t peak_use[3];
   88: static __thread uintptr_t start_sp;
   89: 
   90: /* A few macros to make the source more readable.  */
   91: #define peak_heap       peak_use[0]
   92: #define peak_stack      peak_use[1]
   93: #define peak_total      peak_use[2]
   94: 
   95: #define DEFAULT_BUFFER_SIZE     1024
   96: static size_t buffer_size;
   97: 
   98: static int fd = -1;
   99: 
  100: static bool not_me;
  101: static int initialized;
  102: static bool trace_mmap;
  103: extern const char *__progname;
  104: 
  105: struct entry
  106: {
  107:   uint64_t heap;
  108:   uint64_t stack;
  109:   uint32_t time_low;
  110:   uint32_t time_high;
  111: };
  112: 
  113: static struct entry buffer[2 * DEFAULT_BUFFER_SIZE];
  114: static uatomic32_t buffer_cnt;
  115: static struct entry first;
  116: 
  117: 
  118: /* Update the global data after a successful function call.  */
  119: static void
  120: update_data (struct header *result, size_t len, size_t old_len)
  121: {
  122:   if (result != NULL)
  123:     {
  124:       /* Record the information we need and mark the block using a
  125:          magic number.  */
  126:       result->length = len;
  127:       result->magic = MAGIC;
  128:     }
  129: 
  130:   /* Compute current heap usage and compare it with the maximum value.  */
  131:   memusage_size_t heap
  132:     = catomic_exchange_and_add (&current_heap, len - old_len) + len - old_len;
  133:   catomic_max (&peak_heap, heap);
  134: 
  135:   /* Compute current stack usage and compare it with the maximum
  136:      value.  The base stack pointer might not be set if this is not
  137:      the main thread and it is the first call to any of these
  138:      functions.  */
  139:   if (__builtin_expect (!start_sp, 0))
  140:     start_sp = GETSP ();
  141: 
  142:   uintptr_t sp = GETSP ();
  143: #ifdef STACK_GROWS_UPWARD
  144:   /* This can happen in threads where we didn't catch the thread's
  145:      stack early enough.  */
  146:   if (__builtin_expect (sp < start_sp, 0))
  147:     start_sp = sp;
  148:   size_t current_stack = sp - start_sp;
  149: #else
  150:   /* This can happen in threads where we didn't catch the thread's
  151:      stack early enough.  */
  152:   if (__builtin_expect (sp > start_sp, 0))
  153:     start_sp = sp;
  154:   size_t current_stack = start_sp - sp;
  155: #endif
  156:   catomic_max (&peak_stack, current_stack);
  157: 
  158:   /* Add up heap and stack usage and compare it with the maximum value.  */
  159:   catomic_max (&peak_total, heap + current_stack);
  160: 
  161:   /* Store the value only if we are writing to a file.  */
  162:   if (fd != -1)
  163:     {
  164:       uatomic32_t idx = catomic_exchange_and_add (&buffer_cnt, 1);
  165:       if (idx >= 2 * buffer_size)
  166:         {
  167:           /* We try to reset the counter to the correct range.  If
  168:              this fails because of another thread increasing the
  169:              counter it does not matter since that thread will take
  170:              care of the correction.  */
  171:           unsigned int reset = idx - 2 * buffer_size;
  172:           catomic_compare_and_exchange_val_acq (&buffer_size, reset, idx);
  173:           idx = reset;
  174:         }
  175: 
  176:       buffer[idx].heap = current_heap;
  177:       buffer[idx].stack = current_stack;
  178:       GETTIME (buffer[idx].time_low, buffer[idx].time_high);
  179: 
  180:       /* Write out buffer if it is full.  */
  181:       if (idx + 1 == buffer_size)
  182:         write (fd, buffer, buffer_size * sizeof (struct entry));
  183:       else if (idx + 1 == 2 * buffer_size)
  184:         write (fd, &buffer[buffer_size], buffer_size * sizeof (struct entry));
  185:     }
  186: }
  187: 
  188: 
  189: /* Interrupt handler.  */
  190: static void
  191: int_handler (int signo)
  192: {
  193:   /* Nothing gets allocated.  Just record the stack pointer position.  */
  194:   update_data (NULL, 0, 0);
  195: }
  196: 
  197: 
  198: /* Find out whether this is the program we are supposed to profile.
  199:    For this the name in the variable `__progname' must match the one
  200:    given in the environment variable MEMUSAGE_PROG_NAME.  If the variable
  201:    is not present every program assumes it should be profiling.
  202: 
  203:    If this is the program open a file descriptor to the output file.
  204:    We will write to it whenever the buffer overflows.  The name of the
  205:    output file is determined by the environment variable MEMUSAGE_OUTPUT.
  206: 
  207:    If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical
  208:    value determines the size of the internal buffer.  The number gives
  209:    the number of elements in the buffer.  By setting the number to one
  210:    one effectively selects unbuffered operation.
  211: 
  212:    If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
  213:    which at the highest possible frequency records the stack pointer.  */
  214: static void
  215: me (void)
  216: {
  217:   const char *env = getenv ("MEMUSAGE_PROG_NAME");
  218:   size_t prog_len = strlen (__progname);
  219: 
  220:   initialized = -1;
  221:   mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
  222:   reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
  223:   callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
  224:   freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
  225: 
  226:   mmapp = (void *(*) (void *, size_t, int, int, int, off_t)) dlsym (RTLD_NEXT,
  227:                                                                     "mmap");
  228:   mmap64p =
  229:     (void *(*) (void *, size_t, int, int, int, off64_t)) dlsym (RTLD_NEXT,
  230:                                                                 "mmap64");
  231:   mremapp = (void *(*) (void *, size_t, size_t, int, void *)) dlsym (RTLD_NEXT,
  232:                                                                      "mremap");
  233:   munmapp = (int (*) (void *, size_t)) dlsym (RTLD_NEXT, "munmap");
  234:   initialized = 1;
  235: 
  236:   if (env != NULL)
  237:     {
  238:       /* Check for program name.  */
  239:       size_t len = strlen (env);
  240:       if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
  241:           || (prog_len != len && __progname[prog_len - len - 1] != '/'))
  242:         not_me = true;
  243:     }
  244: 
  245:   /* Only open the file if it's really us.  */
  246:   if (!not_me && fd == -1)
  247:     {
  248:       const char *outname;
  249: 
  250:       if (!start_sp)
  251:         start_sp = GETSP ();
  252: 
  253:       outname = getenv ("MEMUSAGE_OUTPUT");
  254:       if (outname != NULL && outname[0] != '\0'
  255:           && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
  256:         {
  257:           fd = creat64 (outname, 0666);
  258: 
  259:           if (fd == -1)
  260:             /* Don't do anything in future calls if we cannot write to
  261:                the output file.  */
  262:             not_me = true;
  263:           else
  264:             {
  265:               /* Write the first entry.  */
  266:               first.heap = 0;
  267:               first.stack = 0;
  268:               GETTIME (first.time_low, first.time_high);
  269:               /* Write it two times since we need the starting and end time. */
  270:               write (fd, &first, sizeof (first));
  271:               write (fd, &first, sizeof (first));
  272: 
  273:               /* Determine the buffer size.  We use the default if the
  274:                  environment variable is not present.  */
  275:               buffer_size = DEFAULT_BUFFER_SIZE;
  276:               if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
  277:                 {
  278:                   buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
  279:                   if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
  280:                     buffer_size = DEFAULT_BUFFER_SIZE;
  281:                 }
  282: 
  283:               /* Possibly enable timer-based stack pointer retrieval.  */
  284:               if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
  285:                 {
  286:                   struct sigaction act;
  287: 
  288:                   act.sa_handler = (sighandler_t) &int_handler;
  289:                   act.sa_flags = SA_RESTART;
  290:                   sigfillset (&act.sa_mask);
  291: 
  292:                   if (sigaction (SIGPROF, &act, NULL) >= 0)
  293:                     {
  294:                       struct itimerval timer;
  295: 
  296:                       timer.it_value.tv_sec = 0;
  297:                       timer.it_value.tv_usec = 1;
  298:                       timer.it_interval = timer.it_value;
  299:                       setitimer (ITIMER_PROF, &timer, NULL);
  300:                     }
  301:                 }
  302:             }
  303:         }
  304: 
  305:       if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL)
  306:         trace_mmap = true;
  307:     }
  308: }
  309: 
  310: 
  311: /* Record the initial stack position.  */
  312: static void
  313: __attribute__ ((constructor))
  314: init (void)
  315: {
  316:   start_sp = GETSP ();
  317:   if (! initialized)
  318:     me ();
  319: }
  320: 
  321: 
  322: /* `malloc' replacement.  We keep track of the memory usage if this is the
  323:    correct program.  */
  324: void *
  325: malloc (size_t len)
  326: {
  327:   struct header *result = NULL;
  328: 
  329:   /* Determine real implementation if not already happened.  */
  330:   if (__builtin_expect (initialized <= 0, 0))
  331:     {
  332:       if (initialized == -1)
  333:         return NULL;
  334:       me ();
  335:     }
  336: 
  337:   /* If this is not the correct program just use the normal function.  */
  338:   if (not_me)
  339:     return (*mallocp) (len);
  340: 
  341:   /* Keep track of number of calls.  */
  342:   catomic_increment (&calls[idx_malloc]);
  343:   /* Keep track of total memory consumption for `malloc'.  */
  344:   catomic_add (&total[idx_malloc], len);
  345:   /* Keep track of total memory requirement.  */
  346:   catomic_add (&grand_total, len);
  347:   /* Remember the size of the request.  */
  348:   if (len < 65536)
  349:     catomic_increment (&histogram[len / 16]);
  350:   else
  351:     catomic_increment (&large);
  352:   /* Total number of calls of any of the functions.  */
  353:   catomic_increment (&calls_total);
  354: 
  355:   /* Do the real work.  */
  356:   result = (struct header *) (*mallocp) (len + sizeof (struct header));
  357:   if (result == NULL)
  358:     {
  359:       catomic_increment (&failed[idx_malloc]);
  360:       return NULL;
  361:     }
  362: 
  363:   /* Update the allocation data and write out the records if necessary.  */
  364:   update_data (result, len, 0);
  365: 
  366:   /* Return the pointer to the user buffer.  */
  367:   return (void *) (result + 1);
  368: }
  369: 
  370: 
  371: /* `realloc' replacement.  We keep track of the memory usage if this is the
  372:    correct program.  */
  373: void *
  374: realloc (void *old, size_t len)
  375: {
  376:   struct header *result = NULL;
  377:   struct header *real;
  378:   size_t old_len;
  379: 
  380:   /* Determine real implementation if not already happened.  */
  381:   if (__builtin_expect (initialized <= 0, 0))
  382:     {
  383:       if (initialized == -1)
  384:         return NULL;
  385:       me ();
  386:     }
  387: 
  388:   /* If this is not the correct program just use the normal function.  */
  389:   if (not_me)
  390:     return (*reallocp) (old, len);
  391: 
  392:   if (old == NULL)
  393:     {
  394:       /* This is really a `malloc' call.  */
  395:       real = NULL;
  396:       old_len = 0;
  397:     }
  398:   else
  399:     {
  400:       real = ((struct header *) old) - 1;
  401:       if (real->magic != MAGIC)
  402:         /* This is no memory allocated here.  */
  403:         return (*reallocp) (old, len);
  404:       old_len = real->length;
  405:     }
  406: 
  407:   /* Keep track of number of calls.  */
  408:   catomic_increment (&calls[idx_realloc]);
  409:   if (len > old_len)
  410:     {
  411:       /* Keep track of total memory consumption for `realloc'.  */
  412:       catomic_add (&total[idx_realloc], len - old_len);
  413:       /* Keep track of total memory requirement.  */
  414:       catomic_add (&grand_total, len - old_len);
  415:     }
  416: 
  417:   if (len == 0 && old != NULL)
  418:     {
  419:       /* Special case.  */
  420:       catomic_increment (&realloc_free);
  421:       /* Keep track of total memory freed using `free'.  */
  422:       catomic_add (&total[idx_free], real->length);
  423: 
  424:       /* Update the allocation data and write out the records if necessary.  */
  425:       update_data (NULL, 0, old_len);
  426: 
  427:       /* Do the real work.  */
  428:       (*freep) (real);
  429: 
  430:       return NULL;
  431:     }
  432: 
  433:   /* Remember the size of the request.  */
  434:   if (len < 65536)
  435:     catomic_increment (&histogram[len / 16]);
  436:   else
  437:     catomic_increment (&large);
  438:   /* Total number of calls of any of the functions.  */
  439:   catomic_increment (&calls_total);
  440: 
  441:   /* Do the real work.  */
  442:   result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
  443:   if (result == NULL)
  444:     {
  445:       catomic_increment (&failed[idx_realloc]);
  446:       return NULL;
  447:     }
  448: 
  449:   /* Record whether the reduction/increase happened in place.  */
  450:   if (real == result)
  451:     catomic_increment (&inplace);
  452:   /* Was the buffer increased?  */
  453:   if (old_len > len)
  454:     catomic_increment (&decreasing);
  455: 
  456:   /* Update the allocation data and write out the records if necessary.  */
  457:   update_data (result, len, old_len);
  458: 
  459:   /* Return the pointer to the user buffer.  */
  460:   return (void *) (result + 1);
  461: }
  462: 
  463: 
  464: /* `calloc' replacement.  We keep track of the memory usage if this is the
  465:    correct program.  */
  466: void *
  467: calloc (size_t n, size_t len)
  468: {
  469:   struct header *result;
  470:   size_t size = n * len;
  471: 
  472:   /* Determine real implementation if not already happened.  */
  473:   if (__builtin_expect (initialized <= 0, 0))
  474:     {
  475:       if (initialized == -1)
  476:         return NULL;
  477:       me ();
  478:     }
  479: 
  480:   /* If this is not the correct program just use the normal function.  */
  481:   if (not_me)
  482:     return (*callocp) (n, len);
  483: 
  484:   /* Keep track of number of calls.  */
  485:   catomic_increment (&calls[idx_calloc]);
  486:   /* Keep track of total memory consumption for `calloc'.  */
  487:   catomic_add (&total[idx_calloc], size);
  488:   /* Keep track of total memory requirement.  */
  489:   catomic_add (&grand_total, size);
  490:   /* Remember the size of the request.  */
  491:   if (size < 65536)
  492:     catomic_increment (&histogram[size / 16]);
  493:   else
  494:     catomic_increment (&large);
  495:   /* Total number of calls of any of the functions.  */
  496:   ++calls_total;
  497: 
  498:   /* Do the real work.  */
  499:   result = (struct header *) (*mallocp) (size + sizeof (struct header));
  500:   if (result == NULL)
  501:     {
  502:       catomic_increment (&failed[idx_calloc]);
  503:       return NULL;
  504:     }
  505: 
  506:   /* Update the allocation data and write out the records if necessary.  */
  507:   update_data (result, size, 0);
  508: 
  509:   /* Do what `calloc' would have done and return the buffer to the caller.  */
  510:   return memset (result + 1, '\0', size);
  511: }
  512: 
  513: 
  514: /* `free' replacement.  We keep track of the memory usage if this is the
  515:    correct program.  */
  516: void
  517: free (void *ptr)
  518: {
  519:   struct header *real;
  520: 
  521:   /* Determine real implementation if not already happened.  */
  522:   if (__builtin_expect (initialized <= 0, 0))
  523:     {
  524:       if (initialized == -1)
  525:         return;
  526:       me ();
  527:     }
  528: 
  529:   /* If this is not the correct program just use the normal function.  */
  530:   if (not_me)
  531:     {
  532:       (*freep) (ptr);
  533:       return;
  534:     }
  535: 
  536:   /* `free (NULL)' has no effect.  */
  537:   if (ptr == NULL)
  538:     {
  539:       catomic_increment (&calls[idx_free]);
  540:       return;
  541:     }
  542: 
  543:   /* Determine the pointer to the header.  */
  544:   real = ((struct header *) ptr) - 1;
  545:   if (real->magic != MAGIC)
  546:     {
  547:       /* This block wasn't allocated here.  */
  548:       (*freep) (ptr);
  549:       return;
  550:     }
  551: 
  552:   /* Keep track of number of calls.  */
  553:   catomic_increment (&calls[idx_free]);
  554:   /* Keep track of total memory freed using `free'.  */
  555:   catomic_add (&total[idx_free], real->length);
  556: 
  557:   /* Update the allocation data and write out the records if necessary.  */
  558:   update_data (NULL, 0, real->length);
  559: 
  560:   /* Do the real work.  */
  561:   (*freep) (real);
  562: }
  563: 
  564: 
  565: /* `mmap' replacement.  We do not have to keep track of the sizesince
  566:    `munmap' will get it as a parameter.  */
  567: void *
  568: mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
  569: {
  570:   void *result = NULL;
  571: 
  572:   /* Determine real implementation if not already happened.  */
  573:   if (__builtin_expect (initialized <= 0, 0))
  574:     {
  575:       if (initialized == -1)
  576:         return NULL;
  577:       me ();
  578:     }
  579: 
  580:   /* Always get a block.  We don't need extra memory.  */
  581:   result = (*mmapp) (start, len, prot, flags, fd, offset);
  582: 
  583:   if (!not_me && trace_mmap)
  584:     {
  585:       int idx = (flags & MAP_ANON
  586:                  ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
  587: 
  588:       /* Keep track of number of calls.  */
  589:       catomic_increment (&calls[idx]);
  590:       /* Keep track of total memory consumption for `malloc'.  */
  591:       catomic_add (&total[idx], len);
  592: