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