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