b35444da109ea903d4671833783a8e74fc3192e5
[kopensolaris-gnu/glibc.git] / malloc / memusage.c
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.
5
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.
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
14    Lesser General Public License for more details.
15
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.  */
20
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>
31
32 #include <memusage.h>
33
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 *);
40
41 enum
42 {
43   idx_malloc = 0,
44   idx_realloc,
45   idx_calloc,
46   idx_free,
47   idx_last
48 };
49
50
51 struct header
52 {
53   size_t length;
54   size_t magic;
55 };
56
57 #define MAGIC 0xfeedbeaf
58
59
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;
72
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]
79
80 #define DEFAULT_BUFFER_SIZE     1024
81 static size_t buffer_size;
82
83 static int fd = -1;
84
85 static int not_me;
86 static int initialized;
87 extern const char *__progname;
88
89 struct entry
90 {
91   size_t heap;
92   size_t stack;
93   uint32_t time_low;
94   uint32_t time_high;
95 };
96
97 static struct entry buffer[DEFAULT_BUFFER_SIZE];
98 static size_t buffer_cnt;
99 static struct entry first;
100
101
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)
105 {
106   long int total_use;
107
108   if (result != NULL)
109     {
110       /* Record the information we need and mark the block using a
111          magic number.  */
112       result->length = len;
113       result->magic = MAGIC;
114     }
115
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;
120
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;
129
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;
134
135   /* Store the value only if we are writing to a file.  */
136   if (fd != -1)
137     {
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;
142
143       /* Write out buffer if it is full.  */
144       if (buffer_cnt == buffer_size)
145         {
146           write (fd, buffer, buffer_cnt * sizeof (struct entry));
147           buffer_cnt = 0;
148         }
149     }
150 }
151
152
153 /* Interrupt handler.  */
154 static void
155 int_handler (int signo)
156 {
157   /* Nothing gets allocated.  Just record the stack pointer position.  */
158   update_data (NULL, 0, 0);
159 }
160
161
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.
166
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.
170
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.
175
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)
180 {
181   const char *env = getenv ("MEMUSAGE_PROG_NAME");
182   size_t prog_len = strlen (__progname);
183
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;
190
191   if (env != NULL)
192     {
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;
198     }
199
200   /* Only open the file if it's really us.  */
201   if (!not_me && fd == -1)
202     {
203       const char *outname;
204
205       if (!start_sp)
206         start_sp = GETSP ();
207
208       outname = getenv ("MEMUSAGE_OUTPUT");
209       if (outname != NULL && outname[0] != '\0'
210           && (access (outname, R_OK | W_OK) == 0 || errno == ENOENT))
211         {
212           fd = creat (outname, 0666);
213
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
219             {
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));
226
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)
231                 {
232                   buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
233                   if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
234                     buffer_size = DEFAULT_BUFFER_SIZE;
235                 }
236
237               /* Possibly enable timer-based stack pointer retrieval.  */
238               if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
239                 {
240                   struct sigaction act;
241
242                   act.sa_handler = (sighandler_t) &int_handler;
243                   act.sa_flags = SA_RESTART;
244                   sigfillset (&act.sa_mask);
245
246                   if (sigaction (SIGPROF, &act, NULL) >= 0)
247                     {
248                       struct itimerval timer;
249
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);
254                     }
255                 }
256             }
257         }
258     }
259 }
260
261
262 /* Record the initial stack position.  */
263 static void
264 __attribute__ ((constructor))
265 init (void)
266 {
267   start_sp = GETSP ();
268   if (! initialized)
269     me ();
270 }
271
272
273 /* `malloc' replacement.  We keep track of the memory usage if this is the
274    correct program.  */
275 void *
276 malloc (size_t len)
277 {
278   struct header *result = NULL;
279
280   /* Determine real implementation if not already happened.  */
281   if (__builtin_expect (initialized <= 0, 0))
282     {
283       if (initialized == -1)
284         return NULL;
285       me ();
286     }
287
288   /* If this is not the correct program just use the normal function.  */
289   if (not_me)
290     return (*mallocp) (len);
291
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;
305
306   /* Do the real work.  */
307   result = (struct header *) (*mallocp) (len + sizeof (struct header));
308   if (result == NULL)
309     {
310       ++failed[idx_malloc];
311       return NULL;
312     }
313
314   /* Update the allocation data and write out the records if necessary.  */
315   update_data (result, len, 0);
316
317   /* Return the pointer to the user buffer.  */
318   return (void *) (result + 1);
319 }
320
321
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)
326 {
327   struct header *result = NULL;
328   struct header *real;
329   size_t old_len;
330
331   /* Determine real implementation if not already happened.  */
332   if (__builtin_expect (initialized <= 0, 0))
333     {
334       if (initialized == -1)
335         return NULL;
336       me ();
337     }
338
339   /* If this is not the correct program just use the normal function.  */
340   if (not_me)
341     return (*reallocp) (old, len);
342
343   if (old == NULL)
344     {
345       /* This is really a `malloc' call.  */
346       real = NULL;
347       old_len = 0;
348     }
349   else
350     {
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;
356     }
357
358   /* Keep track of number of calls.  */
359   ++calls[idx_realloc];
360   if (len > old_len)
361     {
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;
366     }
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;
374
375   /* Do the real work.  */
376   result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
377   if (result == NULL)
378     {
379       ++failed[idx_realloc];
380       return NULL;
381     }
382
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;
389
390   /* Update the allocation data and write out the records if necessary.  */
391   update_data (result, len, old_len);
392
393   /* Return the pointer to the user buffer.  */
394   return (void *) (result + 1);
395 }
396
397
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)
402 {
403   struct header *result;
404   size_t size = n * len;
405
406   /* Determine real implementation if not already happened.  */
407   if (__builtin_expect (initialized <= 0, 0))
408     {
409       if (initialized == -1)
410         return NULL;
411       me ();
412     }
413
414   /* If this is not the correct program just use the normal function.  */
415   if (not_me)
416     return (*callocp) (n, len);
417
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;
431
432   /* Do the real work.  */
433   result = (struct header *) (*mallocp) (size + sizeof (struct header));
434   if (result == NULL)
435     {
436       ++failed[idx_calloc];
437       return NULL;
438     }
439
440   /* Update the allocation data and write out the records if necessary.  */
441   update_data (result, size, 0);
442
443   /* Do what `calloc' would have done and return the buffer to the caller.  */
444   return memset (result + 1, '\0', size);
445 }
446
447
448 /* `free' replacement.  We keep track of the memory usage if this is the
449    correct program.  */
450 void
451 free (void *ptr)
452 {
453   struct header *real;
454
455   /* Determine real implementation if not already happened.  */
456   if (__builtin_expect (initialized <= 0, 0))
457     {
458       if (initialized == -1)
459         return;
460       me ();
461     }
462
463   /* If this is not the correct program just use the normal function.  */
464   if (not_me)
465     {
466       (*freep) (ptr);
467       return;
468     }
469
470   /* `free (NULL)' has no effect.  */
471   if (ptr == NULL)
472     {
473       ++calls[idx_free];
474       return;
475     }
476
477   /* Determine the pointer to the header.  */
478   real = ((struct header *) ptr) - 1;
479   if (real->magic != MAGIC)
480     {
481       /* This block wasn't allocated here.  */
482       (*freep) (ptr);
483       return;
484     }
485
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;
490
491   /* Update the allocation data and write out the records if necessary.  */
492   update_data (NULL, 0, real->length);
493
494   /* Do the real work.  */
495   (*freep) (real);
496 }
497
498
499 /* Write some statistics to standard error.  */
500 static void
501 __attribute__ ((destructor))
502 dest (void)
503 {
504   int percent, cnt;
505   unsigned long int maxcalls;
506
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;
512
513   /* Finish the output file.  */
514   if (fd != -1)
515     {
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));
530
531       /* Close the file.  */
532       close (fd);
533       fd = -1;
534     }
535
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]);
554
555   /* Write out a histoogram of the sizes of the allocations.  */
556   fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
557
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];
563
564   for (cnt = 0; cnt < 65536; cnt += 16)
565     /* Only write out the nonzero entries.  */
566     if (histogram[cnt / 16] != 0)
567       {
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);
575
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);
582       }
583
584   if (large != 0)
585     {
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);
596     }
597 }