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