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