(tr_reallochook): Produce better output for real reallocation case.
[kopensolaris-gnu/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           char *buf = (char *) "";
101           if (info.dli_sname && info.dli_sname[0])
102             {
103               size_t len = strlen (info.dli_sname) + 22;
104               buf = alloca (len);
105               if (caller >= (const __ptr_t) info.dli_saddr)
106                 snprintf (buf, len, "(%s+0x%x)", info.dli_sname,
107                           caller - (const __ptr_t) info.dli_saddr);
108               else
109                 snprintf (buf, len, "(%s-0x%x)", info.dli_sname,
110                           (const __ptr_t) info.dli_saddr - caller);
111             }
112
113           fprintf (mallstream, "@ %s%s%s[%p] ",
114                    info.dli_fname ?: "", info.dli_fname ? ":" : "",
115                    buf, caller);
116         }
117       else
118 #endif
119         fprintf (mallstream, "@ [%p] ", caller);
120     }
121 }
122
123 static void tr_freehook __P ((__ptr_t, const __ptr_t));
124 static void
125 tr_freehook (ptr, caller)
126      __ptr_t ptr;
127      const __ptr_t caller;
128 {
129   tr_where (caller);
130   /* Be sure to print it first.  */
131   fprintf (mallstream, "- %p\n", ptr);
132   if (ptr == mallwatch)
133     tr_break ();
134   __libc_lock_lock (lock);
135   __free_hook = tr_old_free_hook;
136   if (tr_old_free_hook != NULL)
137     (*tr_old_free_hook) (ptr, caller);
138   else
139     free (ptr);
140   __free_hook = tr_freehook;
141   __libc_lock_unlock (lock);
142 }
143
144 static __ptr_t tr_mallochook __P ((__malloc_size_t, const __ptr_t));
145 static __ptr_t
146 tr_mallochook (size, caller)
147      __malloc_size_t size;
148      const __ptr_t caller;
149 {
150   __ptr_t hdr;
151
152   __libc_lock_lock (lock);
153
154   __malloc_hook = tr_old_malloc_hook;
155   if (tr_old_malloc_hook != NULL)
156     hdr = (__ptr_t) (*tr_old_malloc_hook) (size, caller);
157   else
158     hdr = (__ptr_t) malloc (size);
159   __malloc_hook = tr_mallochook;
160
161   __libc_lock_unlock (lock);
162
163   tr_where (caller);
164   /* We could be printing a NULL here; that's OK.  */
165   fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
166
167   if (hdr == mallwatch)
168     tr_break ();
169
170   return hdr;
171 }
172
173 static __ptr_t tr_reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t));
174 static __ptr_t
175 tr_reallochook (ptr, size, caller)
176      __ptr_t ptr;
177      __malloc_size_t size;
178      const __ptr_t caller;
179 {
180   __ptr_t hdr;
181
182   if (ptr == mallwatch)
183     tr_break ();
184
185   __libc_lock_lock (lock);
186
187   __free_hook = tr_old_free_hook;
188   __malloc_hook = tr_old_malloc_hook;
189   __realloc_hook = tr_old_realloc_hook;
190   if (tr_old_realloc_hook != NULL)
191     hdr = (__ptr_t) (*tr_old_realloc_hook) (ptr, size, caller);
192   else
193     hdr = (__ptr_t) realloc (ptr, size);
194   __free_hook = tr_freehook;
195   __malloc_hook = tr_mallochook;
196   __realloc_hook = tr_reallochook;
197
198   __libc_lock_unlock (lock);
199
200   tr_where (caller);
201   if (hdr == NULL)
202     /* Failed realloc.  */
203     fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
204   else if (ptr == NULL)
205     fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
206   else
207     {
208       fprintf (mallstream, "< %p\n", ptr);
209       tr_where (caller);
210       fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
211     }
212
213   if (hdr == mallwatch)
214     tr_break ();
215
216   return hdr;
217 }
218
219
220 #ifdef _LIBC
221 extern void __libc_freeres (void);
222
223 /* This function gets called to make sure all memory the library
224    allocates get freed and so does not irritate the user when studying
225    the mtrace output.  */
226 static void
227 release_libc_mem (void)
228 {
229   /* Only call the free function if we still are running in mtrace mode.  */
230   if (mallstream != NULL)
231     __libc_freeres ();
232 }
233 #endif
234
235
236 /* We enable tracing if either the environment variable MALLOC_TRACE
237    is set, or if the variable mallwatch has been patched to an address
238    that the debugging user wants us to stop on.  When patching mallwatch,
239    don't forget to set a breakpoint on tr_break!  */
240
241 void
242 mtrace ()
243 {
244 #ifdef _LIBC
245   static int added_atexit_handler = 0;
246 #endif
247   char *mallfile;
248
249   /* Don't panic if we're called more than once.  */
250   if (mallstream != NULL)
251     return;
252
253 #ifdef _LIBC
254   /* When compiling the GNU libc we use the secure getenv function
255      which prevents the misuse in case of SUID or SGID enabled
256      programs.  */
257   mallfile = __secure_getenv (mallenv);
258 #else
259   mallfile = getenv (mallenv);
260 #endif
261   if (mallfile != NULL || mallwatch != NULL)
262     {
263       mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w");
264       if (mallstream != NULL)
265         {
266           /* Be sure it doesn't malloc its buffer!  */
267           setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
268           fprintf (mallstream, "= Start\n");
269           tr_old_free_hook = __free_hook;
270           __free_hook = tr_freehook;
271           tr_old_malloc_hook = __malloc_hook;
272           __malloc_hook = tr_mallochook;
273           tr_old_realloc_hook = __realloc_hook;
274           __realloc_hook = tr_reallochook;
275 #ifdef _LIBC
276           if (!added_atexit_handler)
277             {
278               added_atexit_handler = 1;
279               atexit (release_libc_mem);
280             }
281 #endif
282         }
283     }
284 }
285
286 void
287 muntrace ()
288 {
289   if (mallstream == NULL)
290     return;
291
292   fprintf (mallstream, "= End\n");
293   fclose (mallstream);
294   mallstream = NULL;
295   __free_hook = tr_old_free_hook;
296   __malloc_hook = tr_old_malloc_hook;
297   __realloc_hook = tr_old_realloc_hook;
298 }