(__attribute_malloc__): Only define if hasn't happened yet.
[kopensolaris-gnu/glibc.git] / malloc / memprof.c
1 /* Profile heap and stack memory usage of running program.
2    Copyright (C) 1998, 1999 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 /* Pointer to the real functions.  These are determined used `dlsym'
32    when really needed.  */
33 static void *(*mallocp) (size_t);
34 static void *(*reallocp) (void *, size_t);
35 static void *(*callocp) (size_t, size_t);
36 static void (*freep) (void *);
37
38 enum
39 {
40   idx_malloc = 0,
41   idx_realloc,
42   idx_calloc,
43   idx_free,
44   idx_last
45 };
46
47
48 struct header
49 {
50   size_t length;
51   size_t magic;
52 };
53
54 #define MAGIC 0xfeedbeaf
55
56
57 static unsigned long int calls[idx_last];
58 static unsigned long int failed[idx_last];
59 static unsigned long long int total[idx_last];
60 static unsigned long long int grand_total;
61 static unsigned long int histogram[65536 / 16];
62 static unsigned long int large;
63 static unsigned long int calls_total;
64 static unsigned long int inplace;
65 static unsigned long int decreasing;
66 static long int current_use[2];
67 static long int peak_use[3];
68 static uintptr_t start_sp;
69
70 /* A few macros to make the source more readable.  */
71 #define current_heap    current_use[0]
72 #define current_stack   current_use[1]
73 #define peak_heap       peak_use[0]
74 #define peak_stack      peak_use[1]
75 #define peak_total      peak_use[2]
76
77 #ifdef __i386__
78 # define GETSP() ({ register uintptr_t stack_ptr asm ("esp"); stack_ptr; })
79 #endif
80 #ifdef __alpha__
81 # define GETSP() ({ register uintptr_t stack_ptr asm ("$30"); stack_ptr; })
82 #endif
83 #ifdef __sparc__
84 # define GETSP() ({ register uintptr_t stack_ptr asm ("%sp"); stack_ptr; })
85 #endif
86 #ifdef __powerpc__
87 # define GETSP() ({ register uintptr_t stack_ptr asm ("%r1"); stack_ptr; })
88 #endif
89
90 #ifdef __i386__
91 # define GETTIME(low,high) asm ("rdtsc" : "=a" (low), "=d" (high))
92 #endif
93 #ifndef GETTIME
94 # define GETTIME(low,high) \
95   {                                                                           \
96     struct timeval tval;                                                      \
97     uint64_t usecs;                                                           \
98     gettimeofday (&tval, NULL);                                               \
99     usecs = (uint64_t) tval.tv_usec + (uint64_t) tval.tv_usec * 1000000;      \
100     low = usecs & 0xffffffff;                                                 \
101     high = usecs >> 32;                                                       \
102   }
103 #endif
104
105 #define DEFAULT_BUFFER_SIZE     1024
106 static size_t buffer_size;
107
108 static int fd = -1;
109
110 static int not_me;
111 extern const char *__progname;
112
113 struct entry
114 {
115   size_t heap;
116   size_t stack;
117   uint32_t time_low;
118   uint32_t time_high;
119 };
120
121 static struct entry buffer[DEFAULT_BUFFER_SIZE];
122 static size_t buffer_cnt;
123 static struct entry first;
124
125
126 /* Update the global data after a successful function call.  */
127 static void
128 update_data (struct header *result, size_t len, size_t old_len)
129 {
130   long int total_use;
131
132   if (result != NULL)
133     {
134       /* Record the information we need and mark the block using a
135          magic number.  */
136       result->length = len;
137       result->magic = MAGIC;
138     }
139
140   /* Compute current heap usage and compare it with the maximum value.  */
141   current_heap += len - old_len;
142   if (current_heap > peak_heap)
143     peak_heap = current_heap;
144
145   /* Compute current stack usage and compare it with the maximum value.  */
146   current_stack = start_sp - GETSP ();
147   if (current_stack > peak_stack)
148     peak_stack = current_stack;
149
150   /* Add up heap and stack usage and compare it with the maximum value.  */
151   total_use = current_heap + current_stack;
152   if (total_use > peak_total)
153     peak_total = total_use;
154
155   /* Store the value only if we are writing to a file.  */
156   if (fd != -1)
157     {
158       buffer[buffer_cnt].heap = current_heap;
159       buffer[buffer_cnt].stack = current_stack;
160       GETTIME (buffer[buffer_cnt].time_low, buffer[buffer_cnt].time_high);
161       ++buffer_cnt;
162
163       /* Write out buffer if it is full.  */
164       if (buffer_cnt == buffer_size)
165         {
166           write (fd, buffer, buffer_cnt * sizeof (struct entry));
167           buffer_cnt = 0;
168         }
169     }
170 }
171
172
173 /* Interrupt handler.  */
174 static void
175 int_handler (int signo)
176 {
177   /* Nothing gets allocated.  Just record the stack pointer position.  */
178   update_data (NULL, 0, 0);
179 }
180
181
182 /* Record the initial stack position.  */
183 static void
184 __attribute__ ((constructor))
185 init (void)
186 {
187   start_sp = GETSP ();
188 }
189
190
191 /* Find out whether this is the program we are supposed to profile.
192    For this the name in the variable `__progname' must match the one
193    given in the environment variable MEMPROF_PROG_NAME.  If the variable
194    is not present every program assumes it should be profiling.
195
196    If this is the program open a file descriptor to the output file.
197    We will write to it whenever the buffer overflows.  The name of the
198    output file is determined by the environment variable MEMPROF_OUTPUT.
199
200    If the environment variable MEMPROF_BUFFER_SIZE is set its numerical
201    value determines the size of the internal buffer.  The number gives
202    the number of elements in the buffer.  By setting the number to one
203    one effectively selects unbuffered operation.
204
205    If MEMPROF_NO_TIMER is not present an alarm handler is installed
206    which at the highest possible frequency records the stack pointer.  */
207 static void
208 me (void)
209 {
210   const char *env = getenv ("MEMPROF_PROG_NAME");
211   size_t prog_len = strlen (__progname);
212   if (env != NULL)
213     {
214       /* Check for program name.  */
215       size_t len = strlen (env);
216       if (len > prog_len || strcmp (env, &__progname[prog_len - len]) != 0
217           || (prog_len != len && __progname[prog_len - len - 1] != '/'))
218         not_me = 1;
219     }
220
221   /* Only open the file if it's really us.  */
222   if (!not_me && fd == -1)
223     {
224       const char *outname = getenv ("MEMPROF_OUTPUT");
225       if (outname != NULL)
226         {
227           fd = creat (outname, 0666);
228
229           if (fd == -1)
230             /* Don't do anything in future calls if we cannot write to
231                the output file.  */
232             not_me = 1;
233           else
234             {
235               /* Write the first entry.  */
236               first.heap = 0;
237               first.stack = 0;
238               GETTIME (first.time_low, first.time_high);
239               /* Write it two times since we need the starting and end time. */
240               write (fd, &first, sizeof (first));
241
242               /* Determine the buffer size.  We use the default if the
243                  environment variable is not present.  */
244               buffer_size = DEFAULT_BUFFER_SIZE;
245               if (getenv ("MEMPROF_BUFFER_SIZE") != NULL)
246                 {
247                   buffer_size = atoi (getenv ("MEMPROF_BUFFER_SIZE"));
248                   if (buffer_size == 0 || buffer_size > DEFAULT_BUFFER_SIZE)
249                     buffer_size = DEFAULT_BUFFER_SIZE;
250                 }
251
252               /* Possibly enable timer-based stack pointer retrieval.  */
253               if (getenv ("MEMPROF_NO_TIMER") == NULL)
254                 {
255                   struct sigaction act;
256
257                   act.sa_handler = (sighandler_t) &int_handler;
258                   act.sa_flags = SA_RESTART;
259                   sigfillset (&act.sa_mask);
260
261                   if (sigaction (SIGPROF, &act, NULL) >= 0)
262                     {
263                       struct itimerval timer;
264
265                       timer.it_value.tv_sec = 0;
266                       timer.it_value.tv_usec = 1;
267                       timer.it_interval = timer.it_value;
268                       setitimer (ITIMER_PROF, &timer, NULL);
269                     }
270                 }
271             }
272         }
273     }
274 }
275
276
277 /* `malloc' replacement.  We keep track of the memory usage if this is the
278    correct program.  */
279 void *
280 malloc (size_t len)
281 {
282   struct header *result = NULL;
283
284   /* Determine real implementation if not already happened.  */
285   if (mallocp == NULL)
286     {
287       me ();
288       mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
289     }
290
291   /* If this is not the correct program just use the normal function.  */
292   if (not_me)
293     return (*mallocp) (len);
294
295   /* Keep track of number of calls.  */
296   ++calls[idx_malloc];
297   /* Keep track of total memory consumption for `malloc'.  */
298   total[idx_malloc] += len;
299   /* Keep track of total memory requirement.  */
300   grand_total += len;
301   /* Remember the size of the request.  */
302   if (len < 65536)
303     ++histogram[len / 16];
304   else
305     ++large;
306   /* Total number of calls of any of the functions.  */
307   ++calls_total;
308
309   /* Do the real work.  */
310   result = (struct header *) (*mallocp) (len + sizeof (struct header));
311
312   if (result == NULL)
313     ++failed[idx_malloc];
314   else
315     /* Update the allocation data and write out the records if necessary.  */
316     update_data (result, len, 0);
317
318   /* Return the pointer to the user buffer.  */
319   return result ? (void *) (result + 1) : NULL;
320 }
321
322
323 /* `realloc' replacement.  We keep track of the memory usage if this is the
324    correct program.  */
325 void *
326 realloc (void *old, size_t len)
327 {
328   struct header *result = NULL;
329   struct header *real;
330   size_t old_len;
331
332   /* Determine real implementation if not already happened.  */
333   if (reallocp == NULL)
334     {
335       me ();
336       reallocp = (void *(*) (void *, size_t)) dlsym (RTLD_NEXT, "realloc");
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   /* Keep track of total memory consumption for `realloc'.  */
361   total[idx_realloc] += len;
362   /* Keep track of total memory requirement.  */
363   grand_total += len;
364   /* Remember the size of the request.  */
365   if (len < 65536)
366     ++histogram[len / 16];
367   else
368     ++large;
369   /* Total number of calls of any of the functions.  */
370   ++calls_total;
371
372   /* Do the real work.  */
373   result = (struct header *) (*reallocp) (real, len + sizeof (struct header));
374
375   if (result == NULL)
376     ++failed[idx_realloc];
377   else
378     {
379       /* Record whether the reduction/increase happened in place.  */
380       if (real == result)
381         ++inplace;
382       /* Was the buffer increased?  */
383       if (old_len > len)
384         ++decreasing;
385
386       /* Update the allocation data and write out the records if necessary.  */
387       update_data (result, len, old_len);
388     }
389
390   /* Return the pointer to the user buffer.  */
391   return result ? (void *) (result + 1) : NULL;
392 }
393
394
395 /* `calloc' replacement.  We keep track of the memory usage if this is the
396    correct program.  */
397 void *
398 calloc (size_t n, size_t len)
399 {
400   struct header *result;
401   size_t size = n * len;
402
403   /* Determine real implementation if not already happened.  We are
404      searching for the `malloc' implementation since it is not always
405      efficiently possible to use `calloc' because we have to add a bit
406      room to the allocation to put the header in.  */
407   if (mallocp == NULL)
408     {
409       me ();
410       mallocp = (void *(*) (size_t)) dlsym (RTLD_NEXT, "malloc");
411     }
412
413   /* If this is not the correct program just use the normal function.  */
414   if (not_me)
415     {
416       callocp = (void *(*) (size_t, size_t)) dlsym (RTLD_NEXT, "calloc");
417
418       return (*callocp) (n, len);
419     }
420
421   /* Keep track of number of calls.  */
422   ++calls[idx_calloc];
423   /* Keep track of total memory consumption for `calloc'.  */
424   total[idx_calloc] += size;
425   /* Keep track of total memory requirement.  */
426   grand_total += size;
427   /* Remember the size of the request.  */
428   if (size < 65536)
429     ++histogram[size / 16];
430   else
431     ++large;
432   /* Total number of calls of any of the functions.  */
433   ++calls_total;
434
435   /* Do the real work.  */
436   result = (struct header *) (*mallocp) (size + sizeof (struct header));
437   if (result != NULL)
438     memset (result + 1, '\0', size);
439
440   if (result == NULL)
441     ++failed[idx_calloc];
442   else
443     /* Update the allocation data and write out the records if necessary.  */
444     update_data (result, size, 0);
445
446   /* Return the pointer to the user buffer.  */
447   return result ? (void *) (result + 1) : NULL;
448 }
449
450
451 /* `free' replacement.  We keep track of the memory usage if this is the
452    correct program.  */
453 void
454 free (void *ptr)
455 {
456   struct header *real;
457
458   /* `free (NULL)' has no effect.  */
459   if (ptr == NULL)
460     {
461       ++calls[idx_free];
462       return;
463     }
464
465   /* Determine real implementation if not already happened.  */
466   if (freep == NULL)
467     {
468       me ();
469       freep = (void (*) (void *)) dlsym (RTLD_NEXT, "free");
470     }
471
472   /* If this is not the correct program just use the normal function.  */
473   if (not_me)
474     {
475       (*freep) (ptr);
476       return;
477     }
478
479   /* Determine the pointer to the header.  */
480   real = ((struct header *) ptr) - 1;
481   if (real->magic != MAGIC)
482     {
483       /* This block wasn't allocated here.  */
484       (*freep) (ptr);
485       return;
486     }
487
488   /* Keep track of number of calls.  */
489   ++calls[idx_free];
490   /* Keep track of total memory freed using `free'.  */
491   total[idx_free] += real->length;
492
493   /* Update the allocation data and write out the records if necessary.  */
494   update_data (NULL, 0, real->length);
495
496   /* Do the real work.  */
497   (*freep) (real);
498 }
499
500
501 /* Write some statistics to standard error.  */
502 static void
503 __attribute__ ((destructor))
504 dest (void)
505 {
506   int percent, cnt;
507   unsigned long int maxcalls;
508
509   /* If we haven't done anything here just return.  */
510   if (not_me)
511     return;
512   /* If we should call any of the memory functions don't do any profiling.  */
513   not_me = 1;
514
515   /* Finish the output file.  */
516   if (fd != -1)
517     {
518       /* Write the partially filled buffer.  */
519       write (fd, buffer, buffer_cnt * sizeof (struct entry));
520       /* Go back to the beginning of the file.  We allocated two records
521          here when we opened the file.  */
522       lseek (fd, 0, SEEK_SET);
523       /* Write out a record containing the total size.  */
524       first.stack = peak_total;
525       write (fd, &first, sizeof (struct entry));
526       /* Write out another record containing the maximum for heap and
527          stack.  */
528       first.heap = peak_heap;
529       first.stack = peak_stack;
530       GETTIME (first.time_low, first.time_high);
531       write (fd, &first, sizeof (struct entry));
532
533       /* Close the file.  */
534       close (fd);
535       fd = -1;
536     }
537
538   /* Write a colorful statistic.  */
539   fprintf (stderr, "\n\
540 \e[01;32mMemory usage summary:\e[0;0m heap total: %llu, heap peak: %lu, stack peak: %lu\n\
541 \e[04;34m         total calls   total memory   failed calls\e[0m\n\
542 \e[00;34m malloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
543 \e[00;34mrealloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m   (in place: %ld, dec: %ld)\n\
544 \e[00;34m calloc|\e[0m %10lu   %12llu   %s%12lu\e[00;00m\n\
545 \e[00;34m   free|\e[0m %10lu   %12llu\n",
546            grand_total, (unsigned long int) peak_heap,
547            (unsigned long int) peak_stack,
548            calls[idx_malloc], total[idx_malloc],
549            failed[idx_malloc] ? "\e[01;41m" : "", failed[idx_malloc],
550            calls[idx_realloc], total[idx_realloc],
551            failed[idx_realloc] ? "\e[01;41m" : "", failed[idx_realloc],
552            inplace, decreasing,
553            calls[idx_calloc], total[idx_calloc],
554            failed[idx_calloc] ? "\e[01;41m" : "", failed[idx_calloc],
555            calls[idx_free], total[idx_free]);
556
557   /* Write out a histoogram of the sizes of the allocations.  */
558   fprintf (stderr, "\e[01;32mHistogram for block sizes:\e[0;0m\n");
559
560   /* Determine the maximum of all calls for each size range.  */
561   maxcalls = large;
562   for (cnt = 0; cnt < 65536; cnt += 16)
563     if (histogram[cnt / 16] > maxcalls)
564       maxcalls = histogram[cnt / 16];
565
566   for (cnt = 0; cnt < 65536; cnt += 16)
567     /* Only write out the nonzero entries.  */
568     if (histogram[cnt / 16] != 0)
569       {
570         percent = (histogram[cnt / 16] * 100) / calls_total;
571         fprintf (stderr, "%5d-%-5d%12lu ", cnt, cnt + 15,
572                  histogram[cnt / 16]);
573         if (percent == 0)
574           fputs (" <1% \e[41;37m", stderr);
575         else
576           fprintf (stderr, "%3d%% \e[41;37m", percent);
577
578         /* Draw a bar with a length corresponding to the current
579            percentage.  */
580         percent = (histogram[cnt / 16] * 50) / maxcalls;
581         while (percent-- > 0)
582           fputc ('=', stderr);
583          fputs ("\e[0;0m\n", stderr);
584       }
585
586   if (large != 0)
587     {
588       percent = (large * 100) / calls_total;
589       fprintf (stderr, "   large   %12lu ", large);
590       if (percent == 0)
591         fputs (" <1% \e[41;37m", stderr);
592       else
593         fprintf (stderr, "%3d%% \e[41;37m", percent);
594       percent = (large * 50) / maxcalls;
595       while (percent-- > 0)
596         fputc ('=', stderr);
597       fputs ("\e[0;0m\n", stderr);
598     }
599 }