Blame malloc/memusage.c

Packit 6c4009
/* Profile heap and stack memory usage of running program.
Packit 6c4009
   Copyright (C) 1998-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <atomic.h>
Packit 6c4009
#include <dlfcn.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <inttypes.h>
Packit 6c4009
#include <signal.h>
Packit 6c4009
#include <stdarg.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sys/time.h>
Packit 6c4009
Packit 6c4009
#include <memusage.h>
Packit 6c4009
Packit 6c4009
/* Pointer to the real functions.  These are determined used `dlsym'
Packit 6c4009
   when really needed.  */
Packit 6c4009
static void *(*mallocp)(size_t);
Packit 6c4009
static void *(*reallocp) (void *, size_t);
Packit 6c4009
static void *(*callocp) (size_t, size_t);
Packit 6c4009
static void (*freep) (void *);
Packit 6c4009
Packit 6c4009
static void *(*mmapp) (void *, size_t, int, int, int, off_t);
Packit 6c4009
static void *(*mmap64p) (void *, size_t, int, int, int, off64_t);
Packit 6c4009
static int (*munmapp) (void *, size_t);
Packit 6c4009
static void *(*mremapp) (void *, size_t, size_t, int, void *);
Packit 6c4009
Packit 6c4009
enum
Packit 6c4009
{
Packit 6c4009
  idx_malloc = 0,
Packit 6c4009
  idx_realloc,
Packit 6c4009
  idx_calloc,
Packit 6c4009
  idx_free,
Packit 6c4009
  idx_mmap_r,
Packit 6c4009
  idx_mmap_w,
Packit 6c4009
  idx_mmap_a,
Packit 6c4009
  idx_mremap,
Packit 6c4009
  idx_munmap,
Packit 6c4009
  idx_last
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
struct header
Packit 6c4009
{
Packit 6c4009
  size_t length;
Packit 6c4009
  size_t magic;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
#define MAGIC 0xfeedbeaf
Packit 6c4009
Packit 6c4009
Packit 6c4009
static memusage_cntr_t calls[idx_last];
Packit 6c4009
static memusage_cntr_t failed[idx_last];
Packit 6c4009
static memusage_size_t total[idx_last];
Packit 6c4009
static memusage_size_t grand_total;
Packit 6c4009
static memusage_cntr_t histogram[65536 / 16];
Packit 6c4009
static memusage_cntr_t large;
Packit 6c4009
static memusage_cntr_t calls_total;
Packit 6c4009
static memusage_cntr_t inplace;
Packit 6c4009
static memusage_cntr_t decreasing;
Packit 6c4009
static memusage_cntr_t realloc_free;
Packit 6c4009
static memusage_cntr_t inplace_mremap;
Packit 6c4009
static memusage_cntr_t decreasing_mremap;
Packit 6c4009
static memusage_size_t current_heap;
Packit 6c4009
static memusage_size_t peak_use[3];
Packit 6c4009
static __thread uintptr_t start_sp;
Packit 6c4009
Packit 6c4009
/* A few macros to make the source more readable.  */
Packit 6c4009
#define peak_heap       peak_use[0]
Packit 6c4009
#define peak_stack      peak_use[1]
Packit 6c4009
#define peak_total      peak_use[2]
Packit 6c4009
Packit 6c4009
#define DEFAULT_BUFFER_SIZE     32768
Packit 6c4009
static size_t buffer_size;
Packit 6c4009
Packit 6c4009
static int fd = -1;
Packit 6c4009
Packit 6c4009
static bool not_me;
Packit 6c4009
static int initialized;
Packit 6c4009
static bool trace_mmap;
Packit 6c4009
extern const char *__progname;
Packit 6c4009
Packit 6c4009
struct entry
Packit 6c4009
{
Packit 6c4009
  uint64_t heap;
Packit 6c4009
  uint64_t stack;
Packit 6c4009
  uint32_t time_low;
Packit 6c4009
  uint32_t time_high;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static struct entry buffer[2 * DEFAULT_BUFFER_SIZE];
Packit 6c4009
static uatomic32_t buffer_cnt;
Packit 6c4009
static struct entry first;
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Update the global data after a successful function call.  */
Packit 6c4009
static void
Packit 6c4009
update_data (struct header *result, size_t len, size_t old_len)
Packit 6c4009
{
Packit 6c4009
  if (result != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Record the information we need and mark the block using a
Packit 6c4009
         magic number.  */
Packit 6c4009
      result->length = len;
Packit 6c4009
      result->magic = MAGIC;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Compute current heap usage and compare it with the maximum value.  */
Packit 6c4009
  memusage_size_t heap
Packit 6c4009
    = catomic_exchange_and_add (&current_heap, len - old_len) + len - old_len;
Packit 6c4009
  catomic_max (&peak_heap, heap);
Packit 6c4009
Packit 6c4009
  /* Compute current stack usage and compare it with the maximum
Packit 6c4009
     value.  The base stack pointer might not be set if this is not
Packit 6c4009
     the main thread and it is the first call to any of these
Packit 6c4009
     functions.  */
Packit 6c4009
  if (__glibc_unlikely (!start_sp))
Packit 6c4009
    start_sp = GETSP ();
Packit 6c4009
Packit 6c4009
  uintptr_t sp = GETSP ();
Packit 6c4009
#ifdef STACK_GROWS_UPWARD
Packit 6c4009
  /* This can happen in threads where we didn't catch the thread's
Packit 6c4009
     stack early enough.  */
Packit 6c4009
  if (__glibc_unlikely (sp < start_sp))
Packit 6c4009
    start_sp = sp;
Packit 6c4009
  size_t current_stack = sp - start_sp;
Packit 6c4009
#else
Packit 6c4009
  /* This can happen in threads where we didn't catch the thread's
Packit 6c4009
     stack early enough.  */
Packit 6c4009
  if (__glibc_unlikely (sp > start_sp))
Packit 6c4009
    start_sp = sp;
Packit 6c4009
  size_t current_stack = start_sp - sp;
Packit 6c4009
#endif
Packit 6c4009
  catomic_max (&peak_stack, current_stack);
Packit 6c4009
Packit 6c4009
  /* Add up heap and stack usage and compare it with the maximum value.  */
Packit 6c4009
  catomic_max (&peak_total, heap + current_stack);
Packit 6c4009
Packit 6c4009
  /* Store the value only if we are writing to a file.  */
Packit 6c4009
  if (fd != -1)
Packit 6c4009
    {
Packit 6c4009
      uatomic32_t idx = catomic_exchange_and_add (&buffer_cnt, 1);
Packit 6c4009
      if (idx + 1 >= 2 * buffer_size)
Packit 6c4009
        {
Packit 6c4009
          /* We try to reset the counter to the correct range.  If
Packit 6c4009
             this fails because of another thread increasing the
Packit 6c4009
             counter it does not matter since that thread will take
Packit 6c4009
             care of the correction.  */
Packit 6c4009
          uatomic32_t reset = (idx + 1) % (2 * buffer_size);
Packit 6c4009
          catomic_compare_and_exchange_val_acq (&buffer_cnt, reset, idx + 1);
Packit 6c4009
          if (idx >= 2 * buffer_size)
Packit 6c4009
            idx = reset - 1;
Packit 6c4009
        }
Packit 6c4009
      assert (idx < 2 * DEFAULT_BUFFER_SIZE);
Packit 6c4009
Packit 6c4009
      buffer[idx].heap = current_heap;
Packit 6c4009
      buffer[idx].stack = current_stack;
Packit 6c4009
      GETTIME (buffer[idx].time_low, buffer[idx].time_high);
Packit 6c4009
Packit 6c4009
      /* Write out buffer if it is full.  */
Packit 6c4009
      if (idx + 1 == buffer_size)
Packit 6c4009
        write (fd, buffer, buffer_size * sizeof (struct entry));
Packit 6c4009
      else if (idx + 1 == 2 * buffer_size)
Packit 6c4009
        write (fd, &buffer[buffer_size], buffer_size * sizeof (struct entry));
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Interrupt handler.  */
Packit 6c4009
static void
Packit 6c4009
int_handler (int signo)
Packit 6c4009
{
Packit 6c4009
  /* Nothing gets allocated.  Just record the stack pointer position.  */
Packit 6c4009
  update_data (NULL, 0, 0);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Find out whether this is the program we are supposed to profile.
Packit 6c4009
   For this the name in the variable `__progname' must match the one
Packit 6c4009
   given in the environment variable MEMUSAGE_PROG_NAME.  If the variable
Packit 6c4009
   is not present every program assumes it should be profiling.
Packit 6c4009
Packit 6c4009
   If this is the program open a file descriptor to the output file.
Packit 6c4009
   We will write to it whenever the buffer overflows.  The name of the
Packit 6c4009
   output file is determined by the environment variable MEMUSAGE_OUTPUT.
Packit 6c4009
Packit 6c4009
   If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical
Packit 6c4009
   value determines the size of the internal buffer.  The number gives
Packit 6c4009
   the number of elements in the buffer.  By setting the number to one
Packit 6c4009
   one effectively selects unbuffered operation.
Packit 6c4009
Packit 6c4009
   If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
Packit 6c4009
   which at the highest possible frequency records the stack pointer.  */
Packit 6c4009
static void
Packit 6c4009
me (void)
Packit 6c4009
{
Packit 6c4009
  const char *env = getenv ("MEMUSAGE_PROG_NAME");
Packit 6c4009
  size_t prog_len = strlen (__progname);
Packit 6c4009
Packit 6c4009
  initialized = -1;
Packit 6c4009
  mallocp = (void *(*)(size_t))dlsym (RTLD_NEXT, "malloc");
Packit 6c4009
  reallocp = (void *(*)(void *, size_t))dlsym (RTLD_NEXT, "realloc");
Packit 6c4009
  callocp = (void *(*)(size_t, size_t))dlsym (RTLD_NEXT, "calloc");
Packit 6c4009
  freep = (void (*)(void *))dlsym (RTLD_NEXT, "free");
Packit 6c4009
Packit 6c4009
  mmapp = (void *(*)(void *, size_t, int, int, int, off_t))dlsym (RTLD_NEXT,
Packit 6c4009
                                                                  "mmap");
Packit 6c4009
  mmap64p =
Packit 6c4009
    (void *(*)(void *, size_t, int, int, int, off64_t))dlsym (RTLD_NEXT,
Packit 6c4009
                                                              "mmap64");
Packit 6c4009
  mremapp = (void *(*)(void *, size_t, size_t, int, void *))dlsym (RTLD_NEXT,
Packit 6c4009
                                                                   "mremap");
Packit 6c4009
  munmapp = (int (*)(void *, size_t))dlsym (RTLD_NEXT, "munmap");
Packit 6c4009
  initialized = 1;
Packit 6c4009
Packit 6c4009
  if (env != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Check for program name.  */
Packit 6c4009
      size_t len = strlen (env);
Packit 6c4009
      if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
Packit 6c4009
          || (prog_len != len && __progname[prog_len - len - 1] != '/'))
Packit 6c4009
        not_me = true;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Only open the file if it's really us.  */
Packit 6c4009
  if (!not_me && fd == -1)
Packit 6c4009
    {
Packit 6c4009
      const char *outname;
Packit 6c4009
Packit 6c4009
      if (!start_sp)
Packit 6c4009
        start_sp = GETSP ();
Packit 6c4009
Packit 6c4009
      outname = getenv ("MEMUSAGE_OUTPUT");
Packit 6c4009
      if (outname != NULL && outname[0] != '\0'
Packit 6c4009
          && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
Packit 6c4009
        {
Packit 6c4009
          fd = creat64 (outname, 0666);
Packit 6c4009
Packit 6c4009
          if (fd == -1)
Packit 6c4009
            /* Don't do anything in future calls if we cannot write to
Packit 6c4009
               the output file.  */
Packit 6c4009
            not_me = true;
Packit 6c4009
          else
Packit 6c4009
            {
Packit 6c4009
              /* Write the first entry.  */
Packit 6c4009
              first.heap = 0;
Packit 6c4009
              first.stack = 0;
Packit 6c4009
              GETTIME (first.time_low, first.time_high);
Packit 6c4009
              /* Write it two times since we need the starting and end time. */
Packit 6c4009
              write (fd, &first, sizeof (first));
Packit 6c4009
              write (fd, &first, sizeof (first));
Packit 6c4009
Packit 6c4009
              /* Determine the buffer size.  We use the default if the
Packit 6c4009
                 environment variable is not present.  */
Packit 6c4009
              buffer_size = DEFAULT_BUFFER_SIZE;
Packit 6c4009
              const char *str_buffer_size = getenv ("MEMUSAGE_BUFFER_SIZE");
Packit 6c4009
              if (str_buffer_size != NULL)
Packit 6c4009
                {
Packit 6c4009
                  buffer_size = atoi (str_buffer_size);
Packit 6c4009
                  if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
Packit 6c4009
                    buffer_size = DEFAULT_BUFFER_SIZE;
Packit 6c4009
                }
Packit 6c4009
Packit 6c4009
              /* Possibly enable timer-based stack pointer retrieval.  */
Packit 6c4009
              if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
Packit 6c4009
                {
Packit 6c4009
                  struct sigaction act;
Packit 6c4009
Packit 6c4009
                  act.sa_handler = (sighandler_t) &int_handler;
Packit 6c4009
                  act.sa_flags = SA_RESTART;
Packit 6c4009
                  sigfillset (&act.sa_mask);
Packit 6c4009
Packit 6c4009
                  if (sigaction (SIGPROF, &act, NULL) >= 0)
Packit 6c4009
                    {
Packit 6c4009
                      struct itimerval timer;
Packit 6c4009
Packit 6c4009
                      timer.it_value.tv_sec = 0;
Packit 6c4009
                      timer.it_value.tv_usec = 1;
Packit 6c4009
                      timer.it_interval = timer.it_value;
Packit 6c4009
                      setitimer (ITIMER_PROF, &timer, NULL);
Packit 6c4009
                    }
Packit 6c4009
                }
Packit 6c4009
            }
Packit 6c4009
        }
Packit 6c4009
Packit 6c4009
      if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL)
Packit 6c4009
        trace_mmap = true;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Record the initial stack position.  */
Packit 6c4009
static void
Packit 6c4009
__attribute__ ((constructor))
Packit 6c4009
init (void)
Packit 6c4009
{
Packit 6c4009
  start_sp = GETSP ();
Packit 6c4009
  if (!initialized)
Packit 6c4009
    me ();
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* `malloc' replacement.  We keep track of the memory usage if this is the
Packit 6c4009
   correct program.  */
Packit 6c4009
void *
Packit 6c4009
malloc (size_t len)
Packit 6c4009
{
Packit 6c4009
  struct header *result = NULL;
Packit 6c4009
Packit 6c4009
  /* Determine real implementation if not already happened.  */
Packit 6c4009
  if (__glibc_unlikely (initialized <= 0))
Packit 6c4009
    {
Packit 6c4009
      if (initialized == -1)
Packit 6c4009
        return NULL;
Packit 6c4009
Packit 6c4009
      me ();
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If this is not the correct program just use the normal function.  */
Packit 6c4009
  if (not_me)
Packit 6c4009
    return (*mallocp)(len);
Packit 6c4009
Packit 6c4009
  /* Keep track of number of calls.  */
Packit 6c4009
  catomic_increment (&calls[idx_malloc]);
Packit 6c4009
  /* Keep track of total memory consumption for `malloc'.  */
Packit 6c4009
  catomic_add (&total[idx_malloc], len);
Packit 6c4009
  /* Keep track of total memory requirement.  */
Packit 6c4009
  catomic_add (&grand_total, len);
Packit 6c4009
  /* Remember the size of the request.  */
Packit 6c4009
  if (len < 65536)
Packit 6c4009
    catomic_increment (&histogram[len / 16]);
Packit 6c4009
  else
Packit 6c4009
    catomic_increment (&large);
Packit 6c4009
  /* Total number of calls of any of the functions.  */
Packit 6c4009
  catomic_increment (&calls_total);
Packit 6c4009
Packit 6c4009
  /* Do the real work.  */
Packit 6c4009
  result = (struct header *) (*mallocp)(len + sizeof (struct header));
Packit 6c4009
  if (result == NULL)
Packit 6c4009
    {
Packit 6c4009
      catomic_increment (&failed[idx_malloc]);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Update the allocation data and write out the records if necessary.  */
Packit 6c4009
  update_data (result, len, 0);
Packit 6c4009
Packit 6c4009
  /* Return the pointer to the user buffer.  */
Packit 6c4009
  return (void *) (result + 1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* `realloc' replacement.  We keep track of the memory usage if this is the
Packit 6c4009
   correct program.  */
Packit 6c4009
void *
Packit 6c4009
realloc (void *old, size_t len)
Packit 6c4009
{
Packit 6c4009
  struct header *result = NULL;
Packit 6c4009
  struct header *real;
Packit 6c4009
  size_t old_len;
Packit 6c4009
Packit 6c4009
  /* Determine real implementation if not already happened.  */
Packit 6c4009
  if (__glibc_unlikely (initialized <= 0))
Packit 6c4009
    {
Packit 6c4009
      if (initialized == -1)
Packit 6c4009
        return NULL;
Packit 6c4009
Packit 6c4009
      me ();
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If this is not the correct program just use the normal function.  */
Packit 6c4009
  if (not_me)
Packit 6c4009
    return (*reallocp)(old, len);
Packit 6c4009
Packit 6c4009
  if (old == NULL)
Packit 6c4009
    {
Packit 6c4009
      /* This is really a `malloc' call.  */
Packit 6c4009
      real = NULL;
Packit 6c4009
      old_len = 0;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      real = ((struct header *) old) - 1;
Packit 6c4009
      if (real->magic != MAGIC)
Packit 6c4009
        /* This is no memory allocated here.  */
Packit 6c4009
        return (*reallocp)(old, len);
Packit 6c4009
Packit 6c4009
      old_len = real->length;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Keep track of number of calls.  */
Packit 6c4009
  catomic_increment (&calls[idx_realloc]);
Packit 6c4009
  if (len > old_len)
Packit 6c4009
    {
Packit 6c4009
      /* Keep track of total memory consumption for `realloc'.  */
Packit 6c4009
      catomic_add (&total[idx_realloc], len - old_len);
Packit 6c4009
      /* Keep track of total memory requirement.  */
Packit 6c4009
      catomic_add (&grand_total, len - old_len);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (len == 0 && old != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Special case.  */
Packit 6c4009
      catomic_increment (&realloc_free);
Packit 6c4009
      /* Keep track of total memory freed using `free'.  */
Packit 6c4009
      catomic_add (&total[idx_free], real->length);
Packit 6c4009
Packit 6c4009
      /* Update the allocation data and write out the records if necessary.  */
Packit 6c4009
      update_data (NULL, 0, old_len);
Packit 6c4009
Packit 6c4009
      /* Do the real work.  */
Packit 6c4009
      (*freep) (real);
Packit 6c4009
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Remember the size of the request.  */
Packit 6c4009
  if (len < 65536)
Packit 6c4009
    catomic_increment (&histogram[len / 16]);
Packit 6c4009
  else
Packit 6c4009
    catomic_increment (&large);
Packit 6c4009
  /* Total number of calls of any of the functions.  */
Packit 6c4009
  catomic_increment (&calls_total);
Packit 6c4009
Packit 6c4009
  /* Do the real work.  */
Packit 6c4009
  result = (struct header *) (*reallocp)(real, len + sizeof (struct header));
Packit 6c4009
  if (result == NULL)
Packit 6c4009
    {
Packit 6c4009
      catomic_increment (&failed[idx_realloc]);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Record whether the reduction/increase happened in place.  */
Packit 6c4009
  if (real == result)
Packit 6c4009
    catomic_increment (&inplace);
Packit 6c4009
  /* Was the buffer increased?  */
Packit 6c4009
  if (old_len > len)
Packit 6c4009
    catomic_increment (&decreasing);
Packit 6c4009
Packit 6c4009
  /* Update the allocation data and write out the records if necessary.  */
Packit 6c4009
  update_data (result, len, old_len);
Packit 6c4009
Packit 6c4009
  /* Return the pointer to the user buffer.  */
Packit 6c4009
  return (void *) (result + 1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* `calloc' replacement.  We keep track of the memory usage if this is the
Packit 6c4009
   correct program.  */
Packit 6c4009
void *
Packit 6c4009
calloc (size_t n, size_t len)
Packit 6c4009
{
Packit 6c4009
  struct header *result;
Packit 6c4009
  size_t size = n * len;
Packit 6c4009
Packit 6c4009
  /* Determine real implementation if not already happened.  */
Packit 6c4009
  if (__glibc_unlikely (initialized <= 0))
Packit 6c4009
    {
Packit 6c4009
      if (initialized == -1)
Packit 6c4009
        return NULL;
Packit 6c4009
Packit 6c4009
      me ();
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If this is not the correct program just use the normal function.  */
Packit 6c4009
  if (not_me)
Packit 6c4009
    return (*callocp)(n, len);
Packit 6c4009
Packit 6c4009
  /* Keep track of number of calls.  */
Packit 6c4009
  catomic_increment (&calls[idx_calloc]);
Packit 6c4009
  /* Keep track of total memory consumption for `calloc'.  */
Packit 6c4009
  catomic_add (&total[idx_calloc], size);
Packit 6c4009
  /* Keep track of total memory requirement.  */
Packit 6c4009
  catomic_add (&grand_total, size);
Packit 6c4009
  /* Remember the size of the request.  */
Packit 6c4009
  if (size < 65536)
Packit 6c4009
    catomic_increment (&histogram[size / 16]);
Packit 6c4009
  else
Packit 6c4009
    catomic_increment (&large);
Packit 6c4009
  /* Total number of calls of any of the functions.  */
Packit 6c4009
  ++calls_total;
Packit 6c4009
Packit 6c4009
  /* Do the real work.  */
Packit 6c4009
  result = (struct header *) (*mallocp)(size + sizeof (struct header));
Packit 6c4009
  if (result == NULL)
Packit 6c4009
    {
Packit 6c4009
      catomic_increment (&failed[idx_calloc]);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Update the allocation data and write out the records if necessary.  */
Packit 6c4009
  update_data (result, size, 0);
Packit 6c4009
Packit 6c4009
  /* Do what `calloc' would have done and return the buffer to the caller.  */
Packit 6c4009
  return memset (result + 1, '\0', size);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* `free' replacement.  We keep track of the memory usage if this is the
Packit 6c4009
   correct program.  */
Packit 6c4009
void
Packit 6c4009
free (void *ptr)
Packit 6c4009
{
Packit 6c4009
  struct header *real;
Packit 6c4009
Packit 6c4009
  /* Determine real implementation if not already happened.  */
Packit 6c4009
  if (__glibc_unlikely (initialized <= 0))
Packit 6c4009
    {
Packit 6c4009
      if (initialized == -1)
Packit 6c4009
        return;
Packit 6c4009
Packit 6c4009
      me ();
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If this is not the correct program just use the normal function.  */
Packit 6c4009
  if (not_me)
Packit 6c4009
    {
Packit 6c4009
      (*freep) (ptr);
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* `free (NULL)' has no effect.  */
Packit 6c4009
  if (ptr == NULL)
Packit 6c4009
    {
Packit 6c4009
      catomic_increment (&calls[idx_free]);
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Determine the pointer to the header.  */
Packit 6c4009
  real = ((struct header *) ptr) - 1;
Packit 6c4009
  if (real->magic != MAGIC)
Packit 6c4009
    {
Packit 6c4009
      /* This block wasn't allocated here.  */
Packit 6c4009
      (*freep) (ptr);
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Keep track of number of calls.  */
Packit 6c4009
  catomic_increment (&calls[idx_free]);
Packit 6c4009
  /* Keep track of total memory freed using `free'.  */
Packit 6c4009
  catomic_add (&total[idx_free], real->length);
Packit 6c4009
Packit 6c4009
  /* Update the allocation data and write out the records if necessary.  */
Packit 6c4009
  update_data (NULL, 0, real->length);
Packit 6c4009
Packit 6c4009
  /* Do the real work.  */
Packit 6c4009
  (*freep) (real);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* `mmap' replacement.  We do not have to keep track of the size since
Packit 6c4009
   `munmap' will get it as a parameter.  */
Packit 6c4009
void *
Packit 6c4009
mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
Packit 6c4009
{
Packit 6c4009
  void *result = NULL;
Packit 6c4009
Packit 6c4009
  /* Determine real implementation if not already happened.  */
Packit 6c4009
  if (__glibc_unlikely (initialized <= 0))
Packit 6c4009
    {
Packit 6c4009
      if (initialized == -1)
Packit 6c4009
        return NULL;
Packit 6c4009
Packit 6c4009
      me ();
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Always get a block.  We don't need extra memory.  */
Packit 6c4009
  result = (*mmapp)(start, len, prot, flags, fd, offset);
Packit 6c4009
Packit 6c4009
  if (!not_me && trace_mmap)
Packit 6c4009
    {
Packit 6c4009
      int idx = (flags & MAP_ANON
Packit 6c4009
                 ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
Packit 6c4009
Packit 6c4009
      /* Keep track of number of calls.  */
Packit 6c4009
      catomic_increment (&calls[idx]);
Packit 6c4009
      /* Keep track of total memory consumption for `malloc'.  */
Packit 6c4009
      catomic_add (&total[idx], len);
Packit 6c4009
      /* Keep track of total memory requirement.  */
Packit 6c4009
      catomic_add (&grand_total, len);
Packit 6c4009
      /* Remember the size of the request.  */
Packit 6c4009
      if (len < 65536)
Packit 6c4009
        catomic_increment (&histogram[len / 16]);
Packit 6c4009
      else
Packit 6c4009
        catomic_increment (&large);
Packit 6c4009
      /* Total number of calls of any of the functions.  */
Packit 6c4009
      catomic_increment (&calls_total);
Packit 6c4009
Packit 6c4009
      /* Check for failures.  */
Packit 6c4009
      if (result == NULL)
Packit 6c4009
        catomic_increment (&failed[idx]);
Packit 6c4009
      else if (idx == idx_mmap_w)
Packit 6c4009
        /* Update the allocation data and write out the records if
Packit 6c4009
           necessary.  Note the first parameter is NULL which means
Packit 6c4009
           the size is not tracked.  */
Packit 6c4009
        update_data (NULL, len, 0);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Return the pointer to the user buffer.  */
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* `mmap64' replacement.  We do not have to keep track of the size since
Packit 6c4009
   `munmap' will get it as a parameter.  */
Packit 6c4009
void *
Packit 6c4009
mmap64 (void *start, size_t len, int prot, int flags, int fd, off64_t offset)
Packit 6c4009
{
Packit 6c4009
  void *result = NULL;
Packit 6c4009
Packit 6c4009
  /* Determine real implementation if not already happened.  */
Packit 6c4009
  if (__glibc_unlikely (initialized <= 0))
Packit 6c4009
    {
Packit 6c4009
      if (initialized == -1)
Packit 6c4009
        return NULL;
Packit 6c4009
Packit 6c4009
      me ();
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Always get a block.  We don't need extra memory.  */
Packit 6c4009
  result = (*mmap64p)(start, len, prot, flags, fd, offset);
Packit 6c4009
Packit 6c4009
  if (!not_me && trace_mmap)
Packit 6c4009
    {
Packit 6c4009
      int idx = (flags & MAP_ANON
Packit 6c4009
                 ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
Packit 6c4009
Packit 6c4009
      /* Keep track of number of calls.  */
Packit 6c4009
      catomic_increment (&calls[idx]);
Packit 6c4009
      /* Keep track of total memory consumption for `malloc'.  */
Packit 6c4009
      catomic_add (&total[idx], len);
Packit 6c4009
      /* Keep track of total memory requirement.  */
Packit 6c4009
      catomic_add (&grand_total, len);
Packit 6c4009
      /* Remember the size of the request.  */
Packit 6c4009
      if (len < 65536)
Packit 6c4009
        catomic_increment (&histogram[len / 16]);
Packit 6c4009
      else
Packit 6c4009
        catomic_increment (&large);
Packit 6c4009
      /* Total number of calls of any of the functions.  */
Packit 6c4009
      catomic_increment (&calls_total);
Packit 6c4009
Packit 6c4009
      /* Check for failures.  */
Packit 6c4009
      if (result == NULL)
Packit 6c4009
        catomic_increment (&failed[idx]);
Packit 6c4009
      else if (idx == idx_mmap_w)
Packit 6c4009
        /* Update the allocation data and write out the records if
Packit 6c4009
           necessary.  Note the first parameter is NULL which means
Packit 6c4009
           the size is not tracked.  */
Packit 6c4009
        update_data (NULL, len, 0);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Return the pointer to the user buffer.  */
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* `mremap' replacement.  We do not have to keep track of the size since
Packit 6c4009
   `munmap' will get it as a parameter.  */
Packit 6c4009
void *
Packit 6c4009
mremap (void *start, size_t old_len, size_t len, int flags, ...)
Packit 6c4009
{
Packit 6c4009
  void *result = NULL;
Packit 6c4009
  va_list ap;
Packit 6c4009
Packit 6c4009
  va_start (ap, flags);
Packit 6c4009
  void *newaddr = (flags & MREMAP_FIXED) ? va_arg (ap, void *) : NULL;
Packit 6c4009
  va_end (ap);
Packit 6c4009
Packit 6c4009
  /* Determine real implementation if not already happened.  */
Packit 6c4009
  if (__glibc_unlikely (initialized <= 0))
Packit 6c4009
    {
Packit 6c4009
      if (initialized == -1)
Packit 6c4009
        return NULL;
Packit 6c4009
Packit 6c4009
      me ();
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Always get a block.  We don't need extra memory.  */
Packit 6c4009
  result = (*mremapp)(start, old_len, len, flags, newaddr);
Packit 6c4009
Packit 6c4009
  if (!not_me && trace_mmap)
Packit 6c4009
    {
Packit 6c4009
      /* Keep track of number of calls.  */
Packit 6c4009
      catomic_increment (&calls[idx_mremap]);
Packit 6c4009
      if (len > old_len)
Packit 6c4009
        {
Packit 6c4009
          /* Keep track of total memory consumption for `malloc'.  */
Packit 6c4009
          catomic_add (&total[idx_mremap], len - old_len);
Packit 6c4009
          /* Keep track of total memory requirement.  */
Packit 6c4009
          catomic_add (&grand_total, len - old_len);
Packit 6c4009
        }
Packit 6c4009
      /* Remember the size of the request.  */
Packit 6c4009
      if (len < 65536)
Packit 6c4009
        catomic_increment (&histogram[len / 16]);
Packit 6c4009
      else
Packit 6c4009
        catomic_increment (&large);
Packit 6c4009
      /* Total number of calls of any of the functions.  */
Packit 6c4009
      catomic_increment (&calls_total);
Packit 6c4009
Packit 6c4009
      /* Check for failures.  */
Packit 6c4009
      if (result == NULL)
Packit 6c4009
        catomic_increment (&failed[idx_mremap]);
Packit 6c4009
      else
Packit 6c4009
        {
Packit 6c4009
          /* Record whether the reduction/increase happened in place.  */
Packit 6c4009
          if (start == result)
Packit 6c4009
            catomic_increment (&inplace_mremap);
Packit 6c4009
          /* Was the buffer increased?  */
Packit 6c4009
          if (old_len > len)
Packit 6c4009
            catomic_increment (&decreasing_mremap);
Packit 6c4009
Packit 6c4009
          /* Update the allocation data and write out the records if
Packit 6c4009
             necessary.  Note the first parameter is NULL which means
Packit 6c4009
             the size is not tracked.  */
Packit 6c4009
          update_data (NULL, len, old_len);
Packit 6c4009
        }
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Return the pointer to the user buffer.  */
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* `munmap' replacement.  */
Packit 6c4009
int
Packit 6c4009
munmap (void *start, size_t len)
Packit 6c4009
{
Packit 6c4009
  int result;
Packit 6c4009
Packit 6c4009
  /* Determine real implementation if not already happened.  */
Packit 6c4009
  if (__glibc_unlikely (initialized <= 0))
Packit 6c4009
    {
Packit 6c4009
      if (initialized == -1)
Packit 6c4009
        return -1;
Packit 6c4009
Packit 6c4009
      me ();
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Do the real work.  */
Packit 6c4009
  result = (*munmapp)(start, len);
Packit 6c4009
Packit 6c4009
  if (!not_me && trace_mmap)
Packit 6c4009
    {
Packit 6c4009
      /* Keep track of number of calls.  */
Packit 6c4009
      catomic_increment (&calls[idx_munmap]);
Packit 6c4009
Packit 6c4009
      if (__glibc_likely (result == 0))
Packit 6c4009
        {
Packit 6c4009
          /* Keep track of total memory freed using `free'.  */
Packit 6c4009
          catomic_add (&total[idx_munmap], len);
Packit 6c4009
Packit 6c4009
          /* Update the allocation data and write out the records if
Packit 6c4009
             necessary.  */
Packit 6c4009
          update_data (NULL, 0, len);
Packit 6c4009
        }
Packit 6c4009
      else
Packit 6c4009
        catomic_increment (&failed[idx_munmap]);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Write some statistics to standard error.  */
Packit 6c4009
static void
Packit 6c4009
__attribute__ ((destructor))
Packit 6c4009
dest (void)
Packit 6c4009
{
Packit 6c4009
  int percent, cnt;
Packit 6c4009
  unsigned long int maxcalls;
Packit 6c4009
Packit 6c4009
  /* If we haven't done anything here just return.  */
Packit 6c4009
  if (not_me)
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  /* If we should call any of the memory functions don't do any profiling.  */
Packit 6c4009
  not_me = true;
Packit 6c4009
Packit 6c4009
  /* Finish the output file.  */
Packit 6c4009
  if (fd != -1)
Packit 6c4009
    {
Packit 6c4009
      /* Write the partially filled buffer.  */
Packit 6c4009
      if (buffer_cnt > buffer_size)
Packit 6c4009
        write (fd, buffer + buffer_size,
Packit 6c4009
               (buffer_cnt - buffer_size) * sizeof (struct entry));
Packit 6c4009
      else
Packit 6c4009
        write (fd, buffer, buffer_cnt * sizeof (struct entry));
Packit 6c4009
Packit 6c4009
      /* Go back to the beginning of the file.  We allocated two records
Packit 6c4009
         here when we opened the file.  */
Packit 6c4009
      lseek (fd, 0, SEEK_SET);
Packit 6c4009
      /* Write out a record containing the total size.  */
Packit 6c4009
      first.stack = peak_total;
Packit 6c4009
      write (fd, &first, sizeof (struct entry));
Packit 6c4009
      /* Write out another record containing the maximum for heap and
Packit 6c4009
         stack.  */
Packit 6c4009
      first.heap = peak_heap;
Packit 6c4009
      first.stack = peak_stack;
Packit 6c4009
      GETTIME (first.time_low, first.time_high);
Packit 6c4009
      write (fd, &first, sizeof (struct entry));
Packit 6c4009
Packit 6c4009
      /* Close the file.  */
Packit 6c4009
      close (fd);
Packit 6c4009
      fd = -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Write a colorful statistic.  */
Packit 6c4009
  fprintf (stderr, "\n\
Packit 6c4009
\e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\
Packit 6c4009
\e[04;34m         total calls   total memory   failed calls\e[0m\n\
Packit 6c4009
\e[00;34m malloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
Packit 6c4009
\e[00;34mrealloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m  (nomove:%ld, dec:%ld, free:%ld)\n\
Packit 6c4009
\e[00;34m calloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
Packit 6c4009
\e[00;34m   free|\e[0m %10lu   %12llu\n",
Packit 6c4009
           (unsigned long long int) grand_total, (unsigned long int) peak_heap,
Packit 6c4009
           (unsigned long int) peak_stack,
Packit 6c4009
           (unsigned long int) calls[idx_malloc],
Packit 6c4009
           (unsigned long long int) total[idx_malloc],
Packit 6c4009
           failed[idx_malloc] ? "\e[01;41m" : "",
Packit 6c4009
           (unsigned long int) failed[idx_malloc],
Packit 6c4009
           (unsigned long int) calls[idx_realloc],
Packit 6c4009
           (unsigned long long int) total[idx_realloc],
Packit 6c4009
           failed[idx_realloc] ? "\e[01;41m" : "",
Packit 6c4009
           (unsigned long int) failed[idx_realloc],
Packit 6c4009
           (unsigned long int) inplace,
Packit 6c4009
           (unsigned long int) decreasing,
Packit 6c4009
           (unsigned long int) realloc_free,
Packit 6c4009
           (unsigned long int) calls[idx_calloc],
Packit 6c4009
           (unsigned long long int) total[idx_calloc],
Packit 6c4009
           failed[idx_calloc] ? "\e[01;41m" : "",
Packit 6c4009
           (unsigned long int) failed[idx_calloc],
Packit 6c4009
           (unsigned long int) calls[idx_free],
Packit 6c4009
           (unsigned long long int) total[idx_free]);
Packit 6c4009
Packit 6c4009
  if (trace_mmap)
Packit 6c4009
    fprintf (stderr, "\
Packit 6c4009
\e[00;34mmmap(r)|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
Packit 6c4009
\e[00;34mmmap(w)|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
Packit 6c4009
\e[00;34mmmap(a)|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
Packit 6c4009
\e[00;34m mremap|\e[0m %10lu   %12llu   %s%12lu\e[00;00m  (nomove: %ld, dec:%ld)\n\
Packit 6c4009
\e[00;34m munmap|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n",
Packit 6c4009
             (unsigned long int) calls[idx_mmap_r],
Packit 6c4009
             (unsigned long long int) total[idx_mmap_r],
Packit 6c4009
             failed[idx_mmap_r] ? "\e[01;41m" : "",
Packit 6c4009
             (unsigned long int) failed[idx_mmap_r],
Packit 6c4009
             (unsigned long int) calls[idx_mmap_w],
Packit 6c4009
             (unsigned long long int) total[idx_mmap_w],
Packit 6c4009
             failed[idx_mmap_w] ? "\e[01;41m" : "",
Packit 6c4009
             (unsigned long int) failed[idx_mmap_w],
Packit 6c4009
             (unsigned long int) calls[idx_mmap_a],
Packit 6c4009
             (unsigned long long int) total[idx_mmap_a],
Packit 6c4009
             failed[idx_mmap_a] ? "\e[01;41m" : "",
Packit 6c4009
             (unsigned long int) failed[idx_mmap_a],
Packit 6c4009
             (unsigned long int) calls[idx_mremap],
Packit 6c4009
             (unsigned long long int) total[idx_mremap],
Packit 6c4009
             failed[idx_mremap] ? "\e[01;41m" : "",
Packit 6c4009
             (unsigned long int) failed[idx_mremap],
Packit 6c4009
             (unsigned long int) inplace_mremap,
Packit 6c4009
             (unsigned long int) decreasing_mremap,
Packit 6c4009
             (unsigned long int) calls[idx_munmap],
Packit 6c4009
             (unsigned long long int) total[idx_munmap],
Packit 6c4009
             failed[idx_munmap] ? "\e[01;41m" : "",
Packit 6c4009
             (unsigned long int) failed[idx_munmap]);
Packit 6c4009
Packit 6c4009
  /* Write out a histoogram of the sizes of the allocations.  */
Packit 6c4009
  fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
Packit 6c4009
Packit 6c4009
  /* Determine the maximum of all calls for each size range.  */
Packit 6c4009
  maxcalls = large;
Packit 6c4009
  for (cnt = 0; cnt < 65536; cnt += 16)
Packit 6c4009
    if (histogram[cnt / 16] > maxcalls)
Packit 6c4009
      maxcalls = histogram[cnt / 16];
Packit 6c4009
Packit 6c4009
  for (cnt = 0; cnt < 65536; cnt += 16)
Packit 6c4009
    /* Only write out the nonzero entries.  */
Packit 6c4009
    if (histogram[cnt / 16] != 0)
Packit 6c4009
      {
Packit 6c4009
        percent = (histogram[cnt / 16] * 100) / calls_total;
Packit 6c4009
        fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15,
Packit 6c4009
                 (unsigned long int) histogram[cnt / 16]);
Packit 6c4009
        if (percent == 0)
Packit 6c4009
          fputs (" <1% \e[41;37m", stderr);
Packit 6c4009
        else
Packit 6c4009
          fprintf (stderr, "%3d%% \e[41;37m", percent);
Packit 6c4009
Packit 6c4009
        /* Draw a bar with a length corresponding to the current
Packit 6c4009
           percentage.  */
Packit 6c4009
        percent = (histogram[cnt / 16] * 50) / maxcalls;
Packit 6c4009
        while (percent-- > 0)
Packit 6c4009
          fputc ('=', stderr);
Packit 6c4009
        fputs ("\e[0;0m\n", stderr);
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  if (large != 0)
Packit 6c4009
    {
Packit 6c4009
      percent = (large * 100) / calls_total;
Packit 6c4009
      fprintf (stderr, "   large   %12lu ", (unsigned long int) large);
Packit 6c4009
      if (percent == 0)
Packit 6c4009
        fputs (" <1% \e[41;37m", stderr);
Packit 6c4009
      else
Packit 6c4009
        fprintf (stderr, "%3d%% \e[41;37m", percent);
Packit 6c4009
      percent = (large * 50) / maxcalls;
Packit 6c4009
      while (percent-- > 0)
Packit 6c4009
        fputc ('=', stderr);
Packit 6c4009
      fputs ("\e[0;0m\n", stderr);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Any following malloc/free etc. calls should generate statistics again,
Packit 6c4009
     because otherwise freeing something that has been malloced before
Packit 6c4009
     this destructor (including struct header in front of it) wouldn't
Packit 6c4009
     be properly freed.  */
Packit 6c4009
  not_me = false;
Packit 6c4009
}