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