.
[glibc/history.git] / malloc / memusage.c
blobfcd58dc68499fe453841a998dac0e926e3f0e503
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 >= 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 % (2 * buffer_size);
173 catomic_compare_and_exchange_val_acq (&buffer_cnt, reset, idx);
174 idx = reset;
176 assert (idx < 2 * DEFAULT_BUFFER_SIZE);
178 buffer[idx].heap = current_heap;
179 buffer[idx].stack = current_stack;
180 GETTIME (buffer[idx].time_low, buffer[idx].time_high);
182 /* Write out buffer if it is full. */
183 if (idx + 1 == buffer_size)
184 write (fd, buffer, buffer_size * sizeof (struct entry));
185 else if (idx + 1 == 2 * buffer_size)
186 write (fd, &buffer[buffer_size], buffer_size * sizeof (struct entry));
191 /* Interrupt handler. */
192 static void
193 int_handler (int signo)
195 /* Nothing gets allocated. Just record the stack pointer position. */
196 update_data (NULL, 0, 0);
200 /* Find out whether this is the program we are supposed to profile.
201 For this the name in the variable `__progname' must match the one
202 given in the environment variable MEMUSAGE_PROG_NAME. If the variable
203 is not present every program assumes it should be profiling.
205 If this is the program open a file descriptor to the output file.
206 We will write to it whenever the buffer overflows. The name of the
207 output file is determined by the environment variable MEMUSAGE_OUTPUT.
209 If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical
210 value determines the size of the internal buffer. The number gives
211 the number of elements in the buffer. By setting the number to one
212 one effectively selects unbuffered operation.
214 If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
215 which at the highest possible frequency records the stack pointer. */
216 static void
217 me (void)
219 const char *env = getenv ("MEMUSAGE_PROG_NAME");
220 size_t prog_len = strlen (__progname);
222 initialized = -1;
223 mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
224 reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
225 callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
226 freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
228 mmapp = (void *(*) (void *, size_t, int, int, int, off_t)) dlsym (RTLD_NEXT,
229 "mmap");
230 mmap64p =
231 (void *(*) (void *, size_t, int, int, int, off64_t)) dlsym (RTLD_NEXT,
232 "mmap64");
233 mremapp = (void *(*) (void *, size_t, size_t, int, void *)) dlsym (RTLD_NEXT,
234 "mremap");
235 munmapp = (int (*) (void *, size_t)) dlsym (RTLD_NEXT, "munmap");
236 initialized = 1;
238 if (env != NULL)
240 /* Check for program name. */
241 size_t len = strlen (env);
242 if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
243 || (prog_len != len && __progname[prog_len - len - 1] != '/'))
244 not_me = true;
247 /* Only open the file if it's really us. */
248 if (!not_me && fd == -1)
250 const char *outname;
252 if (!start_sp)
253 start_sp = GETSP ();
255 outname = getenv ("MEMUSAGE_OUTPUT");
256 if (outname != NULL && outname[0] != '\0'
257 && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
259 fd = creat64 (outname, 0666);
261 if (fd == -1)
262 /* Don't do anything in future calls if we cannot write to
263 the output file. */
264 not_me = true;
265 else
267 /* Write the first entry. */
268 first.heap = 0;
269 first.stack = 0;
270 GETTIME (first.time_low, first.time_high);
271 /* Write it two times since we need the starting and end time. */
272 write (fd, &first, sizeof (first));
273 write (fd, &first, sizeof (first));
275 /* Determine the buffer size. We use the default if the
276 environment variable is not present. */
277 buffer_size = DEFAULT_BUFFER_SIZE;
278 if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
280 buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
281 if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
282 buffer_size = DEFAULT_BUFFER_SIZE;
285 /* Possibly enable timer-based stack pointer retrieval. */
286 if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
288 struct sigaction act;
290 act.sa_handler = (sighandler_t) &int_handler;
291 act.sa_flags = SA_RESTART;
292 sigfillset (&act.sa_mask);
294 if (sigaction (SIGPROF, &act, NULL) >= 0)
296 struct itimerval timer;
298 timer.it_value.tv_sec = 0;
299 timer.it_value.tv_usec = 1;
300 timer.it_interval = timer.it_value;
301 setitimer (ITIMER_PROF, &timer, NULL);
307 if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL)
308 trace_mmap = true;
313 /* Record the initial stack position. */
314 static void
315 __attribute__ ((constructor))
316 init (void)
318 start_sp = GETSP ();
319 if (! initialized)
320 me ();
324 /* `malloc' replacement. We keep track of the memory usage if this is the
325 correct program. */
326 void *
327 malloc (size_t len)
329 struct header *result = NULL;
331 /* Determine real implementation if not already happened. */
332 if (__builtin_expect (initialized <= 0, 0))
334 if (initialized == -1)
335 return NULL;
336 me ();
339 /* If this is not the correct program just use the normal function. */
340 if (not_me)
341 return (*mallocp) (len);
343 /* Keep track of number of calls. */
344 catomic_increment (&calls[idx_malloc]);
345 /* Keep track of total memory consumption for `malloc'. */
346 catomic_add (&total[idx_malloc], len);
347 /* Keep track of total memory requirement. */
348 catomic_add (&grand_total, len);
349 /* Remember the size of the request. */
350 if (len < 65536)
351 catomic_increment (&histogram[len / 16]);
352 else
353 catomic_increment (&large);
354 /* Total number of calls of any of the functions. */
355 catomic_increment (&calls_total);
357 /* Do the real work. */
358 result = (struct header *) (*mallocp) (len + sizeof (struct header));
359 if (result == NULL)
361 catomic_increment (&failed[idx_malloc]);
362 return NULL;
365 /* Update the allocation data and write out the records if necessary. */
366 update_data (result, len, 0);
368 /* Return the pointer to the user buffer. */
369 return (void *) (result + 1);
373 /* `realloc' replacement. We keep track of the memory usage if this is the
374 correct program. */
375 void *
376 realloc (void *old, size_t len)
378 struct header *result = NULL;
379 struct header *real;
380 size_t old_len;
382 /* Determine real implementation if not already happened. */
383 if (__builtin_expect (initialized <= 0, 0))
385 if (initialized == -1)
386 return NULL;
387 me ();
390 /* If this is not the correct program just use the normal function. */
391 if (not_me)
392 return (*reallocp) (old, len);
394 if (old == NULL)
396 /* This is really a `malloc' call. */
397 real = NULL;
398 old_len = 0;
400 else
402 real = ((struct header *) old) - 1;
403 if (real->magic != MAGIC)
404 /* This is no memory allocated here. */
405 return (*reallocp) (old, len);
406 old_len = real->length;
409 /* Keep track of number of calls. */
410 catomic_increment (&calls[idx_realloc]);
411 if (len > old_len)
413 /* Keep track of total memory consumption for `realloc'. */
414 catomic_add (&total[idx_realloc], len - old_len);
415 /* Keep track of total memory requirement. */
416 catomic_add (&grand_total, len - old_len);
419 if (len == 0 && old != NULL)
421 /* Special case. */
422 catomic_increment (&realloc_free);
423 /* Keep track of total memory freed using `free'. */
424 catomic_add (&total[idx_free], real->length);
426 /* Update the allocation data and write out the records if necessary. */
427 update_data (NULL, 0, old_len);
429 /* Do the real work. */
430 (*freep) (real);
432 return NULL;
435 /* Remember the size of the request. */
436 if (len < 65536)
437 catomic_increment (&histogram[len / 16]);
438 else
439 catomic_increment (&large);
440 /* Total number of calls of any of the functions. */
441 catomic_increment (&calls_total);
443 /* Do the real work. */
444 result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
445 if (result == NULL)
447 catomic_increment (&failed[idx_realloc]);
448 return NULL;
451 /* Record whether the reduction/increase happened in place. */
452 if (real == result)
453 catomic_increment (&inplace);
454 /* Was the buffer increased? */
455 if (old_len > len)
456 catomic_increment (&decreasing);
458 /* Update the allocation data and write out the records if necessary. */
459 update_data (result, len, old_len);
461 /* Return the pointer to the user buffer. */
462 return (void *) (result + 1);
466 /* `calloc' replacement. We keep track of the memory usage if this is the
467 correct program. */
468 void *
469 calloc (size_t n, size_t len)
471 struct header *result;
472 size_t size = n * len;
474 /* Determine real implementation if not already happened. */
475 if (__builtin_expect (initialized <= 0, 0))
477 if (initialized == -1)
478 return NULL;
479 me ();
482 /* If this is not the correct program just use the normal function. */
483 if (not_me)
484 return (*callocp) (n, len);
486 /* Keep track of number of calls. */
487 catomic_increment (&calls[idx_calloc]);
488 /* Keep track of total memory consumption for `calloc'. */
489 catomic_add (&total[idx_calloc], size);
490 /* Keep track of total memory requirement. */
491 catomic_add (&grand_total, size);
492 /* Remember the size of the request. */
493 if (size < 65536)
494 catomic_increment (&histogram[size / 16]);
495 else
496 catomic_increment (&large);
497 /* Total number of calls of any of the functions. */
498 ++calls_total;
500 /* Do the real work. */
501 result = (struct header *) (*mallocp) (size + sizeof (struct header));
502 if (result == NULL)
504 catomic_increment (&failed[idx_calloc]);
505 return NULL;
508 /* Update the allocation data and write out the records if necessary. */
509 update_data (result, size, 0);
511 /* Do what `calloc' would have done and return the buffer to the caller. */
512 return memset (result + 1, '\0', size);
516 /* `free' replacement. We keep track of the memory usage if this is the
517 correct program. */
518 void
519 free (void *ptr)
521 struct header *real;
523 /* Determine real implementation if not already happened. */
524 if (__builtin_expect (initialized <= 0, 0))
526 if (initialized == -1)
527 return;
528 me ();
531 /* If this is not the correct program just use the normal function. */
532 if (not_me)
534 (*freep) (ptr);
535 return;
538 /* `free (NULL)' has no effect. */
539 if (ptr == NULL)
541 catomic_increment (&calls[idx_free]);
542 return;
545 /* Determine the pointer to the header. */
546 real = ((struct header *) ptr) - 1;
547 if (real->magic != MAGIC)
549 /* This block wasn't allocated here. */
550 (*freep) (ptr);
551 return;
554 /* Keep track of number of calls. */
555 catomic_increment (&calls[idx_free]);
556 /* Keep track of total memory freed using `free'. */
557 catomic_add (&total[idx_free], real->length);
559 /* Update the allocation data and write out the records if necessary. */
560 update_data (NULL, 0, real->length);
562 /* Do the real work. */
563 (*freep) (real);
567 /* `mmap' replacement. We do not have to keep track of the sizesince
568 `munmap' will get it as a parameter. */
569 void *
570 mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
572 void *result = NULL;
574 /* Determine real implementation if not already happened. */
575 if (__builtin_expect (initialized <= 0, 0))
577 if (initialized == -1)
578 return NULL;
579 me ();
582 /* Always get a block. We don't need extra memory. */
583 result = (*mmapp) (start, len, prot, flags, fd, offset);
585 if (!not_me && trace_mmap)
587 int idx = (flags & MAP_ANON
588 ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
590 /* Keep track of number of calls. */
591 catomic_increment (&calls[idx]);
592 /* Keep track of total memory consumption for `malloc'. */
593 catomic_add (&total[idx], len);
594 /* Keep track of total memory requirement. */
595 catomic_add (&grand_total, len);
596 /* Remember the size of the request. */
597 if (len < 65536)
598 catomic_increment (&histogram[len / 16]);
599 else
600 catomic_increment (&large);
601 /* Total number of calls of any of the functions. */
602 catomic_increment (&calls_total);
604 /* Check for failures. */
605 if (result == NULL)
606 catomic_increment (&failed[idx]);
607 else if (idx == idx_mmap_w)
608 /* Update the allocation data and write out the records if
609 necessary. Note the first parameter is NULL which means
610 the size is not tracked. */
611 update_data (NULL, len, 0);
614 /* Return the pointer to the user buffer. */
615 return result;
619 /* `mmap' replacement. We do not have to keep track of the sizesince
620 `munmap' will get it as a parameter. */
621 void *
622 mmap64 (void *start, size_t len, int prot, int flags, int fd, off64_t offset)
624 void *result = NULL;
626 /* Determine real implementation if not already happened. */
627 if (__builtin_expect (initialized <= 0, 0))
629 if (initialized == -1)
630 return NULL;
631 me ();
634 /* Always get a block. We don't need extra memory. */
635 result = (*mmap64p) (start, len, prot, flags, fd, offset);
637 if (!not_me && trace_mmap)
639 int idx = (flags & MAP_ANON
640 ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
642 /* Keep track of number of calls. */
643 catomic_increment (&calls[idx]);
644 /* Keep track of total memory consumption for `malloc'. */
645 catomic_add (&total[idx], len);
646 /* Keep track of total memory requirement. */
647 catomic_add (&grand_total, len);
648 /* Remember the size of the request. */
649 if (len < 65536)
650 catomic_increment (&histogram[len / 16]);
651 else
652 catomic_increment (&large);
653 /* Total number of calls of any of the functions. */
654 catomic_increment (&calls_total);
656 /* Check for failures. */
657 if (result == NULL)
658 catomic_increment (&failed[idx]);
659 else if (idx == idx_mmap_w)
660 /* Update the allocation data and write out the records if
661 necessary. Note the first parameter is NULL which means
662 the size is not tracked. */
663 update_data (NULL, len, 0);
666 /* Return the pointer to the user buffer. */
667 return result;
671 /* `mmap' replacement. We do not have to keep track of the sizesince
672 `munmap' will get it as a parameter. */
673 void *
674 mremap (void *start, size_t old_len, size_t len, int flags, ...)
676 void *result = NULL;
677 va_list ap;
679 va_start (ap, flags);
680 void *newaddr = (flags & MREMAP_FIXED) ? va_arg (ap, void *) : NULL;
681 va_end (ap);
683 /* Determine real implementation if not already happened. */
684 if (__builtin_expect (initialized <= 0, 0))
686 if (initialized == -1)
687 return NULL;
688 me ();
691 /* Always get a block. We don't need extra memory. */
692 result = (*mremapp) (start, old_len, len, flags, newaddr);
694 if (!not_me && trace_mmap)
696 /* Keep track of number of calls. */
697 catomic_increment (&calls[idx_mremap]);
698 if (len > old_len)
700 /* Keep track of total memory consumption for `malloc'. */
701 catomic_add (&total[idx_mremap], len - old_len);
702 /* Keep track of total memory requirement. */
703 catomic_add (&grand_total, len - old_len);
705 /* Remember the size of the request. */
706 if (len < 65536)
707 catomic_increment (&histogram[len / 16]);
708 else
709 catomic_increment (&large);
710 /* Total number of calls of any of the functions. */
711 catomic_increment (&calls_total);
713 /* Check for failures. */
714 if (result == NULL)
715 catomic_increment (&failed[idx_mremap]);
716 else
718 /* Record whether the reduction/increase happened in place. */
719 if (start == result)
720 catomic_increment (&inplace_mremap);
721 /* Was the buffer increased? */
722 if (old_len > len)
723 catomic_increment (&decreasing_mremap);
725 /* Update the allocation data and write out the records if
726 necessary. Note the first parameter is NULL which means
727 the size is not tracked. */
728 update_data (NULL, len, old_len);
732 /* Return the pointer to the user buffer. */
733 return result;
737 /* `munmap' replacement. */
739 munmap (void *start, size_t len)
741 int result;
743 /* Determine real implementation if not already happened. */
744 if (__builtin_expect (initialized <= 0, 0))
746 if (initialized == -1)
747 return -1;
748 me ();
751 /* Do the real work. */
752 result = (*munmapp) (start, len);
754 if (!not_me && trace_mmap)
756 /* Keep track of number of calls. */
757 catomic_increment (&calls[idx_munmap]);
759 if (__builtin_expect (result == 0, 1))
761 /* Keep track of total memory freed using `free'. */
762 catomic_add (&total[idx_munmap], len);
764 /* Update the allocation data and write out the records if
765 necessary. */
766 update_data (NULL, 0, len);
768 else
769 catomic_increment (&failed[idx_munmap]);
772 return result;
776 /* Write some statistics to standard error. */
777 static void
778 __attribute__ ((destructor))
779 dest (void)
781 int percent, cnt;
782 unsigned long int maxcalls;
784 /* If we haven't done anything here just return. */
785 if (not_me)
786 return;
787 /* If we should call any of the memory functions don't do any profiling. */
788 not_me = true;
790 /* Finish the output file. */
791 if (fd != -1)
793 /* Write the partially filled buffer. */
794 if (buffer_cnt > buffer_size)
795 write (fd, buffer + buffer_size,
796 (buffer_cnt - buffer_size) * sizeof (struct entry));
797 else
798 write (fd, buffer, buffer_cnt * sizeof (struct entry));
800 /* Go back to the beginning of the file. We allocated two records
801 here when we opened the file. */
802 lseek (fd, 0, SEEK_SET);
803 /* Write out a record containing the total size. */
804 first.stack = peak_total;
805 write (fd, &first, sizeof (struct entry));
806 /* Write out another record containing the maximum for heap and
807 stack. */
808 first.heap = peak_heap;
809 first.stack = peak_stack;
810 GETTIME (first.time_low, first.time_high);
811 write (fd, &first, sizeof (struct entry));
813 /* Close the file. */
814 close (fd);
815 fd = -1;
818 /* Write a colorful statistic. */
819 fprintf (stderr, "\n\
820 \e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\
821 \e[04;34m total calls total memory failed calls\e[0m\n\
822 \e[00;34m malloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
823 \e[00;34mrealloc|\e[0m %10lu %12llu %s%12lu\e[00;00m (nomove:%ld, dec:%ld, free:%ld)\n\
824 \e[00;34m calloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
825 \e[00;34m free|\e[0m %10lu %12llu\n",
826 (unsigned long long int) grand_total, (unsigned long int) peak_heap,
827 (unsigned long int) peak_stack,
828 (unsigned long int) calls[idx_malloc],
829 (unsigned long long int) total[idx_malloc],
830 failed[idx_malloc] ? "\e[01;41m" : "",
831 (unsigned long int) failed[idx_malloc],
832 (unsigned long int) calls[idx_realloc],
833 (unsigned long long int) total[idx_realloc],
834 failed[idx_realloc] ? "\e[01;41m" : "",
835 (unsigned long int) failed[idx_realloc],
836 (unsigned long int) inplace,
837 (unsigned long int) decreasing,
838 (unsigned long int) realloc_free,
839 (unsigned long int) calls[idx_calloc],
840 (unsigned long long int) total[idx_calloc],
841 failed[idx_calloc] ? "\e[01;41m" : "",
842 (unsigned long int) failed[idx_calloc],
843 (unsigned long int) calls[idx_free],
844 (unsigned long long int) total[idx_free]);
846 if (trace_mmap)
847 fprintf (stderr, "\
848 \e[00;34mmmap(r)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
849 \e[00;34mmmap(w)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
850 \e[00;34mmmap(a)|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
851 \e[00;34m mremap|\e[0m %10lu %12llu %s%12lu\e[00;00m (nomove: %ld, dec:%ld)\n\
852 \e[00;34m munmap|\e[0m %10lu %12llu %s%12lu\e[00;00m\n",
853 (unsigned long int) calls[idx_mmap_r],
854 (unsigned long long int) total[idx_mmap_r],
855 failed[idx_mmap_r] ? "\e[01;41m" : "",
856 (unsigned long int) failed[idx_mmap_r],
857 (unsigned long int) calls[idx_mmap_w],
858 (unsigned long long int) total[idx_mmap_w],
859 failed[idx_mmap_w] ? "\e[01;41m" : "",
860 (unsigned long int) failed[idx_mmap_w],
861 (unsigned long int) calls[idx_mmap_a],
862 (unsigned long long int) total[idx_mmap_a],
863 failed[idx_mmap_a] ? "\e[01;41m" : "",
864 (unsigned long int) failed[idx_mmap_a],
865 (unsigned long int) calls[idx_mremap],
866 (unsigned long long int) total[idx_mremap],
867 failed[idx_mremap] ? "\e[01;41m" : "",
868 (unsigned long int) failed[idx_mremap],
869 (unsigned long int) inplace_mremap,
870 (unsigned long int) decreasing_mremap,
871 (unsigned long int) calls[idx_munmap],
872 (unsigned long long int) total[idx_munmap],
873 failed[idx_munmap] ? "\e[01;41m" : "",
874 (unsigned long int) failed[idx_munmap]);
876 /* Write out a histoogram of the sizes of the allocations. */
877 fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
879 /* Determine the maximum of all calls for each size range. */
880 maxcalls = large;
881 for (cnt = 0; cnt < 65536; cnt += 16)
882 if (histogram[cnt / 16] > maxcalls)
883 maxcalls = histogram[cnt / 16];
885 for (cnt = 0; cnt < 65536; cnt += 16)
886 /* Only write out the nonzero entries. */
887 if (histogram[cnt / 16] != 0)
889 percent = (histogram[cnt / 16] * 100) / calls_total;
890 fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15,
891 (unsigned long int) histogram[cnt / 16]);
892 if (percent == 0)
893 fputs (" <1% \e[41;37m", stderr);
894 else
895 fprintf (stderr, "%3d%% \e[41;37m", percent);
897 /* Draw a bar with a length corresponding to the current
898 percentage. */
899 percent = (histogram[cnt / 16] * 50) / maxcalls;
900 while (percent-- > 0)
901 fputc ('=', stderr);
902 fputs ("\e[0;0m\n", stderr);
905 if (large != 0)
907 percent = (large * 100) / calls_total;
908 fprintf (stderr, " large %12lu ", (unsigned long int) large);
909 if (percent == 0)
910 fputs (" <1% \e[41;37m", stderr);
911 else
912 fprintf (stderr, "%3d%% \e[41;37m", percent);
913 percent = (large * 50) / maxcalls;
914 while (percent-- > 0)
915 fputc ('=', stderr);
916 fputs ("\e[0;0m\n", stderr);
919 /* Any following malloc/free etc. calls should generate statistics again,
920 because otherwise freeing something that has been malloced before
921 this destructor (including struct header in front of it) wouldn't
922 be properly freed. */
923 not_me = false;