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