f586ea61ba78d5079fca0e658e07bea78815107d
[kopensolaris-gnu/glibc.git] / malloc / memusage.c
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.
5
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.
10
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.
15
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.  */
20
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 <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/mman.h>
33 #include <sys/time.h>
34
35 #include <memusage.h>
36
37 /* Pointer to the real functions.  These are determined used `dlsym'
38    when really needed.  */
39 static void *(*mallocp) (size_t);
40 static void *(*reallocp) (void *, size_t);
41 static void *(*callocp) (size_t, size_t);
42 static void (*freep) (void *);
43
44 static void *(*mmapp) (void *, size_t, int, int, int, off_t);
45 static void *(*mmap64p) (void *, size_t, int, int, int, off64_t);
46 static int (*munmapp) (void *, size_t);
47 static void *(*mremapp) (void *, size_t, size_t, int);
48
49 enum
50 {
51   idx_malloc = 0,
52   idx_realloc,
53   idx_calloc,
54   idx_free,
55   idx_mmap_r,
56   idx_mmap_w,
57   idx_mmap_a,
58   idx_mremap,
59   idx_munmap,
60   idx_last
61 };
62
63
64 struct header
65 {
66   size_t length;
67   size_t magic;
68 };
69
70 #define MAGIC 0xfeedbeaf
71
72
73 static memusage_cntr_t calls[idx_last];
74 static memusage_cntr_t failed[idx_last];
75 static memusage_size_t total[idx_last];
76 static memusage_size_t grand_total;
77 static memusage_cntr_t histogram[65536 / 16];
78 static memusage_cntr_t large;
79 static memusage_cntr_t calls_total;
80 static memusage_cntr_t inplace;
81 static memusage_cntr_t decreasing;
82 static memusage_cntr_t inplace_mremap;
83 static memusage_cntr_t decreasing_mremap;
84 static memusage_size_t current_heap;
85 static memusage_size_t peak_use[3];
86 static __thread uintptr_t start_sp;
87
88 /* A few macros to make the source more readable.  */
89 #define peak_heap       peak_use[0]
90 #define peak_stack      peak_use[1]
91 #define peak_total      peak_use[2]
92
93 #define DEFAULT_BUFFER_SIZE     1024
94 static size_t buffer_size;
95
96 static int fd = -1;
97
98 static bool not_me;
99 static int initialized;
100 static bool trace_mmap;
101 extern const char *__progname;
102
103 struct entry
104 {
105   size_t heap;
106   size_t stack;
107   uint32_t time_low;
108   uint32_t time_high;
109 };
110
111 static struct entry buffer[2 * DEFAULT_BUFFER_SIZE];
112 static uatomic32_t buffer_cnt;
113 static struct entry first;
114
115
116 /* Update the global data after a successful function call.  */
117 static void
118 update_data (struct header *result, size_t len, size_t old_len)
119 {
120   if (result != NULL)
121     {
122       /* Record the information we need and mark the block using a
123          magic number.  */
124       result->length = len;
125       result->magic = MAGIC;
126     }
127
128   /* Compute current heap usage and compare it with the maximum value.  */
129   memusage_size_t heap
130     = atomic_exchange_and_add (&current_heap, len - old_len) + len - old_len;
131   atomic_max (&peak_heap, heap);
132
133   /* Compute current stack usage and compare it with the maximum
134      value.  The base stack pointer might not be set if this is not
135      the main thread and it is the first call to any of these
136      functions.  */
137   if (__builtin_expect (!start_sp, 0))
138     start_sp = GETSP ();
139
140   uintptr_t sp = GETSP ();
141 #ifdef STACK_GROWS_UPWARD
142   /* This can happen in threads where we didn't catch the thread's
143      stack early enough.  */
144   if (__builtin_expect (sp < start_sp, 0))
145     start_sp = sp;
146   size_t current_stack = sp - start_sp;
147 #else
148   /* This can happen in threads where we didn't catch the thread's
149      stack early enough.  */
150   if (__builtin_expect (sp > start_sp, 0))
151     start_sp = sp;
152   size_t current_stack = start_sp - sp;
153 #endif
154   atomic_max (&peak_stack, current_stack);
155
156   /* Add up heap and stack usage and compare it with the maximum value.  */
157   atomic_max (&peak_total, heap + current_stack);
158
159   /* Store the value only if we are writing to a file.  */
160   if (fd != -1)
161     {
162       uatomic32_t idx = atomic_exchange_and_add (&buffer_cnt, 1);
163       if (idx >= 2 * buffer_size)
164         {
165           /* We try to reset the counter to the correct range.  If
166              this fails because of another thread increasing the
167              counter it does not matter since that thread will take
168              care of the correction.  */
169           unsigned int reset = idx - 2 * buffer_size;
170           atomic_compare_and_exchange_val_acq (&buffer_size, reset, idx);
171           idx = reset;
172         }
173
174       buffer[idx].heap = current_heap;
175       buffer[idx].stack = current_stack;
176       GETTIME (buffer[idx].time_low, buffer[idx].time_high);
177
178       /* Write out buffer if it is full.  */
179       if (idx + 1 == buffer_size)
180         write (fd, buffer, buffer_size * sizeof (struct entry));
181       else if (idx + 1 == 2 * buffer_size)
182         write (fd, &buffer[buffer_size], buffer_size * sizeof (struct entry));
183     }
184 }
185
186
187 /* Interrupt handler.  */
188 static void
189 int_handler (int signo)
190 {
191   /* Nothing gets allocated.  Just record the stack pointer position.  */
192   update_data (NULL, 0, 0);
193 }
194
195
196 /* Find out whether this is the program we are supposed to profile.
197    For this the name in the variable `__progname' must match the one
198    given in the environment variable MEMUSAGE_PROG_NAME.  If the variable
199    is not present every program assumes it should be profiling.
200
201    If this is the program open a file descriptor to the output file.
202    We will write to it whenever the buffer overflows.  The name of the
203    output file is determined by the environment variable MEMUSAGE_OUTPUT.
204
205    If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical
206    value determines the size of the internal buffer.  The number gives
207    the number of elements in the buffer.  By setting the number to one
208    one effectively selects unbuffered operation.
209
210    If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
211    which at the highest possible frequency records the stack pointer.  */
212 static void
213 me (void)
214 {
215   const char *env = getenv ("MEMUSAGE_PROG_NAME");
216   size_t prog_len = strlen (__progname);
217
218   initialized = -1;
219   mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
220   reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
221   callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
222   freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
223
224   mmapp = (void *(*) (void *, size_t, int, int, int, off_t)) dlsym (RTLD_NEXT,
225                                                                     "mmap");
226   mmap64p =
227     (void *(*) (void *, size_t, int, int, int, off64_t)) dlsym (RTLD_NEXT,
228                                                                 "mmap64");
229   mremapp = (void *(*) (void *, size_t, size_t, int)) dlsym (RTLD_NEXT,
230                                                              "mremap");
231   munmapp = (int (*) (void *, size_t)) dlsym (RTLD_NEXT, "munmap");
232   initialized = 1;
233
234   if (env != NULL)
235     {
236       /* Check for program name.  */
237       size_t len = strlen (env);
238       if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
239           || (prog_len != len && __progname[prog_len - len - 1] != '/'))
240         not_me = true;
241     }
242
243   /* Only open the file if it's really us.  */
244   if (!not_me && fd == -1)
245     {
246       const char *outname;
247
248       if (!start_sp)
249         start_sp = GETSP ();
250
251       outname = getenv ("MEMUSAGE_OUTPUT");
252       if (outname != NULL && outname[0] != '\0'
253           && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
254         {
255           fd = creat64 (outname, 0666);
256
257           if (fd == -1)
258             /* Don't do anything in future calls if we cannot write to
259                the output file.  */
260             not_me = true;
261           else
262             {
263               /* Write the first entry.  */
264               first.heap = 0;
265               first.stack = 0;
266               GETTIME (first.time_low, first.time_high);
267               /* Write it two times since we need the starting and end time. */
268               write (fd, &first, sizeof (first));
269
270               /* Determine the buffer size.  We use the default if the
271                  environment variable is not present.  */
272               buffer_size = DEFAULT_BUFFER_SIZE;
273               if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
274                 {
275                   buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
276                   if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
277                     buffer_size = DEFAULT_BUFFER_SIZE;
278                 }
279
280               /* Possibly enable timer-based stack pointer retrieval.  */
281               if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
282                 {
283                   struct sigaction act;
284
285                   act.sa_handler = (sighandler_t) &int_handler;
286                   act.sa_flags = SA_RESTART;
287                   sigfillset (&act.sa_mask);
288
289                   if (sigaction (SIGPROF, &act, NULL) >= 0)
290                     {
291                       struct itimerval timer;
292
293                       timer.it_value.tv_sec = 0;
294                       timer.it_value.tv_usec = 1;
295                       timer.it_interval = timer.it_value;
296                       setitimer (ITIMER_PROF, &timer, NULL);
297                     }
298                 }
299             }
300         }
301
302       if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL)
303         trace_mmap = true;
304     }
305 }
306
307
308 /* Record the initial stack position.  */
309 static void
310 __attribute__ ((constructor))
311 init (void)
312 {
313   start_sp = GETSP ();
314   if (! initialized)
315     me ();
316 }
317
318
319 /* `malloc' replacement.  We keep track of the memory usage if this is the
320    correct program.  */
321 void *
322 malloc (size_t len)
323 {
324   struct header *result = NULL;
325
326   /* Determine real implementation if not already happened.  */
327   if (__builtin_expect (initialized <= 0, 0))
328     {
329       if (initialized == -1)
330         return NULL;
331       me ();
332     }
333
334   /* If this is not the correct program just use the normal function.  */
335   if (not_me)
336     return (*mallocp) (len);
337
338   /* Keep track of number of calls.  */
339   atomic_increment (&calls[idx_malloc]);
340   /* Keep track of total memory consumption for `malloc'.  */
341   atomic_add (&total[idx_malloc], len);
342   /* Keep track of total memory requirement.  */
343   atomic_add (&grand_total, len);
344   /* Remember the size of the request.  */
345   if (len < 65536)
346     atomic_increment (&histogram[len / 16]);
347   else
348     atomic_increment (&large);
349   /* Total number of calls of any of the functions.  */
350   atomic_increment (&calls_total);
351
352   /* Do the real work.  */
353   result = (struct header *) (*mallocp) (len + sizeof (struct header));
354   if (result == NULL)
355     {
356       atomic_increment (&failed[idx_malloc]);
357       return NULL;
358     }
359
360   /* Update the allocation data and write out the records if necessary.  */
361   update_data (result, len, 0);
362
363   /* Return the pointer to the user buffer.  */
364   return (void *) (result + 1);
365 }
366
367
368 /* `realloc' replacement.  We keep track of the memory usage if this is the
369    correct program.  */
370 void *
371 realloc (void *old, size_t len)
372 {
373   struct header *result = NULL;
374   struct header *real;
375   size_t old_len;
376
377   /* Determine real implementation if not already happened.  */
378   if (__builtin_expect (initialized <= 0, 0))
379     {
380       if (initialized == -1)
381         return NULL;
382       me ();
383     }
384
385   /* If this is not the correct program just use the normal function.  */
386   if (not_me)
387     return (*reallocp) (old, len);
388
389   if (old == NULL)
390     {
391       /* This is really a `malloc' call.  */
392       real = NULL;
393       old_len = 0;
394     }
395   else
396     {
397       real = ((struct header *) old) - 1;
398       if (real->magic != MAGIC)
399         /* This is no memory allocated here.  */
400         return (*reallocp) (old, len);
401       old_len = real->length;
402     }
403
404   /* Keep track of number of calls.  */
405   atomic_increment (&calls[idx_realloc]);
406   if (len > old_len)
407     {
408       /* Keep track of total memory consumption for `realloc'.  */
409       atomic_add (&total[idx_realloc], len - old_len);
410       /* Keep track of total memory requirement.  */
411       atomic_add (&grand_total, len - old_len);
412     }
413   /* Remember the size of the request.  */
414   if (len < 65536)
415     atomic_increment (&histogram[len / 16]);
416   else
417     atomic_increment (&large);
418   /* Total number of calls of any of the functions.  */
419   atomic_increment (&calls_total);
420
421   /* Do the real work.  */
422   result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
423   if (result == NULL)
424     {
425       atomic_increment (&failed[idx_realloc]);
426       return NULL;
427     }
428
429   /* Record whether the reduction/increase happened in place.  */
430   if (real == result)
431     atomic_increment (&inplace);
432   /* Was the buffer increased?  */
433   if (old_len > len)
434     atomic_increment (&decreasing);
435
436   /* Update the allocation data and write out the records if necessary.  */
437   update_data (result, len, old_len);
438
439   /* Return the pointer to the user buffer.  */
440   return (void *) (result + 1);
441 }
442
443
444 /* `calloc' replacement.  We keep track of the memory usage if this is the
445    correct program.  */
446 void *
447 calloc (size_t n, size_t len)
448 {
449   struct header *result;
450   size_t size = n * len;
451
452   /* Determine real implementation if not already happened.  */
453   if (__builtin_expect (initialized <= 0, 0))
454     {
455       if (initialized == -1)
456         return NULL;
457       me ();
458     }
459
460   /* If this is not the correct program just use the normal function.  */
461   if (not_me)
462     return (*callocp) (n, len);
463
464   /* Keep track of number of calls.  */
465   atomic_increment (&calls[idx_calloc]);
466   /* Keep track of total memory consumption for `calloc'.  */
467   atomic_add (&total[idx_calloc], size);
468   /* Keep track of total memory requirement.  */
469   atomic_add (&grand_total, size);
470   /* Remember the size of the request.  */
471   if (size < 65536)
472     atomic_increment (&histogram[size / 16]);
473   else
474     atomic_increment (&large);
475   /* Total number of calls of any of the functions.  */
476   ++calls_total;
477
478   /* Do the real work.  */
479   result = (struct header *) (*mallocp) (size + sizeof (struct header));
480   if (result == NULL)
481     {
482       atomic_increment (&failed[idx_calloc]);
483       return NULL;
484     }
485
486   /* Update the allocation data and write out the records if necessary.  */
487   update_data (result, size, 0);
488
489   /* Do what `calloc' would have done and return the buffer to the caller.  */
490   return memset (result + 1, '\0', size);
491 }
492
493
494 /* `free' replacement.  We keep track of the memory usage if this is the
495    correct program.  */
496 void
497 free (void *ptr)
498 {
499   struct header *real;
500
501   /* Determine real implementation if not already happened.  */
502   if (__builtin_expect (initialized <= 0, 0))
503     {
504       if (initialized == -1)
505         return;
506       me ();
507     }
508
509   /* If this is not the correct program just use the normal function.  */
510   if (not_me)
511     {
512       (*freep) (ptr);
513       return;
514     }
515
516   /* `free (NULL)' has no effect.  */
517   if (ptr == NULL)
518     {
519       atomic_increment (&calls[idx_free]);
520       return;
521     }
522
523   /* Determine the pointer to the header.  */
524   real = ((struct header *) ptr) - 1;
525   if (real->magic != MAGIC)
526     {
527       /* This block wasn't allocated here.  */
528       (*freep) (ptr);
529       return;
530     }
531
532   /* Keep track of number of calls.  */
533   atomic_increment (&calls[idx_free]);
534   /* Keep track of total memory freed using `free'.  */
535   atomic_add (&total[idx_free], real->length);
536
537   /* Update the allocation data and write out the records if necessary.  */
538   update_data (NULL, 0, real->length);
539
540   /* Do the real work.  */
541   (*freep) (real);
542 }
543
544
545 /* `mmap' replacement.  We do not have to keep track of the sizesince
546    `munmap' will get it as a parameter.  */
547 void *
548 mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
549 {
550   void *result = NULL;
551
552   /* Determine real implementation if not already happened.  */
553   if (__builtin_expect (initialized <= 0, 0))
554     {
555       if (initialized == -1)
556         return NULL;
557       me ();
558     }
559
560   /* Always get a block.  We don't need extra memory.  */
561   result = (*mmapp) (start, len, prot, flags, fd, offset);
562
563   if (!not_me && trace_mmap)
564     {
565       int idx = (flags & MAP_ANON
566                  ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
567
568       /* Keep track of number of calls.  */
569       atomic_increment (&calls[idx]);
570       /* Keep track of total memory consumption for `malloc'.  */
571       atomic_add (&total[idx], len);
572       /* Keep track of total memory requirement.  */
573       atomic_add (&grand_total, len);
574       /* Remember the size of the request.  */
575       if (len < 65536)
576         atomic_increment (&histogram[len / 16]);
577       else
578         atomic_increment (&large);
579       /* Total number of calls of any of the functions.  */
580       atomic_increment (&calls_total);
581
582       /* Check for failures.  */
583       if (result == NULL)
584         atomic_increment (&failed[idx]);
585       else if (idx == idx_mmap_w)
586         /* Update the allocation data and write out the records if
587            necessary.  Note the first parameter is NULL which means
588            the size is not tracked.  */
589         update_data (NULL, len, 0);
590     }
591
592   /* Return the pointer to the user buffer.  */
593   return result;
594 }
595
596
597 /* `mmap' replacement.  We do not have to keep track of the sizesince
598    `munmap' will get it as a parameter.  */
599 void *
600 mmap64 (void *start, size_t len, int prot, int flags, int fd, off64_t offset)
601 {
602   void *result = NULL;
603
604   /* Determine real implementation if not already happened.  */
605   if (__builtin_expect (initialized <= 0, 0))
606     {
607       if (initialized == -1)
608         return NULL;
609       me ();
610     }
611
612   /* Always get a block.  We don't need extra memory.  */
613   result = (*mmap64p) (start, len, prot, flags, fd, offset);
614
615   if (!not_me && trace_mmap)
616     {
617       int idx = (flags & MAP_ANON
618                  ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
619
620       /* Keep track of number of calls.  */
621       atomic_increment (&calls[idx]);
622       /* Keep track of total memory consumption for `malloc'.  */
623       atomic_add (&total[idx], len);
624       /* Keep track of total memory requirement.  */
625       atomic_add (&grand_total, len);
626       /* Remember the size of the request.  */
627       if (len < 65536)
628         atomic_increment (&histogram[len / 16]);
629       else
630         atomic_increment (&large);
631       /* Total number of calls of any of the functions.  */
632       atomic_increment (&calls_total);
633
634       /* Check for failures.  */
635       if (result == NULL)
636         atomic_increment (&failed[idx]);
637       else if (idx == idx_mmap_w)
638         /* Update the allocation data and write out the records if
639            necessary.  Note the first parameter is NULL which means
640            the size is not tracked.  */
641         update_data (NULL, len, 0);
642     }
643
644   /* Return the pointer to the user buffer.  */
645   return result;
646 }
647
648
649 /* `mmap' replacement.  We do not have to keep track of the sizesince
650    `munmap' will get it as a parameter.  */
651 void *
652 mremap (void *start, size_t old_len, size_t len, int flags)
653 {
654   void *result = NULL;
655
656   /* Determine real implementation if not already happened.  */
657   if (__builtin_expect (initialized <= 0, 0))
658     {
659       if (initialized == -1)
660         return NULL;
661       me ();
662     }
663
664   /* Always get a block.  We don't need extra memory.  */
665   result = (*mremapp) (start, old_len, len, flags);
666
667   if (!not_me && trace_mmap)
668     {
669       /* Keep track of number of calls.  */
670       atomic_increment (&calls[idx_mremap]);
671       if (len > old_len)
672         {
673           /* Keep track of total memory consumption for `malloc'.  */
674           atomic_add (&total[idx_mremap], len - old_len);
675           /* Keep track of total memory requirement.  */
676           atomic_add (&grand_total, len - old_len);
677         }
678       /* Remember the size of the request.  */
679       if (len < 65536)
680         atomic_increment (&histogram[len / 16]);
681       else
682         atomic_increment (&large);
683       /* Total number of calls of any of the functions.  */
684       atomic_increment (&calls_total);
685
686       /* Check for failures.  */
687       if (result == NULL)
688         atomic_increment (&failed[idx_mremap]);
689       else
690         {
691           /* Record whether the reduction/increase happened in place.  */
692           if (start == result)
693             atomic_increment (&inplace_mremap);
694           /* Was the buffer increased?  */
695           if (old_len > len)
696             atomic_increment (&decreasing_mremap);
697
698           /* Update the allocation data and write out the records if
699              necessary.  Note the first parameter is NULL which means
700              the size is not tracked.  */
701           update_data (NULL, len, old_len);
702         }
703     }
704
705   /* Return the pointer to the user buffer.  */
706   return result;
707 }
708
709
710 /* `munmap' replacement.  */
711 int
712 munmap (void *start, size_t len)
713 {
714   int result;
715
716   /* Determine real implementation if not already happened.  */
717   if (__builtin_expect (initialized <= 0, 0))
718     {
719       if (initialized == -1)
720         return -1;
721       me ();
722     }
723
724   /* Do the real work.  */
725   result = (*munmapp) (start, len);
726
727   if (!not_me && trace_mmap)
728     {
729       /* Keep track of number of calls.  */
730       atomic_increment (&calls[idx_munmap]);
731
732       if (__builtin_expect (result == 0, 1))
733         {
734           /* Keep track of total memory freed using `free'.  */
735           atomic_add (&total[idx_munmap], len);
736
737           /* Update the allocation data and write out the records if
738              necessary.  */
739           update_data (NULL, 0, len);
740         }
741       else
742         atomic_increment (&failed[idx_munmap]);
743     }
744
745   return result;
746 }
747
748
749 /* Write some statistics to standard error.  */
750 static void
751 __attribute__ ((destructor))
752 dest (void)
753 {
754   int percent, cnt;
755   unsigned long int maxcalls;
756
757   /* If we haven't done anything here just return.  */
758   if (not_me)
759     return;
760   /* If we should call any of the memory functions don't do any profiling.  */
761   not_me = true;
762
763   /* Finish the output file.  */
764   if (fd != -1)
765     {
766       /* Write the partially filled buffer.  */
767       write (fd, buffer, buffer_cnt * sizeof (struct entry));
768       /* Go back to the beginning of the file.  We allocated two records
769          here when we opened the file.  */
770       lseek (fd, 0, SEEK_SET);
771       /* Write out a record containing the total size.  */
772       first.stack = peak_total;
773       write (fd, &first, sizeof (struct entry));
774       /* Write out another record containing the maximum for heap and
775          stack.  */
776       first.heap = peak_heap;
777       first.stack = peak_stack;
778       GETTIME (first.time_low, first.time_high);
779       write (fd, &first, sizeof (struct entry));
780
781       /* Close the file.  */
782       close (fd);
783       fd = -1;
784     }
785
786   /* Write a colorful statistic.  */
787   fprintf (stderr, "\n\
788 \e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\
789 \e[04;34m         total calls   total memory   failed calls\e[0m\n\
790 \e[00;34m malloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
791 \e[00;34mrealloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m   (in place: %ld, dec: %ld)\n\
792 \e[00;34m calloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
793 \e[00;34m   free|\e[0m %10lu   %12llu\n",
794            (unsigned long long int) grand_total, (unsigned long int) peak_heap,
795            (unsigned long int) peak_stack,
796            (unsigned long int) calls[idx_malloc],
797            (unsigned long long int) total[idx_malloc],
798            failed[idx_malloc] ? "\e[01;41m" : "",
799            (unsigned long int) failed[idx_malloc],
800            (unsigned long int) calls[idx_realloc],
801            (unsigned long long int) total[idx_realloc],
802            failed[idx_realloc] ? "\e[01;41m" : "",
803            (unsigned long int) failed[idx_realloc],
804            (unsigned long int) inplace, (unsigned long int) decreasing,
805            (unsigned long int) calls[idx_calloc],
806            (unsigned long long int) total[idx_calloc],
807            failed[idx_calloc] ? "\e[01;41m" : "",
808            (unsigned long int) failed[idx_calloc],
809            (unsigned long int) calls[idx_free],
810            (unsigned long long int) total[idx_free]);
811
812   if (trace_mmap)
813     fprintf (stderr, "\
814 \e[00;34mmmap(r)|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
815 \e[00;34mmmap(w)|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
816 \e[00;34mmmap(a)|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
817 \e[00;34m mremap|\e[0m %10lu   %12llu   %s%12lu\e[00;00m   (in place: %ld, dec: %ld)\n\
818 \e[00;34m munmap|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n",
819              (unsigned long int) calls[idx_mmap_r],
820              (unsigned long long int) total[idx_mmap_r],
821              failed[idx_mmap_r] ? "\e[01;41m" : "",
822              (unsigned long int) failed[idx_mmap_r],
823              (unsigned long int) calls[idx_mmap_w],
824              (unsigned long long int) total[idx_mmap_w],
825              failed[idx_mmap_w] ? "\e[01;41m" : "",
826              (unsigned long int) failed[idx_mmap_w],
827              (unsigned long int) calls[idx_mmap_a],
828              (unsigned long long int) total[idx_mmap_a],
829              failed[idx_mmap_a] ? "\e[01;41m" : "",
830              (unsigned long int) failed[idx_mmap_a],
831              (unsigned long int) calls[idx_mremap],
832              (unsigned long long int) total[idx_mremap],
833              failed[idx_mremap] ? "\e[01;41m" : "",
834              (unsigned long int) failed[idx_mremap],
835              (unsigned long int) inplace_mremap,
836              (unsigned long int) decreasing_mremap,
837              (unsigned long int) calls[idx_munmap],
838              (unsigned long long int) total[idx_munmap],
839              failed[idx_munmap] ? "\e[01;41m" : "",
840              (unsigned long int) failed[idx_munmap]);
841
842   /* Write out a histoogram of the sizes of the allocations.  */
843   fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
844
845   /* Determine the maximum of all calls for each size range.  */
846   maxcalls = large;
847   for (cnt = 0; cnt < 65536; cnt += 16)
848     if (histogram[cnt / 16] > maxcalls)
849       maxcalls = histogram[cnt / 16];
850
851   for (cnt = 0; cnt < 65536; cnt += 16)
852     /* Only write out the nonzero entries.  */
853     if (histogram[cnt / 16] != 0)
854       {
855         percent = (histogram[cnt / 16] * 100) / calls_total;
856         fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15,
857                  (unsigned long int) histogram[cnt / 16]);
858         if (percent == 0)
859           fputs (" <1% \e[41;37m", stderr);
860         else
861           fprintf (stderr, "%3d%% \e[41;37m", percent);
862
863         /* Draw a bar with a length corresponding to the current
864            percentage.  */
865         percent = (histogram[cnt / 16] * 50) / maxcalls;
866         while (percent-- > 0)
867           fputc ('=', stderr);
868          fputs ("\e[0;0m\n", stderr);
869       }
870
871   if (large != 0)
872     {
873       percent = (large * 100) / calls_total;
874       fprintf (stderr, "   large   %12lu ", (unsigned long int) large);
875       if (percent == 0)
876         fputs (" <1% \e[41;37m", stderr);
877       else
878         fprintf (stderr, "%3d%% \e[41;37m", percent);
879       percent = (large * 50) / maxcalls;
880       while (percent-- > 0)
881         fputc ('=', stderr);
882       fputs ("\e[0;0m\n", stderr);
883     }
884 }