]> sjero.net Git - wget/blob - src/xmalloc.c
[svn] Merge of fix for bugs 20341 and 20410.
[wget] / src / xmalloc.c
1 /* Wrappers around malloc and memory debugging support.
2    Copyright (C) 2003-2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 GNU Wget 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
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Wget.  If not, see <http://www.gnu.org/licenses/>.
18
19 In addition, as a special exception, the Free Software Foundation
20 gives permission to link the code of its release of Wget with the
21 OpenSSL project's "OpenSSL" library (or with modified versions of it
22 that use the same license as the "OpenSSL" library), and distribute
23 the linked executables.  You must obey the GNU General Public License
24 in all respects for all of the code used other than "OpenSSL".  If you
25 modify this file, you may extend this exception to your version of the
26 file, but you are not obligated to do so.  If you do not wish to do
27 so, delete this exception statement from your version.  */
28
29 #include <config.h>
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <assert.h>
36
37 #include "wget.h"
38 #include "xmalloc.h"
39 #include "hash.h"               /* for hash_pointer */
40
41 /* This file implements several wrappers around the basic allocation
42    routines.  This is done for two reasons: first, so that the callers
43    of these functions need not check for errors, which is easy to
44    forget.  If there is not enough virtual memory for running Wget,
45    something is seriously wrong, and Wget exits with an appropriate
46    error message.
47
48    The second reason why these are useful is that, if DEBUG_MALLOC is
49    defined, they also provide a handy (if crude) malloc debugging
50    interface that checks for memory leaks.  */
51
52 /* Croak the fatal memory error and bail out with non-zero exit
53    status.  */
54
55 static void
56 memfatal (const char *context, long attempted_size)
57 {
58   /* Make sure we don't try to store part of the log line, and thus
59      call malloc.  */
60   log_set_save_context (false);
61   logprintf (LOG_ALWAYS,
62              _("%s: %s: Failed to allocate %ld bytes; memory exhausted.\n"),
63              exec_name, context, attempted_size);
64   exit (1);
65 }
66
67 /* These functions end with _real because they need to be
68    distinguished from the debugging functions, and from the macros.
69    Explanation follows:
70
71    If memory debugging is not turned on, xmalloc.h defines these:
72
73      #define xmalloc checking_malloc
74      #define xmalloc0 checking_malloc0
75      #define xrealloc checking_realloc
76      #define xstrdup checking_strdup
77      #define xfree checking_free
78
79    In case of memory debugging, the definitions are a bit more
80    complex, because we want to provide more information, *and* we want
81    to call the debugging code.  (The former is the reason why xmalloc
82    and friends need to be macros in the first place.)  Then it looks
83    like this:
84
85      #define xmalloc(a) debugging_malloc (a, __FILE__, __LINE__)
86      #define xmalloc0(a) debugging_malloc0 (a, __FILE__, __LINE__)
87      #define xrealloc(a, b) debugging_realloc (a, b, __FILE__, __LINE__)
88      #define xstrdup(a) debugging_strdup (a, __FILE__, __LINE__)
89      #define xfree(a) debugging_free (a, __FILE__, __LINE__)
90
91    Each of the debugging_* functions does its magic and calls the
92    corresponding checking_* one.  */
93
94 #ifdef DEBUG_MALLOC
95 # define STATIC_IF_DEBUG static
96 #else
97 # define STATIC_IF_DEBUG
98 #endif
99
100 STATIC_IF_DEBUG void *
101 checking_malloc (size_t size)
102 {
103   void *ptr = malloc (size);
104   if (!ptr)
105     memfatal ("malloc", size);
106   return ptr;
107 }
108
109 STATIC_IF_DEBUG void *
110 checking_malloc0 (size_t size)
111 {
112   /* Using calloc can be faster than malloc+memset because some calloc
113      implementations know when they're dealing with zeroed-out memory
114      from the system and can avoid unnecessary memset.  */
115   void *ptr = calloc (1, size);
116   if (!ptr)
117     memfatal ("calloc", size);
118   return ptr;
119 }
120
121 STATIC_IF_DEBUG void *
122 checking_realloc (void *ptr, size_t newsize)
123 {
124   void *newptr;
125
126   /* Not all Un*xes have the feature of realloc() that calling it with
127      a NULL-pointer is the same as malloc(), but it is easy to
128      simulate.  */
129   if (ptr)
130     newptr = realloc (ptr, newsize);
131   else
132     newptr = malloc (newsize);
133   if (!newptr)
134     memfatal ("realloc", newsize);
135   return newptr;
136 }
137
138 STATIC_IF_DEBUG char *
139 checking_strdup (const char *s)
140 {
141   char *copy;
142
143 #ifndef HAVE_STRDUP
144   int l = strlen (s);
145   copy = malloc (l + 1);
146   if (!copy)
147     memfatal ("strdup", l + 1);
148   memcpy (copy, s, l + 1);
149 #else  /* HAVE_STRDUP */
150   copy = strdup (s);
151   if (!copy)
152     memfatal ("strdup", 1 + strlen (s));
153 #endif /* HAVE_STRDUP */
154
155   return copy;
156 }
157
158 STATIC_IF_DEBUG void
159 checking_free (void *ptr)
160 {
161   /* Wget's xfree() must not be passed a NULL pointer.  This is for
162      historical reasons: pre-C89 systems were reported to bomb at
163      free(NULL), and Wget was careful to not call xfree when there was
164      a possibility of PTR being NULL.  (It might have been better to
165      simply have xfree() do nothing if ptr==NULL.)
166
167      Since the code is already written that way, this assert simply
168      enforces the existing constraint.  The benefit is double-checking
169      the logic: code that thinks it can't be passed a NULL pointer,
170      while it in fact can, aborts here.  If you trip on this, either
171      the code has a pointer handling bug or should have called
172      xfree_null instead of xfree.  Correctly written code should never
173      trigger this assertion.
174
175      The downside is that the uninitiated might not expect xfree(NULL)
176      to abort.  If the assertion proves to be too much of a hassle, it
177      can be removed and a check that makes NULL a no-op placed in its
178      stead.  If that is done, xfree_null is no longer needed and
179      should be removed.  */
180   assert (ptr != NULL);
181
182   free (ptr);
183 }
184 \f
185 #ifdef DEBUG_MALLOC
186
187 /* Crude home-grown routines for debugging some malloc-related
188    problems.  Featured:
189
190    * Counting the number of malloc and free invocations, and reporting
191      the "balance", i.e. how many times more malloc was called than it
192      was the case with free.
193
194    * Making malloc store its entry into a simple array and free remove
195      stuff from that array.  At the end, print the pointers which have
196      not been freed, along with the source file and the line number.
197
198    * Checking for "invalid frees", where free is called on a pointer
199      not obtained with malloc, or where the same pointer is freed
200      twice.
201
202    Note that this kind of memory leak checking strongly depends on
203    every malloc() being followed by a free(), even if the program is
204    about to finish.  Wget is careful to free the data structure it
205    allocated in init.c.  */
206
207 static int malloc_count, free_count;
208
209 /* Home-grown hash table of mallocs: */
210
211 #define SZ 100003               /* Prime just over 100,000.  Increase
212                                    it to debug larger Wget runs.  */
213
214 static struct {
215   const void *ptr;
216   const char *file;
217   int line;
218 } malloc_table[SZ];
219
220 /* Find PTR's position in malloc_table.  If PTR is not found, return
221    the next available position.  */
222
223 static inline int
224 ptr_position (const void *ptr)
225 {
226   int i = hash_pointer (ptr) % SZ;
227   for (; malloc_table[i].ptr != NULL; i = (i + 1) % SZ)
228     if (malloc_table[i].ptr == ptr)
229       return i;
230   return i;
231 }
232
233 /* Register PTR in malloc_table.  Abort if this is not possible
234    (presumably due to the number of current allocations exceeding the
235    size of malloc_table.)  */
236
237 static void
238 register_ptr (const void *ptr, const char *file, int line)
239 {
240   int i;
241   if (malloc_count - free_count > SZ)
242     {
243       fprintf (stderr, "Increase SZ to a larger value and recompile.\n");
244       fflush (stderr);
245       abort ();
246     }
247
248   i = ptr_position (ptr);
249   malloc_table[i].ptr = ptr;
250   malloc_table[i].file = file;
251   malloc_table[i].line = line;
252 }
253
254 /* Unregister PTR from malloc_table.  Return false if PTR is not
255    present in malloc_table.  */
256
257 static bool
258 unregister_ptr (void *ptr)
259 {
260   int i = ptr_position (ptr);
261   if (malloc_table[i].ptr == NULL)
262     return false;
263   malloc_table[i].ptr = NULL;
264
265   /* Relocate malloc_table entries immediately following PTR. */
266   for (i = (i + 1) % SZ; malloc_table[i].ptr != NULL; i = (i + 1) % SZ)
267     {
268       const void *ptr2 = malloc_table[i].ptr;
269       /* Find the new location for the key. */
270       int j = hash_pointer (ptr2) % SZ;
271       for (; malloc_table[j].ptr != NULL; j = (j + 1) % SZ)
272         if (ptr2 == malloc_table[j].ptr)
273           /* No need to relocate entry at [i]; it's already at or near
274              its hash position. */
275           goto cont_outer;
276       malloc_table[j] = malloc_table[i];
277       malloc_table[i].ptr = NULL;
278     cont_outer:
279       ;
280     }
281   return true;
282 }
283
284 /* Print the malloc debug stats gathered from the above information.
285    Currently this is the count of mallocs, frees, the difference
286    between the two, and the dump of the contents of malloc_table.  The
287    last part are the memory leaks.  */
288
289 void
290 print_malloc_debug_stats (void)
291 {
292   int i;
293   printf ("\nMalloc:  %d\nFree:    %d\nBalance: %d\n\n",
294           malloc_count, free_count, malloc_count - free_count);
295   for (i = 0; i < SZ; i++)
296     if (malloc_table[i].ptr != NULL)
297       printf ("0x%0*lx: %s:%d\n", PTR_FORMAT (malloc_table[i].ptr),
298               malloc_table[i].file, malloc_table[i].line);
299 }
300
301 void *
302 debugging_malloc (size_t size, const char *source_file, int source_line)
303 {
304   void *ptr = checking_malloc (size);
305   ++malloc_count;
306   register_ptr (ptr, source_file, source_line);
307   return ptr;
308 }
309
310 void *
311 debugging_malloc0 (size_t size, const char *source_file, int source_line)
312 {
313   void *ptr = checking_malloc0 (size);
314   ++malloc_count;
315   register_ptr (ptr, source_file, source_line);
316   return ptr;
317 }
318
319 void *
320 debugging_realloc (void *ptr, size_t newsize, const char *source_file, int source_line)
321 {
322   void *newptr = checking_realloc (ptr, newsize);
323   if (!ptr)
324     {
325       ++malloc_count;
326       register_ptr (newptr, source_file, source_line);
327     }
328   else if (newptr != ptr)
329     {
330       unregister_ptr (ptr);
331       register_ptr (newptr, source_file, source_line);
332     }
333   return newptr;
334 }
335
336 char *
337 debugging_strdup (const char *s, const char *source_file, int source_line)
338 {
339   char *copy = checking_strdup (s);
340   ++malloc_count;
341   register_ptr (copy, source_file, source_line);
342   return copy;
343 }
344
345 void
346 debugging_free (void *ptr, const char *source_file, int source_line)
347 {
348   /* See checking_free for rationale of this abort.  We repeat it here
349      because we can print the file and the line where the offending
350      free occurred.  */
351   if (ptr == NULL)
352     {
353       fprintf (stderr, "%s: xfree(NULL) at %s:%d\n",
354                exec_name, source_file, source_line);
355       abort ();
356     }
357   if (!unregister_ptr (ptr))
358     {
359       fprintf (stderr, "%s: bad xfree(0x%0*lx) at %s:%d\n",
360                exec_name, PTR_FORMAT (ptr), source_file, source_line);
361       abort ();
362     }
363   ++free_count;
364
365   checking_free (ptr);
366 }
367
368 #endif /* DEBUG_MALLOC */