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