(tr_where): Don't print space in location string, print it afterwards.
[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     fprintf (mallstream, "< %p\n> %p %#lx\n", ptr, hdr,
208              (unsigned long int) size);
209
210   if (hdr == mallwatch)
211     tr_break ();
212
213   return hdr;
214 }
215
216
217 #ifdef _LIBC
218 extern void __libc_freeres (void);
219
220 /* This function gets called to make sure all memory the library
221    allocates get freed and so does not irritate the user when studying
222    the mtrace output.  */
223 static void
224 release_libc_mem (void)
225 {
226   /* Only call the free function if we still are running in mtrace mode.  */
227   if (mallstream != NULL)
228     __libc_freeres ();
229 }
230 #endif
231
232
233 /* We enable tracing if either the environment variable MALLOC_TRACE
234    is set, or if the variable mallwatch has been patched to an address
235    that the debugging user wants us to stop on.  When patching mallwatch,
236    don't forget to set a breakpoint on tr_break!  */
237
238 void
239 mtrace ()
240 {
241 #ifdef _LIBC
242   static int added_atexit_handler = 0;
243 #endif
244   char *mallfile;
245
246   /* Don't panic if we're called more than once.  */
247   if (mallstream != NULL)
248     return;
249
250 #ifdef _LIBC
251   /* When compiling the GNU libc we use the secure getenv function
252      which prevents the misuse in case of SUID or SGID enabled
253      programs.  */
254   mallfile = __secure_getenv (mallenv);
255 #else
256   mallfile = getenv (mallenv);
257 #endif
258   if (mallfile != NULL || mallwatch != NULL)
259     {
260       mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w");
261       if (mallstream != NULL)
262         {
263           /* Be sure it doesn't malloc its buffer!  */
264           setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
265           fprintf (mallstream, "= Start\n");
266           tr_old_free_hook = __free_hook;
267           __free_hook = tr_freehook;
268           tr_old_malloc_hook = __malloc_hook;
269           __malloc_hook = tr_mallochook;
270           tr_old_realloc_hook = __realloc_hook;
271           __realloc_hook = tr_reallochook;
272 #ifdef _LIBC
273           if (!added_atexit_handler)
274             {
275               added_atexit_handler = 1;
276               atexit (release_libc_mem);
277             }
278 #endif
279         }
280     }
281 }
282
283 void
284 muntrace ()
285 {
286   if (mallstream == NULL)
287     return;
288
289   fprintf (mallstream, "= End\n");
290   fclose (mallstream);
291   mallstream = NULL;
292   __free_hook = tr_old_free_hook;
293   __malloc_hook = tr_old_malloc_hook;
294   __realloc_hook = tr_old_realloc_hook;
295 }