chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / malloc / mtrace.c
1 /* More debugging hooks for `malloc'.
2    Copyright (C) 1991-1994,1996-2004, 2008 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 #include <libc-internal.h>
38
39 #include <libio/iolibio.h>
40 #define setvbuf(s, b, f, l) INTUSE(_IO_setvbuf) (s, b, f, l)
41 #define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp)
42
43 #include <kernel-features.h>
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) (__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);
75
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.  */
80
81 extern void tr_break (void) __THROW;
82 libc_hidden_proto (tr_break)
83 void
84 tr_break ()
85 {
86 }
87 libc_hidden_def (tr_break)
88
89 static void tr_where (const __ptr_t) __THROW internal_function;
90 static void
91 internal_function
92 tr_where (caller)
93      const __ptr_t caller;
94 {
95 #ifdef USE_MTRACE_FILE
96   if (_mtrace_file)
97     {
98       fprintf (mallstream, "@ %s:%d ", _mtrace_file, _mtrace_line);
99       _mtrace_file = NULL;
100     }
101   else
102 #endif
103     if (caller != NULL)
104     {
105 #ifdef HAVE_ELF
106       Dl_info info;
107       if (_dl_addr (caller, &info, NULL, NULL))
108         {
109           char *buf = (char *) "";
110           if (info.dli_sname != NULL)
111             {
112               size_t len = strlen (info.dli_sname);
113               buf = alloca (len + 6 + 2 * sizeof (void *));
114
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                         ")");
125             }
126
127           fprintf (mallstream, "@ %s%s%s[%p] ",
128                    info.dli_fname ?: "", info.dli_fname ? ":" : "",
129                    buf, caller);
130         }
131       else
132 #endif
133         fprintf (mallstream, "@ [%p] ", caller);
134     }
135 }
136
137 static void tr_freehook (__ptr_t, const __ptr_t) __THROW;
138 static void
139 tr_freehook (ptr, caller)
140      __ptr_t ptr;
141      const __ptr_t caller;
142 {
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);
160 }
161
162 static __ptr_t tr_mallochook (__malloc_size_t, const __ptr_t) __THROW;
163 static __ptr_t
164 tr_mallochook (size, caller)
165      __malloc_size_t size;
166      const __ptr_t caller;
167 {
168   __ptr_t hdr;
169
170   __libc_lock_lock (lock);
171
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;
178
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);
182
183   __libc_lock_unlock (lock);
184
185   if (hdr == mallwatch)
186     tr_break ();
187
188   return hdr;
189 }
190
191 static __ptr_t tr_reallochook (__ptr_t, __malloc_size_t, const __ptr_t)
192      __THROW;
193 static __ptr_t
194 tr_reallochook (ptr, size, caller)
195      __ptr_t ptr;
196      __malloc_size_t size;
197      const __ptr_t caller;
198 {
199   __ptr_t hdr;
200
201   if (ptr == mallwatch)
202     tr_break ();
203
204   __libc_lock_lock (lock);
205
206   __free_hook = tr_old_free_hook;
207   __malloc_hook = tr_old_malloc_hook;
208   __realloc_hook = tr_old_realloc_hook;
209   if (tr_old_realloc_hook != NULL)
210     hdr = (__ptr_t) (*tr_old_realloc_hook) (ptr, size, caller);
211   else
212     hdr = (__ptr_t) realloc (ptr, size);
213   __free_hook = tr_freehook;
214   __malloc_hook = tr_mallochook;
215   __realloc_hook = tr_reallochook;
216
217   tr_where (caller);
218   if (hdr == NULL)
219     /* Failed realloc.  */
220     fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
221   else if (ptr == NULL)
222     fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
223   else
224     {
225       fprintf (mallstream, "< %p\n", ptr);
226       tr_where (caller);
227       fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
228     }
229
230   __libc_lock_unlock (lock);
231
232   if (hdr == mallwatch)
233     tr_break ();
234
235   return hdr;
236 }
237
238 static __ptr_t tr_memalignhook (__malloc_size_t, __malloc_size_t,
239                                 const __ptr_t) __THROW;
240 static __ptr_t
241 tr_memalignhook (alignment, size, caller)
242      __malloc_size_t alignment, size;
243      const __ptr_t caller;
244 {
245   __ptr_t hdr;
246
247   __libc_lock_lock (lock);
248
249   __memalign_hook = tr_old_memalign_hook;
250   __malloc_hook = tr_old_malloc_hook;
251   if (tr_old_memalign_hook != NULL)
252     hdr = (__ptr_t) (*tr_old_memalign_hook) (alignment, size, caller);
253   else
254     hdr = (__ptr_t) memalign (alignment, size);
255   __memalign_hook = tr_memalignhook;
256   __malloc_hook = tr_mallochook;
257
258   tr_where (caller);
259   /* We could be printing a NULL here; that's OK.  */
260   fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
261
262   __libc_lock_unlock (lock);
263
264   if (hdr == mallwatch)
265     tr_break ();
266
267   return hdr;
268 }
269
270
271
272 #ifdef _LIBC
273
274 /* This function gets called to make sure all memory the library
275    allocates get freed and so does not irritate the user when studying
276    the mtrace output.  */
277 static void __libc_freeres_fn_section
278 release_libc_mem (void)
279 {
280   /* Only call the free function if we still are running in mtrace mode.  */
281   if (mallstream != NULL)
282     __libc_freeres ();
283 }
284 #endif
285
286
287 /* We enable tracing if either the environment variable MALLOC_TRACE
288    is set, or if the variable mallwatch has been patched to an address
289    that the debugging user wants us to stop on.  When patching mallwatch,
290    don't forget to set a breakpoint on tr_break!  */
291
292 void
293 mtrace ()
294 {
295 #ifdef _LIBC
296   static int added_atexit_handler;
297 #endif
298   char *mallfile;
299
300   /* Don't panic if we're called more than once.  */
301   if (mallstream != NULL)
302     return;
303
304 #ifdef _LIBC
305   /* When compiling the GNU libc we use the secure getenv function
306      which prevents the misuse in case of SUID or SGID enabled
307      programs.  */
308   mallfile = __secure_getenv (mallenv);
309 #else
310   mallfile = getenv (mallenv);
311 #endif
312   if (mallfile != NULL || mallwatch != NULL)
313     {
314       char *mtb = malloc (TRACE_BUFFER_SIZE);
315       if (mtb == NULL)
316         return;
317
318       mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
319       if (mallstream != NULL)
320         {
321 #ifndef __ASSUME_O_CLOEXEC
322           /* Make sure we close the file descriptor on exec.  */
323           int flags = __fcntl (fileno (mallstream), F_GETFD, 0);
324           if (flags >= 0)
325             {
326               flags |= FD_CLOEXEC;
327               __fcntl (fileno (mallstream), F_SETFD, flags);
328             }
329 #endif
330           /* Be sure it doesn't malloc its buffer!  */
331           malloc_trace_buffer = mtb;
332           setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
333           fprintf (mallstream, "= Start\n");
334           tr_old_free_hook = __free_hook;
335           __free_hook = tr_freehook;
336           tr_old_malloc_hook = __malloc_hook;
337           __malloc_hook = tr_mallochook;
338           tr_old_realloc_hook = __realloc_hook;
339           __realloc_hook = tr_reallochook;
340           tr_old_memalign_hook = __memalign_hook;
341           __memalign_hook = tr_memalignhook;
342 #ifdef _LIBC
343           if (!added_atexit_handler)
344             {
345               extern void *__dso_handle __attribute__ ((__weak__));
346               added_atexit_handler = 1;
347               __cxa_atexit ((void (*) (void *)) release_libc_mem, NULL,
348                              &__dso_handle ? __dso_handle : NULL);
349             }
350 #endif
351         }
352       else
353         free (mtb);
354     }
355 }
356
357 void
358 muntrace ()
359 {
360   if (mallstream == NULL)
361     return;
362
363   fprintf (mallstream, "= End\n");
364   fclose (mallstream);
365   mallstream = NULL;
366   __free_hook = tr_old_free_hook;
367   __malloc_hook = tr_old_malloc_hook;
368   __realloc_hook = tr_old_realloc_hook;
369   __memalign_hook = tr_old_memalign_hook;
370 }