Fix warnings.
[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 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 && outname[0] != '\0'
204           && access (outname, R_OK | W_OK) == 0)
205         {
206           fd = creat (outname, 0666);
207
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
213             {
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));
220
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)
225                 {
226                   buffer_size = atoi (getenv ("MEMUSAGE_BUFFER_SIZE"));
227                   if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
228                     buffer_size = DEFAULT_BUFFER_SIZE;
229                 }
230
231               /* Possibly enable timer-based stack pointer retrieval.  */
232               if (getenv ("MEMUSAGE_NO_TIMER") == NULL)
233                 {
234                   struct sigaction act;
235
236                   act.sa_handler = (sighandler_t) &int_handler;
237                   act.sa_flags = SA_RESTART;
238                   sigfillset (&act.sa_mask);
239
240                   if (sigaction (SIGPROF, &act, NULL) >= 0)
241                     {
242                       struct itimerval timer;
243
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);
248                     }
249                 }
250             }
251         }
252     }
253 }
254
255
256 /* `malloc' replacement.  We keep track of the memory usage if this is the
257    correct program.  */
258 void *
259 malloc (size_t len)
260 {
261   struct header *result = NULL;
262
263   /* Determine real implementation if not already happened.  */
264   if (mallocp == NULL)
265     {
266       me ();
267       mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
268     }
269
270   /* If this is not the correct program just use the normal function.  */
271   if (not_me)
272     return (*mallocp) (len);
273
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;
287
288   /* Do the real work.  */
289   result = (struct header *) (*mallocp) (len + sizeof (struct header));
290
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);
296
297   /* Return the pointer to the user buffer.  */
298   return result ? (void *) (result + 1) : NULL;
299 }
300
301
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)
306 {
307   struct header *result = NULL;
308   struct header *real;
309   size_t old_len;
310
311   /* Determine real implementation if not already happened.  */
312   if (reallocp == NULL)
313     {
314       me ();
315       reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
316     }
317
318   /* If this is not the correct program just use the normal function.  */
319   if (not_me)
320     return (*reallocp) (old, len);
321
322   if (old == NULL)
323     {
324       /* This is really a `malloc' call.  */
325       real = NULL;
326       old_len = 0;
327     }
328   else
329     {
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;
335     }
336
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;
350
351   /* Do the real work.  */
352   result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
353
354   if (result == NULL)
355     ++failed[idx_realloc];
356   else
357     {
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;
364
365       /* Update the allocation data and write out the records if necessary.  */
366       update_data (result, len, old_len);
367     }
368
369   /* Return the pointer to the user buffer.  */
370   return result ? (void *) (result + 1) : NULL;
371 }
372
373
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)
378 {
379   struct header *result;
380   size_t size = n * len;
381
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)
387     {
388       me ();
389       mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
390     }
391
392   /* If this is not the correct program just use the normal function.  */
393   if (not_me)
394     {
395       callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
396
397       return (*callocp) (n, len);
398     }
399
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;
413
414   /* Do the real work.  */
415   result = (struct header *) (*mallocp) (size + sizeof (struct header));
416   if (result != NULL)
417     memset (result + 1, '\0', size);
418
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);
424
425   /* Return the pointer to the user buffer.  */
426   return result ? (void *) (result + 1) : NULL;
427 }
428
429
430 /* `free' replacement.  We keep track of the memory usage if this is the
431    correct program.  */
432 void
433 free (void *ptr)
434 {
435   struct header *real;
436
437   /* `free (NULL)' has no effect.  */
438   if (ptr == NULL)
439     {
440       ++calls[idx_free];
441       return;
442     }
443
444   /* Determine real implementation if not already happened.  */
445   if (freep == NULL)
446     {
447       me ();
448       freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
449     }
450
451   /* If this is not the correct program just use the normal function.  */
452   if (not_me)
453     {
454       (*freep) (ptr);
455       return;
456     }
457
458   /* Determine the pointer to the header.  */
459   real = ((struct header *) ptr) - 1;
460   if (real->magic != MAGIC)
461     {
462       /* This block wasn't allocated here.  */
463       (*freep) (ptr);
464       return;
465     }
466
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;
471
472   /* Update the allocation data and write out the records if necessary.  */
473   update_data (NULL, 0, real->length);
474
475   /* Do the real work.  */
476   (*freep) (real);
477 }
478
479
480 /* Write some statistics to standard error.  */
481 static void
482 __attribute__ ((destructor))
483 dest (void)
484 {
485   int percent, cnt;
486   unsigned long int maxcalls;
487
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;
493
494   /* Finish the output file.  */
495   if (fd != -1)
496     {
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));
511
512       /* Close the file.  */
513       close (fd);
514       fd = -1;
515     }
516
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]);
535
536   /* Write out a histoogram of the sizes of the allocations.  */
537   fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
538
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];
544
545   for (cnt = 0; cnt < 65536; cnt += 16)
546     /* Only write out the nonzero entries.  */
547     if (histogram[cnt / 16] != 0)
548       {
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);
556
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);
563       }
564
565   if (large != 0)
566     {
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);
577     }
578 }