Update.
[platform/upstream/glibc.git] / malloc / mtrace.c
1 /* More debugging hooks for `malloc'.
2    Copyright (C) 1991, 92, 93, 94, 96, 97, 98 Free Software Foundation, Inc.
3                  Written April 2, 1991 by John Gilmore of Cygnus Support.
4                  Based on mcheck.c by Mike Haertel.
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with this library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.
20
21    The author may be reached (Email) at the address mike@ai.mit.edu,
22    or (US mail) as Mike Haertel c/o Free Software Foundation.  */
23
24 #ifndef _MALLOC_INTERNAL
25 #define _MALLOC_INTERNAL
26 #include <malloc.h>
27 #include <mcheck.h>
28 #include <bits/libc-lock.h>
29 #endif
30
31 #ifdef HAVE_ELF
32 #include <elf/ldsodefs.h>
33 #endif
34
35 #include <stdio.h>
36
37 #ifndef __GNU_LIBRARY__
38 extern char *getenv ();
39 #else
40 #include <stdlib.h>
41 #endif
42
43 #if defined _LIBC && defined USE_IN_LIBIO
44 # include <libio/iolibio.h>
45 # define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l)
46 #endif
47
48 #define TRACE_BUFFER_SIZE 512
49
50 static FILE *mallstream;
51 static const char mallenv[]= "MALLOC_TRACE";
52 static char malloc_trace_buffer[TRACE_BUFFER_SIZE];
53
54 __libc_lock_define_initialized (static, lock);
55
56 /* Address to breakpoint on accesses to... */
57 __ptr_t mallwatch;
58
59 /* File name and line number information, for callers that had
60    the foresight to call through a macro.  */
61 char *_mtrace_file;
62 int _mtrace_line;
63
64 /* Old hook values.  */
65 static void (*tr_old_free_hook) __P ((__ptr_t ptr, const __ptr_t));
66 static __ptr_t (*tr_old_malloc_hook) __P ((__malloc_size_t size,
67                                            const __ptr_t));
68 static __ptr_t (*tr_old_realloc_hook) __P ((__ptr_t ptr,
69                                             __malloc_size_t size,
70                                             const __ptr_t));
71
72 /* This function is called when the block being alloc'd, realloc'd, or
73    freed has an address matching the variable "mallwatch".  In a debugger,
74    set "mallwatch" to the address of interest, then put a breakpoint on
75    tr_break.  */
76
77 void tr_break __P ((void));
78 void
79 tr_break ()
80 {
81 }
82
83 static void tr_where __P ((const __ptr_t)) internal_function;
84 static void
85 internal_function
86 tr_where (caller)
87      const __ptr_t caller;
88 {
89   if (_mtrace_file)
90     {
91       fprintf (mallstream, "@ %s:%d ", _mtrace_file, _mtrace_line);
92       _mtrace_file = NULL;
93     }
94   else if (caller != NULL)
95     {
96 #ifdef HAVE_ELF
97       Dl_info info;
98       if (_dl_addr (caller, &info))
99         {
100           fprintf (mallstream, "@ %s%s%s%s%s[%p]",
101                    info.dli_fname ?: "", info.dli_fname ? ":" : "",
102                    info.dli_sname ? "(" : "",
103                    info.dli_sname ?: "", info.dli_sname ? ") " : " ",
104                    caller);
105         }
106       else
107 #endif
108         fprintf (mallstream, "@ [%p] ", caller);
109     }
110 }
111
112 static void tr_freehook __P ((__ptr_t, const __ptr_t));
113 static void
114 tr_freehook (ptr, caller)
115      __ptr_t ptr;
116      const __ptr_t caller;
117 {
118   tr_where (caller);
119   /* Be sure to print it first.  */
120   fprintf (mallstream, "- %p\n", ptr);
121   if (ptr == mallwatch)
122     tr_break ();
123   __libc_lock_lock (lock);
124   __free_hook = tr_old_free_hook;
125   if (tr_old_free_hook != NULL)
126     (*tr_old_free_hook) (ptr, caller);
127   else
128     free (ptr);
129   __free_hook = tr_freehook;
130   __libc_lock_unlock (lock);
131 }
132
133 static __ptr_t tr_mallochook __P ((__malloc_size_t, const __ptr_t));
134 static __ptr_t
135 tr_mallochook (size, caller)
136      __malloc_size_t size;
137      const __ptr_t caller;
138 {
139   __ptr_t hdr;
140
141   __libc_lock_lock (lock);
142
143   __malloc_hook = tr_old_malloc_hook;
144   if (tr_old_malloc_hook != NULL)
145     hdr = (__ptr_t) (*tr_old_malloc_hook) (size, caller);
146   else
147     hdr = (__ptr_t) malloc (size);
148   __malloc_hook = tr_mallochook;
149
150   __libc_lock_unlock (lock);
151
152   tr_where (caller);
153   /* We could be printing a NULL here; that's OK.  */
154   fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
155
156   if (hdr == mallwatch)
157     tr_break ();
158
159   return hdr;
160 }
161
162 static __ptr_t tr_reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t));
163 static __ptr_t
164 tr_reallochook (ptr, size, caller)
165      __ptr_t ptr;
166      __malloc_size_t size;
167      const __ptr_t caller;
168 {
169   __ptr_t hdr;
170
171   if (ptr == mallwatch)
172     tr_break ();
173
174   __libc_lock_lock (lock);
175
176   __free_hook = tr_old_free_hook;
177   __malloc_hook = tr_old_malloc_hook;
178   __realloc_hook = tr_old_realloc_hook;
179   if (tr_old_realloc_hook != NULL)
180     hdr = (__ptr_t) (*tr_old_realloc_hook) (ptr, size, caller);
181   else
182     hdr = (__ptr_t) realloc (ptr, size);
183   __free_hook = tr_freehook;
184   __malloc_hook = tr_mallochook;
185   __realloc_hook = tr_reallochook;
186
187   __libc_lock_unlock (lock);
188
189   tr_where (caller);
190   if (hdr == NULL)
191     /* Failed realloc.  */
192     fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
193   else if (ptr == NULL)
194     fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
195   else
196     fprintf (mallstream, "< %p\n> %p %#lx\n", ptr, hdr,
197              (unsigned long int) size);
198
199   if (hdr == mallwatch)
200     tr_break ();
201
202   return hdr;
203 }
204
205
206 #ifdef _LIBC
207 extern void __libc_freeres (void);
208
209 /* This function gets called to make sure all memory the library
210    allocates get freed and so does not irritate the user when studying
211    the mtrace output.  */
212 static void
213 release_libc_mem (void)
214 {
215   /* Only call the free function if we still are running in mtrace mode.  */
216   if (mallstream != NULL)
217     __libc_freeres ();
218 }
219 #endif
220
221
222 /* We enable tracing if either the environment variable MALLOC_TRACE
223    is set, or if the variable mallwatch has been patched to an address
224    that the debugging user wants us to stop on.  When patching mallwatch,
225    don't forget to set a breakpoint on tr_break!  */
226
227 void
228 mtrace ()
229 {
230 #ifdef _LIBC
231   static int added_atexit_handler = 0;
232 #endif
233   char *mallfile;
234
235   /* Don't panic if we're called more than once.  */
236   if (mallstream != NULL)
237     return;
238
239 #ifdef _LIBC
240   /* When compiling the GNU libc we use the secure getenv function
241      which prevents the misuse in case of SUID or SGID enabled
242      programs.  */
243   mallfile = __secure_getenv (mallenv);
244 #else
245   mallfile = getenv (mallenv);
246 #endif
247   if (mallfile != NULL || mallwatch != NULL)
248     {
249       mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w");
250       if (mallstream != NULL)
251         {
252           /* Be sure it doesn't malloc its buffer!  */
253           setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
254           fprintf (mallstream, "= Start\n");
255           tr_old_free_hook = __free_hook;
256           __free_hook = tr_freehook;
257           tr_old_malloc_hook = __malloc_hook;
258           __malloc_hook = tr_mallochook;
259           tr_old_realloc_hook = __realloc_hook;
260           __realloc_hook = tr_reallochook;
261 #ifdef _LIBC
262           if (!added_atexit_handler)
263             {
264               added_atexit_handler = 1;
265               atexit (release_libc_mem);
266             }
267 #endif
268         }
269     }
270 }
271
272 void
273 muntrace ()
274 {
275   if (mallstream == NULL)
276     return;
277
278   fprintf (mallstream, "= End\n");
279   fclose (mallstream);
280   mallstream = NULL;
281   __free_hook = tr_old_free_hook;
282   __malloc_hook = tr_old_malloc_hook;
283   __realloc_hook = tr_old_realloc_hook;
284 }