Include <string.h> for strlen.
[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 #include <ldsodefs.h>
32
33 #include <stdio.h>
34 #include <string.h>
35
36 #ifndef __GNU_LIBRARY__
37 extern char *getenv ();
38 #else
39 #include <stdlib.h>
40 #endif
41
42 #if defined _LIBC && defined USE_IN_LIBIO
43 # include <libio/iolibio.h>
44 # define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l)
45 #endif
46
47 #define TRACE_BUFFER_SIZE 512
48
49 static FILE *mallstream;
50 static const char mallenv[]= "MALLOC_TRACE";
51 static char malloc_trace_buffer[TRACE_BUFFER_SIZE];
52
53 __libc_lock_define_initialized (static, lock);
54
55 /* Address to breakpoint on accesses to... */
56 __ptr_t mallwatch;
57
58 /* File name and line number information, for callers that had
59    the foresight to call through a macro.  */
60 char *_mtrace_file;
61 int _mtrace_line;
62
63 /* Old hook values.  */
64 static void (*tr_old_free_hook) __P ((__ptr_t ptr, const __ptr_t));
65 static __ptr_t (*tr_old_malloc_hook) __P ((__malloc_size_t size,
66                                            const __ptr_t));
67 static __ptr_t (*tr_old_realloc_hook) __P ((__ptr_t ptr,
68                                             __malloc_size_t size,
69                                             const __ptr_t));
70
71 /* This function is called when the block being alloc'd, realloc'd, or
72    freed has an address matching the variable "mallwatch".  In a debugger,
73    set "mallwatch" to the address of interest, then put a breakpoint on
74    tr_break.  */
75
76 void tr_break __P ((void));
77 void
78 tr_break ()
79 {
80 }
81
82 static void tr_where __P ((const __ptr_t)) internal_function;
83 static void
84 internal_function
85 tr_where (caller)
86      const __ptr_t caller;
87 {
88   if (_mtrace_file)
89     {
90       fprintf (mallstream, "@ %s:%d ", _mtrace_file, _mtrace_line);
91       _mtrace_file = NULL;
92     }
93   else if (caller != NULL)
94     {
95 #ifdef HAVE_ELF
96       Dl_info info;
97       if (_dl_addr (caller, &info))
98         {
99           char *buf = (char *) "";
100           if (info.dli_sname && info.dli_sname[0])
101             {
102               size_t len = strlen (info.dli_sname) + 22;
103               buf = alloca (len);
104               if (caller >= (const __ptr_t) info.dli_saddr)
105                 snprintf (buf, len, "(%s+0x%x)", info.dli_sname,
106                           caller - (const __ptr_t) info.dli_saddr);
107               else
108                 snprintf (buf, len, "(%s-0x%x)", info.dli_sname,
109                           (const __ptr_t) info.dli_saddr - caller);
110             }
111
112           fprintf (mallstream, "@ %s%s%s[%p] ",
113                    info.dli_fname ?: "", info.dli_fname ? ":" : "",
114                    buf, caller);
115         }
116       else
117 #endif
118         fprintf (mallstream, "@ [%p] ", caller);
119     }
120 }
121
122 static void tr_freehook __P ((__ptr_t, const __ptr_t));
123 static void
124 tr_freehook (ptr, caller)
125      __ptr_t ptr;
126      const __ptr_t caller;
127 {
128   tr_where (caller);
129   /* Be sure to print it first.  */
130   fprintf (mallstream, "- %p\n", ptr);
131   if (ptr == mallwatch)
132     tr_break ();
133   __libc_lock_lock (lock);
134   __free_hook = tr_old_free_hook;
135   if (tr_old_free_hook != NULL)
136     (*tr_old_free_hook) (ptr, caller);
137   else
138     free (ptr);
139   __free_hook = tr_freehook;
140   __libc_lock_unlock (lock);
141 }
142
143 static __ptr_t tr_mallochook __P ((__malloc_size_t, const __ptr_t));
144 static __ptr_t
145 tr_mallochook (size, caller)
146      __malloc_size_t size;
147      const __ptr_t caller;
148 {
149   __ptr_t hdr;
150
151   __libc_lock_lock (lock);
152
153   __malloc_hook = tr_old_malloc_hook;
154   if (tr_old_malloc_hook != NULL)
155     hdr = (__ptr_t) (*tr_old_malloc_hook) (size, caller);
156   else
157     hdr = (__ptr_t) malloc (size);
158   __malloc_hook = tr_mallochook;
159
160   __libc_lock_unlock (lock);
161
162   tr_where (caller);
163   /* We could be printing a NULL here; that's OK.  */
164   fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
165
166   if (hdr == mallwatch)
167     tr_break ();
168
169   return hdr;
170 }
171
172 static __ptr_t tr_reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t));
173 static __ptr_t
174 tr_reallochook (ptr, size, caller)
175      __ptr_t ptr;
176      __malloc_size_t size;
177      const __ptr_t caller;
178 {
179   __ptr_t hdr;
180
181   if (ptr == mallwatch)
182     tr_break ();
183
184   __libc_lock_lock (lock);
185
186   __free_hook = tr_old_free_hook;
187   __malloc_hook = tr_old_malloc_hook;
188   __realloc_hook = tr_old_realloc_hook;
189   if (tr_old_realloc_hook != NULL)
190     hdr = (__ptr_t) (*tr_old_realloc_hook) (ptr, size, caller);
191   else
192     hdr = (__ptr_t) realloc (ptr, size);
193   __free_hook = tr_freehook;
194   __malloc_hook = tr_mallochook;
195   __realloc_hook = tr_reallochook;
196
197   __libc_lock_unlock (lock);
198
199   tr_where (caller);
200   if (hdr == NULL)
201     /* Failed realloc.  */
202     fprintf (mallstream, "! %p %#lx\n", ptr, (unsigned long int) size);
203   else if (ptr == NULL)
204     fprintf (mallstream, "+ %p %#lx\n", hdr, (unsigned long int) size);
205   else
206     {
207       fprintf (mallstream, "< %p\n", ptr);
208       tr_where (caller);
209       fprintf (mallstream, "> %p %#lx\n", hdr, (unsigned long int) size);
210     }
211
212   if (hdr == mallwatch)
213     tr_break ();
214
215   return hdr;
216 }
217
218
219 #ifdef _LIBC
220 extern void __libc_freeres (void);
221
222 /* This function gets called to make sure all memory the library
223    allocates get freed and so does not irritate the user when studying
224    the mtrace output.  */
225 static void
226 release_libc_mem (void)
227 {
228   /* Only call the free function if we still are running in mtrace mode.  */
229   if (mallstream != NULL)
230     __libc_freeres ();
231 }
232 #endif
233
234
235 /* We enable tracing if either the environment variable MALLOC_TRACE
236    is set, or if the variable mallwatch has been patched to an address
237    that the debugging user wants us to stop on.  When patching mallwatch,
238    don't forget to set a breakpoint on tr_break!  */
239
240 void
241 mtrace ()
242 {
243 #ifdef _LIBC
244   static int added_atexit_handler = 0;
245 #endif
246   char *mallfile;
247
248   /* Don't panic if we're called more than once.  */
249   if (mallstream != NULL)
250     return;
251
252 #ifdef _LIBC
253   /* When compiling the GNU libc we use the secure getenv function
254      which prevents the misuse in case of SUID or SGID enabled
255      programs.  */
256   mallfile = __secure_getenv (mallenv);
257 #else
258   mallfile = getenv (mallenv);
259 #endif
260   if (mallfile != NULL || mallwatch != NULL)
261     {
262       mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w");
263       if (mallstream != NULL)
264         {
265           /* Be sure it doesn't malloc its buffer!  */
266           setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
267           fprintf (mallstream, "= Start\n");
268           tr_old_free_hook = __free_hook;
269           __free_hook = tr_freehook;
270           tr_old_malloc_hook = __malloc_hook;
271           __malloc_hook = tr_mallochook;
272           tr_old_realloc_hook = __realloc_hook;
273           __realloc_hook = tr_reallochook;
274 #ifdef _LIBC
275           if (!added_atexit_handler)
276             {
277               added_atexit_handler = 1;
278               atexit (release_libc_mem);
279             }
280 #endif
281         }
282     }
283 }
284
285 void
286 muntrace ()
287 {
288   if (mallstream == NULL)
289     return;
290
291   fprintf (mallstream, "= End\n");
292   fclose (mallstream);
293   mallstream = NULL;
294   __free_hook = tr_old_free_hook;
295   __malloc_hook = tr_old_malloc_hook;
296   __realloc_hook = tr_old_realloc_hook;
297 }