Update.
[glibc.git] / malloc / memusage.c
blobb35444da109ea903d4671833783a8e74fc3192e5
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 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.
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 Lesser General Public License for more details.
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. */
21 #include <dlfcn.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <inttypes.h>
25 #include <signal.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/time.h>
32 #include <memusage.h>
34 /* Pointer to the real functions. These are determined used `dlsym'
35 when really needed. */
36 static void *(*mallocp) (size_t);
37 static void *(*reallocp) (void *, size_t);
38 static void *(*callocp) (size_t, size_t);
39 static void (*freep) (void *);
41 enum
43 idx_malloc = 0,
44 idx_realloc,
45 idx_calloc,
46 idx_free,
47 idx_last
51 struct header
53 size_t length;
54 size_t magic;
57 #define MAGIC 0xfeedbeaf
60 static unsigned long int calls[idx_last];
61 static unsigned long int failed[idx_last];
62 static unsigned long long int total[idx_last];
63 static unsigned long long int grand_total;
64 static unsigned long int histogram[65536 / 16];
65 static unsigned long int large;
66 static unsigned long int calls_total;
67 static unsigned long int inplace;
68 static unsigned long int decreasing;
69 static long int current_use[2];
70 static long int peak_use[3];
71 static uintptr_t start_sp;
73 /* A few macros to make the source more readable. */
74 #define current_heap current_use[0]
75 #define current_stack current_use[1]
76 #define peak_heap peak_use[0]
77 #define peak_stack peak_use[1]
78 #define peak_total peak_use[2]
80 #define DEFAULT_BUFFER_SIZE 1024
81 static size_t buffer_size;
83 static int fd = -1;
85 static int not_me;
86 static int initialized;
87 extern const char *__progname;
89 struct entry
91 size_t heap;
92 size_t stack;
93 uint32_t time_low;
94 uint32_t time_high;
97 static struct entry buffer[DEFAULT_BUFFER_SIZE];
98 static size_t buffer_cnt;
99 static struct entry first;
102 /* Update the global data after a successful function call. */
103 static void
104 update_data (struct header *result, size_t len, size_t old_len)
106 long int total_use;
108 if (result != NULL)
110 /* Record the information we need and mark the block using a
111 magic number. */
112 result->length = len;
113 result->magic = MAGIC;
116 /* Compute current heap usage and compare it with the maximum value. */
117 current_heap += len - old_len;
118 if (current_heap > peak_heap)
119 peak_heap = current_heap;
121 /* Compute current stack usage and compare it with the maximum value. */
122 #ifdef STACK_GROWS_UPWARD
123 current_stack = GETSP () - start_sp;
124 #else
125 current_stack = start_sp - GETSP ();
126 #endif
127 if (current_stack > peak_stack)
128 peak_stack = current_stack;
130 /* Add up heap and stack usage and compare it with the maximum value. */
131 total_use = current_heap + current_stack;
132 if (total_use > peak_total)
133 peak_total = total_use;
135 /* Store the value only if we are writing to a file. */
136 if (fd != -1)
138 buffer[buffer_cnt].heap = current_heap;
139 buffer[buffer_cnt].stack = current_stack;
140 GETTIME (buffer[buffer_cnt].time_low, buffer[buffer_cnt].time_high);
141 ++buffer_cnt;
143 /* Write out buffer if it is full. */
144 if (buffer_cnt == buffer_size)
146 write (fd, buffer, buffer_cnt * sizeof (struct entry));
147 buffer_cnt = 0;
153 /* Interrupt handler. */
154 static void
155 int_handler (int signo)
157 /* Nothing gets allocated. Just record the stack pointer position. */
158 update_data (NULL, 0, 0);
162 /* Find out whether this is the program we are supposed to profile.
163 For this the name in the variable `__progname' must match the one
164 given in the environment variable MEMUSAGE_PROG_NAME. If the variable
165 is not present every program assumes it should be profiling.
167 If this is the program open a file descriptor to the output file.
168 We will write to it whenever the buffer overflows. The name of the
169 output file is determined by the environment variable MEMUSAGE_OUTPUT.
171 If the environment variable MEMUSAGE_BUFFER_SIZE is set its numerical
172 value determines the size of the internal buffer. The number gives
173 the number of elements in the buffer. By setting the number to one
174 one effectively selects unbuffered operation.
176 If MEMUSAGE_NO_TIMER is not present an alarm handler is installed
177 which at the highest possible frequency records the stack pointer. */
178 static void
179 me (void)
181 const char *env = getenv ("MEMUSAGE_PROG_NAME");
182 size_t prog_len = strlen (__progname);
184 initialized = -1;
185 mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
186 reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
187 callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
188 freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
189 initialized = 1;
191 if (env != NULL)
193 /* Check for program name. */
194 size_t len = strlen (env);
195 if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
196 || (prog_len != len && __progname[prog_len - len - 1] != '/'))
197 not_me = 1;
200 /* Only open the file if it's really us. */
201 if (!not_me && fd == -1)
203 const char *outname;
205 if (!start_sp)
206 start_sp = GETSP ();
208 outname = getenv ("MEMUSAGE_OUTPUT");
209 if (outname != NULL && outname[0] != '\0'
210 && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
212 fd = creat (outname, 0666);
214 if (fd == -1)
215 /* Don't do anything in future calls if we cannot write to
216 the output file. */
217 not_me = 1;
218 else
220 /* Write the first entry. */
221 first.heap = 0;
222 first.stack = 0;
223 GETTIME (first.time_low, first.time_high);
224 /* Write it two times since we need the starting and end time. */
225 write (fd, &first, sizeof (first));
227 /* Determine the buffer size. We use the default if the
228 environment variable is not present. */
229 buffer_size = DEFAULT_BUFFER_SIZE;
230 if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
232 buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
233 if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
234 buffer_size = DEFAULT_BUFFER_SIZE;
237 /* Possibly enable timer-based stack pointer retrieval. */
238 if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
240 struct sigaction act;
242 act.sa_handler = (sighandler_t) &int_handler;
243 act.sa_flags = SA_RESTART;
244 sigfillset (&act.sa_mask);
246 if (sigaction (SIGPROF, &act, NULL) >= 0)
248 struct itimerval timer;
250 timer.it_value.tv_sec = 0;
251 timer.it_value.tv_usec = 1;
252 timer.it_interval = timer.it_value;
253 setitimer (ITIMER_PROF, &timer, NULL);
262 /* Record the initial stack position. */
263 static void
264 __attribute__ ((constructor))
265 init (void)
267 start_sp = GETSP ();
268 if (! initialized)
269 me ();
273 /* `malloc' replacement. We keep track of the memory usage if this is the
274 correct program. */
275 void *
276 malloc (size_t len)
278 struct header *result = NULL;
280 /* Determine real implementation if not already happened. */
281 if (__builtin_expect (initialized <= 0, 0))
283 if (initialized == -1)
284 return NULL;
285 me ();
288 /* If this is not the correct program just use the normal function. */
289 if (not_me)
290 return (*mallocp) (len);
292 /* Keep track of number of calls. */
293 ++calls[idx_malloc];
294 /* Keep track of total memory consumption for `malloc'. */
295 total[idx_malloc] += len;
296 /* Keep track of total memory requirement. */
297 grand_total += len;
298 /* Remember the size of the request. */
299 if (len < 65536)
300 ++histogram[len / 16];
301 else
302 ++large;
303 /* Total number of calls of any of the functions. */
304 ++calls_total;
306 /* Do the real work. */
307 result = (struct header *) (*mallocp) (len + sizeof (struct header));
308 if (result == NULL)
310 ++failed[idx_malloc];
311 return NULL;
314 /* Update the allocation data and write out the records if necessary. */
315 update_data (result, len, 0);
317 /* Return the pointer to the user buffer. */
318 return (void *) (result + 1);
322 /* `realloc' replacement. We keep track of the memory usage if this is the
323 correct program. */
324 void *
325 realloc (void *old, size_t len)
327 struct header *result = NULL;
328 struct header *real;
329 size_t old_len;
331 /* Determine real implementation if not already happened. */
332 if (__builtin_expect (initialized <= 0, 0))
334 if (initialized == -1)
335 return NULL;
336 me ();
339 /* If this is not the correct program just use the normal function. */
340 if (not_me)
341 return (*reallocp) (old, len);
343 if (old == NULL)
345 /* This is really a `malloc' call. */
346 real = NULL;
347 old_len = 0;
349 else
351 real = ((struct header *) old) - 1;
352 if (real->magic != MAGIC)
353 /* This is no memory allocated here. */
354 return (*reallocp) (old, len);
355 old_len = real->length;
358 /* Keep track of number of calls. */
359 ++calls[idx_realloc];
360 if (len > old_len)
362 /* Keep track of total memory consumption for `realloc'. */
363 total[idx_realloc] += len - old_len;
364 /* Keep track of total memory requirement. */
365 grand_total += len - old_len;
367 /* Remember the size of the request. */
368 if (len < 65536)
369 ++histogram[len / 16];
370 else
371 ++large;
372 /* Total number of calls of any of the functions. */
373 ++calls_total;
375 /* Do the real work. */
376 result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
377 if (result == NULL)
379 ++failed[idx_realloc];
380 return NULL;
383 /* Record whether the reduction/increase happened in place. */
384 if (real == result)
385 ++inplace;
386 /* Was the buffer increased? */
387 if (old_len > len)
388 ++decreasing;
390 /* Update the allocation data and write out the records if necessary. */
391 update_data (result, len, old_len);
393 /* Return the pointer to the user buffer. */
394 return (void *) (result + 1);
398 /* `calloc' replacement. We keep track of the memory usage if this is the
399 correct program. */
400 void *
401 calloc (size_t n, size_t len)
403 struct header *result;
404 size_t size = n * len;
406 /* Determine real implementation if not already happened. */
407 if (__builtin_expect (initialized <= 0, 0))
409 if (initialized == -1)
410 return NULL;
411 me ();
414 /* If this is not the correct program just use the normal function. */
415 if (not_me)
416 return (*callocp) (n, len);
418 /* Keep track of number of calls. */
419 ++calls[idx_calloc];
420 /* Keep track of total memory consumption for `calloc'. */
421 total[idx_calloc] += size;
422 /* Keep track of total memory requirement. */
423 grand_total += size;
424 /* Remember the size of the request. */
425 if (size < 65536)
426 ++histogram[size / 16];
427 else
428 ++large;
429 /* Total number of calls of any of the functions. */
430 ++calls_total;
432 /* Do the real work. */
433 result = (struct header *) (*mallocp) (size + sizeof (struct header));
434 if (result == NULL)
436 ++failed[idx_calloc];
437 return NULL;
440 /* Update the allocation data and write out the records if necessary. */
441 update_data (result, size, 0);
443 /* Do what `calloc' would have done and return the buffer to the caller. */
444 return memset (result + 1, '\0', size);
448 /* `free' replacement. We keep track of the memory usage if this is the
449 correct program. */
450 void
451 free (void *ptr)
453 struct header *real;
455 /* Determine real implementation if not already happened. */
456 if (__builtin_expect (initialized <= 0, 0))
458 if (initialized == -1)
459 return;
460 me ();
463 /* If this is not the correct program just use the normal function. */
464 if (not_me)
466 (*freep) (ptr);
467 return;
470 /* `free (NULL)' has no effect. */
471 if (ptr == NULL)
473 ++calls[idx_free];
474 return;
477 /* Determine the pointer to the header. */
478 real = ((struct header *) ptr) - 1;
479 if (real->magic != MAGIC)
481 /* This block wasn't allocated here. */
482 (*freep) (ptr);
483 return;
486 /* Keep track of number of calls. */
487 ++calls[idx_free];
488 /* Keep track of total memory freed using `free'. */
489 total[idx_free] += real->length;
491 /* Update the allocation data and write out the records if necessary. */
492 update_data (NULL, 0, real->length);
494 /* Do the real work. */
495 (*freep) (real);
499 /* Write some statistics to standard error. */
500 static void
501 __attribute__ ((destructor))
502 dest (void)
504 int percent, cnt;
505 unsigned long int maxcalls;
507 /* If we haven't done anything here just return. */
508 if (not_me)
509 return;
510 /* If we should call any of the memory functions don't do any profiling. */
511 not_me = 1;
513 /* Finish the output file. */
514 if (fd != -1)
516 /* Write the partially filled buffer. */
517 write (fd, buffer, buffer_cnt * sizeof (struct entry));
518 /* Go back to the beginning of the file. We allocated two records
519 here when we opened the file. */
520 lseek (fd, 0, SEEK_SET);
521 /* Write out a record containing the total size. */
522 first.stack = peak_total;
523 write (fd, &first, sizeof (struct entry));
524 /* Write out another record containing the maximum for heap and
525 stack. */
526 first.heap = peak_heap;
527 first.stack = peak_stack;
528 GETTIME (first.time_low, first.time_high);
529 write (fd, &first, sizeof (struct entry));
531 /* Close the file. */
532 close (fd);
533 fd = -1;
536 /* Write a colorful statistic. */
537 fprintf (stderr, "\n\
538 \e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\
539 \e[04;34m total calls total memory failed calls\e[0m\n\
540 \e[00;34m malloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
541 \e[00;34mrealloc|\e[0m %10lu %12llu %s%12lu\e[00;00m (in place: %ld, dec: %ld)\n\
542 \e[00;34m calloc|\e[0m %10lu %12llu %s%12lu\e[00;00m\n\
543 \e[00;34m free|\e[0m %10lu %12llu\n",
544 grand_total, (unsigned long int) peak_heap,
545 (unsigned long int) peak_stack,
546 calls[idx_malloc], total[idx_malloc],
547 failed[idx_malloc] ? "\e[01;41m" : "", failed[idx_malloc],
548 calls[idx_realloc], total[idx_realloc],
549 failed[idx_realloc] ? "\e[01;41m" : "", failed[idx_realloc],
550 inplace, decreasing,
551 calls[idx_calloc], total[idx_calloc],
552 failed[idx_calloc] ? "\e[01;41m" : "", failed[idx_calloc],
553 calls[idx_free], total[idx_free]);
555 /* Write out a histoogram of the sizes of the allocations. */
556 fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
558 /* Determine the maximum of all calls for each size range. */
559 maxcalls = large;
560 for (cnt = 0; cnt < 65536; cnt += 16)
561 if (histogram[cnt / 16] > maxcalls)
562 maxcalls = histogram[cnt / 16];
564 for (cnt = 0; cnt < 65536; cnt += 16)
565 /* Only write out the nonzero entries. */
566 if (histogram[cnt / 16] != 0)
568 percent = (histogram[cnt / 16] * 100) / calls_total;
569 fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15,
570 histogram[cnt / 16]);
571 if (percent == 0)
572 fputs (" <1% \e[41;37m", stderr);
573 else
574 fprintf (stderr, "%3d%% \e[41;37m", percent);
576 /* Draw a bar with a length corresponding to the current
577 percentage. */
578 percent = (histogram[cnt / 16] * 50) / maxcalls;
579 while (percent-- > 0)
580 fputc ('=', stderr);
581 fputs ("\e[0;0m\n", stderr);
584 if (large != 0)
586 percent = (large * 100) / calls_total;
587 fprintf (stderr, " large %12lu ", large);
588 if (percent == 0)
589 fputs (" <1% \e[41;37m", stderr);
590 else
591 fprintf (stderr, "%3d%% \e[41;37m", percent);
592 percent = (large * 50) / maxcalls;
593 while (percent-- > 0)
594 fputc ('=', stderr);
595 fputs ("\e[0;0m\n", stderr);