34e33475e3299be5bc459112ea14f31febdbcf12
[kopensolaris-gnu/glibc.git] / malloc / memusage.c
1 /* Profile heap and stack memory usage of running program.
2    Copyright (C) 1998, 1999, 2000 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 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.
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    Library General Public License for more details.
15
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.  */
20
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>
30
31 #include <memusage.h>
32
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 *);
39
40 enum
41 {
42   idx_malloc = 0,
43   idx_realloc,
44   idx_calloc,
45   idx_free,
46   idx_last
47 };
48
49
50 struct header
51 {
52   size_t length;
53   size_t magic;
54 };
55
56 #define MAGIC 0xfeedbeaf
57
58
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;
71
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]
78
79 #define DEFAULT_BUFFER_SIZE     1024
80 static size_t buffer_size;
81
82 static int fd = -1;
83
84 static int not_me;
85 extern const char *__progname;
86
87 struct entry
88 {
89   size_t heap;
90   size_t stack;
91   uint32_t time_low;
92   uint32_t time_high;
93 };
94
95 static struct entry buffer[DEFAULT_BUFFER_SIZE];
96 static size_t buffer_cnt;
97 static struct entry first;
98
99
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)
103 {
104   long int total_use;
105
106   if (result != NULL)
107     {
108       /* Record the information we need and mark the block using a
109          magic number.  */
110       result->length = len;
111       result->magic = MAGIC;
112     }
113
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;
118
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;
127
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;
132
133   /* Store the value only if we are writing to a file.  */
134   if (fd != -1)
135     {
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;
140
141       /* Write out buffer if it is full.  */
142       if (buffer_cnt == buffer_size)
143         {
144           write (fd, buffer, buffer_cnt * sizeof (struct entry));
145           buffer_cnt = 0;
146         }
147     }
148 }
149
150
151 /* Interrupt handler.  */
152 static void
153 int_handler (int signo)
154 {
155   /* Nothing gets allocated.  Just record the stack pointer position.  */
156   update_data (NULL, 0, 0);
157 }
158
159
160 /* Record the initial stack position.  */
161 static void
162 __attribute__ ((constructor))
163 init (void)
164 {
165   start_sp = GETSP ();
166 }
167
168
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.
173
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.
177
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.
182
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)
187 {
188   const char *env = getenv ("MEMUSAGE_PROG_NAME");
189   size_t prog_len = strlen (__progname);
190   if (env != NULL)
191     {
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;
197     }
198
199   /* Only open the file if it's really us.  */
200   if (!not_me && fd == -1)
201     {
202       const char *outname = getenv ("MEMUSAGE_OUTPUT");
203       if (outname != NULL)
204         {
205           fd = creat (outname, 0666);
206
207           if (fd == -1)
208             /* Don't do anything in future calls if we cannot write to
209                the output file.  */
210             not_me = 1;
211           else
212             {
213               /* Write the first entry.  */
214               first.heap = 0;
215               first.stack = 0;
216               GETTIME (first.time_low, first.time_high);
217               /* Write it two times since we need the starting and end time. */
218               write (fd, &first, sizeof (first));
219
220               /* Determine the buffer size.  We use the default if the
221                  environment variable is not present.  */
222               buffer_size = DEFAULT_BUFFER_SIZE;
223               if (getenv ("MEMUSAGE_BUFFER_SIZE") != NULL)
224                 {
225                   buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
226                   if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
227                     buffer_size = DEFAULT_BUFFER_SIZE;
228                 }
229
230               /* Possibly enable timer-based stack pointer retrieval.  */
231               if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
232                 {
233                   struct sigaction act;
234
235                   act.sa_handler = (sighandler_t) &int_handler;
236                   act.sa_flags = SA_RESTART;
237                   sigfillset (&act.sa_mask);
238
239                   if (sigaction (SIGPROF, &act, NULL) >= 0)
240                     {
241                       struct itimerval timer;
242
243                       timer.it_value.tv_sec = 0;
244                       timer.it_value.tv_usec = 1;
245                       timer.it_interval = timer.it_value;
246                       setitimer (ITIMER_PROF, &timer, NULL);
247                     }
248                 }
249             }
250         }
251     }
252 }
253
254
255 /* `malloc' replacement.  We keep track of the memory usage if this is the
256    correct program.  */
257 void *
258 malloc (size_t len)
259 {
260   struct header *result = NULL;
261
262   /* Determine real implementation if not already happened.  */
263   if (mallocp == NULL)
264     {
265       me ();
266       mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
267     }
268
269   /* If this is not the correct program just use the normal function.  */
270   if (not_me)
271     return (*mallocp) (len);
272
273   /* Keep track of number of calls.  */
274   ++calls[idx_malloc];
275   /* Keep track of total memory consumption for `malloc'.  */
276   total[idx_malloc] += len;
277   /* Keep track of total memory requirement.  */
278   grand_total += len;
279   /* Remember the size of the request.  */
280   if (len < 65536)
281     ++histogram[len / 16];
282   else
283     ++large;
284   /* Total number of calls of any of the functions.  */
285   ++calls_total;
286
287   /* Do the real work.  */
288   result = (struct header *) (*mallocp) (len + sizeof (struct header));
289
290   if (result == NULL)
291     ++failed[idx_malloc];
292   else
293     /* Update the allocation data and write out the records if necessary.  */
294     update_data (result, len, 0);
295
296   /* Return the pointer to the user buffer.  */
297   return result ? (void *) (result + 1) : NULL;
298 }
299
300
301 /* `realloc' replacement.  We keep track of the memory usage if this is the
302    correct program.  */
303 void *
304 realloc (void *old, size_t len)
305 {
306   struct header *result = NULL;
307   struct header *real;
308   size_t old_len;
309
310   /* Determine real implementation if not already happened.  */
311   if (reallocp == NULL)
312     {
313       me ();
314       reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
315     }
316
317   /* If this is not the correct program just use the normal function.  */
318   if (not_me)
319     return (*reallocp) (old, len);
320
321   if (old == NULL)
322     {
323       /* This is really a `malloc' call.  */
324       real = NULL;
325       old_len = 0;
326     }
327   else
328     {
329       real = ((struct header *) old) - 1;
330       if (real->magic != MAGIC)
331         /* This is no memory allocated here.  */
332         return (*reallocp) (old, len);
333       old_len = real->length;
334     }
335
336   /* Keep track of number of calls.  */
337   ++calls[idx_realloc];
338   /* Keep track of total memory consumption for `realloc'.  */
339   total[idx_realloc] += len;
340   /* Keep track of total memory requirement.  */
341   grand_total += len;
342   /* Remember the size of the request.  */
343   if (len < 65536)
344     ++histogram[len / 16];
345   else
346     ++large;
347   /* Total number of calls of any of the functions.  */
348   ++calls_total;
349
350   /* Do the real work.  */
351   result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
352
353   if (result == NULL)
354     ++failed[idx_realloc];
355   else
356     {
357       /* Record whether the reduction/increase happened in place.  */
358       if (real == result)
359         ++inplace;
360       /* Was the buffer increased?  */
361       if (old_len > len)
362         ++decreasing;
363
364       /* Update the allocation data and write out the records if necessary.  */
365       update_data (result, len, old_len);
366     }
367
368   /* Return the pointer to the user buffer.  */
369   return result ? (void *) (result + 1) : NULL;
370 }
371
372
373 /* `calloc' replacement.  We keep track of the memory usage if this is the
374    correct program.  */
375 void *
376 calloc (size_t n, size_t len)
377 {
378   struct header *result;
379   size_t size = n * len;
380
381   /* Determine real implementation if not already happened.  We are
382      searching for the `malloc' implementation since it is not always
383      efficiently possible to use `calloc' because we have to add a bit
384      room to the allocation to put the header in.  */
385   if (mallocp == NULL)
386     {
387       me ();
388       mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
389     }
390
391   /* If this is not the correct program just use the normal function.  */
392   if (not_me)
393     {
394       callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
395
396       return (*callocp) (n, len);
397     }
398
399   /* Keep track of number of calls.  */
400   ++calls[idx_calloc];
401   /* Keep track of total memory consumption for `calloc'.  */
402   total[idx_calloc] += size;
403   /* Keep track of total memory requirement.  */
404   grand_total += size;
405   /* Remember the size of the request.  */
406   if (size < 65536)
407     ++histogram[size / 16];
408   else
409     ++large;
410   /* Total number of calls of any of the functions.  */
411   ++calls_total;
412
413   /* Do the real work.  */
414   result = (struct header *) (*mallocp) (size + sizeof (struct header));
415   if (result != NULL)
416     memset (result + 1, '\0', size);
417
418   if (result == NULL)
419     ++failed[idx_calloc];
420   else
421     /* Update the allocation data and write out the records if necessary.  */
422     update_data (result, size, 0);
423
424   /* Return the pointer to the user buffer.  */
425   return result ? (void *) (result + 1) : NULL;
426 }
427
428
429 /* `free' replacement.  We keep track of the memory usage if this is the
430    correct program.  */
431 void
432 free (void *ptr)
433 {
434   struct header *real;
435
436   /* `free (NULL)' has no effect.  */
437   if (ptr == NULL)
438     {
439       ++calls[idx_free];
440       return;
441     }
442
443   /* Determine real implementation if not already happened.  */
444   if (freep == NULL)
445     {
446       me ();
447       freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
448     }
449
450   /* If this is not the correct program just use the normal function.  */
451   if (not_me)
452     {
453       (*freep) (ptr);
454       return;
455     }
456
457   /* Determine the pointer to the header.  */
458   real = ((struct header *) ptr) - 1;
459   if (real->magic != MAGIC)
460     {
461       /* This block wasn't allocated here.  */
462       (*freep) (ptr);
463       return;
464     }
465
466   /* Keep track of number of calls.  */
467   ++calls[idx_free];
468   /* Keep track of total memory freed using `free'.  */
469   total[idx_free] += real->length;
470
471   /* Update the allocation data and write out the records if necessary.  */
472   update_data (NULL, 0, real->length);
473
474   /* Do the real work.  */
475   (*freep) (real);
476 }
477
478
479 /* Write some statistics to standard error.  */
480 static void
481 __attribute__ ((destructor))
482 dest (void)
483 {
484   int percent, cnt;
485   unsigned long int maxcalls;
486
487   /* If we haven't done anything here just return.  */
488   if (not_me)
489     return;
490   /* If we should call any of the memory functions don't do any profiling.  */
491   not_me = 1;
492
493   /* Finish the output file.  */
494   if (fd != -1)
495     {
496       /* Write the partially filled buffer.  */
497       write (fd, buffer, buffer_cnt * sizeof (struct entry));
498       /* Go back to the beginning of the file.  We allocated two records
499          here when we opened the file.  */
500       lseek (fd, 0, SEEK_SET);
501       /* Write out a record containing the total size.  */
502       first.stack = peak_total;
503       write (fd, &first, sizeof (struct entry));
504       /* Write out another record containing the maximum for heap and
505          stack.  */
506       first.heap = peak_heap;
507       first.stack = peak_stack;
508       GETTIME (first.time_low, first.time_high);
509       write (fd, &first, sizeof (struct entry));
510
511       /* Close the file.  */
512       close (fd);
513       fd = -1;
514     }
515
516   /* Write a colorful statistic.  */
517   fprintf (stderr, "\n\
518 \e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\
519 \e[04;34m         total calls   total memory   failed calls\e[0m\n\
520 \e[00;34m malloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
521 \e[00;34mrealloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m   (in place: %ld, dec: %ld)\n\
522 \e[00;34m calloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
523 \e[00;34m   free|\e[0m %10lu   %12llu\n",
524            grand_total, (unsigned long int) peak_heap,
525            (unsigned long int) peak_stack,
526            calls[idx_malloc], total[idx_malloc],
527            failed[idx_malloc] ? "\e[01;41m" : "", failed[idx_malloc],
528            calls[idx_realloc], total[idx_realloc],
529            failed[idx_realloc] ? "\e[01;41m" : "", failed[idx_realloc],
530            inplace, decreasing,
531            calls[idx_calloc], total[idx_calloc],
532            failed[idx_calloc] ? "\e[01;41m" : "", failed[idx_calloc],
533            calls[idx_free], total[idx_free]);
534
535   /* Write out a histoogram of the sizes of the allocations.  */
536   fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
537
538   /* Determine the maximum of all calls for each size range.  */
539   maxcalls = large;
540   for (cnt = 0; cnt < 65536; cnt += 16)
541     if (histogram[cnt / 16] > maxcalls)
542       maxcalls = histogram[cnt / 16];
543
544   for (cnt = 0; cnt < 65536; cnt += 16)
545     /* Only write out the nonzero entries.  */
546     if (histogram[cnt / 16] != 0)
547       {
548         percent = (histogram[cnt / 16] * 100) / calls_total;
549         fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15,
550                  histogram[cnt / 16]);
551         if (percent == 0)
552           fputs (" <1% \e[41;37m", stderr);
553         else
554           fprintf (stderr, "%3d%% \e[41;37m", percent);
555
556         /* Draw a bar with a length corresponding to the current
557            percentage.  */
558         percent = (histogram[cnt / 16] * 50) / maxcalls;
559         while (percent-- > 0)
560           fputc ('=', stderr);
561          fputs ("\e[0;0m\n", stderr);
562       }
563
564   if (large != 0)
565     {
566       percent = (large * 100) / calls_total;
567       fprintf (stderr, "   large   %12lu ", large);
568       if (percent == 0)
569         fputs (" <1% \e[41;37m", stderr);
570       else
571         fprintf (stderr, "%3d%% \e[41;37m", percent);
572       percent = (large * 50) / maxcalls;
573       while (percent-- > 0)
574         fputc ('=', stderr);
575       fputs ("\e[0;0m\n", stderr);
576     }
577 }