1/* mtrace implementation for `malloc'.
2 Copyright (C) 1991-2021 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Written April 2, 1991 by John Gilmore of Cygnus Support.
5 Based on mcheck.c by Mike Haertel.
6
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with the GNU C Library; if not, see
19 <https://www.gnu.org/licenses/>. */
20
21
22#include <malloc.h>
23#include <mcheck.h>
24
25#include <dlfcn.h>
26#include <fcntl.h>
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30#include <inttypes.h>
31
32#include <libc-internal.h>
33#include <dso_handle.h>
34
35#include <kernel-features.h>
36
37#define TRACE_BUFFER_SIZE 512
38
39static FILE *mallstream;
40static const char mallenv[] = "MALLOC_TRACE";
41static char *malloc_trace_buffer;
42
43static void
44tr_where (const void *caller, Dl_info *info)
45{
46 if (caller != NULL)
47 {
48 if (info != NULL)
49 {
50 char *buf = (char *) "";
51 if (info->dli_sname != NULL)
52 {
53 size_t len = strlen (info->dli_sname);
54 buf = alloca (len + 6 + 2 * sizeof (void *));
55 char sign;
56 ptrdiff_t offset =
57 (ptrdiff_t) info->dli_saddr - (ptrdiff_t) caller;
58
59 if (caller >= (const void *) info->dli_saddr)
60 {
61 sign = '+';
62 offset = -offset;
63 }
64 else
65 sign = '-';
66
67 sprintf (buf, "(%s%c%" PRIxPTR ")", info->dli_sname, sign,
68 offset);
69 }
70
71 fprintf (mallstream, "@ %s%s%s[%p] ", info->dli_fname ? : "",
72 info->dli_fname ? ":" : "",
73 buf, caller);
74 }
75 else
76 fprintf (mallstream, "@ [%p] ", caller);
77 }
78}
79
80static Dl_info *
81lock_and_info (const void *caller, Dl_info *mem)
82{
83 if (caller == NULL)
84 return NULL;
85
86 Dl_info *res = dladdr (caller, mem) ? mem : NULL;
87
88 flockfile (mallstream);
89
90 return res;
91}
92
93static void
94free_mtrace (void *ptr, const void *caller)
95{
96 if (ptr == NULL)
97 return;
98
99 Dl_info mem;
100 Dl_info *info = lock_and_info (caller, &mem);
101 tr_where (caller, info);
102 /* Be sure to print it first. */
103 fprintf (mallstream, "- %p\n", ptr);
104 funlockfile (mallstream);
105}
106
107static void
108malloc_mtrace_after (void *block, size_t size, const void *caller)
109{
110 Dl_info mem;
111 Dl_info *info = lock_and_info (caller, &mem);
112
113 tr_where (caller, info);
114 /* We could be printing a NULL here; that's OK. */
115 fprintf (mallstream, "+ %p %#lx\n", block, (unsigned long int) size);
116
117 funlockfile (mallstream);
118}
119
120static void
121realloc_mtrace_after (void *block, const void *oldptr, size_t size,
122 const void *caller)
123{
124 Dl_info mem;
125 Dl_info *info = lock_and_info (caller, &mem);
126
127 tr_where (caller, info);
128 if (block == NULL)
129 {
130 if (size != 0)
131 /* Failed realloc. */
132 fprintf (mallstream, "! %p %#lx\n", oldptr, (unsigned long int) size);
133 else
134 fprintf (mallstream, "- %p\n", oldptr);
135 }
136 else if (oldptr == NULL)
137 fprintf (mallstream, "+ %p %#lx\n", block, (unsigned long int) size);
138 else
139 {
140 fprintf (mallstream, "< %p\n", oldptr);
141 tr_where (caller, info);
142 fprintf (mallstream, "> %p %#lx\n", block, (unsigned long int) size);
143 }
144
145 funlockfile (mallstream);
146}
147
148static void
149memalign_mtrace_after (void *block, size_t size, const void *caller)
150{
151 Dl_info mem;
152 Dl_info *info = lock_and_info (caller, &mem);
153
154 tr_where (caller, info);
155 /* We could be printing a NULL here; that's OK. */
156 fprintf (mallstream, "+ %p %#lx\n", block, (unsigned long int) size);
157
158 funlockfile (mallstream);
159}
160
161/* This function gets called to make sure all memory the library
162 allocates get freed and so does not irritate the user when studying
163 the mtrace output. */
164static void
165release_libc_mem (void)
166{
167 /* Only call the free function if we still are running in mtrace mode. */
168 if (mallstream != NULL)
169 __libc_freeres ();
170}
171
172/* We enable tracing if the environment variable MALLOC_TRACE is set. */
173
174static void
175do_mtrace (void)
176{
177 static int added_atexit_handler;
178 char *mallfile;
179
180 /* Don't panic if we're called more than once. */
181 if (mallstream != NULL)
182 return;
183
184 mallfile = secure_getenv (mallenv);
185 if (mallfile != NULL)
186 {
187 char *mtb = malloc (TRACE_BUFFER_SIZE);
188 if (mtb == NULL)
189 return;
190
191 mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
192 if (mallstream != NULL)
193 {
194 /* Be sure it doesn't malloc its buffer! */
195 malloc_trace_buffer = mtb;
196 setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
197 fprintf (mallstream, "= Start\n");
198 if (!added_atexit_handler)
199 {
200 added_atexit_handler = 1;
201 __cxa_atexit ((void (*)(void *))release_libc_mem, NULL,
202 __dso_handle);
203 }
204 __malloc_debug_enable (MALLOC_MTRACE_HOOK);
205 }
206 else
207 free (mtb);
208 }
209}
210
211static void
212do_muntrace (void)
213{
214 __malloc_debug_disable (MALLOC_MTRACE_HOOK);
215 if (mallstream == NULL)
216 return;
217
218 /* Do the reverse of what done in mtrace: first reset the hooks and
219 MALLSTREAM, and only after that write the trailer and close the
220 file. */
221 FILE *f = mallstream;
222 mallstream = NULL;
223
224 fprintf (f, "= End\n");
225 fclose (f);
226}
227