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