(otherlibs): Added.
[glibc.git] / malloc / memusage.c
blob7d1ee94b565bffbe8491ebb356c5caa5e4f6de49
1 /* Profile heap and stack memory usage of running program.
2 Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 #include <dlfcn.h>
22 #include <fcntl.h>
23 #include <inttypes.h>
24 #include <signal.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/time.h>
31 #include <memusage.h>
33 /* Pointer to the real functions. These are determined used `dlsym'
34 when really needed. */
35 static void *(*mallocp) (size_t);
36 static void *(*reallocp) (void *, size_t);
37 static void *(*callocp) (size_t, size_t);
38 static void (*freep) (void *);
40 enum
42 idx_malloc = 0,
43 idx_realloc,
44 idx_calloc,
45 idx_free,
46 idx_last
50 struct header
52 size_t length;
53 size_t magic;
56 #define MAGIC 0xfeedbeaf
59 static unsigned long int calls[idx_last];
60 static unsigned long int failed[idx_last];
61 static unsigned long long int total[idx_last];
62 static unsigned long long int grand_total;
63 static unsigned long int histogram[65536 / 16];
64 static unsigned long int large;
65 static unsigned long int calls_total;
66 static unsigned long int inplace;
67 static unsigned long int decreasing;
68 static long int current_use[2];
69 static long int peak_use[3];
70 static uintptr_t start_sp;
72 /* A few macros to make the source more readable. */
73 #define current_heap current_use[0]
74 #define current_stack current_use[1]
75 #define peak_heap peak_use[0]
76 #define peak_stack peak_use[1]
77 #define peak_total peak_use[2]
79 #define DEFAULT_BUFFER_SIZE 1024
80 static size_t buffer_size;
82 static int fd = -1;
84 static int not_me;
85 extern const char *__progname;
87 struct entry
89 size_t heap;
90 size_t stack;
91 uint32_t time_low;
92 uint32_t time_high;
95 static struct entry buffer[DEFAULT_BUFFER_SIZE];
96 static size_t buffer_cnt;
97 static struct entry first;
100 /* Update the global data after a successful function call. */
101 static void
102 update_data (struct header *result, size_t len, size_t old_len)
104 long int total_use;
106 if (result != NULL)
108 /* Record the information we need and mark the block using a
109 magic number. */
110 result->length = len;
111 result->magic = MAGIC;
114 /* Compute current heap usage and compare it with the maximum value. */
115 current_heap += len - old_len;
116 if (current_heap > peak_heap)
117 peak_heap = current_heap;
119 /* Compute current stack usage and compare it with the maximum value. */
120 #ifdef STACK_GROWS_UPWARD
121 current_stack = GETSP () - start_sp;
122 #else
123 current_stack = start_sp - GETSP ();
124 #endif
125 if (current_stack > peak_stack)
126 peak_stack = current_stack;
128 /* Add up heap and stack usage and compare it with the maximum value. */
129 total_use = current_heap + current_stack;
130 if (total_use > peak_total)
131 peak_total = total_use;
133 /* Store the value only if we are writing to a file. */
134 if (fd != -1)
136 buffer[buffer_cnt].heap = current_heap;
137 buffer[buffer_cnt].stack = current_stack;
138 GETTIME (buffer[buffer_cnt].time_low, buffer[buffer_cnt].time_high);
139 ++buffer_cnt;
141 /* Write out buffer if it is full. */
142 if (buffer_cnt == buffer_size)
144 write (fd, buffer, buffer_cnt * sizeof (struct entry));
145 buffer_cnt = 0;
151 /* Interrupt handler. */
152 static void
153 int_handler (int signo)
155 /* Nothing gets allocated. Just record the stack pointer position. */
156 update_data (NULL, 0, 0);
160 /* Record the initial stack position. */
161 static void
162 __attribute__ ((constructor))
163 init (void)
165 start_sp = GETSP ();
169 /* Find out whether this is the program we are supposed to profile.
170 For this the name in the variable `__progname' must match the one
171 given in the environment variable MEMUSAGE_PROG_NAME. If the variable
172 is not present every program assumes it should be profiling.
174 If this is the program open a file descriptor to the output file.
175 We will write to it whenever the buffer overflows. The name of the
176 output file is determined by the environment variable MEMUSAGE_OUTPUT.
178 If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical
179 value determines the size of the internal buffer. The number gives
180 the number of elements in the buffer. By setting the number to one
181 one effectively selects unbuffered operation.
183 If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
184 which at the highest possible frequency records the stack pointer. */
185 static void
186 me (void)
188 const char *env = getenv ("MEMUSAGE_PROG_NAME");
189 size_t prog_len = strlen (__progname);
190 if (env != NULL)
192 /* Check for program name. */
193 size_t len = strlen (env);
194 if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
195 || (prog_len != len && __progname[prog_len - len - 1] != '/'))
196 not_me = 1;
199 /* Only open the file if it's really us. */
200 if (!not_me && fd == -1)
202 const char *outname = getenv ("MEMUSAGE_OUTPUT");
203 if (outname != NULL && outname[0] != '\0'
204 && access (outname, R_OK | W_OK) == 0)
206 fd = creat (outname, 0666);
208 if (fd == -1)
209 /* Don't do anything in future calls if we cannot write to
210 the output file. */
211 not_me = 1;
212 else
214 /* Write the first entry. */
215 first.heap = 0;
216 first.stack = 0;
217 GETTIME (first.time_low, first.time_high);
218 /* Write it two times since we need the starting and end time. */
219 write (fd, &first, sizeof (first));
221 /* Determine the buffer size. We use the default if the
222 environment variable is not present. */
223 buffer_size = DEFAULT_BUFFER_SIZE;
224 if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
226 buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
227 if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
228 buffer_size = DEFAULT_BUFFER_SIZE;
231 /* Possibly enable timer-based stack pointer retrieval. */
232 if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
234 struct sigaction act;
236 act.sa_handler = (sighandler_t) &int_handler;
237 act.sa_flags = SA_RESTART;
238 sigfillset (&act.sa_mask);
240 if (sigaction (SIGPROF, &act, NULL) >= 0)
242 struct itimerval timer;
244 timer.it_value.tv_sec = 0;
245 timer.it_value.tv_usec = 1;
246 timer.it_interval = timer.it_value;
247 setitimer (ITIMER_PROF, &timer, NULL);
256 /* `malloc' replacement. We keep track of the memory usage if this is the
257 correct program. */
258 void *
259 malloc (size_t len)
261 struct header *result = NULL;
263 /* Determine real implementation if not already happened. */
264 if (mallocp == NULL)
266 me ();
267 mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
270 /* If this is not the correct program just use the normal function. */
271 if (not_me)
272 return (*mallocp) (len);
274 /* Keep track of number of calls. */
275 ++calls[idx_malloc];
276 /* Keep track of total memory consumption for `malloc'. */
277 total[idx_malloc] += len;
278 /* Keep track of total memory requirement. */
279 grand_total += len;
280 /* Remember the size of the request. */
281 if (len < 65536)
282 ++histogram[len / 16];
283 else
284 ++large;
285 /* Total number of calls of any of the functions. */
286 ++calls_total;
288 /* Do the real work. */
289 result = (struct header *) (*mallocp) (len + sizeof (struct header));
291 if (result == NULL)
292 ++failed[idx_malloc];
293 else
294 /* Update the allocation data and write out the records if necessary. */
295 update_data (result, len, 0);
297 /* Return the pointer to the user buffer. */
298 return result ? (void *) (result + 1) : NULL;
302 /* `realloc' replacement. We keep track of the memory usage if this is the
303 correct program. */
304 void *
305 realloc (void *old, size_t len)
307 struct header *result = NULL;
308 struct header *real;
309 size_t old_len;
311 /* Determine real implementation if not already happened. */
312 if (reallocp == NULL)
314 me ();
315 reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
318 /* If this is not the correct program just use the normal function. */
319 if (not_me)
320 return (*reallocp) (old, len);
322 if (old == NULL)
324 /* This is really a `malloc' call. */
325 real = NULL;
326 old_len = 0;
328 else
330 real = ((struct header *) old) - 1;
331 if (real->magic != MAGIC)
332 /* This is no memory allocated here. */
333 return (*reallocp) (old, len);
334 old_len = real->length;
337 /* Keep track of number of calls. */
338 ++calls[idx_realloc];
339 /* Keep track of total memory consumption for `realloc'. */
340 total[idx_realloc] += len;
341 /* Keep track of total memory requirement. */
342 grand_total += len;
343 /* Remember the size of the request. */
344 if (len < 65536)
345 ++histogram[len / 16];
346 else
347 ++large;
348 /* Total number of calls of any of the functions. */
349 ++calls_total;
351 /* Do the real work. */
352 result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
354 if (result == NULL)
355 ++failed[idx_realloc];
356 else
358 /* Record whether the reduction/increase happened in place. */
359 if (real == result)
360 ++inplace;
361 /* Was the buffer increased? */
362 if (old_len > len)
363 ++decreasing;
365 /* Update the allocation data and write out the records if necessary. */
366 update_data (result, len, old_len);
369 /* Return the pointer to the user buffer. */
370 return result ? (void *) (result + 1) : NULL;
374 /* `calloc' replacement. We keep track of the memory usage if this is the
375 correct program. */
376 void *
377 calloc (size_t n, size_t len)
379 struct header *result;
380 size_t size = n * len;
382 /* Determine real implementation if not already happened. We are
383 searching for the `malloc' implementation since it is not always
384 efficiently possible to use `calloc' because we have to add a bit
385 room to the allocation to put the header in. */
386 if (mallocp == NULL)
388 me ();
389 mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
392 /* If this is not the correct program just use the normal function. */
393 if (not_me)
395 callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
397 return (*callocp) (n, len);
400 /* Keep track of number of calls. */
401 ++calls[idx_calloc];
402 /* Keep track of total memory consumption for `calloc'. */
403 total[idx_calloc] += size;
404 /* Keep track of total memory requirement. */
405 grand_total += size;
406 /* Remember the size of the request. */
407 if (size < 65536)
408 ++histogram[size / 16];
409 else
410 ++large;
411 /* Total number of calls of any of the functions. */
412 ++calls_total;
414 /* Do the real work. */
415 result = (struct header *) (*mallocp) (size + sizeof (struct header));
416 if (result != NULL)
417 memset (result + 1, '\0', size);
419 if (result == NULL)
420 ++failed[idx_calloc];
421 else
422 /* Update the allocation data and write out the records if necessary. */
423 update_data (result, size, 0);
425 /* Return the pointer to the user buffer. */
426 return result ? (void *) (result + 1) : NULL;
430 /* `free' replacement. We keep track of the memory usage if this is the
431 correct program. */
432 void
433 free (void *ptr)
435 struct header *real;
437 /* `free (NULL)' has no effect. */
438 if (ptr == NULL)
440 ++calls[idx_free];
441 return;
444 /* Determine real implementation if not already happened. */
445 if (freep == NULL)
447 me ();
448 freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
451 /* If this is not the correct program just use the normal function. */
452 if (not_me)
454 (*freep) (ptr);
455 return;
458 /* Determine the pointer to the header. */
459 real = ((struct header *) ptr) - 1;
460 if (real->magic != MAGIC)
462 /* This block wasn't allocated here. */
463 (*freep) (ptr);
464 return;
467 /* Keep track of number of calls. */
468 ++calls[idx_free];
469 /* Keep track of total memory freed using `free'. */
470 total[idx_free] += real->length;
472 /* Update the allocation data and write out the records if necessary. */
473 update_data (NULL, 0, real->length);
475 /* Do the real work. */
476 (*freep) (real);
480 /* Write some statistics to standard error. */
481 static void
482 __attribute__ ((destructor))
483 dest (void)
485 int percent, cnt;
486 unsigned long int maxcalls;
488 /* If we haven't done anything here just return. */
489 if (not_me)
490 return;
491 /* If we should call any of the memory functions don't do any profiling. */
492 not_me = 1;
494 /* Finish the output file. */
495 if (fd != -1)
497 /* Write the partially filled buffer. */
498 write (fd, buffer, buffer_cnt * sizeof (struct entry));
499 /* Go back to the beginning of the file. We allocated two records
500 here when we opened the file. */
501 lseek (fd, 0, SEEK_SET);
502 /* Write out a record containing the total size. */
503 first.stack = peak_total;
504 write (fd, &first, sizeof (struct entry));
505 /* Write out another record containing the maximum for heap and
506 stack. */
507 first.heap = peak_heap;
508 first.stack = peak_stack;
509 GETTIME (first.time_low, first.time_high);
510 write (fd, &first, sizeof (struct entry));
512 /* Close the file. */
513 close (fd);
514 fd = -1;
517 /* Write a colorful statistic. */
518 fprintf (stderr, "\n\
519 \e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\
520 \e[04;34m total calls total memory failed calls\e[0m\n\
521 \e[00;34m malloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
522 \e[00;34mrealloc|\e[0m %10lu %12llu %s%12lu\e[00;00m (in place: %ld, dec: %ld)\n\
523 \e[00;34m calloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
524 \e[00;34m free|\e[0m %10lu %12llu\n",
525 grand_total, (unsigned long int) peak_heap,
526 (unsigned long int) peak_stack,
527 calls[idx_malloc], total[idx_malloc],
528 failed[idx_malloc] ? "\e[01;41m" : "", failed[idx_malloc],
529 calls[idx_realloc], total[idx_realloc],
530 failed[idx_realloc] ? "\e[01;41m" : "", failed[idx_realloc],
531 inplace, decreasing,
532 calls[idx_calloc], total[idx_calloc],
533 failed[idx_calloc] ? "\e[01;41m" : "", failed[idx_calloc],
534 calls[idx_free], total[idx_free]);
536 /* Write out a histoogram of the sizes of the allocations. */
537 fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
539 /* Determine the maximum of all calls for each size range. */
540 maxcalls = large;
541 for (cnt = 0; cnt < 65536; cnt += 16)
542 if (histogram[cnt / 16] > maxcalls)
543 maxcalls = histogram[cnt / 16];
545 for (cnt = 0; cnt < 65536; cnt += 16)
546 /* Only write out the nonzero entries. */
547 if (histogram[cnt / 16] != 0)
549 percent = (histogram[cnt / 16] * 100) / calls_total;
550 fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15,
551 histogram[cnt / 16]);
552 if (percent == 0)
553 fputs (" <1% \e[41;37m", stderr);
554 else
555 fprintf (stderr, "%3d%% \e[41;37m", percent);
557 /* Draw a bar with a length corresponding to the current
558 percentage. */
559 percent = (histogram[cnt / 16] * 50) / maxcalls;
560 while (percent-- > 0)
561 fputc ('=', stderr);
562 fputs ("\e[0;0m\n", stderr);
565 if (large != 0)
567 percent = (large * 100) / calls_total;
568 fprintf (stderr, " large %12lu ", large);
569 if (percent == 0)
570 fputs (" <1% \e[41;37m", stderr);
571 else
572 fprintf (stderr, "%3d%% \e[41;37m", percent);
573 percent = (large * 50) / maxcalls;
574 while (percent-- > 0)
575 fputc ('=', stderr);
576 fputs ("\e[0;0m\n", stderr);