[BZ #377]
[glibc.git] / malloc / mtrace.c
blob3ae9e8d88fa1e9fbb7188a1dddcdfae3249cec58
1 /* More debugging hooks for `malloc'.
2 Copyright (C) 1991-1994,1996-2003, 2004 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.
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.
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.
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. */
22 #ifndef _MALLOC_INTERNAL
23 #define _MALLOC_INTERNAL
24 #include <malloc.h>
25 #include <mcheck.h>
26 #include <bits/libc-lock.h>
27 #endif
29 #include <dlfcn.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
35 #include <stdio-common/_itoa.h>
37 #ifdef _LIBC
38 # include <libc-internal.h>
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
45 #ifndef attribute_hidden
46 # define attribute_hidden
47 #endif
49 #define TRACE_BUFFER_SIZE 512
51 static FILE *mallstream;
52 static const char mallenv[]= "MALLOC_TRACE";
53 static char *malloc_trace_buffer;
55 __libc_lock_define_initialized (static, lock);
57 /* Address to breakpoint on accesses to... */
58 __ptr_t mallwatch;
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
67 /* Old hook values. */
68 static void (*tr_old_free_hook) (__ptr_t ptr, const __ptr_t);
69 static __ptr_t (*tr_old_malloc_hook) (__malloc_size_t size, const __ptr_t);
70 static __ptr_t (*tr_old_realloc_hook) (__ptr_t ptr, __malloc_size_t size,
71 const __ptr_t);
72 static __ptr_t (*tr_old_memalign_hook) (__malloc_size_t __alignment,
73 __malloc_size_t __size,
74 __const __ptr_t);
76 /* This function is called when the block being alloc'd, realloc'd, or
77 freed has an address matching the variable "mallwatch". In a debugger,
78 set "mallwatch" to the address of interest, then put a breakpoint on
79 tr_break. */
81 extern void tr_break __P ((void));
82 libc_hidden_proto (tr_break)
83 void
84 tr_break ()
87 libc_hidden_def (tr_break)
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;
95 #ifdef USE_MTRACE_FILE
96 if (_mtrace_file)
98 fprintf (mallstream, "@ %s:%d ", _mtrace_file, _mtrace_line);
99 _mtrace_file = NULL;
101 else
102 #endif
103 if (caller != NULL)
105 #ifdef HAVE_ELF
106 Dl_info info;
107 if (_dl_addr (caller, &info, NULL, NULL))
109 char *buf = (char *) "";
110 if (info.dli_sname != NULL)
112 size_t len = strlen (info.dli_sname);
113 buf = alloca (len + 6 + 2 * sizeof (void *));
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 ")");
127 fprintf (mallstream, "@ %s%s%s[%p] ",
128 info.dli_fname ?: "", info.dli_fname ? ":" : "",
129 buf, caller);
131 else
132 #endif
133 fprintf (mallstream, "@ [%p] ", caller);
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;
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);
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;
168 __ptr_t hdr;
170 __libc_lock_lock (lock);
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;
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);
183 __libc_lock_unlock (lock);
185 if (hdr == mallwatch)
186 tr_break ();
188 return hdr;
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;
198 __ptr_t hdr;
200 if (ptr == mallwatch)
201 tr_break ();
203 __libc_lock_lock (lock);
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;
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
224 fprintf (mallstream, "< %p\n", ptr);
225 tr_where (caller);
226 fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
229 __libc_lock_unlock (lock);
231 if (hdr == mallwatch)
232 tr_break ();
234 return hdr;
237 static __ptr_t tr_memalignhook __P ((__malloc_size_t, __malloc_size_t,
238 const __ptr_t));
239 static __ptr_t
240 tr_memalignhook (alignment, size, caller)
241 __malloc_size_t alignment, size;
242 const __ptr_t caller;
244 __ptr_t hdr;
246 __libc_lock_lock (lock);
248 __memalign_hook = tr_old_memalign_hook;
249 __malloc_hook = tr_old_malloc_hook;
250 if (tr_old_memalign_hook != NULL)
251 hdr = (__ptr_t) (*tr_old_memalign_hook) (alignment, size, caller);
252 else
253 hdr = (__ptr_t) memalign (alignment, size);
254 __memalign_hook = tr_memalignhook;
255 __malloc_hook = tr_mallochook;
257 tr_where (caller);
258 /* We could be printing a NULL here; that's OK. */
259 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
261 __libc_lock_unlock (lock);
263 if (hdr == mallwatch)
264 tr_break ();
266 return hdr;
271 #ifdef _LIBC
273 /* This function gets called to make sure all memory the library
274 allocates get freed and so does not irritate the user when studying
275 the mtrace output. */
276 static void __libc_freeres_fn_section
277 release_libc_mem (void)
279 /* Only call the free function if we still are running in mtrace mode. */
280 if (mallstream != NULL)
281 __libc_freeres ();
283 #endif
286 /* We enable tracing if either the environment variable MALLOC_TRACE
287 is set, or if the variable mallwatch has been patched to an address
288 that the debugging user wants us to stop on. When patching mallwatch,
289 don't forget to set a breakpoint on tr_break! */
291 void
292 mtrace ()
294 #ifdef _LIBC
295 static int added_atexit_handler;
296 #endif
297 char *mallfile;
299 /* Don't panic if we're called more than once. */
300 if (mallstream != NULL)
301 return;
303 #ifdef _LIBC
304 /* When compiling the GNU libc we use the secure getenv function
305 which prevents the misuse in case of SUID or SGID enabled
306 programs. */
307 mallfile = __secure_getenv (mallenv);
308 #else
309 mallfile = getenv (mallenv);
310 #endif
311 if (mallfile != NULL || mallwatch != NULL)
313 char *mtb = malloc (TRACE_BUFFER_SIZE);
314 if (mtb == NULL)
315 return;
317 mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wc");
318 if (mallstream != NULL)
320 /* Make sure we close the file descriptor on exec. */
321 int flags = __fcntl (fileno (mallstream), F_GETFD, 0);
322 if (flags >= 0)
324 flags |= FD_CLOEXEC;
325 __fcntl (fileno (mallstream), F_SETFD, flags);
327 /* Be sure it doesn't malloc its buffer! */
328 malloc_trace_buffer = mtb;
329 setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
330 fprintf (mallstream, "= Start\n");
331 tr_old_free_hook = __free_hook;
332 __free_hook = tr_freehook;
333 tr_old_malloc_hook = __malloc_hook;
334 __malloc_hook = tr_mallochook;
335 tr_old_realloc_hook = __realloc_hook;
336 __realloc_hook = tr_reallochook;
337 tr_old_memalign_hook = __memalign_hook;
338 __memalign_hook = tr_memalignhook;
339 #ifdef _LIBC
340 if (!added_atexit_handler)
342 extern void *__dso_handle __attribute__ ((__weak__));
343 added_atexit_handler = 1;
344 __cxa_atexit ((void (*) (void *)) release_libc_mem, NULL,
345 &__dso_handle ? __dso_handle : NULL);
347 #endif
349 else
350 free (mtb);
354 void
355 muntrace ()
357 if (mallstream == NULL)
358 return;
360 fprintf (mallstream, "= End\n");
361 fclose (mallstream);
362 mallstream = NULL;
363 __free_hook = tr_old_free_hook;
364 __malloc_hook = tr_old_malloc_hook;
365 __realloc_hook = tr_old_realloc_hook;
366 __memalign_hook = tr_old_memalign_hook;