Files
glibc/malloc/memusage.c
T

848 lines
23 KiB
C
Raw Normal View History

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