Revert "Patch [1/4] async-signal safe TLS."
[glibc.git] / malloc / mtrace.c
blob99ebaffb9b786129b38f2bd47183efa488c792f3
1 /* More debugging hooks for `malloc'.
2 Copyright (C) 1991-2014 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, see
19 <http://www.gnu.org/licenses/>. */
21 #ifndef _MALLOC_INTERNAL
22 # define _MALLOC_INTERNAL
23 # include <malloc.h>
24 # include <mcheck.h>
25 # include <bits/libc-lock.h>
26 #endif
28 #include <dlfcn.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
34 #include <_itoa.h>
36 #include <libc-internal.h>
38 #include <libio/iolibio.h>
39 #define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l)
40 #define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp)
42 #include <kernel-features.h>
44 #ifndef attribute_hidden
45 # define attribute_hidden
46 #endif
48 #define TRACE_BUFFER_SIZE 512
50 static FILE *mallstream;
51 static const char mallenv[] = "MALLOC_TRACE";
52 static char *malloc_trace_buffer;
54 __libc_lock_define_initialized (static, lock);
56 /* Address to breakpoint on accesses to... */
57 __ptr_t mallwatch;
59 /* Old hook values. */
60 static void (*tr_old_free_hook) (__ptr_t ptr, const __ptr_t);
61 static __ptr_t (*tr_old_malloc_hook) (size_t size, const __ptr_t);
62 static __ptr_t (*tr_old_realloc_hook) (__ptr_t ptr, size_t size,
63 const __ptr_t);
64 static __ptr_t (*tr_old_memalign_hook) (size_t __alignment, size_t __size,
65 const __ptr_t);
67 /* This function is called when the block being alloc'd, realloc'd, or
68 freed has an address matching the variable "mallwatch". In a debugger,
69 set "mallwatch" to the address of interest, then put a breakpoint on
70 tr_break. */
72 extern void tr_break (void) __THROW;
73 libc_hidden_proto (tr_break)
74 void
75 tr_break (void)
78 libc_hidden_def (tr_break)
80 static void tr_where (const __ptr_t, Dl_info *) __THROW internal_function;
81 static void
82 internal_function tr_where (caller, info)
83 const __ptr_t caller;
84 Dl_info *info;
86 if (caller != NULL)
88 if (info != NULL)
90 char *buf = (char *) "";
91 if (info->dli_sname != NULL)
93 size_t len = strlen (info->dli_sname);
94 buf = alloca (len + 6 + 2 * sizeof (void *));
96 buf[0] = '(';
97 __stpcpy (_fitoa (caller >= (const __ptr_t) info->dli_saddr
98 ? caller - (const __ptr_t) info->dli_saddr
99 : (const __ptr_t) info->dli_saddr - caller,
100 __stpcpy (__mempcpy (buf + 1, info->dli_sname,
101 len),
102 caller >= (__ptr_t) info->dli_saddr
103 ? "+0x" : "-0x"),
104 16, 0),
105 ")");
108 fprintf (mallstream, "@ %s%s%s[%p] ",
109 info->dli_fname ? : "", info->dli_fname ? ":" : "",
110 buf, caller);
112 else
113 fprintf (mallstream, "@ [%p] ", caller);
118 static Dl_info *
119 lock_and_info (const __ptr_t caller, Dl_info *mem)
121 if (caller == NULL)
122 return NULL;
124 Dl_info *res = _dl_addr (caller, mem, NULL, NULL) ? mem : NULL;
126 __libc_lock_lock (lock);
128 return res;
132 static void tr_freehook (__ptr_t, const __ptr_t) __THROW;
133 static void tr_freehook (ptr, caller)
134 __ptr_t ptr;
135 const __ptr_t caller;
137 if (ptr == NULL)
138 return;
140 Dl_info mem;
141 Dl_info *info = lock_and_info (caller, &mem);
142 tr_where (caller, info);
143 /* Be sure to print it first. */
144 fprintf (mallstream, "- %p\n", ptr);
145 if (ptr == mallwatch)
147 __libc_lock_unlock (lock);
148 tr_break ();
149 __libc_lock_lock (lock);
151 __free_hook = tr_old_free_hook;
152 if (tr_old_free_hook != NULL)
153 (*tr_old_free_hook)(ptr, caller);
154 else
155 free (ptr);
156 __free_hook = tr_freehook;
157 __libc_lock_unlock (lock);
160 static __ptr_t tr_mallochook (size_t, const __ptr_t) __THROW;
161 static __ptr_t tr_mallochook (size, caller)
162 size_t size;
163 const __ptr_t caller;
165 __ptr_t hdr;
167 Dl_info mem;
168 Dl_info *info = lock_and_info (caller, &mem);
170 __malloc_hook = tr_old_malloc_hook;
171 if (tr_old_malloc_hook != NULL)
172 hdr = (__ptr_t) (*tr_old_malloc_hook)(size, caller);
173 else
174 hdr = (__ptr_t) malloc (size);
175 __malloc_hook = tr_mallochook;
177 tr_where (caller, info);
178 /* We could be printing a NULL here; that's OK. */
179 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
181 __libc_lock_unlock (lock);
183 if (hdr == mallwatch)
184 tr_break ();
186 return hdr;
189 static __ptr_t tr_reallochook (__ptr_t, size_t, const __ptr_t)
190 __THROW;
191 static __ptr_t tr_reallochook (ptr, size, caller)
192 __ptr_t ptr;
193 size_t size;
194 const __ptr_t caller;
196 __ptr_t hdr;
198 if (ptr == mallwatch)
199 tr_break ();
201 Dl_info mem;
202 Dl_info *info = lock_and_info (caller, &mem);
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;
215 tr_where (caller, info);
216 if (hdr == NULL)
218 if (size != 0)
219 /* Failed realloc. */
220 fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
221 else
222 fprintf (mallstream, "- %p\n", ptr);
224 else if (ptr == NULL)
225 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
226 else
228 fprintf (mallstream, "< %p\n", ptr);
229 tr_where (caller, info);
230 fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
233 __libc_lock_unlock (lock);
235 if (hdr == mallwatch)
236 tr_break ();
238 return hdr;
241 static __ptr_t tr_memalignhook (size_t, size_t,
242 const __ptr_t) __THROW;
243 static __ptr_t tr_memalignhook (alignment, size, caller)
244 size_t alignment, size;
245 const __ptr_t caller;
247 __ptr_t hdr;
249 Dl_info mem;
250 Dl_info *info = lock_and_info (caller, &mem);
252 __memalign_hook = tr_old_memalign_hook;
253 __malloc_hook = tr_old_malloc_hook;
254 if (tr_old_memalign_hook != NULL)
255 hdr = (__ptr_t) (*tr_old_memalign_hook)(alignment, size, caller);
256 else
257 hdr = (__ptr_t) memalign (alignment, size);
258 __memalign_hook = tr_memalignhook;
259 __malloc_hook = tr_mallochook;
261 tr_where (caller, info);
262 /* We could be printing a NULL here; that's OK. */
263 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
265 __libc_lock_unlock (lock);
267 if (hdr == mallwatch)
268 tr_break ();
270 return hdr;
275 #ifdef _LIBC
277 /* This function gets called to make sure all memory the library
278 allocates get freed and so does not irritate the user when studying
279 the mtrace output. */
280 static void __libc_freeres_fn_section
281 release_libc_mem (void)
283 /* Only call the free function if we still are running in mtrace mode. */
284 if (mallstream != NULL)
285 __libc_freeres ();
287 #endif
290 /* We enable tracing if either the environment variable MALLOC_TRACE
291 is set, or if the variable mallwatch has been patched to an address
292 that the debugging user wants us to stop on. When patching mallwatch,
293 don't forget to set a breakpoint on tr_break! */
295 void
296 mtrace (void)
298 #ifdef _LIBC
299 static int added_atexit_handler;
300 #endif
301 char *mallfile;
303 /* Don't panic if we're called more than once. */
304 if (mallstream != NULL)
305 return;
307 #ifdef _LIBC
308 /* When compiling the GNU libc we use the secure getenv function
309 which prevents the misuse in case of SUID or SGID enabled
310 programs. */
311 mallfile = __libc_secure_getenv (mallenv);
312 #else
313 mallfile = getenv (mallenv);
314 #endif
315 if (mallfile != NULL || mallwatch != NULL)
317 char *mtb = malloc (TRACE_BUFFER_SIZE);
318 if (mtb == NULL)
319 return;
321 mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
322 if (mallstream != NULL)
324 #ifndef __ASSUME_O_CLOEXEC
325 /* Make sure we close the file descriptor on exec. */
326 int flags = __fcntl (fileno (mallstream), F_GETFD, 0);
327 if (flags >= 0)
329 flags |= FD_CLOEXEC;
330 __fcntl (fileno (mallstream), F_SETFD, flags);
332 #endif
333 /* Be sure it doesn't malloc its buffer! */
334 malloc_trace_buffer = mtb;
335 setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
336 fprintf (mallstream, "= Start\n");
337 tr_old_free_hook = __free_hook;
338 __free_hook = tr_freehook;
339 tr_old_malloc_hook = __malloc_hook;
340 __malloc_hook = tr_mallochook;
341 tr_old_realloc_hook = __realloc_hook;
342 __realloc_hook = tr_reallochook;
343 tr_old_memalign_hook = __memalign_hook;
344 __memalign_hook = tr_memalignhook;
345 #ifdef _LIBC
346 if (!added_atexit_handler)
348 extern void *__dso_handle __attribute__ ((__weak__));
349 added_atexit_handler = 1;
350 __cxa_atexit ((void (*)(void *))release_libc_mem, NULL,
351 &__dso_handle ? __dso_handle : NULL);
353 #endif
355 else
356 free (mtb);
360 void
361 muntrace (void)
363 if (mallstream == NULL)
364 return;
366 /* Do the reverse of what done in mtrace: first reset the hooks and
367 MALLSTREAM, and only after that write the trailer and close the
368 file. */
369 FILE *f = mallstream;
370 mallstream = NULL;
371 __free_hook = tr_old_free_hook;
372 __malloc_hook = tr_old_malloc_hook;
373 __realloc_hook = tr_old_realloc_hook;
374 __memalign_hook = tr_old_memalign_hook;
376 fprintf (f, "= End\n");
377 fclose (f);