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