1 /* Profile heap and stack memory usage of running program.
2 Copyright (C) 1998-2018 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
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.
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.
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/>. */
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 *);
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 *);
72 #define MAGIC 0xfeedbeaf
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
;
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]
96 #define DEFAULT_BUFFER_SIZE 32768
97 static size_t buffer_size
;
102 static int initialized
;
103 static bool trace_mmap
;
104 extern const char *__progname
;
114 static struct entry buffer
[2 * DEFAULT_BUFFER_SIZE
];
115 static uatomic32_t buffer_cnt
;
116 static struct entry first
;
119 /* Update the global data after a successful function call. */
121 update_data (struct header
*result
, size_t len
, size_t old_len
)
125 /* Record the information we need and mark the block using a
127 result
->length
= len
;
128 result
->magic
= MAGIC
;
131 /* Compute current heap usage and compare it with the maximum value. */
133 = catomic_exchange_and_add (¤t_heap
, len
- old_len
) + len
- old_len
;
134 catomic_max (&peak_heap
, heap
);
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
140 if (__glibc_unlikely (!start_sp
))
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
))
149 size_t current_stack
= sp
- start_sp
;
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
))
155 size_t current_stack
= start_sp
- sp
;
157 catomic_max (&peak_stack
, current_stack
);
159 /* Add up heap and stack usage and compare it with the maximum value. */
160 catomic_max (&peak_total
, heap
+ current_stack
);
162 /* Store the value only if we are writing to a file. */
165 uatomic32_t idx
= catomic_exchange_and_add (&buffer_cnt
, 1);
166 if (idx
+ 1 >= 2 * buffer_size
)
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
)
177 assert (idx
< 2 * DEFAULT_BUFFER_SIZE
);
179 buffer
[idx
].heap
= current_heap
;
180 buffer
[idx
].stack
= current_stack
;
181 GETTIME (buffer
[idx
].time_low
, buffer
[idx
].time_high
);
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
));
192 /* Interrupt handler. */
194 int_handler (int signo
)
196 /* Nothing gets allocated. Just record the stack pointer position. */
197 update_data (NULL
, 0, 0);
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.
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.
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.
215 If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
216 which at the highest possible frequency records the stack pointer. */
220 const char *env
= getenv ("MEMUSAGE_PROG_NAME");
221 size_t prog_len
= strlen (__progname
);
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");
229 mmapp
= (void *(*)(void *, size_t, int, int, int, off_t
))dlsym (RTLD_NEXT
,
232 (void *(*)(void *, size_t, int, int, int, off64_t
))dlsym (RTLD_NEXT
,
234 mremapp
= (void *(*)(void *, size_t, size_t, int, void *))dlsym (RTLD_NEXT
,
236 munmapp
= (int (*)(void *, size_t))dlsym (RTLD_NEXT
, "munmap");
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] != '/'))
248 /* Only open the file if it's really us. */
249 if (!not_me
&& fd
== -1)
256 outname
= getenv ("MEMUSAGE_OUTPUT");
257 if (outname
!= NULL
&& outname
[0] != '\0'
258 && (access (outname
, R_OK
| W_OK
) == 0 || errno
== ENOENT
))
260 fd
= creat64 (outname
, 0666);
263 /* Don't do anything in future calls if we cannot write to
268 /* Write the first entry. */
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
));
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
)
282 buffer_size
= atoi (str_buffer_size
);
283 if (buffer_size
== 0 || buffer_size
> DEFAULT_BUFFER_SIZE
)
284 buffer_size
= DEFAULT_BUFFER_SIZE
;
287 /* Possibly enable timer-based stack pointer retrieval. */
288 if (getenv ("MEMUSAGE_NO_TIMER") == NULL
)
290 struct sigaction act
;
292 act
.sa_handler
= (sighandler_t
) &int_handler
;
293 act
.sa_flags
= SA_RESTART
;
294 sigfillset (&act
.sa_mask
);
296 if (sigaction (SIGPROF
, &act
, NULL
) >= 0)
298 struct itimerval timer
;
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
);
309 if (!not_me
&& getenv ("MEMUSAGE_TRACE_MMAP") != NULL
)
315 /* Record the initial stack position. */
317 __attribute__ ((constructor
))
326 /* `malloc' replacement. We keep track of the memory usage if this is the
331 struct header
*result
= NULL
;
333 /* Determine real implementation if not already happened. */
334 if (__glibc_unlikely (initialized
<= 0))
336 if (initialized
== -1)
342 /* If this is not the correct program just use the normal function. */
344 return (*mallocp
)(len
);
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. */
354 catomic_increment (&histogram
[len
/ 16]);
356 catomic_increment (&large
);
357 /* Total number of calls of any of the functions. */
358 catomic_increment (&calls_total
);
360 /* Do the real work. */
361 result
= (struct header
*) (*mallocp
)(len
+ sizeof (struct header
));
364 catomic_increment (&failed
[idx_malloc
]);
368 /* Update the allocation data and write out the records if necessary. */
369 update_data (result
, len
, 0);
371 /* Return the pointer to the user buffer. */
372 return (void *) (result
+ 1);
376 /* `realloc' replacement. We keep track of the memory usage if this is the
379 realloc (void *old
, size_t len
)
381 struct header
*result
= NULL
;
385 /* Determine real implementation if not already happened. */
386 if (__glibc_unlikely (initialized
<= 0))
388 if (initialized
== -1)
394 /* If this is not the correct program just use the normal function. */
396 return (*reallocp
)(old
, len
);
400 /* This is really a `malloc' call. */
406 real
= ((struct header
*) old
) - 1;
407 if (real
->magic
!= MAGIC
)
408 /* This is no memory allocated here. */
409 return (*reallocp
)(old
, len
);
411 old_len
= real
->length
;
414 /* Keep track of number of calls. */
415 catomic_increment (&calls
[idx_realloc
]);
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
);
424 if (len
== 0 && old
!= NULL
)
427 catomic_increment (&realloc_free
);
428 /* Keep track of total memory freed using `free'. */
429 catomic_add (&total
[idx_free
], real
->length
);
431 /* Update the allocation data and write out the records if necessary. */
432 update_data (NULL
, 0, old_len
);
434 /* Do the real work. */
440 /* Remember the size of the request. */
442 catomic_increment (&histogram
[len
/ 16]);
444 catomic_increment (&large
);
445 /* Total number of calls of any of the functions. */
446 catomic_increment (&calls_total
);
448 /* Do the real work. */
449 result
= (struct header
*) (*reallocp
)(real
, len
+ sizeof (struct header
));
452 catomic_increment (&failed
[idx_realloc
]);
456 /* Record whether the reduction/increase happened in place. */
458 catomic_increment (&inplace
);
459 /* Was the buffer increased? */
461 catomic_increment (&decreasing
);
463 /* Update the allocation data and write out the records if necessary. */
464 update_data (result
, len
, old_len
);
466 /* Return the pointer to the user buffer. */
467 return (void *) (result
+ 1);
471 /* `calloc' replacement. We keep track of the memory usage if this is the
474 calloc (size_t n
, size_t len
)
476 struct header
*result
;
477 size_t size
= n
* len
;
479 /* Determine real implementation if not already happened. */
480 if (__glibc_unlikely (initialized
<= 0))
482 if (initialized
== -1)
488 /* If this is not the correct program just use the normal function. */
490 return (*callocp
)(n
, len
);
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. */
500 catomic_increment (&histogram
[size
/ 16]);
502 catomic_increment (&large
);
503 /* Total number of calls of any of the functions. */
506 /* Do the real work. */
507 result
= (struct header
*) (*mallocp
)(size
+ sizeof (struct header
));
510 catomic_increment (&failed
[idx_calloc
]);
514 /* Update the allocation data and write out the records if necessary. */
515 update_data (result
, size
, 0);
517 /* Do what `calloc' would have done and return the buffer to the caller. */
518 return memset (result
+ 1, '\0', size
);
522 /* `free' replacement. We keep track of the memory usage if this is the
529 /* Determine real implementation if not already happened. */
530 if (__glibc_unlikely (initialized
<= 0))
532 if (initialized
== -1)
538 /* If this is not the correct program just use the normal function. */
545 /* `free (NULL)' has no effect. */
548 catomic_increment (&calls
[idx_free
]);
552 /* Determine the pointer to the header. */
553 real
= ((struct header
*) ptr
) - 1;
554 if (real
->magic
!= MAGIC
)
556 /* This block wasn't allocated here. */
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
);
566 /* Update the allocation data and write out the records if necessary. */
567 update_data (NULL
, 0, real
->length
);
569 /* Do the real work. */
574 /* `mmap' replacement. We do not have to keep track of the size since
575 `munmap' will get it as a parameter. */
577 mmap (void *start
, size_t len
, int prot
, int flags
, int fd
, off_t offset
)
581 /* Determine real implementation if not already happened. */
582 if (__glibc_unlikely (initialized
<= 0))
584 if (initialized
== -1)
590 /* Always get a block. We don't need extra memory. */
591 result
= (*mmapp
)(start
, len
, prot
, flags
, fd
, offset
);
593 if (!not_me
&& trace_mmap
)
595 int idx
= (flags
& MAP_ANON
596 ? idx_mmap_a
: prot
& PROT_WRITE
? idx_mmap_w
: idx_mmap_r
);
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. */
606 catomic_increment (&histogram
[len
/ 16]);
608 catomic_increment (&large
);
609 /* Total number of calls of any of the functions. */
610 catomic_increment (&calls_total
);
612 /* Check for failures. */
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);
622 /* Return the pointer to the user buffer. */
627 /* `mmap64' replacement. We do not have to keep track of the size since
628 `munmap' will get it as a parameter. */
630 mmap64 (void *start
, size_t len
, int prot
, int flags
, int fd
, off64_t offset
)
634 /* Determine real implementation if not already happened. */
635 if (__glibc_unlikely (initialized
<= 0))
637 if (initialized
== -1)
643 /* Always get a block. We don't need extra memory. */
644 result
= (*mmap64p
)(start
, len
, prot
, flags
, fd
, offset
);
646 if (!not_me
&& trace_mmap
)
648 int idx
= (flags
& MAP_ANON
649 ? idx_mmap_a
: prot
& PROT_WRITE
? idx_mmap_w
: idx_mmap_r
);
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. */
659 catomic_increment (&histogram
[len
/ 16]);
661 catomic_increment (&large
);
662 /* Total number of calls of any of the functions. */
663 catomic_increment (&calls_total
);
665 /* Check for failures. */
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);
675 /* Return the pointer to the user buffer. */
680 /* `mremap' replacement. We do not have to keep track of the size since
681 `munmap' will get it as a parameter. */
683 mremap (void *start
, size_t old_len
, size_t len
, int flags
, ...)
688 va_start (ap
, flags
);
689 void *newaddr
= (flags
& MREMAP_FIXED
) ? va_arg (ap
, void *) : NULL
;
692 /* Determine real implementation if not already happened. */
693 if (__glibc_unlikely (initialized
<= 0))
695 if (initialized
== -1)
701 /* Always get a block. We don't need extra memory. */
702 result
= (*mremapp
)(start
, old_len
, len
, flags
, newaddr
);
704 if (!not_me
&& trace_mmap
)
706 /* Keep track of number of calls. */
707 catomic_increment (&calls
[idx_mremap
]);
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
);
715 /* Remember the size of the request. */
717 catomic_increment (&histogram
[len
/ 16]);
719 catomic_increment (&large
);
720 /* Total number of calls of any of the functions. */
721 catomic_increment (&calls_total
);
723 /* Check for failures. */
725 catomic_increment (&failed
[idx_mremap
]);
728 /* Record whether the reduction/increase happened in place. */
730 catomic_increment (&inplace_mremap
);
731 /* Was the buffer increased? */
733 catomic_increment (&decreasing_mremap
);
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
);
742 /* Return the pointer to the user buffer. */
747 /* `munmap' replacement. */
749 munmap (void *start
, size_t len
)
753 /* Determine real implementation if not already happened. */
754 if (__glibc_unlikely (initialized
<= 0))
756 if (initialized
== -1)
762 /* Do the real work. */
763 result
= (*munmapp
)(start
, len
);
765 if (!not_me
&& trace_mmap
)
767 /* Keep track of number of calls. */
768 catomic_increment (&calls
[idx_munmap
]);
770 if (__glibc_likely (result
== 0))
772 /* Keep track of total memory freed using `free'. */
773 catomic_add (&total
[idx_munmap
], len
);
775 /* Update the allocation data and write out the records if
777 update_data (NULL
, 0, len
);
780 catomic_increment (&failed
[idx_munmap
]);
787 /* Write some statistics to standard error. */
789 __attribute__ ((destructor
))
793 unsigned long int maxcalls
;
795 /* If we haven't done anything here just return. */
799 /* If we should call any of the memory functions don't do any profiling. */
802 /* Finish the output file. */
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
));
810 write (fd
, buffer
, buffer_cnt
* sizeof (struct entry
));
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
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
));
825 /* Close the file. */
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
]);
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
]);
888 /* Write out a histoogram of the sizes of the allocations. */
889 fprintf (stderr
, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
891 /* Determine the maximum of all calls for each size range. */
893 for (cnt
= 0; cnt
< 65536; cnt
+= 16)
894 if (histogram
[cnt
/ 16] > maxcalls
)
895 maxcalls
= histogram
[cnt
/ 16];
897 for (cnt
= 0; cnt
< 65536; cnt
+= 16)
898 /* Only write out the nonzero entries. */
899 if (histogram
[cnt
/ 16] != 0)
901 percent
= (histogram
[cnt
/ 16] * 100) / calls_total
;
902 fprintf (stderr
, "%5d-%-5d%12lu ", cnt
, cnt
+ 15,
903 (unsigned long int) histogram
[cnt
/ 16]);
905 fputs (" <1% \e[41;37m", stderr
);
907 fprintf (stderr
, "%3d%% \e[41;37m", percent
);
909 /* Draw a bar with a length corresponding to the current
911 percent
= (histogram
[cnt
/ 16] * 50) / maxcalls
;
912 while (percent
-- > 0)
914 fputs ("\e[0;0m\n", stderr
);
919 percent
= (large
* 100) / calls_total
;
920 fprintf (stderr
, " large %12lu ", (unsigned long int) large
);
922 fputs (" <1% \e[41;37m", stderr
);
924 fprintf (stderr
, "%3d%% \e[41;37m", percent
);
925 percent
= (large
* 50) / maxcalls
;
926 while (percent
-- > 0)
928 fputs ("\e[0;0m\n", stderr
);
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. */