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