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