2002-09-17 Roland McGrath <roland@redhat.com>
[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
40 # include <libio/iolibio.h>
41 # define setvbuf(s, b, f, l) INTUSE(_IO_setvbuf) (s, b, f, l)
42 # define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp)
43 #endif
44
45 #ifndef attribute_hidden
46 # define attribute_hidden
47 #endif
48
49 #define TRACE_BUFFER_SIZE 512
50
51 static FILE *mallstream;
52 static const char mallenv[]= "MALLOC_TRACE";
53 static char malloc_trace_buffer[TRACE_BUFFER_SIZE];
54
55 __libc_lock_define_initialized (static, lock);
56
57 /* Address to breakpoint on accesses to... */
58 __ptr_t mallwatch;
59
60 #ifdef USE_MTRACE_FILE
61 /* File name and line number information, for callers that had
62    the foresight to call through a macro.  */
63 char *_mtrace_file;
64 int _mtrace_line;
65 #endif
66
67 /* Old hook values.  */
68 static void (*tr_old_free_hook) __P ((__ptr_t ptr, const __ptr_t));
69 static __ptr_t (*tr_old_malloc_hook) __P ((__malloc_size_t size,
70                                            const __ptr_t));
71 static __ptr_t (*tr_old_realloc_hook) __P ((__ptr_t ptr,
72                                             __malloc_size_t size,
73                                             const __ptr_t));
74
75 /* This function is called when the block being alloc'd, realloc'd, or
76    freed has an address matching the variable "mallwatch".  In a debugger,
77    set "mallwatch" to the address of interest, then put a breakpoint on
78    tr_break.  */
79
80 void tr_break __P ((void));
81 void
82 tr_break ()
83 {
84 }
85
86 static void tr_where __P ((const __ptr_t)) internal_function;
87 static void
88 internal_function
89 tr_where (caller)
90      const __ptr_t caller;
91 {
92 #ifdef USE_MTRACE_FILE
93   if (_mtrace_file)
94     {
95       fprintf (mallstream, "@ %s:%d ", _mtrace_file, _mtrace_line);
96       _mtrace_file = NULL;
97     }
98   else
99 #endif
100     if (caller != NULL)
101     {
102 #ifdef HAVE_ELF
103       Dl_info info;
104       if (_dl_addr (caller, &info))
105         {
106           char *buf = (char *) "";
107           if (info.dli_sname != NULL)
108             {
109               size_t len = strlen (info.dli_sname);
110               buf = alloca (len + 6 + 2 * sizeof (void *));
111
112               buf[0] = '(';
113               __stpcpy (_fitoa (caller >= (const __ptr_t) info.dli_saddr
114                                 ? caller - (const __ptr_t) info.dli_saddr
115                                 : (const __ptr_t) info.dli_saddr - caller,
116                                 __stpcpy (__mempcpy (buf + 1, info.dli_sname,
117                                                      len),
118                                           caller >= (__ptr_t) info.dli_saddr
119                                           ? "+0x" : "-0x"),
120                                 16, 0),
121                         ")");
122             }
123
124           fprintf (mallstream, "@ %s%s%s[%p] ",
125                    info.dli_fname ?: "", info.dli_fname ? ":" : "",
126                    buf, caller);
127         }
128       else
129 #endif
130         fprintf (mallstream, "@ [%p] ", caller);
131     }
132 }
133
134 static void tr_freehook __P ((__ptr_t, const __ptr_t));
135 static void
136 tr_freehook (ptr, caller)
137      __ptr_t ptr;
138      const __ptr_t caller;
139 {
140   if (ptr == NULL)
141     return;
142   __libc_lock_lock (lock);
143   tr_where (caller);
144   /* Be sure to print it first.  */
145   fprintf (mallstream, "- %p\n", ptr);
146   __libc_lock_unlock (lock);
147   if (ptr == mallwatch)
148     tr_break ();
149   __libc_lock_lock (lock);
150   __free_hook = tr_old_free_hook;
151   if (tr_old_free_hook != NULL)
152     (*tr_old_free_hook) (ptr, caller);
153   else
154     free (ptr);
155   __free_hook = tr_freehook;
156   __libc_lock_unlock (lock);
157 }
158
159 static __ptr_t tr_mallochook __P ((__malloc_size_t, const __ptr_t));
160 static __ptr_t
161 tr_mallochook (size, caller)
162      __malloc_size_t size;
163      const __ptr_t caller;
164 {
165   __ptr_t hdr;
166
167   __libc_lock_lock (lock);
168
169   __malloc_hook = tr_old_malloc_hook;
170   if (tr_old_malloc_hook != NULL)
171     hdr = (__ptr_t) (*tr_old_malloc_hook) (size, caller);
172   else
173     hdr = (__ptr_t) malloc (size);
174   __malloc_hook = tr_mallochook;
175
176   tr_where (caller);
177   /* We could be printing a NULL here; that's OK.  */
178   fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
179
180   __libc_lock_unlock (lock);
181
182   if (hdr == mallwatch)
183     tr_break ();
184
185   return hdr;
186 }
187
188 static __ptr_t tr_reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t));
189 static __ptr_t
190 tr_reallochook (ptr, size, caller)
191      __ptr_t ptr;
192      __malloc_size_t size;
193      const __ptr_t caller;
194 {
195   __ptr_t hdr;
196
197   if (ptr == mallwatch)
198     tr_break ();
199
200   __libc_lock_lock (lock);
201
202   __free_hook = tr_old_free_hook;
203   __malloc_hook = tr_old_malloc_hook;
204   __realloc_hook = tr_old_realloc_hook;
205   if (tr_old_realloc_hook != NULL)
206     hdr = (__ptr_t) (*tr_old_realloc_hook) (ptr, size, caller);
207   else
208     hdr = (__ptr_t) realloc (ptr, size);
209   __free_hook = tr_freehook;
210   __malloc_hook = tr_mallochook;
211   __realloc_hook = tr_reallochook;
212
213   tr_where (caller);
214   if (hdr == NULL)
215     /* Failed realloc.  */
216     fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
217   else if (ptr == NULL)
218     fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
219   else
220     {
221       fprintf (mallstream, "< %p\n", ptr);
222       tr_where (caller);
223       fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
224     }
225
226   __libc_lock_unlock (lock);
227
228   if (hdr == mallwatch)
229     tr_break ();
230
231   return hdr;
232 }
233
234
235 #ifdef _LIBC
236
237 /* This function gets called to make sure all memory the library
238    allocates get freed and so does not irritate the user when studying
239    the mtrace output.  */
240 static void
241 release_libc_mem (void)
242 {
243   /* Only call the free function if we still are running in mtrace mode.  */
244   if (mallstream != NULL)
245     __libc_freeres ();
246 }
247 #endif
248
249
250 /* We enable tracing if either the environment variable MALLOC_TRACE
251    is set, or if the variable mallwatch has been patched to an address
252    that the debugging user wants us to stop on.  When patching mallwatch,
253    don't forget to set a breakpoint on tr_break!  */
254
255 void
256 mtrace ()
257 {
258 #ifdef _LIBC
259   static int added_atexit_handler;
260 #endif
261   char *mallfile;
262
263   /* Don't panic if we're called more than once.  */
264   if (mallstream != NULL)
265     return;
266
267 #ifdef _LIBC
268   /* When compiling the GNU libc we use the secure getenv function
269      which prevents the misuse in case of SUID or SGID enabled
270      programs.  */
271   mallfile = __secure_getenv (mallenv);
272 #else
273   mallfile = getenv (mallenv);
274 #endif
275   if (mallfile != NULL || mallwatch != NULL)
276     {
277       mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w");
278       if (mallstream != NULL)
279         {
280           /* Make sure we close the file descriptor on exec.  */
281           int flags = __fcntl (fileno (mallstream), F_GETFD, 0);
282           if (flags >= 0)
283             {
284               flags |= FD_CLOEXEC;
285               __fcntl (fileno (mallstream), F_SETFD, flags);
286             }
287           /* Be sure it doesn't malloc its buffer!  */
288           setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
289           fprintf (mallstream, "= Start\n");
290           tr_old_free_hook = __free_hook;
291           __free_hook = tr_freehook;
292           tr_old_malloc_hook = __malloc_hook;
293           __malloc_hook = tr_mallochook;
294           tr_old_realloc_hook = __realloc_hook;
295           __realloc_hook = tr_reallochook;
296 #ifdef _LIBC
297           if (!added_atexit_handler)
298             {
299               extern void *__dso_handle __attribute__ ((__weak__));
300               added_atexit_handler = 1;
301               __cxa_atexit ((void (*) (void *)) release_libc_mem, NULL,
302                              &__dso_handle ? __dso_handle : NULL);
303             }
304 #endif
305         }
306     }
307 }
308
309 void
310 muntrace ()
311 {
312   if (mallstream == NULL)
313     return;
314
315   fprintf (mallstream, "= End\n");
316   fclose (mallstream);
317   mallstream = NULL;
318   __free_hook = tr_old_free_hook;
319   __malloc_hook = tr_old_malloc_hook;
320   __realloc_hook = tr_old_realloc_hook;
321 }