8b37c43a8afb0ecfd2ce7790a7e0646aceefb341
[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 <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>
35
36 #include <memusage.h>
37
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 *);
44
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 *);
49
50 enum
51 {
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
62 };
63
64
65 struct header
66 {
67   size_t length;
68   size_t magic;
69 };
70
71 #define MAGIC 0xfeedbeaf
72
73
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;
88
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]
93
94 #define DEFAULT_BUFFER_SIZE     1024
95 static size_t buffer_size;
96
97 static int fd = -1;
98
99 static bool not_me;
100 static int initialized;
101 static bool trace_mmap;
102 extern const char *__progname;
103
104 struct entry
105 {
106   size_t heap;
107   size_t stack;
108   uint32_t time_low;
109   uint32_t time_high;
110 };
111
112 static struct entry buffer[2 * DEFAULT_BUFFER_SIZE];
113 static uatomic32_t buffer_cnt;
114 static struct entry first;
115
116
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)
120 {
121   if (result != NULL)
122     {
123       /* Record the information we need and mark the block using a
124          magic number.  */
125       result->length = len;
126       result->magic = MAGIC;
127     }
128
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);
133
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 ();
140
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);
156
157   /* Add up heap and stack usage and compare it with the maximum value.  */
158   atomic_max (&peak_total, heap + current_stack);
159
160   /* Store the value only if we are writing to a file.  */
161   if (fd != -1)
162     {
163       uatomic32_t idx = atomic_exchange_and_add (&buffer_cnt, 1);
164       if (idx >= 2 * buffer_size)
165         {
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;
173         }
174
175       buffer[idx].heap = current_heap;
176       buffer[idx].stack = current_stack;
177       GETTIME (buffer[idx].time_low, buffer[idx].time_high);
178
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));
184     }
185 }
186
187
188 /* Interrupt handler.  */
189 static void
190 int_handler (int signo)
191 {
192   /* Nothing gets allocated.  Just record the stack pointer position.  */
193   update_data (NULL, 0, 0);
194 }
195
196
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.
201
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.
205
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.
210
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)
215 {
216   const char *env = getenv ("MEMUSAGE_PROG_NAME");
217   size_t prog_len = strlen (__progname);
218
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");
224
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;
234
235   if (env != NULL)
236     {
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;
242     }
243
244   /* Only open the file if it's really us.  */
245   if (!not_me && fd == -1)
246     {
247       const char *outname;
248
249       if (!start_sp)
250         start_sp = GETSP ();
251
252       outname = getenv ("MEMUSAGE_OUTPUT");
253       if (outname != NULL && outname[0] != '\0'
254           && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
255         {
256           fd = creat64 (outname, 0666);
257
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
263             {
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));
270
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)
275                 {
276                   buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
277                   if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
278                     buffer_size = DEFAULT_BUFFER_SIZE;
279                 }
280
281               /* Possibly enable timer-based stack pointer retrieval.  */
282               if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
283                 {
284                   struct sigaction act;
285
286                   act.sa_handler = (sighandler_t) &int_handler;
287                   act.sa_flags = SA_RESTART;
288                   sigfillset (&act.sa_mask);
289
290                   if (sigaction (SIGPROF, &act, NULL) >= 0)
291                     {
292                       struct itimerval timer;
293
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);
298                     }
299                 }
300             }
301         }
302
303       if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL)
304         trace_mmap = true;
305     }
306 }
307
308
309 /* Record the initial stack position.  */
310 static void
311 __attribute__ ((constructor))
312 init (void)
313 {
314   start_sp = GETSP ();
315   if (! initialized)
316     me ();
317 }
318
319
320 /* `malloc' replacement.  We keep track of the memory usage if this is the
321    correct program.  */
322 void *
323 malloc (size_t len)
324 {
325   struct header *result = NULL;
326
327   /* Determine real implementation if not already happened.  */
328   if (__builtin_expect (initialized <= 0, 0))
329     {
330       if (initialized == -1)
331         return NULL;
332       me ();
333     }
334
335   /* If this is not the correct program just use the normal function.  */
336   if (not_me)
337     return (*mallocp) (len);
338
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);
352
353   /* Do the real work.  */
354   result = (struct header *) (*mallocp) (len + sizeof (struct header));
355   if (result == NULL)
356     {
357       atomic_increment (&failed[idx_malloc]);
358       return NULL;
359     }
360
361   /* Update the allocation data and write out the records if necessary.  */
362   update_data (result, len, 0);
363
364   /* Return the pointer to the user buffer.  */
365   return (void *) (result + 1);
366 }
367
368
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)
373 {
374   struct header *result = NULL;
375   struct header *real;
376   size_t old_len;
377
378   /* Determine real implementation if not already happened.  */
379   if (__builtin_expect (initialized <= 0, 0))
380     {
381       if (initialized == -1)
382         return NULL;
383       me ();
384     }
385
386   /* If this is not the correct program just use the normal function.  */
387   if (not_me)
388     return (*reallocp) (old, len);
389
390   if (old == NULL)
391     {
392       /* This is really a `malloc' call.  */
393       real = NULL;
394       old_len = 0;
395     }
396   else
397     {
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;
403     }
404
405   /* Keep track of number of calls.  */
406   atomic_increment (&calls[idx_realloc]);
407   if (len > old_len)
408     {
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);
413     }
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);
421
422   /* Do the real work.  */
423   result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
424   if (result == NULL)
425     {
426       atomic_increment (&failed[idx_realloc]);
427       return NULL;
428     }
429
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);
436
437   /* Update the allocation data and write out the records if necessary.  */
438   update_data (result, len, old_len);
439
440   /* Return the pointer to the user buffer.  */
441   return (void *) (result + 1);
442 }
443
444
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)
449 {
450   struct header *result;
451   size_t size = n * len;
452
453   /* Determine real implementation if not already happened.  */
454   if (__builtin_expect (initialized <= 0, 0))
455     {
456       if (initialized == -1)
457         return NULL;
458       me ();
459     }
460
461   /* If this is not the correct program just use the normal function.  */
462   if (not_me)
463     return (*callocp) (n, len);
464
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;
478
479   /* Do the real work.  */
480   result = (struct header *) (*mallocp) (size + sizeof (struct header));
481   if (result == NULL)
482     {
483       atomic_increment (&failed[idx_calloc]);
484       return NULL;
485     }
486
487   /* Update the allocation data and write out the records if necessary.  */
488   update_data (result, size, 0);
489
490   /* Do what `calloc' would have done and return the buffer to the caller.  */
491   return memset (result + 1, '\0', size);
492 }
493
494
495 /* `free' replacement.  We keep track of the memory usage if this is the
496    correct program.  */
497 void
498 free (void *ptr)
499 {
500   struct header *real;
501
502   /* Determine real implementation if not already happened.  */
503   if (__builtin_expect (initialized <= 0, 0))
504     {
505       if (initialized == -1)
506         return;
507       me ();
508     }
509
510   /* If this is not the correct program just use the normal function.  */
511   if (not_me)
512     {
513       (*freep) (ptr);
514       return;
515     }
516
517   /* `free (NULL)' has no effect.  */
518   if (ptr == NULL)
519     {
520       atomic_increment (&calls[idx_free]);
521       return;
522     }
523
524   /* Determine the pointer to the header.  */
525   real = ((struct header *) ptr) - 1;
526   if (real->magic != MAGIC)
527     {
528       /* This block wasn't allocated here.  */
529       (*freep) (ptr);
530       return;
531     }
532
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);
537
538   /* Update the allocation data and write out the records if necessary.  */
539   update_data (NULL, 0, real->length);
540
541   /* Do the real work.  */
542   (*freep) (real);
543 }
544
545
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)
550 {
551   void *result = NULL;
552
553   /* Determine real implementation if not already happened.  */
554   if (__builtin_expect (initialized <= 0, 0))
555     {
556       if (initialized == -1)
557         return NULL;
558       me ();
559     }
560
561   /* Always get a block.  We don't need extra memory.  */
562   result = (*mmapp) (start, len, prot, flags, fd, offset);
563
564   if (!not_me && trace_mmap)
565     {
566       int idx = (flags & MAP_ANON
567                  ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
568
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);
582
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);
591     }
592
593   /* Return the pointer to the user buffer.  */
594   return result;
595 }
596
597
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)
602 {
603   void *result = NULL;
604
605   /* Determine real implementation if not already happened.  */
606   if (__builtin_expect (initialized <= 0, 0))
607     {
608       if (initialized == -1)
609         return NULL;
610       me ();
611     }
612
613   /* Always get a block.  We don't need extra memory.  */
614   result = (*mmap64p) (start, len, prot, flags, fd, offset);
615
616   if (!not_me && trace_mmap)
617     {
618       int idx = (flags & MAP_ANON
619                  ? idx_mmap_a : prot & PROT_WRITE ? idx_mmap_w : idx_mmap_r);
620
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);
634
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);
643     }
644
645   /* Return the pointer to the user buffer.  */
646   return result;
647 }
648
649
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,  ...)
654 {
655   void *result = NULL;
656   va_list ap;
657
658   va_start (ap, flags);
659   void *newaddr = (flags & MREMAP_FIXED) ? va_arg (ap, void *) : NULL;
660   va_end (ap);
661
662   /* Determine real implementation if not already happened.  */
663   if (__builtin_expect (initialized <= 0, 0))
664     {
665       if (initialized == -1)
666         return NULL;
667       me ();
668     }
669
670   /* Always get a block.  We don't need extra memory.  */
671   result = (*mremapp) (start, old_len, len, flags, newaddr);
672
673   if (!not_me && trace_mmap)
674     {
675       /* Keep track of number of calls.  */
676       atomic_increment (&calls[idx_mremap]);
677       if (len > old_len)
678         {
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);
683         }
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);
691
692       /* Check for failures.  */
693       if (result == NULL)
694         atomic_increment (&failed[idx_mremap]);
695       else
696         {
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);
703
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);
708         }
709     }
710
711   /* Return the pointer to the user buffer.  */
712   return result;
713 }
714
715
716 /* `munmap' replacement.  */
717 int
718 munmap (void *start, size_t len)
719 {
720   int result;
721
722   /* Determine real implementation if not already happened.  */
723   if (__builtin_expect (initialized <= 0, 0))
724     {
725       if (initialized == -1)
726         return -1;
727       me ();
728     }
729
730   /* Do the real work.  */
731   result = (*munmapp) (start, len);
732
733   if (!not_me && trace_mmap)
734     {
735       /* Keep track of number of calls.  */
736       atomic_increment (&calls[idx_munmap]);
737
738       if (__builtin_expect (result == 0, 1))
739         {
740           /* Keep track of total memory freed using `free'.  */
741           atomic_add (&total[idx_munmap], len);
742
743           /* Update the allocation data and write out the records if
744              necessary.  */
745           update_data (NULL, 0, len);
746         }
747       else
748         atomic_increment (&failed[idx_munmap]);
749     }
750
751   return result;
752 }
753
754
755 /* Write some statistics to standard error.  */
756 static void
757 __attribute__ ((destructor))
758 dest (void)
759 {
760   int percent, cnt;
761   unsigned long int maxcalls;
762
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;
768
769   /* Finish the output file.  */
770   if (fd != -1)
771     {
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));
786
787       /* Close the file.  */
788       close (fd);
789       fd = -1;
790     }
791
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]);
817
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]);
847
848   /* Write out a histoogram of the sizes of the allocations.  */
849   fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
850
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];
856
857   for (cnt = 0; cnt < 65536; cnt += 16)
858     /* Only write out the nonzero entries.  */
859     if (histogram[cnt / 16] != 0)
860       {
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);
868
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);
875       }
876
877   if (large != 0)
878     {
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);
889     }
890 }