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