From ba80a015ee80c01be5100b8b94794b8784aa562b Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Tue, 20 Jun 2000 04:46:22 +0000 Subject: Update. * malloc/Makefile: Change all references to memprof into memusage. * malloc/memprof.c: Rename to... * malloc/memusage.c: ...this. New file. * malloc/memprof.sh: Rename to... * malloc/memusage.sh: ...this. New file. * malloc/memprofstat.c: Rename to... * malloc/memusagestat.c: ...this. New file. --- malloc/memprof.c | 573 ---------------------------------------------- malloc/memprof.sh | 254 --------------------- malloc/memprofstat.c | 612 -------------------------------------------------- malloc/memusage.c | 573 ++++++++++++++++++++++++++++++++++++++++++++++ malloc/memusage.sh | 254 +++++++++++++++++++++ malloc/memusagestat.c | 612 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1439 insertions(+), 1439 deletions(-) delete mode 100644 malloc/memprof.c delete mode 100755 malloc/memprof.sh delete mode 100644 malloc/memprofstat.c create mode 100644 malloc/memusage.c create mode 100755 malloc/memusage.sh create mode 100644 malloc/memusagestat.c (limited to 'malloc') diff --git a/malloc/memprof.c b/malloc/memprof.c deleted file mode 100644 index 56d3ac556e..0000000000 --- a/malloc/memprof.c +++ /dev/null @@ -1,573 +0,0 @@ -/* Profile heap and stack memory usage of running program. - Copyright (C) 1998, 1999 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Ulrich Drepper , 1998. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* 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 *); - -enum -{ - idx_malloc = 0, - idx_realloc, - idx_calloc, - idx_free, - 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; -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; - -static int not_me; -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. */ - current_stack = start_sp - GETSP (); - 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); -} - - -/* Record the initial stack position. */ -static void -__attribute__ ((constructor)) -init (void) -{ - start_sp = GETSP (); -} - - -/* Find out whether this is the program we are supposed to profile. - For this the name in the variable `__progname' must match the one - given in the environment variable MEMPROF_PROG_NAME. If the variable - 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 - output file is determined by the environment variable MEMPROF_OUTPUT. - - If the environment variable MEMPROF_BUFFER_SIZE is set its numerical - 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. - - If MEMPROF_NO_TIMER is not present an alarm handler is installed - which at the highest possible frequency records the stack pointer. */ -static void -me (void) -{ - const char *env = getenv ("MEMPROF_PROG_NAME"); - size_t prog_len = strlen (__progname); - 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] != '/')) - not_me = 1; - } - - /* Only open the file if it's really us. */ - if (!not_me && fd == -1) - { - const char *outname = getenv ("MEMPROF_OUTPUT"); - if (outname != NULL) - { - fd = creat (outname, 0666); - - if (fd == -1) - /* Don't do anything in future calls if we cannot write to - the output file. */ - not_me = 1; - 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; - if (getenv ("MEMPROF_BUFFER_SIZE") != NULL) - { - buffer_size = atoi (getenv ("MEMPROF_BUFFER_SIZE")); - if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE) - buffer_size = DEFAULT_BUFFER_SIZE; - } - - /* Possibly enable timer-based stack pointer retrieval. */ - if (getenv ("MEMPROF_NO_TIMER") == NULL) - { - 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); - } - } - } - } - } -} - - -/* `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. */ - if (mallocp == NULL) - { - me (); - mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc"); - } - - /* 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) - ++failed[idx_malloc]; - else - /* Update the allocation data and write out the records if necessary. */ - update_data (result, len, 0); - - /* Return the pointer to the user buffer. */ - return result ? (void *) (result + 1) : NULL; -} - - -/* `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. */ - if (reallocp == NULL) - { - me (); - reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc"); - } - - /* 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]; - /* Keep track of total memory consumption for `realloc'. */ - total[idx_realloc] += 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 *) (*reallocp) (real, len + sizeof (struct header)); - - if (result == NULL) - ++failed[idx_realloc]; - else - { - /* 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); - } - - /* Return the pointer to the user buffer. */ - return result ? (void *) (result + 1) : NULL; -} - - -/* `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; - - /* Determine real implementation if not already happened. We are - searching for the `malloc' implementation since it is not always - efficiently possible to use `calloc' because we have to add a bit - room to the allocation to put the header in. */ - if (mallocp == NULL) - { - me (); - mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc"); - } - - /* If this is not the correct program just use the normal function. */ - if (not_me) - { - callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc"); - - return (*callocp) (n, len); - } - - /* 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) - memset (result + 1, '\0', size); - - if (result == NULL) - ++failed[idx_calloc]; - else - /* Update the allocation data and write out the records if necessary. */ - update_data (result, size, 0); - - /* Return the pointer to the user buffer. */ - return result ? (void *) (result + 1) : NULL; -} - - -/* `free' replacement. We keep track of the memory usage if this is the - correct program. */ -void -free (void *ptr) -{ - struct header *real; - - /* `free (NULL)' has no effect. */ - if (ptr == NULL) - { - ++calls[idx_free]; - return; - } - - /* Determine real implementation if not already happened. */ - if (freep == NULL) - { - me (); - freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free"); - } - - /* If this is not the correct program just use the normal function. */ - if (not_me) - { - (*freep) (ptr); - return; - } - - /* 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); -} - - -/* 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. */ - not_me = 1; - - /* 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]); - - /* 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); - } -} diff --git a/malloc/memprof.sh b/malloc/memprof.sh deleted file mode 100755 index 3113f591e6..0000000000 --- a/malloc/memprof.sh +++ /dev/null @@ -1,254 +0,0 @@ -#! @BASH@ -# Copyright (C) 1999 Free Software Foundation, Inc. -# This file is part of the GNU C Library. -# Contributed by Ulrich Drepper , 1999. - -# The GNU C Library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Library General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. - -# 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 -# Library General Public License for more details. - -# You should have received a copy of the GNU Library General Public -# License along with the GNU C Library; see the file COPYING.LIB. If not, -# write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -memprofso=@LIBDIR@/libmemprof.so -memprofstat=@BINDIR@/memprofstat - -# Print usage message. -do_usage() { - echo >&2 $"Try \`memprof --help' for more information." - exit 1 -} - -# Message for missing argument. -do_missing_arg() { - echo >&2 $"memprof: option \`$1' requires an argument" - do_usage -} - -# Print help message -do_help() { - echo $"Usage: memprof [OPTION]... PROGRAM [PROGRAMOPTION]... -Profile memory usage of PROGRAM. - - -n,--progname=NAME Name of the program file to profile - -p,--png=FILE Generate PNG graphic and store it in FILE - -d,--data=FILE Generate binary data file and store it in FILE - -u,--unbuffered Don't buffer output - -b,--buffer=SIZE Collect SIZE entries before writing them out - --no-timer Don't collect additional information though timer - - -?,--help Print this help and exit - --usage Give a short usage message - -V,--version Print version information and exit - - The following options only apply when generating graphical output: - -t,--time-based Make graph linear in time - -T,--total Also draw graph of total memory use - --title=STRING Use STRING as title of the graph - -x,--x-size=SIZE Make graphic SIZE pixels wide - -y,--y-size=SIZE Make graphic SIZE pixels high - -Mandatory arguments to long options are also mandatory for any corresponding -short options. - -Report bugs using the \`glibcbug' script to ." - exit 0 -} - -do_version() { - echo 'memprof (GNU libc) @VERSION@' - echo $"Copyright (C) 1999 Free Software Foundation, Inc. -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -Written by Ulrich Drepper." - exit 0 -} - -# Process arguments. But stop as soon as the program name is found. -while test $# -gt 0; do - case "$1" in - -V | --v | --ve | --ver | --vers | --versi | --versio | --version) - do_version - ;; - -\? | --h | --he | --hel | --help) - do_help - ;; - --us | --usa | --usag | --usage) - echo $"Syntax: memprof [--data=FILE] [--progname=NAME] [--png=FILE] [--unbuffered] - [--buffer=SIZE] [--no-timer] [--time-based] [--total] - [--title=STRING] [--x-size=SIZE] [--y-size=SIZE] - PROGRAM [PROGRAMOPTION]..." - exit 0 - ;; - -n | --pr | --pro | --prog | --progn | --progna | --prognam | --progname) - if test $# -eq 1; then - do_missing_arg $1 - fi - shift - progname="$1" - ;; - --pr=* | --pro=* | --prog=* | --progn=* | --progna=* | --prognam=* | --progname=*) - progname=${1##*=} - ;; - -p | --pn | --png) - if test $# -eq 1; then - do_missing_arg $1 - fi - shift - png="$1" - ;; - --pn=* | --png=*) - png=${1##*=} - ;; - -d | --d | --da | --dat | --data) - if test $# -eq 1; then - do_missing_arg $1 - fi - shift - data="$1" - ;; - --d=* | --da=* | --dat=* | --data=*) - data=${1##*=} - ;; - -u | --un | --unb | --unbu | --unbuf | --unbuff | --unbuffe | --unbuffer | --unbuffere | --unbuffered) - buffer=1 - ;; - -b | --b | --bu | --buf | --buff | --buffe | --buffer) - if test $# -eq 1; then - do_missing_arg $1 - fi - shift - buffer="$1" - ;; - --b=* | --bu=* | --buf=* | --buff=* | --buffe=* | --buffer=*) - buffer=${1##*=} - ;; - --n | --no | --no- | --no-t | --no-ti | --no-tim | --no-time | --no-timer) - notimer=yes - ;; - -t | --tim | --time | --time- | --time-b | --time-ba | --time-bas | --time-base | --time-based) - memprofstat_args="$memprofstat_args -t" - ;; - -T | --to | --tot | --tota | --total) - memprofstat_args="$memprofstat_args -T" - ;; - --tit | --titl | --title) - if test $# -eq 1; then - do_missing_arg $1 - fi - shift - memprofstat_args="$memprofstat_args -s $1" - ;; - --tit=* | --titl=* | --title=*) - memprofstat_args="$memprofstat_args -s ${1##*=}" - ;; - -x | --x | --x- | --x-s | --x-si | --x-siz | --x-size) - if test $# -eq 1; then - do_missing_arg $1 - fi - shift - memprofstat_args="$memprofstat_args -x $1" - ;; - --x=* | --x-=* | --x-s=* | --x-si=* | --x-siz=* | --x-size=*) - memprofstat_args="$memprofstat_args -x ${1##*=}" - ;; - -y | --y | --y- | --y-s | --y-si | --y-siz | --y-size) - if test $# -eq 1; then - do_missing_arg $1 - fi - shift - memprofstat_args="$memprofstat_args -y $1" - ;; - --y=* | --y-=* | --y-s=* | --y-si=* | --y-siz=* | --y-size=*) - memprofstat_args="$memprofstat_args -y ${1##*=}" - ;; - --p | --p=* | --t | --t=* | --ti | --ti=* | --u) - echo >&2 $"memprof: option \`${1##*=}' is ambiguous" - do_usage - ;; - --) - # Stop processing arguments. - shift - break - ;; - --*) - echo >&2 $"memprof: unrecognized option \`$1'" - do_usage - ;; - *) - # Unknown option. This means the rest is the program name and parameters. - break - ;; - esac - shift -done - -# See whether any arguments are left. -if test $# -eq 0; then - echo >&2 $"No program name given" - do_usage -fi - -# This will be in the environment. -add_env="LD_PRELOAD=$memprofso" - -# Generate data file name. -datafile= -if test -n "$data"; then - datafile="$data" -elif test -n "$png"; then - datafile=$(mktemp ${TMPDIR:-/tmp}/memprof.XXXXXX 2> /dev/null) - if test $? -ne 0; then - # Lame, but if there is no `mktemp' program the user cannot expect more. - if test "$RANDOM" != "$RANDOM"; then - datafile=${TMPDIR:-/tmp}/memprof.$RANDOM - else - datafile=${TMPDIR:-/tmp}/memprof.$$ - fi - fi -fi -if test -n "$datafile"; then - add_env="$add_env MEMPROF_OUTPUT=$datafile" -fi - -# Set buffer size. -if test -n "$buffer"; then - add_env="$add_env MEMPROF_BUFFER_SIZE=$buffer" -fi - -# Disable timers. -if test -n "$notimer"; then - add_env="$add_env MEMPROF_NO_TIMER=yes" -fi - -# Execute the program itself. -eval $add_env '"$@"' -result=$? - -# Generate the PNG data file if wanted and there is something to generate -# it from. -if test -n "$png" -a -n "$datafile" -a -s "$datafile"; then - # Append extension .png if it isn't already there. - case $png in - *.png) ;; - *) png="$png.png" ;; - esac - $memprofstat $memprofstat_args "$datafile" "$png" -fi - -if test -z "$data" -a -n "$datafile"; then - rm -f "$datafile" -fi - -exit $result -# Local Variables: -# mode:ksh -# End: diff --git a/malloc/memprofstat.c b/malloc/memprofstat.c deleted file mode 100644 index 638b3beb73..0000000000 --- a/malloc/memprofstat.c +++ /dev/null @@ -1,612 +0,0 @@ -/* Generate graphic from memory profiling data. - Copyright (C) 1998, 1999 Free Software Foundation, Inc. - This file is part of the GNU C Library. - Contributed by Ulrich Drepper , 1998. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - -/* Default size of the generated image. */ -#define XSIZE 800 -#define YSIZE 600 - -#ifndef N_ -# define N_(Arg) Arg -#endif - - -/* Definitions of arguments for argp functions. */ -static const struct argp_option options[] = -{ - { "output", 'o', "FILE", 0, N_("Name output file") }, - { "string", 's', "STRING", 0, N_("Title string used in output graphic") }, - { "time", 't', NULL, 0, N_("Generate output linear to time (default is linear to number of function calls)") }, - { "total", 'T', NULL, 0, - N_("Also draw graph for total memory consumption") }, - { "x-size", 'x', "VALUE", 0, N_("make output graphic VALUE pixel wide") }, - { "y-size", 'y', "VALUE", 0, N_("make output graphic VALUE pixel high") }, - { NULL, 0, NULL, 0, NULL } -}; - -/* Short description of program. */ -static const char doc[] = N_("Generate graphic from memory profiling data"); - -/* Strings for arguments in help texts. */ -static const char args_doc[] = N_("DATAFILE [OUTFILE]"); - -/* Prototype for option handler. */ -static error_t parse_opt (int key, char *arg, struct argp_state *state); - -/* Function to print some extra text in the help message. */ -static char *more_help (int key, const char *text, void *input); - -/* Data structure to communicate with argp functions. */ -static struct argp argp = -{ - options, parse_opt, args_doc, doc, NULL, more_help -}; - - -struct entry -{ - size_t heap; - size_t stack; - uint32_t time_low; - uint32_t time_high; -}; - - -/* Size of the image. */ -static size_t xsize; -static size_t ysize; - -/* Name of the output file. */ -static char *outname; - -/* Title string for the graphic. */ -static const char *string; - -/* Nonzero if graph should be generated linear in time. */ -static int time_based; - -/* Nonzero if graph to display total use of memory should be drawn as well. */ -static int also_total = 0; - - -int -main (int argc, char *argv[]) -{ - int remaining; - const char *inname; - gdImagePtr im_out; - int grey, blue, red, green, yellow, black; - int fd; - struct stat st; - size_t maxsize_heap; - size_t maxsize_stack; - size_t maxsize_total; - uint64_t total; - uint64_t cnt, cnt2; - FILE *outfile; - char buf[30]; - size_t last_heap; - size_t last_stack; - size_t last_total; - struct entry headent[2]; - uint64_t start_time; - uint64_t end_time; - uint64_t total_time; - - outname = NULL; - xsize = XSIZE; - ysize = YSIZE; - string = NULL; - - /* Parse and process arguments. */ - argp_parse (&argp, argc, argv, 0, &remaining, NULL); - - if (remaining >= argc || remaining + 2 < argc) - { - argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, - program_invocation_short_name); - exit (1); - } - - inname = argv[remaining++]; - - if (remaining < argc) - outname = argv[remaining]; - else if (outname == NULL) - { - size_t len = strlen (inname); - outname = alloca (len + 5); - stpcpy (stpcpy (outname, inname), ".png"); - } - - /* Open for read/write since we try to repair the file in case the - application hasn't terminated cleanly. */ - fd = open (inname, O_RDWR); - if (fd == -1) - error (EXIT_FAILURE, errno, "cannot open input file"); - if (fstat (fd, &st) != 0) - { - close (fd); - error (EXIT_FAILURE, errno, "cannot get size of input file"); - } - /* Test whether the file contains only full records. */ - if ((st.st_size % sizeof (struct entry)) != 0 - /* The file must at least contain the two administrative records. */ - || st.st_size < 2 * sizeof (struct entry)) - { - close (fd); - error (EXIT_FAILURE, 0, "input file as incorrect size"); - } - /* Compute number of data entries. */ - total = st.st_size / sizeof (struct entry) - 2; - - /* Read the administrative information. */ - read (fd, headent, sizeof (headent)); - maxsize_heap = headent[1].heap; - maxsize_stack = headent[1].stack; - maxsize_total = headent[0].stack; - if (also_total) - { - /* We use one scale and since we also draw the total amount of - memory used we have to adapt the maximum. */ - maxsize_heap = maxsize_total; - maxsize_stack = maxsize_total; - } - - if (maxsize_heap == 0 && maxsize_stack == 0) - { - /* The program aborted before memprof was able to write the - information about the maximum heap and stack use. Repair - the file now. */ - struct entry next; - - while (1) - { - if (read (fd, &next, sizeof (next)) == 0) - break; - if (next.heap > headent[1].heap) - headent[1].heap = next.heap; - if (next.stack > headent[1].stack) - headent[1].stack = next.stack; - } - - headent[1].time_low = next.time_low; - headent[1].time_high = next.time_high; - - /* Write the computed values in the file. */ - lseek (fd, sizeof (struct entry), SEEK_SET); - write (fd, &headent[1], sizeof (struct entry)); - } - - start_time = ((uint64_t) headent[0].time_high) << 32 | headent[0].time_low; - end_time = ((uint64_t) headent[1].time_high) << 32 | headent[1].time_low; - total_time = end_time - start_time; - - if (xsize < 100) - xsize = 100; - if (ysize < 80) - ysize = 80; - - /* Create output image with the specified size. */ - im_out = gdImageCreate (xsize, ysize); - - /* First color allocated is background. */ - grey = gdImageColorAllocate (im_out, 224, 224, 224); - - /* Set transparent color. */ - gdImageColorTransparent (im_out, grey); - - /* These are all the other colors we need (in the moment). */ - red = gdImageColorAllocate (im_out, 255, 0, 0); - green = gdImageColorAllocate (im_out, 0, 130, 0); - blue = gdImageColorAllocate (im_out, 0, 0, 255); - yellow = gdImageColorAllocate (im_out, 154, 205, 50); - black = gdImageColorAllocate (im_out, 0, 0, 0); - - gdImageRectangle (im_out, 40, 20, xsize - 40, ysize - 20, blue); - - gdImageString (im_out, gdFontSmall, 38, ysize - 14, (unsigned char *) "0", - blue); - gdImageString (im_out, gdFontSmall, maxsize_heap < 1024 ? 32 : 26, - ysize - 26, - (unsigned char *) (maxsize_heap < 1024 ? "0" : "0k"), red); - gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26, - (unsigned char *) (maxsize_stack < 1024 ? "0" : "0k"), green); - - if (string != NULL) - gdImageString (im_out, gdFontLarge, (xsize - strlen (string) * 8) / 2, - 2, (char *) string, green); - - gdImageStringUp (im_out, gdFontSmall, 1, ysize / 2 - 10, - (unsigned char *) "allocated", red); - gdImageStringUp (im_out, gdFontSmall, 11, ysize / 2 - 10, - (unsigned char *) "memory", red); - - gdImageStringUp (im_out, gdFontSmall, xsize - 39, ysize / 2 - 10, - (unsigned char *) "used", green); - gdImageStringUp (im_out, gdFontSmall, xsize - 27, ysize / 2 - 10, - (unsigned char *) "stack", green); - - if (maxsize_heap < 1024) - { - snprintf (buf, sizeof (buf), "%Zu", maxsize_heap); - gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, 14, buf, red); - } - else - { - snprintf (buf, sizeof (buf), "%Zuk", maxsize_heap / 1024); - gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, 14, buf, red); - } - if (maxsize_stack < 1024) - { - snprintf (buf, sizeof (buf), "%Zu", maxsize_stack); - gdImageString (im_out, gdFontSmall, xsize - 37, 14, buf, green); - } - else - { - snprintf (buf, sizeof (buf), "%Zuk", maxsize_stack / 1024); - gdImageString (im_out, gdFontSmall, xsize - 37, 14, buf, green); - } - - - if (maxsize_heap < 1024) - { - cnt = ((ysize - 40) * (maxsize_heap / 4)) / maxsize_heap; - gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, - ysize - 20 - cnt, red); - snprintf (buf, sizeof (buf), "%Zu", maxsize_heap / 4); - gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, - ysize - 26 - cnt, buf, red); - } - else - { - cnt = ((ysize - 40) * (maxsize_heap / 4096)) / (maxsize_heap / 1024); - gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, - ysize - 20 - cnt, red); - snprintf (buf, sizeof (buf), "%Zuk", maxsize_heap / 4096); - gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, - ysize - 26 - cnt, buf, red); - } - if (maxsize_stack < 1024) - { - cnt2 = ((ysize - 40) * (maxsize_stack / 4)) / maxsize_stack; - if (cnt != cnt2) - gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, - ysize - 20 - cnt2, green); - snprintf (buf, sizeof (buf), "%Zu", maxsize_stack / 4); - gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, - buf, green); - } - else - { - cnt2 = ((ysize - 40) * (maxsize_stack / 4096)) / (maxsize_stack / 1024); - if (cnt != cnt2) - gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, - ysize - 20 - cnt2, green); - snprintf (buf, sizeof (buf), "%Zuk", maxsize_stack / 4096); - gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, - buf, green); - } - - if (maxsize_heap < 1024) - { - cnt = ((ysize - 40) * (maxsize_heap / 2)) / maxsize_heap; - gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, - ysize - 20 - cnt, red); - snprintf (buf, sizeof (buf), "%Zu", maxsize_heap / 2); - gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, - ysize - 26 - cnt, buf, red); - } - else - { - cnt = ((ysize - 40) * (maxsize_heap / 2048)) / (maxsize_heap / 1024); - gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, - ysize - 20 - cnt, red); - snprintf (buf, sizeof (buf), "%Zuk", maxsize_heap / 2048); - gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, - ysize - 26 - cnt, buf, red); - } - if (maxsize_stack < 1024) - { - cnt2 = ((ysize - 40) * (maxsize_stack / 2)) / maxsize_stack; - if (cnt != cnt2) - gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, - ysize - 20 - cnt2, green); - snprintf (buf, sizeof (buf), "%Zu", maxsize_stack / 2); - gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, - buf, green); - } - else - { - cnt2 = ((ysize - 40) * (maxsize_stack / 2048)) / (maxsize_stack / 1024); - if (cnt != cnt2) - gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, - ysize - 20 - cnt2, green); - snprintf (buf, sizeof (buf), "%Zuk", maxsize_stack / 2048); - gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, - buf, green); - } - - if (maxsize_heap < 1024) - { - cnt = ((ysize - 40) * ((3 * maxsize_heap) / 4)) / maxsize_heap; - gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, - ysize - 20 - cnt, red); - snprintf (buf, sizeof (buf), "%Zu", (3 * maxsize_heap) / 4); - gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, - ysize - 26 - cnt, buf, red); - } - else - { - cnt = ((ysize - 40) * ((3 * maxsize_heap) / 4096)) / (maxsize_heap - / 1024); - gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, - ysize - 20 - cnt, red); - snprintf (buf, sizeof (buf), "%Zuk", (3 * maxsize_heap) / 4096); - gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, - ysize - 26 - cnt, buf, red); - } - if (maxsize_stack < 1024) - { - cnt2 = ((ysize - 40) * ((3 * maxsize_stack) / 4)) / maxsize_stack; - if (cnt != cnt2) - gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, - ysize - 20 - cnt2, green); - snprintf (buf, sizeof (buf), "%Zu", (3 * maxsize_stack) / 4); - gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, - buf, green); - } - else - { - cnt2 = (((ysize - 40) * ((3 * maxsize_stack) / 4096)) - / (maxsize_stack / 1024)); - if (cnt != cnt2) - gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, - ysize - 20 - cnt2, green); - snprintf (buf, sizeof (buf), "%Zuk", (3 * maxsize_stack) / 4096); - gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, - buf, green); - } - - - snprintf (buf, sizeof (buf), "%llu", total); - gdImageString (im_out, gdFontSmall, xsize - 50, ysize - 14, buf, blue); - - if (!time_based) - { - uint64_t previously = start_time; - - gdImageString (im_out, gdFontSmall, 40 + (xsize - 32 * 6 - 80) / 2, - ysize - 12, - (unsigned char *) "# memory handling function calls", - blue); - - - last_stack = last_heap = last_total = ysize - 20; - for (cnt = 1; cnt <= total; ++cnt) - { - struct entry entry; - size_t new[2]; - uint64_t now; - - read (fd, &entry, sizeof (entry)); - - now = ((uint64_t) entry.time_high) << 32 | entry.time_low; - - if ((((previously - start_time) * 100) / total_time) % 10 < 5) - gdImageFilledRectangle (im_out, - 40 + ((cnt - 1) * (xsize - 80)) / total, - ysize - 19, - 39 + (cnt * (xsize - 80)) / total, - ysize - 14, yellow); - previously = now; - - if (also_total) - { - size_t new3; - - new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) - * (entry.heap + entry.stack)) - / maxsize_heap); - gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total, - last_total, - 40 + ((xsize - 80) * cnt) / total, new3, - black); - last_total = new3; - } - - // assert (entry.heap <= maxsize_heap); - new[0] = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) - * entry.heap) / maxsize_heap); - gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total, - last_heap, 40 + ((xsize - 80) * cnt) / total, new[0], - red); - last_heap = new[0]; - - // assert (entry.stack <= maxsize_stack); - new[1] = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) - * entry.stack) / maxsize_stack); - gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total, - last_stack, 40 + ((xsize - 80) * cnt) / total, new[1], - green); - last_stack = new[1]; - } - - cnt = 0; - while (cnt < total) - { - gdImageLine (im_out, 40 + ((xsize - 80) * cnt) / total, ysize - 20, - 40 + ((xsize - 80) * cnt) / total, ysize - 15, blue); - cnt += MAX (1, total / 20); - } - gdImageLine (im_out, xsize - 40, ysize - 20, xsize - 40, ysize - 15, - blue); - } - else - { - uint64_t next_tick = MAX (1, total / 20); - size_t last_xpos = 40; - - gdImageString (im_out, gdFontSmall, 40 + (xsize - 39 * 6 - 80) / 2, - ysize - 12, - (unsigned char *) "\ -# memory handling function calls / time", blue); - - for (cnt = 0; cnt < 20; cnt += 2) - gdImageFilledRectangle (im_out, - 40 + (cnt * (xsize - 80)) / 20, ysize - 19, - 39 + ((cnt + 1) * (xsize - 80)) / 20, - ysize - 14, yellow); - - last_stack = last_heap = last_total = ysize - 20; - for (cnt = 1; cnt <= total; ++cnt) - { - struct entry entry; - size_t new[2]; - size_t xpos; - uint64_t now; - - read (fd, &entry, sizeof (entry)); - - now = ((uint64_t) entry.time_high) << 32 | entry.time_low; - xpos = 40 + ((xsize - 80) * (now - start_time)) / total_time; - - if (cnt == next_tick) - { - gdImageLine (im_out, xpos, ysize - 20, xpos, ysize - 15, blue); - next_tick += MAX (1, total / 20); - } - - if (also_total) - { - size_t new3; - - new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) - * (entry.heap + entry.stack)) - / maxsize_heap); - gdImageLine (im_out, last_xpos, last_total, xpos, new3, black); - last_total = new3; - } - - new[0] = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) - * entry.heap) / maxsize_heap); - gdImageLine (im_out, last_xpos, last_heap, xpos, new[0], red); - last_heap = new[0]; - - // assert (entry.stack <= maxsize_stack); - new[1] = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) - * entry.stack) / maxsize_stack); - gdImageLine (im_out, last_xpos, last_stack, xpos, new[1], green); - last_stack = new[1]; - - last_xpos = xpos; - } - } - - /* Write out the result. */ - outfile = fopen (outname, "w"); - if (outfile == NULL) - error (EXIT_FAILURE, errno, "cannot open output file"); - - gdImagePng (im_out, outfile); - - fclose (outfile); - - gdImageDestroy (im_out); - - exit (0); -} - - -/* Handle program arguments. */ -static error_t -parse_opt (int key, char *arg, struct argp_state *state) -{ - switch (key) - { - case 'o': - outname = arg; - break; - case 's': - string = arg; - break; - case 't': - time_based = 1; - break; - case 'T': - also_total = 1; - break; - case 'x': - xsize = atoi (arg); - if (xsize == 0) - xsize = XSIZE; - break; - case 'y': - ysize = atoi (arg); - if (ysize == 0) - ysize = XSIZE; - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - - -static char * -more_help (int key, const char *text, void *input) -{ - char *orig; - char *cp; - - switch (key) - { - case ARGP_KEY_HELP_EXTRA: - /* We print some extra information. */ - orig = gettext ("\ -Report bugs using the `glibcbug' script to .\n"); - cp = strdup (orig); - if (cp == NULL) - cp = orig; - return cp; - default: - break; - } - return (char *) text; -} diff --git a/malloc/memusage.c b/malloc/memusage.c new file mode 100644 index 0000000000..6296d99230 --- /dev/null +++ b/malloc/memusage.c @@ -0,0 +1,573 @@ +/* Profile heap and stack memory usage of running program. + Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* 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 *); + +enum +{ + idx_malloc = 0, + idx_realloc, + idx_calloc, + idx_free, + 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; +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; + +static int not_me; +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. */ + current_stack = start_sp - GETSP (); + 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); +} + + +/* Record the initial stack position. */ +static void +__attribute__ ((constructor)) +init (void) +{ + start_sp = GETSP (); +} + + +/* Find out whether this is the program we are supposed to profile. + For this the name in the variable `__progname' must match the one + given in the environment variable MEMUSAGE_PROG_NAME. If the variable + 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 + output file is determined by the environment variable MEMUSAGE_OUTPUT. + + If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical + 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. + + If MEMUSAGE_NO_TIMER is not present an alarm handler is installed + which at the highest possible frequency records the stack pointer. */ +static void +me (void) +{ + const char *env = getenv ("MEMUSAGE_PROG_NAME"); + size_t prog_len = strlen (__progname); + 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] != '/')) + not_me = 1; + } + + /* Only open the file if it's really us. */ + if (!not_me && fd == -1) + { + const char *outname = getenv ("MEMUSAGE_OUTPUT"); + if (outname != NULL) + { + fd = creat (outname, 0666); + + if (fd == -1) + /* Don't do anything in future calls if we cannot write to + the output file. */ + not_me = 1; + 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; + if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL) + { + buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE")); + if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE) + buffer_size = DEFAULT_BUFFER_SIZE; + } + + /* Possibly enable timer-based stack pointer retrieval. */ + if (getenv ("MEMUSAGE_NO_TIMER") == NULL) + { + 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); + } + } + } + } + } +} + + +/* `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. */ + if (mallocp == NULL) + { + me (); + mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc"); + } + + /* 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) + ++failed[idx_malloc]; + else + /* Update the allocation data and write out the records if necessary. */ + update_data (result, len, 0); + + /* Return the pointer to the user buffer. */ + return result ? (void *) (result + 1) : NULL; +} + + +/* `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. */ + if (reallocp == NULL) + { + me (); + reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc"); + } + + /* 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]; + /* Keep track of total memory consumption for `realloc'. */ + total[idx_realloc] += 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 *) (*reallocp) (real, len + sizeof (struct header)); + + if (result == NULL) + ++failed[idx_realloc]; + else + { + /* 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); + } + + /* Return the pointer to the user buffer. */ + return result ? (void *) (result + 1) : NULL; +} + + +/* `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; + + /* Determine real implementation if not already happened. We are + searching for the `malloc' implementation since it is not always + efficiently possible to use `calloc' because we have to add a bit + room to the allocation to put the header in. */ + if (mallocp == NULL) + { + me (); + mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc"); + } + + /* If this is not the correct program just use the normal function. */ + if (not_me) + { + callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc"); + + return (*callocp) (n, len); + } + + /* 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) + memset (result + 1, '\0', size); + + if (result == NULL) + ++failed[idx_calloc]; + else + /* Update the allocation data and write out the records if necessary. */ + update_data (result, size, 0); + + /* Return the pointer to the user buffer. */ + return result ? (void *) (result + 1) : NULL; +} + + +/* `free' replacement. We keep track of the memory usage if this is the + correct program. */ +void +free (void *ptr) +{ + struct header *real; + + /* `free (NULL)' has no effect. */ + if (ptr == NULL) + { + ++calls[idx_free]; + return; + } + + /* Determine real implementation if not already happened. */ + if (freep == NULL) + { + me (); + freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free"); + } + + /* If this is not the correct program just use the normal function. */ + if (not_me) + { + (*freep) (ptr); + return; + } + + /* 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); +} + + +/* 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. */ + not_me = 1; + + /* 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]); + + /* 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); + } +} diff --git a/malloc/memusage.sh b/malloc/memusage.sh new file mode 100755 index 0000000000..744991ff02 --- /dev/null +++ b/malloc/memusage.sh @@ -0,0 +1,254 @@ +#! @BASH@ +# Copyright (C) 1999, 2000 Free Software Foundation, Inc. +# This file is part of the GNU C Library. +# Contributed by Ulrich Drepper , 1999. + +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. + +# 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 +# Library General Public License for more details. + +# You should have received a copy of the GNU Library General Public +# License along with the GNU C Library; see the file COPYING.LIB. If not, +# write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +memusageso=@LIBDIR@/libmemusage.so +memusagestat=@BINDIR@/memusagestat + +# Print usage message. +do_usage() { + echo >&2 $"Try \`memusage --help' for more information." + exit 1 +} + +# Message for missing argument. +do_missing_arg() { + echo >&2 $"memusage: option \`$1' requires an argument" + do_usage +} + +# Print help message +do_help() { + echo $"Usage: memusage [OPTION]... PROGRAM [PROGRAMOPTION]... +Profile memory usage of PROGRAM. + + -n,--progname=NAME Name of the program file to profile + -p,--png=FILE Generate PNG graphic and store it in FILE + -d,--data=FILE Generate binary data file and store it in FILE + -u,--unbuffered Don't buffer output + -b,--buffer=SIZE Collect SIZE entries before writing them out + --no-timer Don't collect additional information though timer + + -?,--help Print this help and exit + --usage Give a short usage message + -V,--version Print version information and exit + + The following options only apply when generating graphical output: + -t,--time-based Make graph linear in time + -T,--total Also draw graph of total memory use + --title=STRING Use STRING as title of the graph + -x,--x-size=SIZE Make graphic SIZE pixels wide + -y,--y-size=SIZE Make graphic SIZE pixels high + +Mandatory arguments to long options are also mandatory for any corresponding +short options. + +Report bugs using the \`glibcbug' script to ." + exit 0 +} + +do_version() { + echo 'memusage (GNU libc) @VERSION@' + echo $"Copyright (C) 2000 Free Software Foundation, Inc. +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +Written by Ulrich Drepper." + exit 0 +} + +# Process arguments. But stop as soon as the program name is found. +while test $# -gt 0; do + case "$1" in + -V | --v | --ve | --ver | --vers | --versi | --versio | --version) + do_version + ;; + -\? | --h | --he | --hel | --help) + do_help + ;; + --us | --usa | --usag | --usage) + echo $"Syntax: memusage [--data=FILE] [--progname=NAME] [--png=FILE] [--unbuffered] + [--buffer=SIZE] [--no-timer] [--time-based] [--total] + [--title=STRING] [--x-size=SIZE] [--y-size=SIZE] + PROGRAM [PROGRAMOPTION]..." + exit 0 + ;; + -n | --pr | --pro | --prog | --progn | --progna | --prognam | --progname) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + progname="$1" + ;; + --pr=* | --pro=* | --prog=* | --progn=* | --progna=* | --prognam=* | --progname=*) + progname=${1##*=} + ;; + -p | --pn | --png) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + png="$1" + ;; + --pn=* | --png=*) + png=${1##*=} + ;; + -d | --d | --da | --dat | --data) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + data="$1" + ;; + --d=* | --da=* | --dat=* | --data=*) + data=${1##*=} + ;; + -u | --un | --unb | --unbu | --unbuf | --unbuff | --unbuffe | --unbuffer | --unbuffere | --unbuffered) + buffer=1 + ;; + -b | --b | --bu | --buf | --buff | --buffe | --buffer) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + buffer="$1" + ;; + --b=* | --bu=* | --buf=* | --buff=* | --buffe=* | --buffer=*) + buffer=${1##*=} + ;; + --n | --no | --no- | --no-t | --no-ti | --no-tim | --no-time | --no-timer) + notimer=yes + ;; + -t | --tim | --time | --time- | --time-b | --time-ba | --time-bas | --time-base | --time-based) + memusagestat_args="$memusagestat_args -t" + ;; + -T | --to | --tot | --tota | --total) + memusagestat_args="$memusagestat_args -T" + ;; + --tit | --titl | --title) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + memusagestat_args="$memusagestat_args -s $1" + ;; + --tit=* | --titl=* | --title=*) + memusagestat_args="$memusagestat_args -s ${1##*=}" + ;; + -x | --x | --x- | --x-s | --x-si | --x-siz | --x-size) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + memusagestat_args="$memusagestat_args -x $1" + ;; + --x=* | --x-=* | --x-s=* | --x-si=* | --x-siz=* | --x-size=*) + memusagestat_args="$memusagestat_args -x ${1##*=}" + ;; + -y | --y | --y- | --y-s | --y-si | --y-siz | --y-size) + if test $# -eq 1; then + do_missing_arg $1 + fi + shift + memusagestat_args="$memusagestat_args -y $1" + ;; + --y=* | --y-=* | --y-s=* | --y-si=* | --y-siz=* | --y-size=*) + memusagestat_args="$memusagestat_args -y ${1##*=}" + ;; + --p | --p=* | --t | --t=* | --ti | --ti=* | --u) + echo >&2 $"memusage: option \`${1##*=}' is ambiguous" + do_usage + ;; + --) + # Stop processing arguments. + shift + break + ;; + --*) + echo >&2 $"memusage: unrecognized option \`$1'" + do_usage + ;; + *) + # Unknown option. This means the rest is the program name and parameters. + break + ;; + esac + shift +done + +# See whether any arguments are left. +if test $# -eq 0; then + echo >&2 $"No program name given" + do_usage +fi + +# This will be in the environment. +add_env="LD_PRELOAD=$memusageso" + +# Generate data file name. +datafile= +if test -n "$data"; then + datafile="$data" +elif test -n "$png"; then + datafile=$(mktemp ${TMPDIR:-/tmp}/memusage.XXXXXX 2> /dev/null) + if test $? -ne 0; then + # Lame, but if there is no `mktemp' program the user cannot expect more. + if test "$RANDOM" != "$RANDOM"; then + datafile=${TMPDIR:-/tmp}/memusage.$RANDOM + else + datafile=${TMPDIR:-/tmp}/memusage.$$ + fi + fi +fi +if test -n "$datafile"; then + add_env="$add_env MEMUSAGE_OUTPUT=$datafile" +fi + +# Set buffer size. +if test -n "$buffer"; then + add_env="$add_env MEMUSAGE_BUFFER_SIZE=$buffer" +fi + +# Disable timers. +if test -n "$notimer"; then + add_env="$add_env MEMUSAGE_NO_TIMER=yes" +fi + +# Execute the program itself. +eval $add_env '"$@"' +result=$? + +# Generate the PNG data file if wanted and there is something to generate +# it from. +if test -n "$png" -a -n "$datafile" -a -s "$datafile"; then + # Append extension .png if it isn't already there. + case $png in + *.png) ;; + *) png="$png.png" ;; + esac + $memusagestat $memusagestat_args "$datafile" "$png" +fi + +if test -z "$data" -a -n "$datafile"; then + rm -f "$datafile" +fi + +exit $result +# Local Variables: +# mode:ksh +# End: diff --git a/malloc/memusagestat.c b/malloc/memusagestat.c new file mode 100644 index 0000000000..1da0ff0ce9 --- /dev/null +++ b/malloc/memusagestat.c @@ -0,0 +1,612 @@ +/* Generate graphic from memory profiling data. + Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper , 1998. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +/* Default size of the generated image. */ +#define XSIZE 800 +#define YSIZE 600 + +#ifndef N_ +# define N_(Arg) Arg +#endif + + +/* Definitions of arguments for argp functions. */ +static const struct argp_option options[] = +{ + { "output", 'o', "FILE", 0, N_("Name output file") }, + { "string", 's', "STRING", 0, N_("Title string used in output graphic") }, + { "time", 't', NULL, 0, N_("Generate output linear to time (default is linear to number of function calls)") }, + { "total", 'T', NULL, 0, + N_("Also draw graph for total memory consumption") }, + { "x-size", 'x', "VALUE", 0, N_("make output graphic VALUE pixel wide") }, + { "y-size", 'y', "VALUE", 0, N_("make output graphic VALUE pixel high") }, + { NULL, 0, NULL, 0, NULL } +}; + +/* Short description of program. */ +static const char doc[] = N_("Generate graphic from memory profiling data"); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("DATAFILE [OUTFILE]"); + +/* Prototype for option handler. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +/* Function to print some extra text in the help message. */ +static char *more_help (int key, const char *text, void *input); + +/* Data structure to communicate with argp functions. */ +static struct argp argp = +{ + options, parse_opt, args_doc, doc, NULL, more_help +}; + + +struct entry +{ + size_t heap; + size_t stack; + uint32_t time_low; + uint32_t time_high; +}; + + +/* Size of the image. */ +static size_t xsize; +static size_t ysize; + +/* Name of the output file. */ +static char *outname; + +/* Title string for the graphic. */ +static const char *string; + +/* Nonzero if graph should be generated linear in time. */ +static int time_based; + +/* Nonzero if graph to display total use of memory should be drawn as well. */ +static int also_total = 0; + + +int +main (int argc, char *argv[]) +{ + int remaining; + const char *inname; + gdImagePtr im_out; + int grey, blue, red, green, yellow, black; + int fd; + struct stat st; + size_t maxsize_heap; + size_t maxsize_stack; + size_t maxsize_total; + uint64_t total; + uint64_t cnt, cnt2; + FILE *outfile; + char buf[30]; + size_t last_heap; + size_t last_stack; + size_t last_total; + struct entry headent[2]; + uint64_t start_time; + uint64_t end_time; + uint64_t total_time; + + outname = NULL; + xsize = XSIZE; + ysize = YSIZE; + string = NULL; + + /* Parse and process arguments. */ + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + + if (remaining >= argc || remaining + 2 < argc) + { + argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR, + program_invocation_short_name); + exit (1); + } + + inname = argv[remaining++]; + + if (remaining < argc) + outname = argv[remaining]; + else if (outname == NULL) + { + size_t len = strlen (inname); + outname = alloca (len + 5); + stpcpy (stpcpy (outname, inname), ".png"); + } + + /* Open for read/write since we try to repair the file in case the + application hasn't terminated cleanly. */ + fd = open (inname, O_RDWR); + if (fd == -1) + error (EXIT_FAILURE, errno, "cannot open input file"); + if (fstat (fd, &st) != 0) + { + close (fd); + error (EXIT_FAILURE, errno, "cannot get size of input file"); + } + /* Test whether the file contains only full records. */ + if ((st.st_size % sizeof (struct entry)) != 0 + /* The file must at least contain the two administrative records. */ + || st.st_size < 2 * sizeof (struct entry)) + { + close (fd); + error (EXIT_FAILURE, 0, "input file as incorrect size"); + } + /* Compute number of data entries. */ + total = st.st_size / sizeof (struct entry) - 2; + + /* Read the administrative information. */ + read (fd, headent, sizeof (headent)); + maxsize_heap = headent[1].heap; + maxsize_stack = headent[1].stack; + maxsize_total = headent[0].stack; + if (also_total) + { + /* We use one scale and since we also draw the total amount of + memory used we have to adapt the maximum. */ + maxsize_heap = maxsize_total; + maxsize_stack = maxsize_total; + } + + if (maxsize_heap == 0 && maxsize_stack == 0) + { + /* The program aborted before memusage was able to write the + information about the maximum heap and stack use. Repair + the file now. */ + struct entry next; + + while (1) + { + if (read (fd, &next, sizeof (next)) == 0) + break; + if (next.heap > headent[1].heap) + headent[1].heap = next.heap; + if (next.stack > headent[1].stack) + headent[1].stack = next.stack; + } + + headent[1].time_low = next.time_low; + headent[1].time_high = next.time_high; + + /* Write the computed values in the file. */ + lseek (fd, sizeof (struct entry), SEEK_SET); + write (fd, &headent[1], sizeof (struct entry)); + } + + start_time = ((uint64_t) headent[0].time_high) << 32 | headent[0].time_low; + end_time = ((uint64_t) headent[1].time_high) << 32 | headent[1].time_low; + total_time = end_time - start_time; + + if (xsize < 100) + xsize = 100; + if (ysize < 80) + ysize = 80; + + /* Create output image with the specified size. */ + im_out = gdImageCreate (xsize, ysize); + + /* First color allocated is background. */ + grey = gdImageColorAllocate (im_out, 224, 224, 224); + + /* Set transparent color. */ + gdImageColorTransparent (im_out, grey); + + /* These are all the other colors we need (in the moment). */ + red = gdImageColorAllocate (im_out, 255, 0, 0); + green = gdImageColorAllocate (im_out, 0, 130, 0); + blue = gdImageColorAllocate (im_out, 0, 0, 255); + yellow = gdImageColorAllocate (im_out, 154, 205, 50); + black = gdImageColorAllocate (im_out, 0, 0, 0); + + gdImageRectangle (im_out, 40, 20, xsize - 40, ysize - 20, blue); + + gdImageString (im_out, gdFontSmall, 38, ysize - 14, (unsigned char *) "0", + blue); + gdImageString (im_out, gdFontSmall, maxsize_heap < 1024 ? 32 : 26, + ysize - 26, + (unsigned char *) (maxsize_heap < 1024 ? "0" : "0k"), red); + gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26, + (unsigned char *) (maxsize_stack < 1024 ? "0" : "0k"), green); + + if (string != NULL) + gdImageString (im_out, gdFontLarge, (xsize - strlen (string) * 8) / 2, + 2, (char *) string, green); + + gdImageStringUp (im_out, gdFontSmall, 1, ysize / 2 - 10, + (unsigned char *) "allocated", red); + gdImageStringUp (im_out, gdFontSmall, 11, ysize / 2 - 10, + (unsigned char *) "memory", red); + + gdImageStringUp (im_out, gdFontSmall, xsize - 39, ysize / 2 - 10, + (unsigned char *) "used", green); + gdImageStringUp (im_out, gdFontSmall, xsize - 27, ysize / 2 - 10, + (unsigned char *) "stack", green); + + if (maxsize_heap < 1024) + { + snprintf (buf, sizeof (buf), "%Zu", maxsize_heap); + gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, 14, buf, red); + } + else + { + snprintf (buf, sizeof (buf), "%Zuk", maxsize_heap / 1024); + gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, 14, buf, red); + } + if (maxsize_stack < 1024) + { + snprintf (buf, sizeof (buf), "%Zu", maxsize_stack); + gdImageString (im_out, gdFontSmall, xsize - 37, 14, buf, green); + } + else + { + snprintf (buf, sizeof (buf), "%Zuk", maxsize_stack / 1024); + gdImageString (im_out, gdFontSmall, xsize - 37, 14, buf, green); + } + + + if (maxsize_heap < 1024) + { + cnt = ((ysize - 40) * (maxsize_heap / 4)) / maxsize_heap; + gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, + ysize - 20 - cnt, red); + snprintf (buf, sizeof (buf), "%Zu", maxsize_heap / 4); + gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, + ysize - 26 - cnt, buf, red); + } + else + { + cnt = ((ysize - 40) * (maxsize_heap / 4096)) / (maxsize_heap / 1024); + gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, + ysize - 20 - cnt, red); + snprintf (buf, sizeof (buf), "%Zuk", maxsize_heap / 4096); + gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, + ysize - 26 - cnt, buf, red); + } + if (maxsize_stack < 1024) + { + cnt2 = ((ysize - 40) * (maxsize_stack / 4)) / maxsize_stack; + if (cnt != cnt2) + gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, + ysize - 20 - cnt2, green); + snprintf (buf, sizeof (buf), "%Zu", maxsize_stack / 4); + gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, + buf, green); + } + else + { + cnt2 = ((ysize - 40) * (maxsize_stack / 4096)) / (maxsize_stack / 1024); + if (cnt != cnt2) + gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, + ysize - 20 - cnt2, green); + snprintf (buf, sizeof (buf), "%Zuk", maxsize_stack / 4096); + gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, + buf, green); + } + + if (maxsize_heap < 1024) + { + cnt = ((ysize - 40) * (maxsize_heap / 2)) / maxsize_heap; + gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, + ysize - 20 - cnt, red); + snprintf (buf, sizeof (buf), "%Zu", maxsize_heap / 2); + gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, + ysize - 26 - cnt, buf, red); + } + else + { + cnt = ((ysize - 40) * (maxsize_heap / 2048)) / (maxsize_heap / 1024); + gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, + ysize - 20 - cnt, red); + snprintf (buf, sizeof (buf), "%Zuk", maxsize_heap / 2048); + gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, + ysize - 26 - cnt, buf, red); + } + if (maxsize_stack < 1024) + { + cnt2 = ((ysize - 40) * (maxsize_stack / 2)) / maxsize_stack; + if (cnt != cnt2) + gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, + ysize - 20 - cnt2, green); + snprintf (buf, sizeof (buf), "%Zu", maxsize_stack / 2); + gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, + buf, green); + } + else + { + cnt2 = ((ysize - 40) * (maxsize_stack / 2048)) / (maxsize_stack / 1024); + if (cnt != cnt2) + gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, + ysize - 20 - cnt2, green); + snprintf (buf, sizeof (buf), "%Zuk", maxsize_stack / 2048); + gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, + buf, green); + } + + if (maxsize_heap < 1024) + { + cnt = ((ysize - 40) * ((3 * maxsize_heap) / 4)) / maxsize_heap; + gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, + ysize - 20 - cnt, red); + snprintf (buf, sizeof (buf), "%Zu", (3 * maxsize_heap) / 4); + gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, + ysize - 26 - cnt, buf, red); + } + else + { + cnt = ((ysize - 40) * ((3 * maxsize_heap) / 4096)) / (maxsize_heap + / 1024); + gdImageDashedLine (im_out, 40, ysize - 20 - cnt, xsize - 40, + ysize - 20 - cnt, red); + snprintf (buf, sizeof (buf), "%Zuk", (3 * maxsize_heap) / 4096); + gdImageString (im_out, gdFontSmall, 39 - strlen (buf) * 6, + ysize - 26 - cnt, buf, red); + } + if (maxsize_stack < 1024) + { + cnt2 = ((ysize - 40) * ((3 * maxsize_stack) / 4)) / maxsize_stack; + if (cnt != cnt2) + gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, + ysize - 20 - cnt2, green); + snprintf (buf, sizeof (buf), "%Zu", (3 * maxsize_stack) / 4); + gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, + buf, green); + } + else + { + cnt2 = (((ysize - 40) * ((3 * maxsize_stack) / 4096)) + / (maxsize_stack / 1024)); + if (cnt != cnt2) + gdImageDashedLine (im_out, 40, ysize - 20 - cnt2, xsize - 40, + ysize - 20 - cnt2, green); + snprintf (buf, sizeof (buf), "%Zuk", (3 * maxsize_stack) / 4096); + gdImageString (im_out, gdFontSmall, xsize - 37, ysize - 26 - cnt2, + buf, green); + } + + + snprintf (buf, sizeof (buf), "%llu", total); + gdImageString (im_out, gdFontSmall, xsize - 50, ysize - 14, buf, blue); + + if (!time_based) + { + uint64_t previously = start_time; + + gdImageString (im_out, gdFontSmall, 40 + (xsize - 32 * 6 - 80) / 2, + ysize - 12, + (unsigned char *) "# memory handling function calls", + blue); + + + last_stack = last_heap = last_total = ysize - 20; + for (cnt = 1; cnt <= total; ++cnt) + { + struct entry entry; + size_t new[2]; + uint64_t now; + + read (fd, &entry, sizeof (entry)); + + now = ((uint64_t) entry.time_high) << 32 | entry.time_low; + + if ((((previously - start_time) * 100) / total_time) % 10 < 5) + gdImageFilledRectangle (im_out, + 40 + ((cnt - 1) * (xsize - 80)) / total, + ysize - 19, + 39 + (cnt * (xsize - 80)) / total, + ysize - 14, yellow); + previously = now; + + if (also_total) + { + size_t new3; + + new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) + * (entry.heap + entry.stack)) + / maxsize_heap); + gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total, + last_total, + 40 + ((xsize - 80) * cnt) / total, new3, + black); + last_total = new3; + } + + // assert (entry.heap <= maxsize_heap); + new[0] = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) + * entry.heap) / maxsize_heap); + gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total, + last_heap, 40 + ((xsize - 80) * cnt) / total, new[0], + red); + last_heap = new[0]; + + // assert (entry.stack <= maxsize_stack); + new[1] = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) + * entry.stack) / maxsize_stack); + gdImageLine (im_out, 40 + ((xsize - 80) * (cnt - 1)) / total, + last_stack, 40 + ((xsize - 80) * cnt) / total, new[1], + green); + last_stack = new[1]; + } + + cnt = 0; + while (cnt < total) + { + gdImageLine (im_out, 40 + ((xsize - 80) * cnt) / total, ysize - 20, + 40 + ((xsize - 80) * cnt) / total, ysize - 15, blue); + cnt += MAX (1, total / 20); + } + gdImageLine (im_out, xsize - 40, ysize - 20, xsize - 40, ysize - 15, + blue); + } + else + { + uint64_t next_tick = MAX (1, total / 20); + size_t last_xpos = 40; + + gdImageString (im_out, gdFontSmall, 40 + (xsize - 39 * 6 - 80) / 2, + ysize - 12, + (unsigned char *) "\ +# memory handling function calls / time", blue); + + for (cnt = 0; cnt < 20; cnt += 2) + gdImageFilledRectangle (im_out, + 40 + (cnt * (xsize - 80)) / 20, ysize - 19, + 39 + ((cnt + 1) * (xsize - 80)) / 20, + ysize - 14, yellow); + + last_stack = last_heap = last_total = ysize - 20; + for (cnt = 1; cnt <= total; ++cnt) + { + struct entry entry; + size_t new[2]; + size_t xpos; + uint64_t now; + + read (fd, &entry, sizeof (entry)); + + now = ((uint64_t) entry.time_high) << 32 | entry.time_low; + xpos = 40 + ((xsize - 80) * (now - start_time)) / total_time; + + if (cnt == next_tick) + { + gdImageLine (im_out, xpos, ysize - 20, xpos, ysize - 15, blue); + next_tick += MAX (1, total / 20); + } + + if (also_total) + { + size_t new3; + + new3 = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) + * (entry.heap + entry.stack)) + / maxsize_heap); + gdImageLine (im_out, last_xpos, last_total, xpos, new3, black); + last_total = new3; + } + + new[0] = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) + * entry.heap) / maxsize_heap); + gdImageLine (im_out, last_xpos, last_heap, xpos, new[0], red); + last_heap = new[0]; + + // assert (entry.stack <= maxsize_stack); + new[1] = (ysize - 20) - ((((unsigned long long int) (ysize - 40)) + * entry.stack) / maxsize_stack); + gdImageLine (im_out, last_xpos, last_stack, xpos, new[1], green); + last_stack = new[1]; + + last_xpos = xpos; + } + } + + /* Write out the result. */ + outfile = fopen (outname, "w"); + if (outfile == NULL) + error (EXIT_FAILURE, errno, "cannot open output file"); + + gdImagePng (im_out, outfile); + + fclose (outfile); + + gdImageDestroy (im_out); + + exit (0); +} + + +/* Handle program arguments. */ +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'o': + outname = arg; + break; + case 's': + string = arg; + break; + case 't': + time_based = 1; + break; + case 'T': + also_total = 1; + break; + case 'x': + xsize = atoi (arg); + if (xsize == 0) + xsize = XSIZE; + break; + case 'y': + ysize = atoi (arg); + if (ysize == 0) + ysize = XSIZE; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +static char * +more_help (int key, const char *text, void *input) +{ + char *orig; + char *cp; + + switch (key) + { + case ARGP_KEY_HELP_EXTRA: + /* We print some extra information. */ + orig = gettext ("\ +Report bugs using the `glibcbug' script to .\n"); + cp = strdup (orig); + if (cp == NULL) + cp = orig; + return cp; + default: + break; + } + return (char *) text; +} -- cgit v1.2.3