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