2.12.90-11
[glibc.git] / malloc / memusage.c
blob382261c1c48e477210fc8234cb1a5dd8fe0c6dd5
1 /* Profile heap and stack memory usage of running program.
2 Copyright (C) 1998-2002, 2004-2006, 2009 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 <assert.h>
22 #include <atomic.h>
23 #include <dlfcn.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <inttypes.h>
27 #include <signal.h>
28 #include <stdarg.h>
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/mman.h>
35 #include <sys/time.h>
37 #include <memusage.h>
39 /* Pointer to the real functions. These are determined used `dlsym'
40 when really needed. */
41 static void *(*mallocp) (size_t);
42 static void *(*reallocp) (void *, size_t);
43 static void *(*callocp) (size_t, size_t);
44 static void (*freep) (void *);
46 static void *(*mmapp) (void *, size_t, int, int, int, off_t);
47 static void *(*mmap64p) (void *, size_t, int, int, int, off64_t);
48 static int (*munmapp) (void *, size_t);
49 static void *(*mremapp) (void *, size_t, size_t, int, void *);
51 enum
53 idx_malloc = 0,
54 idx_realloc,
55 idx_calloc,
56 idx_free,
57 idx_mmap_r,
58 idx_mmap_w,
59 idx_mmap_a,
60 idx_mremap,
61 idx_munmap,
62 idx_last
66 struct header
68 size_t length;
69 size_t magic;
72 #define MAGIC 0xfeedbeaf
75 static memusage_cntr_t calls[idx_last];
76 static memusage_cntr_t failed[idx_last];
77 static memusage_size_t total[idx_last];
78 static memusage_size_t grand_total;
79 static memusage_cntr_t histogram[65536 / 16];
80 static memusage_cntr_t large;
81 static memusage_cntr_t calls_total;
82 static memusage_cntr_t inplace;
83 static memusage_cntr_t decreasing;
84 static memusage_cntr_t realloc_free;
85 static memusage_cntr_t inplace_mremap;
86 static memusage_cntr_t decreasing_mremap;
87 static memusage_size_t current_heap;
88 static memusage_size_t peak_use[3];
89 static __thread uintptr_t start_sp;
91 /* A few macros to make the source more readable. */
92 #define peak_heap peak_use[0]
93 #define peak_stack peak_use[1]
94 #define peak_total peak_use[2]
96 #define DEFAULT_BUFFER_SIZE 32768
97 static size_t buffer_size;
99 static int fd = -1;
101 static bool not_me;
102 static int initialized;
103 static bool trace_mmap;
104 extern const char *__progname;
106 struct entry
108 uint64_t heap;
109 uint64_t stack;
110 uint32_t time_low;
111 uint32_t time_high;
114 static struct entry buffer[2 * DEFAULT_BUFFER_SIZE];
115 static uatomic32_t buffer_cnt;
116 static struct entry first;
119 /* Update the global data after a successful function call. */
120 static void
121 update_data (struct header *result, size_t len, size_t old_len)
123 if (result != NULL)
125 /* Record the information we need and mark the block using a
126 magic number. */
127 result->length = len;
128 result->magic = MAGIC;
131 /* Compute current heap usage and compare it with the maximum value. */
132 memusage_size_t heap
133 = catomic_exchange_and_add (&current_heap, len - old_len) + len - old_len;
134 catomic_max (&peak_heap, heap);
136 /* Compute current stack usage and compare it with the maximum
137 value. The base stack pointer might not be set if this is not
138 the main thread and it is the first call to any of these
139 functions. */
140 if (__builtin_expect (!start_sp, 0))
141 start_sp = GETSP ();
143 uintptr_t sp = GETSP ();
144 #ifdef STACK_GROWS_UPWARD
145 /* This can happen in threads where we didn't catch the thread's
146 stack early enough. */
147 if (__builtin_expect (sp < start_sp, 0))
148 start_sp = sp;
149 size_t current_stack = sp - start_sp;
150 #else
151 /* This can happen in threads where we didn't catch the thread's
152 stack early enough. */
153 if (__builtin_expect (sp > start_sp, 0))
154 start_sp = sp;
155 size_t current_stack = start_sp - sp;
156 #endif
157 catomic_max (&peak_stack, current_stack);
159 /* Add up heap and stack usage and compare it with the maximum value. */
160 catomic_max (&peak_total, heap + current_stack);
162 /* Store the value only if we are writing to a file. */
163 if (fd != -1)
165 uatomic32_t idx = catomic_exchange_and_add (&buffer_cnt, 1);
166 if (idx + 1 >= 2 * buffer_size)
168 /* We try to reset the counter to the correct range. If
169 this fails because of another thread increasing the
170 counter it does not matter since that thread will take
171 care of the correction. */
172 uatomic32_t reset = (idx + 1) % (2 * buffer_size);
173 catomic_compare_and_exchange_val_acq (&buffer_cnt, reset, idx + 1);
174 if (idx >= 2 * buffer_size)
175 idx = reset - 1;
177 assert (idx < 2 * DEFAULT_BUFFER_SIZE);
179 buffer[idx].heap = current_heap;
180 buffer[idx].stack = current_stack;
181 GETTIME (buffer[idx].time_low, buffer[idx].time_high);
183 /* Write out buffer if it is full. */
184 if (idx + 1 == buffer_size)
185 write (fd, buffer, buffer_size * sizeof (struct entry));
186 else if (idx + 1 == 2 * buffer_size)
187 write (fd, &buffer[buffer_size], buffer_size * sizeof (struct entry));
192 /* Interrupt handler. */
193 static void
194 int_handler (int signo)
196 /* Nothing gets allocated. Just record the stack pointer position. */
197 update_data (NULL, 0, 0);
201 /* Find out whether this is the program we are supposed to profile.
202 For this the name in the variable `__progname' must match the one
203 given in the environment variable MEMUSAGE_PROG_NAME. If the variable
204 is not present every program assumes it should be profiling.
206 If this is the program open a file descriptor to the output file.
207 We will write to it whenever the buffer overflows. The name of the
208 output file is determined by the environment variable MEMUSAGE_OUTPUT.
210 If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical
211 value determines the size of the internal buffer. The number gives
212 the number of elements in the buffer. By setting the number to one
213 one effectively selects unbuffered operation.
215 If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
216 which at the highest possible frequency records the stack pointer. */
217 static void
218 me (void)
220 const char *env = getenv ("MEMUSAGE_PROG_NAME");
221 size_t prog_len = strlen (__progname);
223 initialized = -1;
224 mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
225 reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
226 callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
227 freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
229 mmapp = (void *(*) (void *, size_t, int, int, int, off_t)) dlsym (RTLD_NEXT,
230 "mmap");
231 mmap64p =
232 (void *(*) (void *, size_t, int, int, int, off64_t)) dlsym (RTLD_NEXT,
233 "mmap64");
234 mremapp = (void *(*) (void *, size_t, size_t, int, void *)) dlsym (RTLD_NEXT,
235 "mremap");
236 munmapp = (int (*) (void *, size_t)) dlsym (RTLD_NEXT, "munmap");
237 initialized = 1;
239 if (env != NULL)
241 /* Check for program name. */
242 size_t len = strlen (env);
243 if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
244 || (prog_len != len && __progname[prog_len - len - 1] != '/'))
245 not_me = true;
248 /* Only open the file if it's really us. */
249 if (!not_me && fd == -1)
251 const char *outname;
253 if (!start_sp)
254 start_sp = GETSP ();
256 outname = getenv ("MEMUSAGE_OUTPUT");
257 if (outname != NULL && outname[0] != '\0'
258 && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
260 fd = creat64 (outname, 0666);
262 if (fd == -1)
263 /* Don't do anything in future calls if we cannot write to
264 the output file. */
265 not_me = true;
266 else
268 /* Write the first entry. */
269 first.heap = 0;
270 first.stack = 0;
271 GETTIME (first.time_low, first.time_high);
272 /* Write it two times since we need the starting and end time. */
273 write (fd, &first, sizeof (first));
274 write (fd, &first, sizeof (first));
276 /* Determine the buffer size. We use the default if the
277 environment variable is not present. */
278 buffer_size = DEFAULT_BUFFER_SIZE;
279 if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
281 buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
282 if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
283 buffer_size = DEFAULT_BUFFER_SIZE;
286 /* Possibly enable timer-based stack pointer retrieval. */
287 if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
289 struct sigaction act;
291 act.sa_handler = (sighandler_t) &int_handler;
292 act.sa_flags = SA_RESTART;
293 sigfillset (&act.sa_mask);
295 if (sigaction (SIGPROF, &act, NULL) >= 0)
297 struct itimerval timer;
299 timer.it_value.tv_sec = 0;
300 timer.it_value.tv_usec = 1;
301 timer.it_interval = timer.it_value;
302 setitimer (ITIMER_PROF, &timer, NULL);
308 if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL)
309 trace_mmap = true;
314 /* Record the initial stack position. */
315 static void
316 __attribute__ ((constructor))
317 init (void)
319 start_sp = GETSP ();
320 if (! initialized)
321 me ();
325 /* `malloc' replacement. We keep track of the memory usage if this is the
326 correct program. */
327 void *
328 malloc (size_t len)
330 struct header *result = NULL;
332 /* Determine real implementation if not already happened. */
333 if (__builtin_expect (initialized <= 0, 0))
335 if (initialized == -1)
336 return NULL;
337 me ();
340 /* If this is not the correct program just use the normal function. */
341 if (not_me)
342 return (*mallocp) (len);
344 /* Keep track of number of calls. */
345 catomic_increment (&calls[idx_malloc]);
346 /* Keep track of total memory consumption for `malloc'. */
347 catomic_add (&total[idx_malloc], len);
348 /* Keep track of total memory requirement. */
349 catomic_add (&grand_total, len);
350 /* Remember the size of the request. */
351 if (len < 65536)
352 catomic_increment (&histogram[len / 16]);
353 else
354 catomic_increment (&large);
355 /* Total number of calls of any of the functions. */
356 catomic_increment (&calls_total);
358 /* Do the real work. */
359 result = (struct header *) (*mallocp) (len + sizeof (struct header));
360 if (result == NULL)
362 catomic_increment (&failed[idx_malloc]);
363 return NULL;
366 /* Update the allocation data and write out the records if necessary. */
367 update_data (result, len, 0);
369 /* Return the pointer to the user buffer. */
370 return (void *) (result + 1);
374 /* `realloc' replacement. We keep track of the memory usage if this is the
375 correct program. */
376 void *
377 realloc (void *old, size_t len)
379 struct header *result = NULL;
380 struct header *real;
381 size_t old_len;
383 /* Determine real implementation if not already happened. */
384 if (__builtin_expect (initialized <= 0, 0))
386 if (initialized == -1)
387 return NULL;
388 me ();
391 /* If this is not the correct program just use the normal function. */
392 if (not_me)
393 return (*reallocp) (old, len);
395 if (old == NULL)
397 /* This is really a `malloc' call. */
398 real = NULL;
399 old_len = 0;
401 else
403 real = ((struct header *) old) - 1;
404 if (real->magic != MAGIC)
405 /* This is no memory allocated here. */
406 return (*reallocp) (old, len);
407 old_len = real->length;
410 /* Keep track of number of calls. */
411 catomic_increment (&calls[idx_realloc]);
412 if (len > old_len)
414 /* Keep track of total memory consumption for `realloc'. */
415 catomic_add (&total[idx_realloc], len - old_len);
416 /* Keep track of total memory requirement. */
417 catomic_add (&grand_total, len - old_len);
420 if (len == 0 && old != NULL)
422 /* Special case. */
423 catomic_increment (&realloc_free);
424 /* Keep track of total memory freed using `free'. */
425 catomic_add (&total[idx_free], real->length);
427 /* Update the allocation data and write out the records if necessary. */
428 update_data (NULL, 0, old_len);
430 /* Do the real work. */
431 (*freep) (real);
433 return NULL;
436 /* Remember the size of the request. */
437 if (len < 65536)
438 catomic_increment (&histogram[len / 16]);
439 else
440 catomic_increment (&large);
441 /* Total number of calls of any of the functions. */
442 catomic_increment (&calls_total);
444 /* Do the real work. */
445 result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
446 if (result == NULL)
448 catomic_increment (&failed[idx_realloc]);
449 return NULL;
452 /* Record whether the reduction/increase happened in place. */
453 if (real == result)
454 catomic_increment (&inplace);
455 /* Was the buffer increased? */
456 if (old_len > len)
457 catomic_increment (&decreasing);
459 /* Update the allocation data and write out the records if necessary. */
460 update_data (result, len, old_len);
462 /* Return the pointer to the user buffer. */
463 return (void *) (result + 1);
467 /* `calloc' replacement. We keep track of the memory usage if this is the
468 correct program. */
469 void *
470 calloc (size_t n, size_t len)
472 struct header *result;
473 size_t size = n * len;
475 /* Determine real implementation if not already happened. */
476 if (__builtin_expect (initialized <= 0, 0))
478 if (initialized == -1)
479 return NULL;
480 me ();
483 /* If this is not the correct program just use the normal function. */
484 if (not_me)
485 return (*callocp) (n, len);
487 /* Keep track of number of calls. */
488 catomic_increment (&calls[idx_calloc]);
489 /* Keep track of total memory consumption for `calloc'. */
490 catomic_add (&total[idx_calloc], size);
491 /* Keep track of total memory requirement. */
492 catomic_add (&grand_total, size);
493 /* Remember the size of the request. */
494 if (size < 65536)
495 catomic_increment (&histogram[size / 16]);
496 else
497 catomic_increment (&large);
498 /* Total number of calls of any of the functions. */
499 ++calls_total;
501 /* Do the real work. */
502 result = (struct header *) (*mallocp) (size + sizeof (struct header));
503 if (result == NULL)
505 catomic_increment (&failed[idx_calloc]);
506 return NULL;
509 /* Update the allocation data and write out the records if necessary. */
510 update_data (result, size, 0);
512 /* Do what `calloc' would have done and return the buffer to the caller. */
513 return memset (result + 1, '\0', size);
517 /* `free' replacement. We keep track of the memory usage if this is the
518 correct program. */
519 void
520 free (void *ptr)
522 struct header *real;
524 /* Determine real implementation if not already happened. */
525 if (__builtin_expect (initialized <= 0, 0))
527 if (initialized == -1)
528 return;
529 me ();
532 /* If this is not the correct program just use the normal function. */
533 if (not_me)
535 (*freep) (ptr);
536 return;
539 /* `free (NULL)' has no effect. */
540 if (ptr == NULL)
542 catomic_increment (&calls[idx_free]);
543 return;
546 /* Determine the pointer to the header. */
547 real = ((struct header *) ptr) - 1;
548 if (real->magic != MAGIC)
550 /* This block wasn't allocated here. */
551 (*freep) (ptr);
552 return;
555 /* Keep track of number of calls. */
556 catomic_increment (&calls[idx_free]);
557 /* Keep track of total memory freed using `free'. */
558 catomic_add (&total[idx_free], real->length);
560 /* Update the allocation data and write out the records if necessary. */
561 update_data (NULL, 0, real->length);
563 /* Do the real work. */
564 (*freep) (real);
568 /* `mmap' replacement. We do not have to keep track of the sizesince
569 `munmap' will get it as a parameter. */
570 void *
571 mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
573 void *result = NULL;
575 /* Determine real implementation if not already happened. */
576 if (__builtin_expect (initialized <= 0, 0))
578 if (initialized == -1)
579 return NULL;
580 me ();
583 /* Always get a block. We don't need extra memory. */
584 result = (*mmapp) (start, len, prot, flags, fd, offset);
586 if (!not_me && trace_mmap)
588 int idx = (flags & MAP_ANON
589 ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
591 /* Keep track of number of calls. */
592 catomic_increment (&calls[idx]);
593 /* Keep track of total memory consumption for `malloc'. */
594 catomic_add (&total[idx], len);
595 /* Keep track of total memory requirement. */
596 catomic_add (&grand_total, len);
597 /* Remember the size of the request. */
598 if (len < 65536)
599 catomic_increment (&histogram[len / 16]);
600 else
601 catomic_increment (&large);
602 /* Total number of calls of any of the functions. */
603 catomic_increment (&calls_total);
605 /* Check for failures. */
606 if (result == NULL)
607 catomic_increment (&failed[idx]);
608 else if (idx == idx_mmap_w)
609 /* Update the allocation data and write out the records if
610 necessary. Note the first parameter is NULL which means
611 the size is not tracked. */
612 update_data (NULL, len, 0);
615 /* Return the pointer to the user buffer. */
616 return result;
620 /* `mmap' replacement. We do not have to keep track of the sizesince
621 `munmap' will get it as a parameter. */
622 void *
623 mmap64 (void *start, size_t len, int prot, int flags, int fd, off64_t offset)
625 void *result = NULL;
627 /* Determine real implementation if not already happened. */
628 if (__builtin_expect (initialized <= 0, 0))
630 if (initialized == -1)
631 return NULL;
632 me ();
635 /* Always get a block. We don't need extra memory. */
636 result = (*mmap64p) (start, len, prot, flags, fd, offset);
638 if (!not_me && trace_mmap)
640 int idx = (flags & MAP_ANON
641 ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
643 /* Keep track of number of calls. */
644 catomic_increment (&calls[idx]);
645 /* Keep track of total memory consumption for `malloc'. */
646 catomic_add (&total[idx], len);
647 /* Keep track of total memory requirement. */
648 catomic_add (&grand_total, len);
649 /* Remember the size of the request. */
650 if (len < 65536)
651 catomic_increment (&histogram[len / 16]);
652 else
653 catomic_increment (&large);
654 /* Total number of calls of any of the functions. */
655 catomic_increment (&calls_total);
657 /* Check for failures. */
658 if (result == NULL)
659 catomic_increment (&failed[idx]);
660 else if (idx == idx_mmap_w)
661 /* Update the allocation data and write out the records if
662 necessary. Note the first parameter is NULL which means
663 the size is not tracked. */
664 update_data (NULL, len, 0);
667 /* Return the pointer to the user buffer. */
668 return result;
672 /* `mmap' replacement. We do not have to keep track of the sizesince
673 `munmap' will get it as a parameter. */
674 void *
675 mremap (void *start, size_t old_len, size_t len, int flags, ...)
677 void *result = NULL;
678 va_list ap;
680 va_start (ap, flags);
681 void *newaddr = (flags & MREMAP_FIXED) ? va_arg (ap, void *) : NULL;
682 va_end (ap);
684 /* Determine real implementation if not already happened. */
685 if (__builtin_expect (initialized <= 0, 0))
687 if (initialized == -1)
688 return NULL;
689 me ();
692 /* Always get a block. We don't need extra memory. */
693 result = (*mremapp) (start, old_len, len, flags, newaddr);
695 if (!not_me && trace_mmap)
697 /* Keep track of number of calls. */
698 catomic_increment (&calls[idx_mremap]);
699 if (len > old_len)
701 /* Keep track of total memory consumption for `malloc'. */
702 catomic_add (&total[idx_mremap], len - old_len);
703 /* Keep track of total memory requirement. */
704 catomic_add (&grand_total, len - old_len);
706 /* Remember the size of the request. */
707 if (len < 65536)
708 catomic_increment (&histogram[len / 16]);
709 else
710 catomic_increment (&large);
711 /* Total number of calls of any of the functions. */
712 catomic_increment (&calls_total);
714 /* Check for failures. */
715 if (result == NULL)
716 catomic_increment (&failed[idx_mremap]);
717 else
719 /* Record whether the reduction/increase happened in place. */
720 if (start == result)
721 catomic_increment (&inplace_mremap);
722 /* Was the buffer increased? */
723 if (old_len > len)
724 catomic_increment (&decreasing_mremap);
726 /* Update the allocation data and write out the records if
727 necessary. Note the first parameter is NULL which means
728 the size is not tracked. */
729 update_data (NULL, len, old_len);
733 /* Return the pointer to the user buffer. */
734 return result;
738 /* `munmap' replacement. */
740 munmap (void *start, size_t len)
742 int result;
744 /* Determine real implementation if not already happened. */
745 if (__builtin_expect (initialized <= 0, 0))
747 if (initialized == -1)
748 return -1;
749 me ();
752 /* Do the real work. */
753 result = (*munmapp) (start, len);
755 if (!not_me && trace_mmap)
757 /* Keep track of number of calls. */
758 catomic_increment (&calls[idx_munmap]);
760 if (__builtin_expect (result == 0, 1))
762 /* Keep track of total memory freed using `free'. */
763 catomic_add (&total[idx_munmap], len);
765 /* Update the allocation data and write out the records if
766 necessary. */
767 update_data (NULL, 0, len);
769 else
770 catomic_increment (&failed[idx_munmap]);
773 return result;
777 /* Write some statistics to standard error. */
778 static void
779 __attribute__ ((destructor))
780 dest (void)
782 int percent, cnt;
783 unsigned long int maxcalls;
785 /* If we haven't done anything here just return. */
786 if (not_me)
787 return;
788 /* If we should call any of the memory functions don't do any profiling. */
789 not_me = true;
791 /* Finish the output file. */
792 if (fd != -1)
794 /* Write the partially filled buffer. */
795 if (buffer_cnt > buffer_size)
796 write (fd, buffer + buffer_size,
797 (buffer_cnt - buffer_size) * sizeof (struct entry));
798 else
799 write (fd, buffer, buffer_cnt * sizeof (struct entry));
801 /* Go back to the beginning of the file. We allocated two records
802 here when we opened the file. */
803 lseek (fd, 0, SEEK_SET);
804 /* Write out a record containing the total size. */
805 first.stack = peak_total;
806 write (fd, &first, sizeof (struct entry));
807 /* Write out another record containing the maximum for heap and
808 stack. */
809 first.heap = peak_heap;
810 first.stack = peak_stack;
811 GETTIME (first.time_low, first.time_high);
812 write (fd, &first, sizeof (struct entry));
814 /* Close the file. */
815 close (fd);
816 fd = -1;
819 /* Write a colorful statistic. */
820 fprintf (stderr, "\n\
821 \e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\
822 \e[04;34m total calls total memory failed calls\e[0m\n\
823 \e[00;34m malloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
824 \e[00;34mrealloc|\e[0m %10lu %12llu %s%12lu\e[00;00m (nomove:%ld, dec:%ld, free:%ld)\n\
825 \e[00;34m calloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
826 \e[00;34m free|\e[0m %10lu %12llu\n",
827 (unsigned long long int) grand_total, (unsigned long int) peak_heap,
828 (unsigned long int) peak_stack,
829 (unsigned long int) calls[idx_malloc],
830 (unsigned long long int) total[idx_malloc],
831 failed[idx_malloc] ? "\e[01;41m" : "",
832 (unsigned long int) failed[idx_malloc],
833 (unsigned long int) calls[idx_realloc],
834 (unsigned long long int) total[idx_realloc],
835 failed[idx_realloc] ? "\e[01;41m" : "",
836 (unsigned long int) failed[idx_realloc],
837 (unsigned long int) inplace,
838 (unsigned long int) decreasing,
839 (unsigned long int) realloc_free,
840 (unsigned long int) calls[idx_calloc],
841 (unsigned long long int) total[idx_calloc],
842 failed[idx_calloc] ? "\e[01;41m" : "",
843 (unsigned long int) failed[idx_calloc],
844 (unsigned long int) calls[idx_free],
845 (unsigned long long int) total[idx_free]);
847 if (trace_mmap)
848 fprintf (stderr, "\
849 \e[00;34mmmap(r)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
850 \e[00;34mmmap(w)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
851 \e[00;34mmmap(a)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
852 \e[00;34m mremap|\e[0m %10lu %12llu %s%12lu\e[00;00m (nomove: %ld, dec:%ld)\n\
853 \e[00;34m munmap|\e[0m %10lu %12llu %s%12lu\e[00;00m\n",
854 (unsigned long int) calls[idx_mmap_r],
855 (unsigned long long int) total[idx_mmap_r],
856 failed[idx_mmap_r] ? "\e[01;41m" : "",
857 (unsigned long int) failed[idx_mmap_r],
858 (unsigned long int) calls[idx_mmap_w],
859 (unsigned long long int) total[idx_mmap_w],
860 failed[idx_mmap_w] ? "\e[01;41m" : "",
861 (unsigned long int) failed[idx_mmap_w],
862 (unsigned long int) calls[idx_mmap_a],
863 (unsigned long long int) total[idx_mmap_a],
864 failed[idx_mmap_a] ? "\e[01;41m" : "",
865 (unsigned long int) failed[idx_mmap_a],
866 (unsigned long int) calls[idx_mremap],
867 (unsigned long long int) total[idx_mremap],
868 failed[idx_mremap] ? "\e[01;41m" : "",
869 (unsigned long int) failed[idx_mremap],
870 (unsigned long int) inplace_mremap,
871 (unsigned long int) decreasing_mremap,
872 (unsigned long int) calls[idx_munmap],
873 (unsigned long long int) total[idx_munmap],
874 failed[idx_munmap] ? "\e[01;41m" : "",
875 (unsigned long int) failed[idx_munmap]);
877 /* Write out a histoogram of the sizes of the allocations. */
878 fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
880 /* Determine the maximum of all calls for each size range. */
881 maxcalls = large;
882 for (cnt = 0; cnt < 65536; cnt += 16)
883 if (histogram[cnt / 16] > maxcalls)
884 maxcalls = histogram[cnt / 16];
886 for (cnt = 0; cnt < 65536; cnt += 16)
887 /* Only write out the nonzero entries. */
888 if (histogram[cnt / 16] != 0)
890 percent = (histogram[cnt / 16] * 100) / calls_total;
891 fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15,
892 (unsigned long int) histogram[cnt / 16]);
893 if (percent == 0)
894 fputs (" <1% \e[41;37m", stderr);
895 else
896 fprintf (stderr, "%3d%% \e[41;37m", percent);
898 /* Draw a bar with a length corresponding to the current
899 percentage. */
900 percent = (histogram[cnt / 16] * 50) / maxcalls;
901 while (percent-- > 0)
902 fputc ('=', stderr);
903 fputs ("\e[0;0m\n", stderr);
906 if (large != 0)
908 percent = (large * 100) / calls_total;
909 fprintf (stderr, " large %12lu ", (unsigned long int) large);
910 if (percent == 0)
911 fputs (" <1% \e[41;37m", stderr);
912 else
913 fprintf (stderr, "%3d%% \e[41;37m", percent);
914 percent = (large * 50) / maxcalls;
915 while (percent-- > 0)
916 fputc ('=', stderr);
917 fputs ("\e[0;0m\n", stderr);
920 /* Any following malloc/free etc. calls should generate statistics again,
921 because otherwise freeing something that has been malloced before
922 this destructor (including struct header in front of it) wouldn't
923 be properly freed. */
924 not_me = false;