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