[BZ #2510, BZ #2830, BZ #3137, BZ #3313, BZ #3426, BZ #3465, BZ #3480, BZ #3483,...
[glibc.git] / malloc / memusage.c
blobd11e9e6ed52f33e3176c9ae92fe6c55e0fe4303a
1 /* Profile heap and stack memory usage of running program.
2 Copyright (C) 1998-2002, 2004, 2005, 2006 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
21 #include <atomic.h>
22 #include <dlfcn.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <inttypes.h>
26 #include <signal.h>
27 #include <stdarg.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/mman.h>
34 #include <sys/time.h>
36 #include <memusage.h>
38 /* Pointer to the real functions. These are determined used `dlsym'
39 when really needed. */
40 static void *(*mallocp) (size_t);
41 static void *(*reallocp) (void *, size_t);
42 static void *(*callocp) (size_t, size_t);
43 static void (*freep) (void *);
45 static void *(*mmapp) (void *, size_t, int, int, int, off_t);
46 static void *(*mmap64p) (void *, size_t, int, int, int, off64_t);
47 static int (*munmapp) (void *, size_t);
48 static void *(*mremapp) (void *, size_t, size_t, int, void *);
50 enum
52 idx_malloc = 0,
53 idx_realloc,
54 idx_calloc,
55 idx_free,
56 idx_mmap_r,
57 idx_mmap_w,
58 idx_mmap_a,
59 idx_mremap,
60 idx_munmap,
61 idx_last
65 struct header
67 size_t length;
68 size_t magic;
71 #define MAGIC 0xfeedbeaf
74 static memusage_cntr_t calls[idx_last];
75 static memusage_cntr_t failed[idx_last];
76 static memusage_size_t total[idx_last];
77 static memusage_size_t grand_total;
78 static memusage_cntr_t histogram[65536 / 16];
79 static memusage_cntr_t large;
80 static memusage_cntr_t calls_total;
81 static memusage_cntr_t inplace;
82 static memusage_cntr_t decreasing;
83 static memusage_cntr_t realloc_free;
84 static memusage_cntr_t inplace_mremap;
85 static memusage_cntr_t decreasing_mremap;
86 static memusage_size_t current_heap;
87 static memusage_size_t peak_use[3];
88 static __thread uintptr_t start_sp;
90 /* A few macros to make the source more readable. */
91 #define peak_heap peak_use[0]
92 #define peak_stack peak_use[1]
93 #define peak_total peak_use[2]
95 #define DEFAULT_BUFFER_SIZE 1024
96 static size_t buffer_size;
98 static int fd = -1;
100 static bool not_me;
101 static int initialized;
102 static bool trace_mmap;
103 extern const char *__progname;
105 struct entry
107 uint64_t heap;
108 uint64_t stack;
109 uint32_t time_low;
110 uint32_t time_high;
113 static struct entry buffer[2 * DEFAULT_BUFFER_SIZE];
114 static uatomic32_t buffer_cnt;
115 static struct entry first;
118 /* Update the global data after a successful function call. */
119 static void
120 update_data (struct header *result, size_t len, size_t old_len)
122 if (result != NULL)
124 /* Record the information we need and mark the block using a
125 magic number. */
126 result->length = len;
127 result->magic = MAGIC;
130 /* Compute current heap usage and compare it with the maximum value. */
131 memusage_size_t heap
132 = catomic_exchange_and_add (&current_heap, len - old_len) + len - old_len;
133 catomic_max (&peak_heap, heap);
135 /* Compute current stack usage and compare it with the maximum
136 value. The base stack pointer might not be set if this is not
137 the main thread and it is the first call to any of these
138 functions. */
139 if (__builtin_expect (!start_sp, 0))
140 start_sp = GETSP ();
142 uintptr_t sp = GETSP ();
143 #ifdef STACK_GROWS_UPWARD
144 /* This can happen in threads where we didn't catch the thread's
145 stack early enough. */
146 if (__builtin_expect (sp < start_sp, 0))
147 start_sp = sp;
148 size_t current_stack = sp - start_sp;
149 #else
150 /* This can happen in threads where we didn't catch the thread's
151 stack early enough. */
152 if (__builtin_expect (sp > start_sp, 0))
153 start_sp = sp;
154 size_t current_stack = start_sp - sp;
155 #endif
156 catomic_max (&peak_stack, current_stack);
158 /* Add up heap and stack usage and compare it with the maximum value. */
159 catomic_max (&peak_total, heap + current_stack);
161 /* Store the value only if we are writing to a file. */
162 if (fd != -1)
164 uatomic32_t idx = catomic_exchange_and_add (&buffer_cnt, 1);
165 if (idx >= 2 * buffer_size)
167 /* We try to reset the counter to the correct range. If
168 this fails because of another thread increasing the
169 counter it does not matter since that thread will take
170 care of the correction. */
171 unsigned int reset = idx - 2 * buffer_size;
172 catomic_compare_and_exchange_val_acq (&buffer_size, reset, idx);
173 idx = reset;
176 buffer[idx].heap = current_heap;
177 buffer[idx].stack = current_stack;
178 GETTIME (buffer[idx].time_low, buffer[idx].time_high);
180 /* Write out buffer if it is full. */
181 if (idx + 1 == buffer_size)
182 write (fd, buffer, buffer_size * sizeof (struct entry));
183 else if (idx + 1 == 2 * buffer_size)
184 write (fd, &buffer[buffer_size], buffer_size * sizeof (struct entry));
189 /* Interrupt handler. */
190 static void
191 int_handler (int signo)
193 /* Nothing gets allocated. Just record the stack pointer position. */
194 update_data (NULL, 0, 0);
198 /* Find out whether this is the program we are supposed to profile.
199 For this the name in the variable `__progname' must match the one
200 given in the environment variable MEMUSAGE_PROG_NAME. If the variable
201 is not present every program assumes it should be profiling.
203 If this is the program open a file descriptor to the output file.
204 We will write to it whenever the buffer overflows. The name of the
205 output file is determined by the environment variable MEMUSAGE_OUTPUT.
207 If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical
208 value determines the size of the internal buffer. The number gives
209 the number of elements in the buffer. By setting the number to one
210 one effectively selects unbuffered operation.
212 If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
213 which at the highest possible frequency records the stack pointer. */
214 static void
215 me (void)
217 const char *env = getenv ("MEMUSAGE_PROG_NAME");
218 size_t prog_len = strlen (__progname);
220 initialized = -1;
221 mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
222 reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
223 callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
224 freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
226 mmapp = (void *(*) (void *, size_t, int, int, int, off_t)) dlsym (RTLD_NEXT,
227 "mmap");
228 mmap64p =
229 (void *(*) (void *, size_t, int, int, int, off64_t)) dlsym (RTLD_NEXT,
230 "mmap64");
231 mremapp = (void *(*) (void *, size_t, size_t, int, void *)) dlsym (RTLD_NEXT,
232 "mremap");
233 munmapp = (int (*) (void *, size_t)) dlsym (RTLD_NEXT, "munmap");
234 initialized = 1;
236 if (env != NULL)
238 /* Check for program name. */
239 size_t len = strlen (env);
240 if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
241 || (prog_len != len && __progname[prog_len - len - 1] != '/'))
242 not_me = true;
245 /* Only open the file if it's really us. */
246 if (!not_me && fd == -1)
248 const char *outname;
250 if (!start_sp)
251 start_sp = GETSP ();
253 outname = getenv ("MEMUSAGE_OUTPUT");
254 if (outname != NULL && outname[0] != '\0'
255 && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
257 fd = creat64 (outname, 0666);
259 if (fd == -1)
260 /* Don't do anything in future calls if we cannot write to
261 the output file. */
262 not_me = true;
263 else
265 /* Write the first entry. */
266 first.heap = 0;
267 first.stack = 0;
268 GETTIME (first.time_low, first.time_high);
269 /* Write it two times since we need the starting and end time. */
270 write (fd, &first, sizeof (first));
271 write (fd, &first, sizeof (first));
273 /* Determine the buffer size. We use the default if the
274 environment variable is not present. */
275 buffer_size = DEFAULT_BUFFER_SIZE;
276 if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
278 buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
279 if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
280 buffer_size = DEFAULT_BUFFER_SIZE;
283 /* Possibly enable timer-based stack pointer retrieval. */
284 if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
286 struct sigaction act;
288 act.sa_handler = (sighandler_t) &int_handler;
289 act.sa_flags = SA_RESTART;
290 sigfillset (&act.sa_mask);
292 if (sigaction (SIGPROF, &act, NULL) >= 0)
294 struct itimerval timer;
296 timer.it_value.tv_sec = 0;
297 timer.it_value.tv_usec = 1;
298 timer.it_interval = timer.it_value;
299 setitimer (ITIMER_PROF, &timer, NULL);
305 if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL)
306 trace_mmap = true;
311 /* Record the initial stack position. */
312 static void
313 __attribute__ ((constructor))
314 init (void)
316 start_sp = GETSP ();
317 if (! initialized)
318 me ();
322 /* `malloc' replacement. We keep track of the memory usage if this is the
323 correct program. */
324 void *
325 malloc (size_t len)
327 struct header *result = NULL;
329 /* Determine real implementation if not already happened. */
330 if (__builtin_expect (initialized <= 0, 0))
332 if (initialized == -1)
333 return NULL;
334 me ();
337 /* If this is not the correct program just use the normal function. */
338 if (not_me)
339 return (*mallocp) (len);
341 /* Keep track of number of calls. */
342 catomic_increment (&calls[idx_malloc]);
343 /* Keep track of total memory consumption for `malloc'. */
344 catomic_add (&total[idx_malloc], len);
345 /* Keep track of total memory requirement. */
346 catomic_add (&grand_total, len);
347 /* Remember the size of the request. */
348 if (len < 65536)
349 catomic_increment (&histogram[len / 16]);
350 else
351 catomic_increment (&large);
352 /* Total number of calls of any of the functions. */
353 catomic_increment (&calls_total);
355 /* Do the real work. */
356 result = (struct header *) (*mallocp) (len + sizeof (struct header));
357 if (result == NULL)
359 catomic_increment (&failed[idx_malloc]);
360 return NULL;
363 /* Update the allocation data and write out the records if necessary. */
364 update_data (result, len, 0);
366 /* Return the pointer to the user buffer. */
367 return (void *) (result + 1);
371 /* `realloc' replacement. We keep track of the memory usage if this is the
372 correct program. */
373 void *
374 realloc (void *old, size_t len)
376 struct header *result = NULL;
377 struct header *real;
378 size_t old_len;
380 /* Determine real implementation if not already happened. */
381 if (__builtin_expect (initialized <= 0, 0))
383 if (initialized == -1)
384 return NULL;
385 me ();
388 /* If this is not the correct program just use the normal function. */
389 if (not_me)
390 return (*reallocp) (old, len);
392 if (old == NULL)
394 /* This is really a `malloc' call. */
395 real = NULL;
396 old_len = 0;
398 else
400 real = ((struct header *) old) - 1;
401 if (real->magic != MAGIC)
402 /* This is no memory allocated here. */
403 return (*reallocp) (old, len);
404 old_len = real->length;
407 /* Keep track of number of calls. */
408 catomic_increment (&calls[idx_realloc]);
409 if (len > old_len)
411 /* Keep track of total memory consumption for `realloc'. */
412 catomic_add (&total[idx_realloc], len - old_len);
413 /* Keep track of total memory requirement. */
414 catomic_add (&grand_total, len - old_len);
417 if (len == 0 && old != NULL)
419 /* Special case. */
420 catomic_increment (&realloc_free);
421 /* Keep track of total memory freed using `free'. */
422 catomic_add (&total[idx_free], real->length);
424 /* Update the allocation data and write out the records if necessary. */
425 update_data (NULL, 0, old_len);
427 /* Do the real work. */
428 (*freep) (real);
430 return NULL;
433 /* Remember the size of the request. */
434 if (len < 65536)
435 catomic_increment (&histogram[len / 16]);
436 else
437 catomic_increment (&large);
438 /* Total number of calls of any of the functions. */
439 catomic_increment (&calls_total);
441 /* Do the real work. */
442 result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
443 if (result == NULL)
445 catomic_increment (&failed[idx_realloc]);
446 return NULL;
449 /* Record whether the reduction/increase happened in place. */
450 if (real == result)
451 catomic_increment (&inplace);
452 /* Was the buffer increased? */
453 if (old_len > len)
454 catomic_increment (&decreasing);
456 /* Update the allocation data and write out the records if necessary. */
457 update_data (result, len, old_len);
459 /* Return the pointer to the user buffer. */
460 return (void *) (result + 1);
464 /* `calloc' replacement. We keep track of the memory usage if this is the
465 correct program. */
466 void *
467 calloc (size_t n, size_t len)
469 struct header *result;
470 size_t size = n * len;
472 /* Determine real implementation if not already happened. */
473 if (__builtin_expect (initialized <= 0, 0))
475 if (initialized == -1)
476 return NULL;
477 me ();
480 /* If this is not the correct program just use the normal function. */
481 if (not_me)
482 return (*callocp) (n, len);
484 /* Keep track of number of calls. */
485 catomic_increment (&calls[idx_calloc]);
486 /* Keep track of total memory consumption for `calloc'. */
487 catomic_add (&total[idx_calloc], size);
488 /* Keep track of total memory requirement. */
489 catomic_add (&grand_total, size);
490 /* Remember the size of the request. */
491 if (size < 65536)
492 catomic_increment (&histogram[size / 16]);
493 else
494 catomic_increment (&large);
495 /* Total number of calls of any of the functions. */
496 ++calls_total;
498 /* Do the real work. */
499 result = (struct header *) (*mallocp) (size + sizeof (struct header));
500 if (result == NULL)
502 catomic_increment (&failed[idx_calloc]);
503 return NULL;
506 /* Update the allocation data and write out the records if necessary. */
507 update_data (result, size, 0);
509 /* Do what `calloc' would have done and return the buffer to the caller. */
510 return memset (result + 1, '\0', size);
514 /* `free' replacement. We keep track of the memory usage if this is the
515 correct program. */
516 void
517 free (void *ptr)
519 struct header *real;
521 /* Determine real implementation if not already happened. */
522 if (__builtin_expect (initialized <= 0, 0))
524 if (initialized == -1)
525 return;
526 me ();
529 /* If this is not the correct program just use the normal function. */
530 if (not_me)
532 (*freep) (ptr);
533 return;
536 /* `free (NULL)' has no effect. */
537 if (ptr == NULL)
539 catomic_increment (&calls[idx_free]);
540 return;
543 /* Determine the pointer to the header. */
544 real = ((struct header *) ptr) - 1;
545 if (real->magic != MAGIC)
547 /* This block wasn't allocated here. */
548 (*freep) (ptr);
549 return;
552 /* Keep track of number of calls. */
553 catomic_increment (&calls[idx_free]);
554 /* Keep track of total memory freed using `free'. */
555 catomic_add (&total[idx_free], real->length);
557 /* Update the allocation data and write out the records if necessary. */
558 update_data (NULL, 0, real->length);
560 /* Do the real work. */
561 (*freep) (real);
565 /* `mmap' replacement. We do not have to keep track of the sizesince
566 `munmap' will get it as a parameter. */
567 void *
568 mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
570 void *result = NULL;
572 /* Determine real implementation if not already happened. */
573 if (__builtin_expect (initialized <= 0, 0))
575 if (initialized == -1)
576 return NULL;
577 me ();
580 /* Always get a block. We don't need extra memory. */
581 result = (*mmapp) (start, len, prot, flags, fd, offset);
583 if (!not_me && trace_mmap)
585 int idx = (flags & MAP_ANON
586 ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
588 /* Keep track of number of calls. */
589 catomic_increment (&calls[idx]);
590 /* Keep track of total memory consumption for `malloc'. */
591 catomic_add (&total[idx], len);
592 /* Keep track of total memory requirement. */
593 catomic_add (&grand_total, len);
594 /* Remember the size of the request. */
595 if (len < 65536)
596 catomic_increment (&histogram[len / 16]);
597 else
598 catomic_increment (&large);
599 /* Total number of calls of any of the functions. */
600 catomic_increment (&calls_total);
602 /* Check for failures. */
603 if (result == NULL)
604 catomic_increment (&failed[idx]);
605 else if (idx == idx_mmap_w)
606 /* Update the allocation data and write out the records if
607 necessary. Note the first parameter is NULL which means
608 the size is not tracked. */
609 update_data (NULL, len, 0);
612 /* Return the pointer to the user buffer. */
613 return result;
617 /* `mmap' replacement. We do not have to keep track of the sizesince
618 `munmap' will get it as a parameter. */
619 void *
620 mmap64 (void *start, size_t len, int prot, int flags, int fd, off64_t offset)
622 void *result = NULL;
624 /* Determine real implementation if not already happened. */
625 if (__builtin_expect (initialized <= 0, 0))
627 if (initialized == -1)
628 return NULL;
629 me ();
632 /* Always get a block. We don't need extra memory. */
633 result = (*mmap64p) (start, len, prot, flags, fd, offset);
635 if (!not_me && trace_mmap)
637 int idx = (flags & MAP_ANON
638 ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
640 /* Keep track of number of calls. */
641 catomic_increment (&calls[idx]);
642 /* Keep track of total memory consumption for `malloc'. */
643 catomic_add (&total[idx], len);
644 /* Keep track of total memory requirement. */
645 catomic_add (&grand_total, len);
646 /* Remember the size of the request. */
647 if (len < 65536)
648 catomic_increment (&histogram[len / 16]);
649 else
650 catomic_increment (&large);
651 /* Total number of calls of any of the functions. */
652 catomic_increment (&calls_total);
654 /* Check for failures. */
655 if (result == NULL)
656 catomic_increment (&failed[idx]);
657 else if (idx == idx_mmap_w)
658 /* Update the allocation data and write out the records if
659 necessary. Note the first parameter is NULL which means
660 the size is not tracked. */
661 update_data (NULL, len, 0);
664 /* Return the pointer to the user buffer. */
665 return result;
669 /* `mmap' replacement. We do not have to keep track of the sizesince
670 `munmap' will get it as a parameter. */
671 void *
672 mremap (void *start, size_t old_len, size_t len, int flags, ...)
674 void *result = NULL;
675 va_list ap;
677 va_start (ap, flags);
678 void *newaddr = (flags & MREMAP_FIXED) ? va_arg (ap, void *) : NULL;
679 va_end (ap);
681 /* Determine real implementation if not already happened. */
682 if (__builtin_expect (initialized <= 0, 0))
684 if (initialized == -1)
685 return NULL;
686 me ();
689 /* Always get a block. We don't need extra memory. */
690 result = (*mremapp) (start, old_len, len, flags, newaddr);
692 if (!not_me && trace_mmap)
694 /* Keep track of number of calls. */
695 catomic_increment (&calls[idx_mremap]);
696 if (len > old_len)
698 /* Keep track of total memory consumption for `malloc'. */
699 catomic_add (&total[idx_mremap], len - old_len);
700 /* Keep track of total memory requirement. */
701 catomic_add (&grand_total, len - old_len);
703 /* Remember the size of the request. */
704 if (len < 65536)
705 catomic_increment (&histogram[len / 16]);
706 else
707 catomic_increment (&large);
708 /* Total number of calls of any of the functions. */
709 catomic_increment (&calls_total);
711 /* Check for failures. */
712 if (result == NULL)
713 catomic_increment (&failed[idx_mremap]);
714 else
716 /* Record whether the reduction/increase happened in place. */
717 if (start == result)
718 catomic_increment (&inplace_mremap);
719 /* Was the buffer increased? */
720 if (old_len > len)
721 catomic_increment (&decreasing_mremap);
723 /* Update the allocation data and write out the records if
724 necessary. Note the first parameter is NULL which means
725 the size is not tracked. */
726 update_data (NULL, len, old_len);
730 /* Return the pointer to the user buffer. */
731 return result;
735 /* `munmap' replacement. */
737 munmap (void *start, size_t len)
739 int result;
741 /* Determine real implementation if not already happened. */
742 if (__builtin_expect (initialized <= 0, 0))
744 if (initialized == -1)
745 return -1;
746 me ();
749 /* Do the real work. */
750 result = (*munmapp) (start, len);
752 if (!not_me && trace_mmap)
754 /* Keep track of number of calls. */
755 catomic_increment (&calls[idx_munmap]);
757 if (__builtin_expect (result == 0, 1))
759 /* Keep track of total memory freed using `free'. */
760 catomic_add (&total[idx_munmap], len);
762 /* Update the allocation data and write out the records if
763 necessary. */
764 update_data (NULL, 0, len);
766 else
767 catomic_increment (&failed[idx_munmap]);
770 return result;
774 /* Write some statistics to standard error. */
775 static void
776 __attribute__ ((destructor))
777 dest (void)
779 int percent, cnt;
780 unsigned long int maxcalls;
782 /* If we haven't done anything here just return. */
783 if (not_me)
784 return;
785 /* If we should call any of the memory functions don't do any profiling. */
786 not_me = true;
788 /* Finish the output file. */
789 if (fd != -1)
791 /* Write the partially filled buffer. */
792 if (buffer_cnt > buffer_size)
793 write (fd, buffer + buffer_size,
794 (buffer_cnt - buffer_size) * sizeof (struct entry));
795 else
796 write (fd, buffer, buffer_cnt * sizeof (struct entry));
798 /* Go back to the beginning of the file. We allocated two records
799 here when we opened the file. */
800 lseek (fd, 0, SEEK_SET);
801 /* Write out a record containing the total size. */
802 first.stack = peak_total;
803 write (fd, &first, sizeof (struct entry));
804 /* Write out another record containing the maximum for heap and
805 stack. */
806 first.heap = peak_heap;
807 first.stack = peak_stack;
808 GETTIME (first.time_low, first.time_high);
809 write (fd, &first, sizeof (struct entry));
811 /* Close the file. */
812 close (fd);
813 fd = -1;
816 /* Write a colorful statistic. */
817 fprintf (stderr, "\n\
818 \e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\
819 \e[04;34m total calls total memory failed calls\e[0m\n\
820 \e[00;34m malloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
821 \e[00;34mrealloc|\e[0m %10lu %12llu %s%12lu\e[00;00m (nomove:%ld, dec:%ld, free:%ld)\n\
822 \e[00;34m calloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
823 \e[00;34m free|\e[0m %10lu %12llu\n",
824 (unsigned long long int) grand_total, (unsigned long int) peak_heap,
825 (unsigned long int) peak_stack,
826 (unsigned long int) calls[idx_malloc],
827 (unsigned long long int) total[idx_malloc],
828 failed[idx_malloc] ? "\e[01;41m" : "",
829 (unsigned long int) failed[idx_malloc],
830 (unsigned long int) calls[idx_realloc],
831 (unsigned long long int) total[idx_realloc],
832 failed[idx_realloc] ? "\e[01;41m" : "",
833 (unsigned long int) failed[idx_realloc],
834 (unsigned long int) inplace,
835 (unsigned long int) decreasing,
836 (unsigned long int) realloc_free,
837 (unsigned long int) calls[idx_calloc],
838 (unsigned long long int) total[idx_calloc],
839 failed[idx_calloc] ? "\e[01;41m" : "",
840 (unsigned long int) failed[idx_calloc],
841 (unsigned long int) calls[idx_free],
842 (unsigned long long int) total[idx_free]);
844 if (trace_mmap)
845 fprintf (stderr, "\
846 \e[00;34mmmap(r)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
847 \e[00;34mmmap(w)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
848 \e[00;34mmmap(a)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
849 \e[00;34m mremap|\e[0m %10lu %12llu %s%12lu\e[00;00m (nomove: %ld, dec:%ld)\n\
850 \e[00;34m munmap|\e[0m %10lu %12llu %s%12lu\e[00;00m\n",
851 (unsigned long int) calls[idx_mmap_r],
852 (unsigned long long int) total[idx_mmap_r],
853 failed[idx_mmap_r] ? "\e[01;41m" : "",
854 (unsigned long int) failed[idx_mmap_r],
855 (unsigned long int) calls[idx_mmap_w],
856 (unsigned long long int) total[idx_mmap_w],
857 failed[idx_mmap_w] ? "\e[01;41m" : "",
858 (unsigned long int) failed[idx_mmap_w],
859 (unsigned long int) calls[idx_mmap_a],
860 (unsigned long long int) total[idx_mmap_a],
861 failed[idx_mmap_a] ? "\e[01;41m" : "",
862 (unsigned long int) failed[idx_mmap_a],
863 (unsigned long int) calls[idx_mremap],
864 (unsigned long long int) total[idx_mremap],
865 failed[idx_mremap] ? "\e[01;41m" : "",
866 (unsigned long int) failed[idx_mremap],
867 (unsigned long int) inplace_mremap,
868 (unsigned long int) decreasing_mremap,
869 (unsigned long int) calls[idx_munmap],
870 (unsigned long long int) total[idx_munmap],
871 failed[idx_munmap] ? "\e[01;41m" : "",
872 (unsigned long int) failed[idx_munmap]);
874 /* Write out a histoogram of the sizes of the allocations. */
875 fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
877 /* Determine the maximum of all calls for each size range. */
878 maxcalls = large;
879 for (cnt = 0; cnt < 65536; cnt += 16)
880 if (histogram[cnt / 16] > maxcalls)
881 maxcalls = histogram[cnt / 16];
883 for (cnt = 0; cnt < 65536; cnt += 16)
884 /* Only write out the nonzero entries. */
885 if (histogram[cnt / 16] != 0)
887 percent = (histogram[cnt / 16] * 100) / calls_total;
888 fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15,
889 (unsigned long int) histogram[cnt / 16]);
890 if (percent == 0)
891 fputs (" <1% \e[41;37m", stderr);
892 else
893 fprintf (stderr, "%3d%% \e[41;37m", percent);
895 /* Draw a bar with a length corresponding to the current
896 percentage. */
897 percent = (histogram[cnt / 16] * 50) / maxcalls;
898 while (percent-- > 0)
899 fputc ('=', stderr);
900 fputs ("\e[0;0m\n", stderr);
903 if (large != 0)
905 percent = (large * 100) / calls_total;
906 fprintf (stderr, " large %12lu ", (unsigned long int) large);
907 if (percent == 0)
908 fputs (" <1% \e[41;37m", stderr);
909 else
910 fprintf (stderr, "%3d%% \e[41;37m", percent);
911 percent = (large * 50) / maxcalls;
912 while (percent-- > 0)
913 fputc ('=', stderr);
914 fputs ("\e[0;0m\n", stderr);
917 /* Any following malloc/free etc. calls should generate statistics again,
918 because otherwise freeing something that has been malloced before
919 this destructor (including struct header in front of it) wouldn't
920 be properly freed. */
921 not_me = false;