Redirect fwrite calls to _IO_fwrite.
[kopensolaris-gnu/glibc.git] / malloc / mtrace.c
1 /* More debugging hooks for `malloc'.
2    Copyright (C) 1991-1994,1996-2001,2002 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4                  Written April 2, 1991 by John Gilmore of Cygnus Support.
5                  Based on mcheck.c by Mike Haertel.
6
7    The GNU C Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11
12    The GNU C Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with the GNU C Library; if not, write to the Free
19    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20    02111-1307 USA.  */
21
22 #ifndef _MALLOC_INTERNAL
23 #define _MALLOC_INTERNAL
24 #include <malloc.h>
25 #include <mcheck.h>
26 #include <bits/libc-lock.h>
27 #endif
28
29 #include <dlfcn.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34
35 #include <stdio-common/_itoa.h>
36
37 #ifdef _LIBC
38 # include <libc-internal.h>
39 #endif
40
41 #ifdef USE_IN_LIBIO
42 # include <libio/iolibio.h>
43 # define fopen(f, n) _IO_fopen64 (f, n)
44 # define setvbuf(s, b, f, l) INTUSE(_IO_setvbuf) (s, b, f, l)
45 # define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp)
46 #endif
47
48 #ifndef attribute_hidden
49 # define attribute_hidden
50 #endif
51
52 #define TRACE_BUFFER_SIZE 512
53
54 static FILE *mallstream;
55 static const char mallenv[]= "MALLOC_TRACE";
56 static char malloc_trace_buffer[TRACE_BUFFER_SIZE];
57
58 __libc_lock_define_initialized (static, lock);
59
60 /* Address to breakpoint on accesses to... */
61 __ptr_t mallwatch;
62
63 #ifdef USE_MTRACE_FILE
64 /* File name and line number information, for callers that had
65    the foresight to call through a macro.  */
66 char *_mtrace_file;
67 int _mtrace_line;
68 #endif
69
70 /* Old hook values.  */
71 static void (*tr_old_free_hook) __P ((__ptr_t ptr, const __ptr_t));
72 static __ptr_t (*tr_old_malloc_hook) __P ((__malloc_size_t size,
73                                            const __ptr_t));
74 static __ptr_t (*tr_old_realloc_hook) __P ((__ptr_t ptr,
75                                             __malloc_size_t size,
76                                             const __ptr_t));
77
78 /* This function is called when the block being alloc'd, realloc'd, or
79    freed has an address matching the variable "mallwatch".  In a debugger,
80    set "mallwatch" to the address of interest, then put a breakpoint on
81    tr_break.  */
82
83 void tr_break __P ((void));
84 void
85 tr_break ()
86 {
87 }
88
89 static void tr_where __P ((const __ptr_t)) internal_function;
90 static void
91 internal_function
92 tr_where (caller)
93      const __ptr_t caller;
94 {
95 #ifdef USE_MTRACE_FILE
96   if (_mtrace_file)
97     {
98       fprintf (mallstream, "@ %s:%d ", _mtrace_file, _mtrace_line);
99       _mtrace_file = NULL;
100     }
101   else
102 #endif
103     if (caller != NULL)
104     {
105 #ifdef HAVE_ELF
106       Dl_info info;
107       if (_dl_addr (caller, &info))
108         {
109           char *buf = (char *) "";
110           if (info.dli_sname != NULL)
111             {
112               size_t len = strlen (info.dli_sname);
113               buf = alloca (len + 6 + 2 * sizeof (void *));
114
115               buf[0] = '(';
116               __stpcpy (_fitoa (caller >= (const __ptr_t) info.dli_saddr
117                                 ? caller - (const __ptr_t) info.dli_saddr
118                                 : (const __ptr_t) info.dli_saddr - caller,
119                                 __stpcpy (__mempcpy (buf + 1, info.dli_sname,
120                                                      len),
121                                           caller >= (__ptr_t) info.dli_saddr
122                                           ? "+0x" : "-0x"),
123                                 16, 0),
124                         ")");
125             }
126
127           fprintf (mallstream, "@ %s%s%s[%p] ",
128                    info.dli_fname ?: "", info.dli_fname ? ":" : "",
129                    buf, caller);
130         }
131       else
132 #endif
133         fprintf (mallstream, "@ [%p] ", caller);
134     }
135 }
136
137 static void tr_freehook __P ((__ptr_t, const __ptr_t));
138 static void
139 tr_freehook (ptr, caller)
140      __ptr_t ptr;
141      const __ptr_t caller;
142 {
143   if (ptr == NULL)
144     return;
145   __libc_lock_lock (lock);
146   tr_where (caller);
147   /* Be sure to print it first.  */
148   fprintf (mallstream, "- %p\n", ptr);
149   __libc_lock_unlock (lock);
150   if (ptr == mallwatch)
151     tr_break ();
152   __libc_lock_lock (lock);
153   __free_hook = tr_old_free_hook;
154   if (tr_old_free_hook != NULL)
155     (*tr_old_free_hook) (ptr, caller);
156   else
157     free (ptr);
158   __free_hook = tr_freehook;
159   __libc_lock_unlock (lock);
160 }
161
162 static __ptr_t tr_mallochook __P ((__malloc_size_t, const __ptr_t));
163 static __ptr_t
164 tr_mallochook (size, caller)
165      __malloc_size_t size;
166      const __ptr_t caller;
167 {
168   __ptr_t hdr;
169
170   __libc_lock_lock (lock);
171
172   __malloc_hook = tr_old_malloc_hook;
173   if (tr_old_malloc_hook != NULL)
174     hdr = (__ptr_t) (*tr_old_malloc_hook) (size, caller);
175   else
176     hdr = (__ptr_t) malloc (size);
177   __malloc_hook = tr_mallochook;
178
179   tr_where (caller);
180   /* We could be printing a NULL here; that's OK.  */
181   fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
182
183   __libc_lock_unlock (lock);
184
185   if (hdr == mallwatch)
186     tr_break ();
187
188   return hdr;
189 }
190
191 static __ptr_t tr_reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t));
192 static __ptr_t
193 tr_reallochook (ptr, size, caller)
194      __ptr_t ptr;
195      __malloc_size_t size;
196      const __ptr_t caller;
197 {
198   __ptr_t hdr;
199
200   if (ptr == mallwatch)
201     tr_break ();
202
203   __libc_lock_lock (lock);
204
205   __free_hook = tr_old_free_hook;
206   __malloc_hook = tr_old_malloc_hook;
207   __realloc_hook = tr_old_realloc_hook;
208   if (tr_old_realloc_hook != NULL)
209     hdr = (__ptr_t) (*tr_old_realloc_hook) (ptr, size, caller);
210   else
211     hdr = (__ptr_t) realloc (ptr, size);
212   __free_hook = tr_freehook;
213   __malloc_hook = tr_mallochook;
214   __realloc_hook = tr_reallochook;
215
216   tr_where (caller);
217   if (hdr == NULL)
218     /* Failed realloc.  */
219     fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
220   else if (ptr == NULL)
221     fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
222   else
223     {
224       fprintf (mallstream, "< %p\n", ptr);
225       tr_where (caller);
226       fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
227     }
228
229   __libc_lock_unlock (lock);
230
231   if (hdr == mallwatch)
232     tr_break ();
233
234   return hdr;
235 }
236
237
238 #ifdef _LIBC
239
240 /* This function gets called to make sure all memory the library
241    allocates get freed and so does not irritate the user when studying
242    the mtrace output.  */
243 static void
244 release_libc_mem (void)
245 {
246   /* Only call the free function if we still are running in mtrace mode.  */
247   if (mallstream != NULL)
248     __libc_freeres ();
249 }
250 #endif
251
252
253 /* We enable tracing if either the environment variable MALLOC_TRACE
254    is set, or if the variable mallwatch has been patched to an address
255    that the debugging user wants us to stop on.  When patching mallwatch,
256    don't forget to set a breakpoint on tr_break!  */
257
258 void
259 mtrace ()
260 {
261 #ifdef _LIBC
262   static int added_atexit_handler;
263 #endif
264   char *mallfile;
265
266   /* Don't panic if we're called more than once.  */
267   if (mallstream != NULL)
268     return;
269
270 #ifdef _LIBC
271   /* When compiling the GNU libc we use the secure getenv function
272      which prevents the misuse in case of SUID or SGID enabled
273      programs.  */
274   mallfile = __secure_getenv (mallenv);
275 #else
276   mallfile = getenv (mallenv);
277 #endif
278   if (mallfile != NULL || mallwatch != NULL)
279     {
280       mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w");
281       if (mallstream != NULL)
282         {
283           /* Make sure we close the file descriptor on exec.  */
284           int flags = __fcntl (fileno (mallstream), F_GETFD, 0);
285           if (flags >= 0)
286             {
287               flags |= FD_CLOEXEC;
288               __fcntl (fileno (mallstream), F_SETFD, flags);
289             }
290           /* Be sure it doesn't malloc its buffer!  */
291           setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
292           fprintf (mallstream, "= Start\n");
293           tr_old_free_hook = __free_hook;
294           __free_hook = tr_freehook;
295           tr_old_malloc_hook = __malloc_hook;
296           __malloc_hook = tr_mallochook;
297           tr_old_realloc_hook = __realloc_hook;
298           __realloc_hook = tr_reallochook;
299 #ifdef _LIBC
300           if (!added_atexit_handler)
301             {
302               extern void *__dso_handle __attribute__ ((__weak__));
303               added_atexit_handler = 1;
304               __cxa_atexit ((void (*) (void *)) release_libc_mem, NULL,
305                              &__dso_handle ? __dso_handle : NULL);
306             }
307 #endif
308         }
309     }
310 }
311
312 void
313 muntrace ()
314 {
315   if (mallstream == NULL)
316     return;
317
318   fprintf (mallstream, "= End\n");
319   fclose (mallstream);
320   mallstream = NULL;
321   __free_hook = tr_old_free_hook;
322   __malloc_hook = tr_old_malloc_hook;
323   __realloc_hook = tr_old_realloc_hook;
324 }