]> git.ipfire.org Git - thirdparty/glibc.git/blame - malloc/memusage.c
* sysdeps/mach/hurd/close.c: #undef __close before function defn,
[thirdparty/glibc.git] / malloc / memusage.c
CommitLineData
c8f3e6db 1/* Profile heap and stack memory usage of running program.
94eb5b36 2 Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
c8f3e6db
UD
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
41bdb6e2
AJ
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.
c8f3e6db
UD
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
41bdb6e2 14 Lesser General Public License for more details.
c8f3e6db 15
41bdb6e2
AJ
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. */
c8f3e6db
UD
20
21#include <dlfcn.h>
6f47f645 22#include <errno.h>
c8f3e6db
UD
23#include <fcntl.h>
24#include <inttypes.h>
25#include <signal.h>
94eb5b36 26#include <stdbool.h>
c8f3e6db
UD
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
94eb5b36 31#include <sys/mman.h>
c8f3e6db
UD
32#include <sys/time.h>
33
ba80a015 34#include <memusage.h>
586599b5 35
c8f3e6db
UD
36/* Pointer to the real functions. These are determined used `dlsym'
37 when really needed. */
38static void *(*mallocp) (size_t);
39static void *(*reallocp) (void *, size_t);
40static void *(*callocp) (size_t, size_t);
41static void (*freep) (void *);
42
94eb5b36
UD
43static void *(*mmapp) (void *, size_t, int, int, int, off_t);
44static void *(*mmap64p) (void *, size_t, int, int, int, off64_t);
45static int (*munmapp) (void *, size_t);
46static void *(*mremapp) (void *, size_t, size_t, int);
47
c8f3e6db
UD
48enum
49{
50 idx_malloc = 0,
51 idx_realloc,
52 idx_calloc,
53 idx_free,
94eb5b36
UD
54 idx_mmap_r,
55 idx_mmap_w,
56 idx_mremap,
57 idx_munmap,
c8f3e6db
UD
58 idx_last
59};
60
61
62struct header
63{
64 size_t length;
65 size_t magic;
66};
67
68#define MAGIC 0xfeedbeaf
69
70
71static unsigned long int calls[idx_last];
72static unsigned long int failed[idx_last];
73static unsigned long long int total[idx_last];
74static unsigned long long int grand_total;
75static unsigned long int histogram[65536 / 16];
76static unsigned long int large;
77static unsigned long int calls_total;
78static unsigned long int inplace;
79static unsigned long int decreasing;
94eb5b36
UD
80static unsigned long int inplace_mremap;
81static unsigned long int decreasing_mremap;
c8f3e6db
UD
82static long int current_use[2];
83static long int peak_use[3];
84static 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
c8f3e6db
UD
93#define DEFAULT_BUFFER_SIZE 1024
94static size_t buffer_size;
95
96static int fd = -1;
97
94eb5b36 98static bool not_me;
b4123507 99static int initialized;
94eb5b36 100static bool trace_mmap;
c8f3e6db
UD
101extern const char *__progname;
102
103struct entry
104{
105 size_t heap;
106 size_t stack;
107 uint32_t time_low;
108 uint32_t time_high;
109};
110
111static struct entry buffer[DEFAULT_BUFFER_SIZE];
112static size_t buffer_cnt;
113static struct entry first;
114
115
116/* Update the global data after a successful function call. */
117static void
118update_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. */
b15cb495
UD
136#ifdef STACK_GROWS_UPWARD
137 current_stack = GETSP () - start_sp;
138#else
c8f3e6db 139 current_stack = start_sp - GETSP ();
b15cb495 140#endif
c8f3e6db
UD
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. */
168static void
169int_handler (int signo)
170{
171 /* Nothing gets allocated. Just record the stack pointer position. */
172 update_data (NULL, 0, 0);
173}
174
175
c8f3e6db
UD
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
ba80a015 178 given in the environment variable MEMUSAGE_PROG_NAME. If the variable
c8f3e6db
UD
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
ba80a015 183 output file is determined by the environment variable MEMUSAGE_OUTPUT.
c8f3e6db 184
ba80a015 185 If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical
c8f3e6db
UD
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
ba80a015 190 If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
c8f3e6db
UD
191 which at the highest possible frequency records the stack pointer. */
192static void
193me (void)
194{
ba80a015 195 const char *env = getenv ("MEMUSAGE_PROG_NAME");
c8f3e6db 196 size_t prog_len = strlen (__progname);
b4123507 197
0e21f776
UD
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");
94eb5b36
UD
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");
0e21f776
UD
212 initialized = 1;
213
c8f3e6db
UD
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] != '/'))
94eb5b36 220 not_me = true;
c8f3e6db
UD
221 }
222
223 /* Only open the file if it's really us. */
224 if (!not_me && fd == -1)
225 {
b4123507
UD
226 const char *outname;
227
228 if (!start_sp)
229 start_sp = GETSP ();
230
b4123507 231 outname = getenv ("MEMUSAGE_OUTPUT");
cf1bb6d5 232 if (outname != NULL && outname[0] != '\0'
6f47f645 233 && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
c8f3e6db
UD
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. */
94eb5b36 240 not_me = true;
c8f3e6db
UD
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;
ba80a015 253 if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
c8f3e6db 254 {
ba80a015 255 buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
c8f3e6db
UD
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. */
ba80a015 261 if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
c8f3e6db
UD
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 }
94eb5b36
UD
281
282 if (!not_me && getenv ("MEMUSAGE_TRACE_MMAP") != NULL)
283 trace_mmap = true;
c8f3e6db
UD
284 }
285}
286
287
b4123507
UD
288/* Record the initial stack position. */
289static void
290__attribute__ ((constructor))
291init (void)
292{
293 start_sp = GETSP ();
294 if (! initialized)
295 me ();
296}
297
298
c8f3e6db
UD
299/* `malloc' replacement. We keep track of the memory usage if this is the
300 correct program. */
301void *
302malloc (size_t len)
303{
304 struct header *result = NULL;
305
306 /* Determine real implementation if not already happened. */
b4123507 307 if (__builtin_expect (initialized <= 0, 0))
c8f3e6db 308 {
b4123507
UD
309 if (initialized == -1)
310 return NULL;
c8f3e6db 311 me ();
c8f3e6db
UD
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));
c8f3e6db 334 if (result == NULL)
b4123507
UD
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);
c8f3e6db
UD
342
343 /* Return the pointer to the user buffer. */
b4123507 344 return (void *) (result + 1);
c8f3e6db
UD
345}
346
347
348/* `realloc' replacement. We keep track of the memory usage if this is the
349 correct program. */
350void *
351realloc (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. */
b4123507 358 if (__builtin_expect (initialized <= 0, 0))
c8f3e6db 359 {
b4123507
UD
360 if (initialized == -1)
361 return NULL;
c8f3e6db 362 me ();
c8f3e6db
UD
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];
d3acfb61
UD
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 }
c8f3e6db
UD
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));
c8f3e6db 403 if (result == NULL)
c8f3e6db 404 {
b4123507
UD
405 ++failed[idx_realloc];
406 return NULL;
c8f3e6db
UD
407 }
408
b4123507
UD
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
c8f3e6db 419 /* Return the pointer to the user buffer. */
b4123507 420 return (void *) (result + 1);
c8f3e6db
UD
421}
422
423
424/* `calloc' replacement. We keep track of the memory usage if this is the
425 correct program. */
426void *
427calloc (size_t n, size_t len)
428{
429 struct header *result;
430 size_t size = n * len;
431
b4123507
UD
432 /* Determine real implementation if not already happened. */
433 if (__builtin_expect (initialized <= 0, 0))
c8f3e6db 434 {
b4123507
UD
435 if (initialized == -1)
436 return NULL;
c8f3e6db 437 me ();
c8f3e6db
UD
438 }
439
440 /* If this is not the correct program just use the normal function. */
441 if (not_me)
b4123507 442 return (*callocp) (n, len);
c8f3e6db
UD
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));
c8f3e6db 460 if (result == NULL)
b4123507
UD
461 {
462 ++failed[idx_calloc];
463 return NULL;
464 }
c8f3e6db 465
b4123507
UD
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);
c8f3e6db
UD
471}
472
473
474/* `free' replacement. We keep track of the memory usage if this is the
475 correct program. */
476void
477free (void *ptr)
478{
479 struct header *real;
480
c8f3e6db 481 /* Determine real implementation if not already happened. */
b4123507 482 if (__builtin_expect (initialized <= 0, 0))
c8f3e6db 483 {
b4123507
UD
484 if (initialized == -1)
485 return;
c8f3e6db 486 me ();
c8f3e6db
UD
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
b4123507
UD
496 /* `free (NULL)' has no effect. */
497 if (ptr == NULL)
498 {
499 ++calls[idx_free];
500 return;
501 }
502
c8f3e6db
UD
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
94eb5b36
UD
525/* `mmap' replacement. We do not have to keep track of the sizesince
526 `munmap' will get it as a parameter. */
527void *
528mmap (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. */
578void *
579mmap64 (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. */
629void *
630mremap (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. */
689int
690munmap (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
c8f3e6db
UD
727/* Write some statistics to standard error. */
728static void
729__attribute__ ((destructor))
730dest (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. */
94eb5b36 739 not_me = true;
c8f3e6db
UD
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
94eb5b36
UD
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
c8f3e6db
UD
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}