malloc: Set and reset all hooks for tracing (Bug 16573)
[glibc.git] / malloc / mtrace.c
blob2fda2625084270a1e75d8901ee8bd83f53d8806d
1 /* More debugging hooks for `malloc'.
2 Copyright (C) 1991-2019 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 <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>
37 #include <dso_handle.h>
39 #include <libio/iolibio.h>
40 #define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l)
41 #define fwrite(buf, size, count, fp) _IO_fwrite (buf, size, count, fp)
43 #include <kernel-features.h>
45 #define TRACE_BUFFER_SIZE 512
47 static FILE *mallstream;
48 static const char mallenv[] = "MALLOC_TRACE";
49 static char *malloc_trace_buffer;
51 __libc_lock_define_initialized (static, lock);
53 /* Address to breakpoint on accesses to... */
54 void *mallwatch;
56 /* Old hook values. */
57 static void (*tr_old_free_hook) (void *ptr, const void *);
58 static void *(*tr_old_malloc_hook) (size_t size, const void *);
59 static void *(*tr_old_realloc_hook) (void *ptr, size_t size,
60 const void *);
61 static void *(*tr_old_memalign_hook) (size_t __alignment, size_t __size,
62 const void *);
64 /* This function is called when the block being alloc'd, realloc'd, or
65 freed has an address matching the variable "mallwatch". In a debugger,
66 set "mallwatch" to the address of interest, then put a breakpoint on
67 tr_break. */
69 extern void tr_break (void) __THROW;
70 libc_hidden_proto (tr_break)
71 void
72 tr_break (void)
75 libc_hidden_def (tr_break)
77 static void
78 tr_where (const void *caller, Dl_info *info)
80 if (caller != NULL)
82 if (info != NULL)
84 char *buf = (char *) "";
85 if (info->dli_sname != NULL)
87 size_t len = strlen (info->dli_sname);
88 buf = alloca (len + 6 + 2 * sizeof (void *));
90 buf[0] = '(';
91 __stpcpy (_fitoa (caller >= (const void *) info->dli_saddr
92 ? caller - (const void *) info->dli_saddr
93 : (const void *) info->dli_saddr - caller,
94 __stpcpy (__mempcpy (buf + 1, info->dli_sname,
95 len),
96 caller >= (void *) info->dli_saddr
97 ? "+0x" : "-0x"),
98 16, 0),
99 ")");
102 fprintf (mallstream, "@ %s%s%s[%p] ",
103 info->dli_fname ? : "", info->dli_fname ? ":" : "",
104 buf, caller);
106 else
107 fprintf (mallstream, "@ [%p] ", caller);
111 static Dl_info *
112 lock_and_info (const void *caller, Dl_info *mem)
114 if (caller == NULL)
115 return NULL;
117 Dl_info *res = _dl_addr (caller, mem, NULL, NULL) ? mem : NULL;
119 __libc_lock_lock (lock);
121 return res;
124 static void tr_freehook (void *, const void *);
125 static void * tr_mallochook (size_t, const void *);
126 static void * tr_reallochook (void *, size_t, const void *);
127 static void * tr_memalignhook (size_t, size_t, const void *);
129 /* Set all the default non-trace hooks. */
130 static __always_inline void
131 set_default_hooks (void)
133 __free_hook = tr_old_free_hook;
134 __malloc_hook = tr_old_malloc_hook;
135 __realloc_hook = tr_old_realloc_hook;
136 __memalign_hook = tr_old_memalign_hook;
139 /* Set all of the tracing hooks used for mtrace. */
140 static __always_inline void
141 set_trace_hooks (void)
143 __free_hook = tr_freehook;
144 __malloc_hook = tr_mallochook;
145 __realloc_hook = tr_reallochook;
146 __memalign_hook = tr_memalignhook;
149 /* Save the current set of hooks as the default hooks. */
150 static __always_inline void
151 save_default_hooks (void)
153 tr_old_free_hook = __free_hook;
154 tr_old_malloc_hook = __malloc_hook;
155 tr_old_realloc_hook = __realloc_hook;
156 tr_old_memalign_hook = __memalign_hook;
159 static void
160 tr_freehook (void *ptr, const void *caller)
162 if (ptr == NULL)
163 return;
165 Dl_info mem;
166 Dl_info *info = lock_and_info (caller, &mem);
167 tr_where (caller, info);
168 /* Be sure to print it first. */
169 fprintf (mallstream, "- %p\n", ptr);
170 if (ptr == mallwatch)
172 __libc_lock_unlock (lock);
173 tr_break ();
174 __libc_lock_lock (lock);
176 set_default_hooks ();
177 if (tr_old_free_hook != NULL)
178 (*tr_old_free_hook)(ptr, caller);
179 else
180 free (ptr);
181 set_trace_hooks ();
182 __libc_lock_unlock (lock);
185 static void *
186 tr_mallochook (size_t size, const void *caller)
188 void *hdr;
190 Dl_info mem;
191 Dl_info *info = lock_and_info (caller, &mem);
193 set_default_hooks ();
194 if (tr_old_malloc_hook != NULL)
195 hdr = (void *) (*tr_old_malloc_hook)(size, caller);
196 else
197 hdr = (void *) malloc (size);
198 set_trace_hooks ();
200 tr_where (caller, info);
201 /* We could be printing a NULL here; that's OK. */
202 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
204 __libc_lock_unlock (lock);
206 if (hdr == mallwatch)
207 tr_break ();
209 return hdr;
212 static void *
213 tr_reallochook (void *ptr, size_t size, const void *caller)
215 void *hdr;
217 if (ptr == mallwatch)
218 tr_break ();
220 Dl_info mem;
221 Dl_info *info = lock_and_info (caller, &mem);
223 set_default_hooks ();
224 if (tr_old_realloc_hook != NULL)
225 hdr = (void *) (*tr_old_realloc_hook)(ptr, size, caller);
226 else
227 hdr = (void *) realloc (ptr, size);
228 set_trace_hooks ();
230 tr_where (caller, info);
231 if (hdr == NULL)
233 if (size != 0)
234 /* Failed realloc. */
235 fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
236 else
237 fprintf (mallstream, "- %p\n", ptr);
239 else if (ptr == NULL)
240 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
241 else
243 fprintf (mallstream, "< %p\n", ptr);
244 tr_where (caller, info);
245 fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
248 __libc_lock_unlock (lock);
250 if (hdr == mallwatch)
251 tr_break ();
253 return hdr;
256 static void *
257 tr_memalignhook (size_t alignment, size_t size, const void *caller)
259 void *hdr;
261 Dl_info mem;
262 Dl_info *info = lock_and_info (caller, &mem);
264 set_default_hooks ();
265 if (tr_old_memalign_hook != NULL)
266 hdr = (void *) (*tr_old_memalign_hook)(alignment, size, caller);
267 else
268 hdr = (void *) memalign (alignment, size);
269 set_trace_hooks ();
271 tr_where (caller, info);
272 /* We could be printing a NULL here; that's OK. */
273 fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
275 __libc_lock_unlock (lock);
277 if (hdr == mallwatch)
278 tr_break ();
280 return hdr;
284 #ifdef _LIBC
286 /* This function gets called to make sure all memory the library
287 allocates get freed and so does not irritate the user when studying
288 the mtrace output. */
289 static void __libc_freeres_fn_section
290 release_libc_mem (void)
292 /* Only call the free function if we still are running in mtrace mode. */
293 if (mallstream != NULL)
294 __libc_freeres ();
296 #endif
299 /* We enable tracing if either the environment variable MALLOC_TRACE
300 is set, or if the variable mallwatch has been patched to an address
301 that the debugging user wants us to stop on. When patching mallwatch,
302 don't forget to set a breakpoint on tr_break! */
304 void
305 mtrace (void)
307 #ifdef _LIBC
308 static int added_atexit_handler;
309 #endif
310 char *mallfile;
312 /* Don't panic if we're called more than once. */
313 if (mallstream != NULL)
314 return;
316 #ifdef _LIBC
317 /* When compiling the GNU libc we use the secure getenv function
318 which prevents the misuse in case of SUID or SGID enabled
319 programs. */
320 mallfile = __libc_secure_getenv (mallenv);
321 #else
322 mallfile = getenv (mallenv);
323 #endif
324 if (mallfile != NULL || mallwatch != NULL)
326 char *mtb = malloc (TRACE_BUFFER_SIZE);
327 if (mtb == NULL)
328 return;
330 mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
331 if (mallstream != NULL)
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 save_default_hooks ();
338 set_trace_hooks ();
339 #ifdef _LIBC
340 if (!added_atexit_handler)
342 added_atexit_handler = 1;
343 __cxa_atexit ((void (*)(void *))release_libc_mem, NULL,
344 __dso_handle);
346 #endif
348 else
349 free (mtb);
353 void
354 muntrace (void)
356 if (mallstream == NULL)
357 return;
359 /* Do the reverse of what done in mtrace: first reset the hooks and
360 MALLSTREAM, and only after that write the trailer and close the
361 file. */
362 FILE *f = mallstream;
363 mallstream = NULL;
364 set_default_hooks ();
366 fprintf (f, "= End\n");
367 fclose (f);