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