[BZ #2517]
[glibc.git] / malloc / memusage.c
blob8b37c43a8afb0ecfd2ce7790a7e0646aceefb341
1 /* Profile heap and stack memory usage of running program.
2 Copyright (C) 1998-2002, 2004, 2005 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 inplace_mremap;
84 static memusage_cntr_t decreasing_mremap;
85 static memusage_size_t current_heap;
86 static memusage_size_t peak_use[3];
87 static __thread uintptr_t start_sp;
89 /* A few macros to make the source more readable. */
90 #define peak_heap peak_use[0]
91 #define peak_stack peak_use[1]
92 #define peak_total peak_use[2]
94 #define DEFAULT_BUFFER_SIZE 1024
95 static size_t buffer_size;
97 static int fd = -1;
99 static bool not_me;
100 static int initialized;
101 static bool trace_mmap;
102 extern const char *__progname;
104 struct entry
106 size_t heap;
107 size_t stack;
108 uint32_t time_low;
109 uint32_t time_high;
112 static struct entry buffer[2 * DEFAULT_BUFFER_SIZE];
113 static uatomic32_t buffer_cnt;
114 static struct entry first;
117 /* Update the global data after a successful function call. */
118 static void
119 update_data (struct header *result, size_t len, size_t old_len)
121 if (result != NULL)
123 /* Record the information we need and mark the block using a
124 magic number. */
125 result->length = len;
126 result->magic = MAGIC;
129 /* Compute current heap usage and compare it with the maximum value. */
130 memusage_size_t heap
131 = atomic_exchange_and_add (&current_heap, len - old_len) + len - old_len;
132 atomic_max (&peak_heap, heap);
134 /* Compute current stack usage and compare it with the maximum
135 value. The base stack pointer might not be set if this is not
136 the main thread and it is the first call to any of these
137 functions. */
138 if (__builtin_expect (!start_sp, 0))
139 start_sp = GETSP ();
141 uintptr_t sp = GETSP ();
142 #ifdef STACK_GROWS_UPWARD
143 /* This can happen in threads where we didn't catch the thread's
144 stack early enough. */
145 if (__builtin_expect (sp < start_sp, 0))
146 start_sp = sp;
147 size_t current_stack = sp - start_sp;
148 #else
149 /* This can happen in threads where we didn't catch the thread's
150 stack early enough. */
151 if (__builtin_expect (sp > start_sp, 0))
152 start_sp = sp;
153 size_t current_stack = start_sp - sp;
154 #endif
155 atomic_max (&peak_stack, current_stack);
157 /* Add up heap and stack usage and compare it with the maximum value. */
158 atomic_max (&peak_total, heap + current_stack);
160 /* Store the value only if we are writing to a file. */
161 if (fd != -1)
163 uatomic32_t idx = atomic_exchange_and_add (&buffer_cnt, 1);
164 if (idx >= 2 * buffer_size)
166 /* We try to reset the counter to the correct range. If
167 this fails because of another thread increasing the
168 counter it does not matter since that thread will take
169 care of the correction. */
170 unsigned int reset = idx - 2 * buffer_size;
171 atomic_compare_and_exchange_val_acq (&buffer_size, reset, idx);
172 idx = reset;
175 buffer[idx].heap = current_heap;
176 buffer[idx].stack = current_stack;
177 GETTIME (buffer[idx].time_low, buffer[idx].time_high);
179 /* Write out buffer if it is full. */
180 if (idx + 1 == buffer_size)
181 write (fd, buffer, buffer_size * sizeof (struct entry));
182 else if (idx + 1 == 2 * buffer_size)
183 write (fd, &buffer[buffer_size], buffer_size * sizeof (struct entry));
188 /* Interrupt handler. */
189 static void
190 int_handler (int signo)
192 /* Nothing gets allocated. Just record the stack pointer position. */
193 update_data (NULL, 0, 0);
197 /* Find out whether this is the program we are supposed to profile.
198 For this the name in the variable `__progname' must match the one
199 given in the environment variable MEMUSAGE_PROG_NAME. If the variable
200 is not present every program assumes it should be profiling.
202 If this is the program open a file descriptor to the output file.
203 We will write to it whenever the buffer overflows. The name of the
204 output file is determined by the environment variable MEMUSAGE_OUTPUT.
206 If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical
207 value determines the size of the internal buffer. The number gives
208 the number of elements in the buffer. By setting the number to one
209 one effectively selects unbuffered operation.
211 If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
212 which at the highest possible frequency records the stack pointer. */
213 static void
214 me (void)
216 const char *env = getenv ("MEMUSAGE_PROG_NAME");
217 size_t prog_len = strlen (__progname);
219 initialized = -1;
220 mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
221 reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
222 callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
223 freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
225 mmapp = (void *(*) (void *, size_t, int, int, int, off_t)) dlsym (RTLD_NEXT,
226 "mmap");
227 mmap64p =
228 (void *(*) (void *, size_t, int, int, int, off64_t)) dlsym (RTLD_NEXT,
229 "mmap64");
230 mremapp = (void *(*) (void *, size_t, size_t, int, void *)) dlsym (RTLD_NEXT,
231 "mremap");
232 munmapp = (int (*) (void *, size_t)) dlsym (RTLD_NEXT, "munmap");
233 initialized = 1;
235 if (env != NULL)
237 /* Check for program name. */
238 size_t len = strlen (env);
239 if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
240 || (prog_len != len && __progname[prog_len - len - 1] != '/'))
241 not_me = true;
244 /* Only open the file if it's really us. */
245 if (!not_me && fd == -1)
247 const char *outname;
249 if (!start_sp)
250 start_sp = GETSP ();
252 outname = getenv ("MEMUSAGE_OUTPUT");
253 if (outname != NULL && outname[0] != '\0'
254 && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
256 fd = creat64 (outname, 0666);
258 if (fd == -1)
259 /* Don't do anything in future calls if we cannot write to
260 the output file. */
261 not_me = true;
262 else
264 /* Write the first entry. */
265 first.heap = 0;
266 first.stack = 0;
267 GETTIME (first.time_low, first.time_high);
268 /* Write it two times since we need the starting and end time. */
269 write (fd, &first, sizeof (first));
271 /* Determine the buffer size. We use the default if the
272 environment variable is not present. */
273 buffer_size = DEFAULT_BUFFER_SIZE;
274 if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
276 buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
277 if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
278 buffer_size = DEFAULT_BUFFER_SIZE;
281 /* Possibly enable timer-based stack pointer retrieval. */
282 if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
284 struct sigaction act;
286 act.sa_handler = (sighandler_t) &int_handler;
287 act.sa_flags = SA_RESTART;
288 sigfillset (&act.sa_mask);
290 if (sigaction (SIGPROF, &act, NULL) >= 0)
292 struct itimerval timer;
294 timer.it_value.tv_sec = 0;
295 timer.it_value.tv_usec = 1;
296 timer.it_interval = timer.it_value;
297 setitimer (ITIMER_PROF, &timer, NULL);
303 if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL)
304 trace_mmap = true;
309 /* Record the initial stack position. */
310 static void
311 __attribute__ ((constructor))
312 init (void)
314 start_sp = GETSP ();
315 if (! initialized)
316 me ();
320 /* `malloc' replacement. We keep track of the memory usage if this is the
321 correct program. */
322 void *
323 malloc (size_t len)
325 struct header *result = NULL;
327 /* Determine real implementation if not already happened. */
328 if (__builtin_expect (initialized <= 0, 0))
330 if (initialized == -1)
331 return NULL;
332 me ();
335 /* If this is not the correct program just use the normal function. */
336 if (not_me)
337 return (*mallocp) (len);
339 /* Keep track of number of calls. */
340 atomic_increment (&calls[idx_malloc]);
341 /* Keep track of total memory consumption for `malloc'. */
342 atomic_add (&total[idx_malloc], len);
343 /* Keep track of total memory requirement. */
344 atomic_add (&grand_total, len);
345 /* Remember the size of the request. */
346 if (len < 65536)
347 atomic_increment (&histogram[len / 16]);
348 else
349 atomic_increment (&large);
350 /* Total number of calls of any of the functions. */
351 atomic_increment (&calls_total);
353 /* Do the real work. */
354 result = (struct header *) (*mallocp) (len + sizeof (struct header));
355 if (result == NULL)
357 atomic_increment (&failed[idx_malloc]);
358 return NULL;
361 /* Update the allocation data and write out the records if necessary. */
362 update_data (result, len, 0);
364 /* Return the pointer to the user buffer. */
365 return (void *) (result + 1);
369 /* `realloc' replacement. We keep track of the memory usage if this is the
370 correct program. */
371 void *
372 realloc (void *old, size_t len)
374 struct header *result = NULL;
375 struct header *real;
376 size_t old_len;
378 /* Determine real implementation if not already happened. */
379 if (__builtin_expect (initialized <= 0, 0))
381 if (initialized == -1)
382 return NULL;
383 me ();
386 /* If this is not the correct program just use the normal function. */
387 if (not_me)
388 return (*reallocp) (old, len);
390 if (old == NULL)
392 /* This is really a `malloc' call. */
393 real = NULL;
394 old_len = 0;
396 else
398 real = ((struct header *) old) - 1;
399 if (real->magic != MAGIC)
400 /* This is no memory allocated here. */
401 return (*reallocp) (old, len);
402 old_len = real->length;
405 /* Keep track of number of calls. */
406 atomic_increment (&calls[idx_realloc]);
407 if (len > old_len)
409 /* Keep track of total memory consumption for `realloc'. */
410 atomic_add (&total[idx_realloc], len - old_len);
411 /* Keep track of total memory requirement. */
412 atomic_add (&grand_total, len - old_len);
414 /* Remember the size of the request. */
415 if (len < 65536)
416 atomic_increment (&histogram[len / 16]);
417 else
418 atomic_increment (&large);
419 /* Total number of calls of any of the functions. */
420 atomic_increment (&calls_total);
422 /* Do the real work. */
423 result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
424 if (result == NULL)
426 atomic_increment (&failed[idx_realloc]);
427 return NULL;
430 /* Record whether the reduction/increase happened in place. */
431 if (real == result)
432 atomic_increment (&inplace);
433 /* Was the buffer increased? */
434 if (old_len > len)
435 atomic_increment (&decreasing);
437 /* Update the allocation data and write out the records if necessary. */
438 update_data (result, len, old_len);
440 /* Return the pointer to the user buffer. */
441 return (void *) (result + 1);
445 /* `calloc' replacement. We keep track of the memory usage if this is the
446 correct program. */
447 void *
448 calloc (size_t n, size_t len)
450 struct header *result;
451 size_t size = n * len;
453 /* Determine real implementation if not already happened. */
454 if (__builtin_expect (initialized <= 0, 0))
456 if (initialized == -1)
457 return NULL;
458 me ();
461 /* If this is not the correct program just use the normal function. */
462 if (not_me)
463 return (*callocp) (n, len);
465 /* Keep track of number of calls. */
466 atomic_increment (&calls[idx_calloc]);
467 /* Keep track of total memory consumption for `calloc'. */
468 atomic_add (&total[idx_calloc], size);
469 /* Keep track of total memory requirement. */
470 atomic_add (&grand_total, size);
471 /* Remember the size of the request. */
472 if (size < 65536)
473 atomic_increment (&histogram[size / 16]);
474 else
475 atomic_increment (&large);
476 /* Total number of calls of any of the functions. */
477 ++calls_total;
479 /* Do the real work. */
480 result = (struct header *) (*mallocp) (size + sizeof (struct header));
481 if (result == NULL)
483 atomic_increment (&failed[idx_calloc]);
484 return NULL;
487 /* Update the allocation data and write out the records if necessary. */
488 update_data (result, size, 0);
490 /* Do what `calloc' would have done and return the buffer to the caller. */
491 return memset (result + 1, '\0', size);
495 /* `free' replacement. We keep track of the memory usage if this is the
496 correct program. */
497 void
498 free (void *ptr)
500 struct header *real;
502 /* Determine real implementation if not already happened. */
503 if (__builtin_expect (initialized <= 0, 0))
505 if (initialized == -1)
506 return;
507 me ();
510 /* If this is not the correct program just use the normal function. */
511 if (not_me)
513 (*freep) (ptr);
514 return;
517 /* `free (NULL)' has no effect. */
518 if (ptr == NULL)
520 atomic_increment (&calls[idx_free]);
521 return;
524 /* Determine the pointer to the header. */
525 real = ((struct header *) ptr) - 1;
526 if (real->magic != MAGIC)
528 /* This block wasn't allocated here. */
529 (*freep) (ptr);
530 return;
533 /* Keep track of number of calls. */
534 atomic_increment (&calls[idx_free]);
535 /* Keep track of total memory freed using `free'. */
536 atomic_add (&total[idx_free], real->length);
538 /* Update the allocation data and write out the records if necessary. */
539 update_data (NULL, 0, real->length);
541 /* Do the real work. */
542 (*freep) (real);
546 /* `mmap' replacement. We do not have to keep track of the sizesince
547 `munmap' will get it as a parameter. */
548 void *
549 mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
551 void *result = NULL;
553 /* Determine real implementation if not already happened. */
554 if (__builtin_expect (initialized <= 0, 0))
556 if (initialized == -1)
557 return NULL;
558 me ();
561 /* Always get a block. We don't need extra memory. */
562 result = (*mmapp) (start, len, prot, flags, fd, offset);
564 if (!not_me && trace_mmap)
566 int idx = (flags & MAP_ANON
567 ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
569 /* Keep track of number of calls. */
570 atomic_increment (&calls[idx]);
571 /* Keep track of total memory consumption for `malloc'. */
572 atomic_add (&total[idx], len);
573 /* Keep track of total memory requirement. */
574 atomic_add (&grand_total, len);
575 /* Remember the size of the request. */
576 if (len < 65536)
577 atomic_increment (&histogram[len / 16]);
578 else
579 atomic_increment (&large);
580 /* Total number of calls of any of the functions. */
581 atomic_increment (&calls_total);
583 /* Check for failures. */
584 if (result == NULL)
585 atomic_increment (&failed[idx]);
586 else if (idx == idx_mmap_w)
587 /* Update the allocation data and write out the records if
588 necessary. Note the first parameter is NULL which means
589 the size is not tracked. */
590 update_data (NULL, len, 0);
593 /* Return the pointer to the user buffer. */
594 return result;
598 /* `mmap' replacement. We do not have to keep track of the sizesince
599 `munmap' will get it as a parameter. */
600 void *
601 mmap64 (void *start, size_t len, int prot, int flags, int fd, off64_t offset)
603 void *result = NULL;
605 /* Determine real implementation if not already happened. */
606 if (__builtin_expect (initialized <= 0, 0))
608 if (initialized == -1)
609 return NULL;
610 me ();
613 /* Always get a block. We don't need extra memory. */
614 result = (*mmap64p) (start, len, prot, flags, fd, offset);
616 if (!not_me && trace_mmap)
618 int idx = (flags & MAP_ANON
619 ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
621 /* Keep track of number of calls. */
622 atomic_increment (&calls[idx]);
623 /* Keep track of total memory consumption for `malloc'. */
624 atomic_add (&total[idx], len);
625 /* Keep track of total memory requirement. */
626 atomic_add (&grand_total, len);
627 /* Remember the size of the request. */
628 if (len < 65536)
629 atomic_increment (&histogram[len / 16]);
630 else
631 atomic_increment (&large);
632 /* Total number of calls of any of the functions. */
633 atomic_increment (&calls_total);
635 /* Check for failures. */
636 if (result == NULL)
637 atomic_increment (&failed[idx]);
638 else if (idx == idx_mmap_w)
639 /* Update the allocation data and write out the records if
640 necessary. Note the first parameter is NULL which means
641 the size is not tracked. */
642 update_data (NULL, len, 0);
645 /* Return the pointer to the user buffer. */
646 return result;
650 /* `mmap' replacement. We do not have to keep track of the sizesince
651 `munmap' will get it as a parameter. */
652 void *
653 mremap (void *start, size_t old_len, size_t len, int flags, ...)
655 void *result = NULL;
656 va_list ap;
658 va_start (ap, flags);
659 void *newaddr = (flags & MREMAP_FIXED) ? va_arg (ap, void *) : NULL;
660 va_end (ap);
662 /* Determine real implementation if not already happened. */
663 if (__builtin_expect (initialized <= 0, 0))
665 if (initialized == -1)
666 return NULL;
667 me ();
670 /* Always get a block. We don't need extra memory. */
671 result = (*mremapp) (start, old_len, len, flags, newaddr);
673 if (!not_me && trace_mmap)
675 /* Keep track of number of calls. */
676 atomic_increment (&calls[idx_mremap]);
677 if (len > old_len)
679 /* Keep track of total memory consumption for `malloc'. */
680 atomic_add (&total[idx_mremap], len - old_len);
681 /* Keep track of total memory requirement. */
682 atomic_add (&grand_total, len - old_len);
684 /* Remember the size of the request. */
685 if (len < 65536)
686 atomic_increment (&histogram[len / 16]);
687 else
688 atomic_increment (&large);
689 /* Total number of calls of any of the functions. */
690 atomic_increment (&calls_total);
692 /* Check for failures. */
693 if (result == NULL)
694 atomic_increment (&failed[idx_mremap]);
695 else
697 /* Record whether the reduction/increase happened in place. */
698 if (start == result)
699 atomic_increment (&inplace_mremap);
700 /* Was the buffer increased? */
701 if (old_len > len)
702 atomic_increment (&decreasing_mremap);
704 /* Update the allocation data and write out the records if
705 necessary. Note the first parameter is NULL which means
706 the size is not tracked. */
707 update_data (NULL, len, old_len);
711 /* Return the pointer to the user buffer. */
712 return result;
716 /* `munmap' replacement. */
718 munmap (void *start, size_t len)
720 int result;
722 /* Determine real implementation if not already happened. */
723 if (__builtin_expect (initialized <= 0, 0))
725 if (initialized == -1)
726 return -1;
727 me ();
730 /* Do the real work. */
731 result = (*munmapp) (start, len);
733 if (!not_me && trace_mmap)
735 /* Keep track of number of calls. */
736 atomic_increment (&calls[idx_munmap]);
738 if (__builtin_expect (result == 0, 1))
740 /* Keep track of total memory freed using `free'. */
741 atomic_add (&total[idx_munmap], len);
743 /* Update the allocation data and write out the records if
744 necessary. */
745 update_data (NULL, 0, len);
747 else
748 atomic_increment (&failed[idx_munmap]);
751 return result;
755 /* Write some statistics to standard error. */
756 static void
757 __attribute__ ((destructor))
758 dest (void)
760 int percent, cnt;
761 unsigned long int maxcalls;
763 /* If we haven't done anything here just return. */
764 if (not_me)
765 return;
766 /* If we should call any of the memory functions don't do any profiling. */
767 not_me = true;
769 /* Finish the output file. */
770 if (fd != -1)
772 /* Write the partially filled buffer. */
773 write (fd, buffer, buffer_cnt * sizeof (struct entry));
774 /* Go back to the beginning of the file. We allocated two records
775 here when we opened the file. */
776 lseek (fd, 0, SEEK_SET);
777 /* Write out a record containing the total size. */
778 first.stack = peak_total;
779 write (fd, &first, sizeof (struct entry));
780 /* Write out another record containing the maximum for heap and
781 stack. */
782 first.heap = peak_heap;
783 first.stack = peak_stack;
784 GETTIME (first.time_low, first.time_high);
785 write (fd, &first, sizeof (struct entry));
787 /* Close the file. */
788 close (fd);
789 fd = -1;
792 /* Write a colorful statistic. */
793 fprintf (stderr, "\n\
794 \e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\
795 \e[04;34m total calls total memory failed calls\e[0m\n\
796 \e[00;34m malloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
797 \e[00;34mrealloc|\e[0m %10lu %12llu %s%12lu\e[00;00m (in place: %ld, dec: %ld)\n\
798 \e[00;34m calloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
799 \e[00;34m free|\e[0m %10lu %12llu\n",
800 (unsigned long long int) grand_total, (unsigned long int) peak_heap,
801 (unsigned long int) peak_stack,
802 (unsigned long int) calls[idx_malloc],
803 (unsigned long long int) total[idx_malloc],
804 failed[idx_malloc] ? "\e[01;41m" : "",
805 (unsigned long int) failed[idx_malloc],
806 (unsigned long int) calls[idx_realloc],
807 (unsigned long long int) total[idx_realloc],
808 failed[idx_realloc] ? "\e[01;41m" : "",
809 (unsigned long int) failed[idx_realloc],
810 (unsigned long int) inplace, (unsigned long int) decreasing,
811 (unsigned long int) calls[idx_calloc],
812 (unsigned long long int) total[idx_calloc],
813 failed[idx_calloc] ? "\e[01;41m" : "",
814 (unsigned long int) failed[idx_calloc],
815 (unsigned long int) calls[idx_free],
816 (unsigned long long int) total[idx_free]);
818 if (trace_mmap)
819 fprintf (stderr, "\
820 \e[00;34mmmap(r)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
821 \e[00;34mmmap(w)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
822 \e[00;34mmmap(a)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
823 \e[00;34m mremap|\e[0m %10lu %12llu %s%12lu\e[00;00m (in place: %ld, dec: %ld)\n\
824 \e[00;34m munmap|\e[0m %10lu %12llu %s%12lu\e[00;00m\n",
825 (unsigned long int) calls[idx_mmap_r],
826 (unsigned long long int) total[idx_mmap_r],
827 failed[idx_mmap_r] ? "\e[01;41m" : "",
828 (unsigned long int) failed[idx_mmap_r],
829 (unsigned long int) calls[idx_mmap_w],
830 (unsigned long long int) total[idx_mmap_w],
831 failed[idx_mmap_w] ? "\e[01;41m" : "",
832 (unsigned long int) failed[idx_mmap_w],
833 (unsigned long int) calls[idx_mmap_a],
834 (unsigned long long int) total[idx_mmap_a],
835 failed[idx_mmap_a] ? "\e[01;41m" : "",
836 (unsigned long int) failed[idx_mmap_a],
837 (unsigned long int) calls[idx_mremap],
838 (unsigned long long int) total[idx_mremap],
839 failed[idx_mremap] ? "\e[01;41m" : "",
840 (unsigned long int) failed[idx_mremap],
841 (unsigned long int) inplace_mremap,
842 (unsigned long int) decreasing_mremap,
843 (unsigned long int) calls[idx_munmap],
844 (unsigned long long int) total[idx_munmap],
845 failed[idx_munmap] ? "\e[01;41m" : "",
846 (unsigned long int) failed[idx_munmap]);
848 /* Write out a histoogram of the sizes of the allocations. */
849 fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
851 /* Determine the maximum of all calls for each size range. */
852 maxcalls = large;
853 for (cnt = 0; cnt < 65536; cnt += 16)
854 if (histogram[cnt / 16] > maxcalls)
855 maxcalls = histogram[cnt / 16];
857 for (cnt = 0; cnt < 65536; cnt += 16)
858 /* Only write out the nonzero entries. */
859 if (histogram[cnt / 16] != 0)
861 percent = (histogram[cnt / 16] * 100) / calls_total;
862 fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15,
863 (unsigned long int) histogram[cnt / 16]);
864 if (percent == 0)
865 fputs (" <1% \e[41;37m", stderr);
866 else
867 fprintf (stderr, "%3d%% \e[41;37m", percent);
869 /* Draw a bar with a length corresponding to the current
870 percentage. */
871 percent = (histogram[cnt / 16] * 50) / maxcalls;
872 while (percent-- > 0)
873 fputc ('=', stderr);
874 fputs ("\e[0;0m\n", stderr);
877 if (large != 0)
879 percent = (large * 100) / calls_total;
880 fprintf (stderr, " large %12lu ", (unsigned long int) large);
881 if (percent == 0)
882 fputs (" <1% \e[41;37m", stderr);
883 else
884 fprintf (stderr, "%3d%% \e[41;37m", percent);
885 percent = (large * 50) / maxcalls;
886 while (percent-- > 0)
887 fputc ('=', stderr);
888 fputs ("\e[0;0m\n", stderr);