Back to index

glibc  2.9
mtrace.c
Go to the documentation of this file.
00001 /* More debugging hooks for `malloc'.
00002    Copyright (C) 1991-1994,1996-2004, 2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004                Written April 2, 1991 by John Gilmore of Cygnus Support.
00005                Based on mcheck.c by Mike Haertel.
00006 
00007    The GNU C Library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Lesser General Public
00009    License as published by the Free Software Foundation; either
00010    version 2.1 of the License, or (at your option) any later version.
00011 
00012    The GNU C Library is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Lesser General Public License for more details.
00016 
00017    You should have received a copy of the GNU Lesser General Public
00018    License along with the GNU C Library; if not, write to the Free
00019    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00020    02111-1307 USA.  */
00021 
00022 #ifndef       _MALLOC_INTERNAL
00023 #define       _MALLOC_INTERNAL
00024 #include <malloc.h>
00025 #include <mcheck.h>
00026 #include <bits/libc-lock.h>
00027 #endif
00028 
00029 #include <dlfcn.h>
00030 #include <fcntl.h>
00031 #include <stdio.h>
00032 #include <string.h>
00033 #include <stdlib.h>
00034 
00035 #include <stdio-common/_itoa.h>
00036 
00037 #include <libc-internal.h>
00038 
00039 #include <libio/iolibio.h>
00040 #define setvbuf(s, b, f, l) INTUSE(_IO_setvbuf) (s, b, f, l)
00041 #define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp)
00042 
00043 #include <kernel-features.h>
00044 
00045 #ifndef attribute_hidden
00046 # define attribute_hidden
00047 #endif
00048 
00049 #define TRACE_BUFFER_SIZE 512
00050 
00051 static FILE *mallstream;
00052 static const char mallenv[]= "MALLOC_TRACE";
00053 static char *malloc_trace_buffer;
00054 
00055 __libc_lock_define_initialized (static, lock);
00056 
00057 /* Address to breakpoint on accesses to... */
00058 __ptr_t mallwatch;
00059 
00060 #ifdef USE_MTRACE_FILE
00061 /* File name and line number information, for callers that had
00062    the foresight to call through a macro.  */
00063 char *_mtrace_file;
00064 int _mtrace_line;
00065 #endif
00066 
00067 /* Old hook values.  */
00068 static void (*tr_old_free_hook) (__ptr_t ptr, const __ptr_t);
00069 static __ptr_t (*tr_old_malloc_hook) (__malloc_size_t size, const __ptr_t);
00070 static __ptr_t (*tr_old_realloc_hook) (__ptr_t ptr, __malloc_size_t size,
00071                                    const __ptr_t);
00072 static __ptr_t (*tr_old_memalign_hook) (__malloc_size_t __alignment,
00073                                    __malloc_size_t __size,
00074                                    __const __ptr_t);
00075 
00076 /* This function is called when the block being alloc'd, realloc'd, or
00077    freed has an address matching the variable "mallwatch".  In a debugger,
00078    set "mallwatch" to the address of interest, then put a breakpoint on
00079    tr_break.  */
00080 
00081 extern void tr_break (void) __THROW;
00082 libc_hidden_proto (tr_break)
00083 void
00084 tr_break ()
00085 {
00086 }
00087 libc_hidden_def (tr_break)
00088 
00089 static void tr_where (const __ptr_t) __THROW internal_function;
00090 static void
00091 internal_function
00092 tr_where (caller)
00093      const __ptr_t caller;
00094 {
00095 #ifdef USE_MTRACE_FILE
00096   if (_mtrace_file)
00097     {
00098       fprintf (mallstream, "@ %s:%d ", _mtrace_file, _mtrace_line);
00099       _mtrace_file = NULL;
00100     }
00101   else
00102 #endif
00103     if (caller != NULL)
00104     {
00105 #ifdef HAVE_ELF
00106       Dl_info info;
00107       if (_dl_addr (caller, &info, NULL, NULL))
00108        {
00109          char *buf = (char *) "";
00110          if (info.dli_sname != NULL)
00111            {
00112              size_t len = strlen (info.dli_sname);
00113              buf = alloca (len + 6 + 2 * sizeof (void *));
00114 
00115              buf[0] = '(';
00116              __stpcpy (_fitoa (caller >= (const __ptr_t) info.dli_saddr
00117                             ? caller - (const __ptr_t) info.dli_saddr
00118                             : (const __ptr_t) info.dli_saddr - caller,
00119                             __stpcpy (__mempcpy (buf + 1, info.dli_sname,
00120                                                len),
00121                                      caller >= (__ptr_t) info.dli_saddr
00122                                      ? "+0x" : "-0x"),
00123                             16, 0),
00124                      ")");
00125            }
00126 
00127          fprintf (mallstream, "@ %s%s%s[%p] ",
00128                  info.dli_fname ?: "", info.dli_fname ? ":" : "",
00129                  buf, caller);
00130        }
00131       else
00132 #endif
00133        fprintf (mallstream, "@ [%p] ", caller);
00134     }
00135 }
00136 
00137 static void tr_freehook (__ptr_t, const __ptr_t) __THROW;
00138 static void
00139 tr_freehook (ptr, caller)
00140      __ptr_t ptr;
00141      const __ptr_t caller;
00142 {
00143   if (ptr == NULL)
00144     return;
00145   __libc_lock_lock (lock);
00146   tr_where (caller);
00147   /* Be sure to print it first.  */
00148   fprintf (mallstream, "- %p\n", ptr);
00149   __libc_lock_unlock (lock);
00150   if (ptr == mallwatch)
00151     tr_break ();
00152   __libc_lock_lock (lock);
00153   __free_hook = tr_old_free_hook;
00154   if (tr_old_free_hook != NULL)
00155     (*tr_old_free_hook) (ptr, caller);
00156   else
00157     free (ptr);
00158   __free_hook = tr_freehook;
00159   __libc_lock_unlock (lock);
00160 }
00161 
00162 static __ptr_t tr_mallochook (__malloc_size_t, const __ptr_t) __THROW;
00163 static __ptr_t
00164 tr_mallochook (size, caller)
00165      __malloc_size_t size;
00166      const __ptr_t caller;
00167 {
00168   __ptr_t hdr;
00169 
00170   __libc_lock_lock (lock);
00171 
00172   __malloc_hook = tr_old_malloc_hook;
00173   if (tr_old_malloc_hook != NULL)
00174     hdr = (__ptr_t) (*tr_old_malloc_hook) (size, caller);
00175   else
00176     hdr = (__ptr_t) malloc (size);
00177   __malloc_hook = tr_mallochook;
00178 
00179   tr_where (caller);
00180   /* We could be printing a NULL here; that's OK.  */
00181   fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
00182 
00183   __libc_lock_unlock (lock);
00184 
00185   if (hdr == mallwatch)
00186     tr_break ();
00187 
00188   return hdr;
00189 }
00190 
00191 static __ptr_t tr_reallochook (__ptr_t, __malloc_size_t, const __ptr_t)
00192      __THROW;
00193 static __ptr_t
00194 tr_reallochook (ptr, size, caller)
00195      __ptr_t ptr;
00196      __malloc_size_t size;
00197      const __ptr_t caller;
00198 {
00199   __ptr_t hdr;
00200 
00201   if (ptr == mallwatch)
00202     tr_break ();
00203 
00204   __libc_lock_lock (lock);
00205 
00206   __free_hook = tr_old_free_hook;
00207   __malloc_hook = tr_old_malloc_hook;
00208   __realloc_hook = tr_old_realloc_hook;
00209   if (tr_old_realloc_hook != NULL)
00210     hdr = (__ptr_t) (*tr_old_realloc_hook) (ptr, size, caller);
00211   else
00212     hdr = (__ptr_t) realloc (ptr, size);
00213   __free_hook = tr_freehook;
00214   __malloc_hook = tr_mallochook;
00215   __realloc_hook = tr_reallochook;
00216 
00217   tr_where (caller);
00218   if (hdr == NULL)
00219     /* Failed realloc.  */
00220     fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
00221   else if (ptr == NULL)
00222     fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
00223   else
00224     {
00225       fprintf (mallstream, "< %p\n", ptr);
00226       tr_where (caller);
00227       fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
00228     }
00229 
00230   __libc_lock_unlock (lock);
00231 
00232   if (hdr == mallwatch)
00233     tr_break ();
00234 
00235   return hdr;
00236 }
00237 
00238 static __ptr_t tr_memalignhook (__malloc_size_t, __malloc_size_t,
00239                             const __ptr_t) __THROW;
00240 static __ptr_t
00241 tr_memalignhook (alignment, size, caller)
00242      __malloc_size_t alignment, size;
00243      const __ptr_t caller;
00244 {
00245   __ptr_t hdr;
00246 
00247   __libc_lock_lock (lock);
00248 
00249   __memalign_hook = tr_old_memalign_hook;
00250   __malloc_hook = tr_old_malloc_hook;
00251   if (tr_old_memalign_hook != NULL)
00252     hdr = (__ptr_t) (*tr_old_memalign_hook) (alignment, size, caller);
00253   else
00254     hdr = (__ptr_t) memalign (alignment, size);
00255   __memalign_hook = tr_memalignhook;
00256   __malloc_hook = tr_mallochook;
00257 
00258   tr_where (caller);
00259   /* We could be printing a NULL here; that's OK.  */
00260   fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
00261 
00262   __libc_lock_unlock (lock);
00263 
00264   if (hdr == mallwatch)
00265     tr_break ();
00266 
00267   return hdr;
00268 }
00269 
00270 
00271 
00272 #ifdef _LIBC
00273 
00274 /* This function gets called to make sure all memory the library
00275    allocates get freed and so does not irritate the user when studying
00276    the mtrace output.  */
00277 static void __libc_freeres_fn_section
00278 release_libc_mem (void)
00279 {
00280   /* Only call the free function if we still are running in mtrace mode.  */
00281   if (mallstream != NULL)
00282     __libc_freeres ();
00283 }
00284 #endif
00285 
00286 
00287 /* We enable tracing if either the environment variable MALLOC_TRACE
00288    is set, or if the variable mallwatch has been patched to an address
00289    that the debugging user wants us to stop on.  When patching mallwatch,
00290    don't forget to set a breakpoint on tr_break!  */
00291 
00292 void
00293 mtrace ()
00294 {
00295 #ifdef _LIBC
00296   static int added_atexit_handler;
00297 #endif
00298   char *mallfile;
00299 
00300   /* Don't panic if we're called more than once.  */
00301   if (mallstream != NULL)
00302     return;
00303 
00304 #ifdef _LIBC
00305   /* When compiling the GNU libc we use the secure getenv function
00306      which prevents the misuse in case of SUID or SGID enabled
00307      programs.  */
00308   mallfile = __secure_getenv (mallenv);
00309 #else
00310   mallfile = getenv (mallenv);
00311 #endif
00312   if (mallfile != NULL || mallwatch != NULL)
00313     {
00314       char *mtb = malloc (TRACE_BUFFER_SIZE);
00315       if (mtb == NULL)
00316        return;
00317 
00318       mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
00319       if (mallstream != NULL)
00320        {
00321 #ifndef __ASSUME_O_CLOEXEC
00322          /* Make sure we close the file descriptor on exec.  */
00323          int flags = __fcntl (fileno (mallstream), F_GETFD, 0);
00324          if (flags >= 0)
00325            {
00326              flags |= FD_CLOEXEC;
00327              __fcntl (fileno (mallstream), F_SETFD, flags);
00328            }
00329 #endif
00330          /* Be sure it doesn't malloc its buffer!  */
00331          malloc_trace_buffer = mtb;
00332          setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
00333          fprintf (mallstream, "= Start\n");
00334          tr_old_free_hook = __free_hook;
00335          __free_hook = tr_freehook;
00336          tr_old_malloc_hook = __malloc_hook;
00337          __malloc_hook = tr_mallochook;
00338          tr_old_realloc_hook = __realloc_hook;
00339          __realloc_hook = tr_reallochook;
00340          tr_old_memalign_hook = __memalign_hook;
00341          __memalign_hook = tr_memalignhook;
00342 #ifdef _LIBC
00343          if (!added_atexit_handler)
00344            {
00345              extern void *__dso_handle __attribute__ ((__weak__));
00346              added_atexit_handler = 1;
00347              __cxa_atexit ((void (*) (void *)) release_libc_mem, NULL,
00348                           &__dso_handle ? __dso_handle : NULL);
00349            }
00350 #endif
00351        }
00352       else
00353        free (mtb);
00354     }
00355 }
00356 
00357 void
00358 muntrace ()
00359 {
00360   if (mallstream == NULL)
00361     return;
00362 
00363   fprintf (mallstream, "= End\n");
00364   fclose (mallstream);
00365   mallstream = NULL;
00366   __free_hook = tr_old_free_hook;
00367   __malloc_hook = tr_old_malloc_hook;
00368   __realloc_hook = tr_old_realloc_hook;
00369   __memalign_hook = tr_old_memalign_hook;
00370 }