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