]>
Commit | Line | Data |
---|---|---|
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. */ | |
40 | static void *(*mallocp) (size_t); | |
41 | static void *(*reallocp) (void *, size_t); | |
42 | static void *(*callocp) (size_t, size_t); | |
43 | static void (*freep) (void *); | |
44 | ||
94eb5b36 UD |
45 | static void *(*mmapp) (void *, size_t, int, int, int, off_t); |
46 | static void *(*mmap64p) (void *, size_t, int, int, int, off64_t); | |
47 | static int (*munmapp) (void *, size_t); | |
18b8e054 | 48 | static void *(*mremapp) (void *, size_t, size_t, int, void *); |
94eb5b36 | 49 | |
c8f3e6db UD |
50 | enum |
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 | ||
65 | struct header | |
66 | { | |
67 | size_t length; | |
68 | size_t magic; | |
69 | }; | |
70 | ||
71 | #define MAGIC 0xfeedbeaf | |
72 | ||
73 | ||
d6c7294e UD |
74 | static memusage_cntr_t calls[idx_last]; |
75 | static memusage_cntr_t failed[idx_last]; | |
76 | static memusage_size_t total[idx_last]; | |
77 | static memusage_size_t grand_total; | |
78 | static memusage_cntr_t histogram[65536 / 16]; | |
79 | static memusage_cntr_t large; | |
80 | static memusage_cntr_t calls_total; | |
81 | static memusage_cntr_t inplace; | |
82 | static memusage_cntr_t decreasing; | |
11bf311e | 83 | static memusage_cntr_t realloc_free; |
d6c7294e UD |
84 | static memusage_cntr_t inplace_mremap; |
85 | static memusage_cntr_t decreasing_mremap; | |
86 | static memusage_size_t current_heap; | |
87 | static memusage_size_t peak_use[3]; | |
88 | static __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 |
96 | static size_t buffer_size; | |
97 | ||
98 | static int fd = -1; | |
99 | ||
94eb5b36 | 100 | static bool not_me; |
b4123507 | 101 | static int initialized; |
94eb5b36 | 102 | static bool trace_mmap; |
c8f3e6db UD |
103 | extern const char *__progname; |
104 | ||
105 | struct 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 |
113 | static struct entry buffer[2 * DEFAULT_BUFFER_SIZE]; |
114 | static uatomic32_t buffer_cnt; | |
c8f3e6db UD |
115 | static struct entry first; |
116 | ||
117 | ||
118 | /* Update the global data after a successful function call. */ | |
119 | static void | |
120 | update_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 (¤t_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. */ | |
190 | static void | |
191 | int_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. */ |
214 | static void | |
215 | me (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. */ |
312 | static void | |
313 | __attribute__ ((constructor)) | |
314 | init (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. */ | |
324 | void * | |
325 | malloc (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. */ | |
373 | void * | |
374 | realloc (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. */ | |
466 | void * | |
467 | calloc (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. */ | |
516 | void | |
517 | free (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. */ | |
567 | void * | |
568 | mmap (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. */ | |
619 | void * | |
620 | mmap64 (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. */ | |
671 | void * | |
18b8e054 | 672 | mremap (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. */ | |
736 | int | |
737 | munmap (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. */ |
775 | static void | |
776 | __attribute__ ((destructor)) | |
777 | dest (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 | } |