]> sjero.net Git - wget/blob - src/xmalloc.c
[svn] Moved malloc-related code to xmalloc.c. Defined new macros xnew, xnew0,
[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 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 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, _("%s: %s: Cannot allocate %ld bytes.\n"),
71              exec_name, context, size);
72   exit (1);
73 }
74
75 /* These functions end with _real because they need to be
76    distinguished from the debugging functions, and from the macros.
77    Explanation follows:
78
79    If memory debugging is not turned on, wget.h defines these:
80
81      #define xmalloc xmalloc_real
82      #define xmalloc0 xmalloc0_real
83      #define xrealloc xrealloc_real
84      #define xstrdup xstrdup_real
85      #define xfree free
86
87    In case of memory debugging, the definitions are a bit more
88    complex, because we want to provide more information, *and* we want
89    to call the debugging code.  (The former is the reason why xmalloc
90    and friends need to be macros in the first place.)  Then it looks
91    like this:
92
93      #define xmalloc(a) xmalloc_debug (a, __FILE__, __LINE__)
94      #define xmalloc0(a) xmalloc0_debug (a, __FILE__, __LINE__)
95      #define xfree(a)   xfree_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
99    Each of the *_debug function does its magic and calls the real one.  */
100
101 #ifdef DEBUG_MALLOC
102 # define STATIC_IF_DEBUG static
103 #else
104 # define STATIC_IF_DEBUG
105 #endif
106
107 STATIC_IF_DEBUG void *
108 xmalloc_real (size_t size)
109 {
110   void *ptr = malloc (size);
111   if (!ptr)
112     memfatal ("malloc", size);
113   return ptr;
114 }
115
116 STATIC_IF_DEBUG void *
117 xmalloc0_real (size_t size)
118 {
119   /* Using calloc can be faster than malloc+memset because some calloc
120      implementations know when they're dealing with zeroed-out memory
121      from the system and can avoid unnecessary memset.  */
122   void *ptr = calloc (1, size);
123   if (!ptr)
124     memfatal ("calloc", size);
125   return ptr;
126 }
127
128 STATIC_IF_DEBUG void *
129 xrealloc_real (void *ptr, size_t newsize)
130 {
131   void *newptr;
132
133   /* Not all Un*xes have the feature of realloc() that calling it with
134      a NULL-pointer is the same as malloc(), but it is easy to
135      simulate.  */
136   if (ptr)
137     newptr = realloc (ptr, newsize);
138   else
139     newptr = malloc (newsize);
140   if (!newptr)
141     memfatal ("realloc", newsize);
142   return newptr;
143 }
144
145 STATIC_IF_DEBUG char *
146 xstrdup_real (const char *s)
147 {
148   char *copy;
149
150 #ifndef HAVE_STRDUP
151   int l = strlen (s);
152   copy = malloc (l + 1);
153   if (!copy)
154     memfatal ("strdup", l + 1);
155   memcpy (copy, s, l + 1);
156 #else  /* HAVE_STRDUP */
157   copy = strdup (s);
158   if (!copy)
159     memfatal ("strdup", 1 + strlen (s));
160 #endif /* HAVE_STRDUP */
161
162   return copy;
163 }
164
165 #ifdef DEBUG_MALLOC
166
167 /* Crude home-grown routines for debugging some malloc-related
168    problems.  Featured:
169
170    * Counting the number of malloc and free invocations, and reporting
171      the "balance", i.e. how many times more malloc was called than it
172      was the case with free.
173
174    * Making malloc store its entry into a simple array and free remove
175      stuff from that array.  At the end, print the pointers which have
176      not been freed, along with the source file and the line number.
177      This also has the side-effect of detecting freeing memory that
178      was never allocated.
179
180    Note that this kind of memory leak checking strongly depends on
181    every malloc() being followed by a free(), even if the program is
182    about to finish.  Wget is careful to free the data structure it
183    allocated in init.c.  */
184
185 static int malloc_count, free_count;
186
187 static struct {
188   char *ptr;
189   const char *file;
190   int line;
191 } malloc_debug[100000];
192
193 /* Both register_ptr and unregister_ptr take O(n) operations to run,
194    which can be a real problem.  It would be nice to use a hash table
195    for malloc_debug, but the functions in hash.c are not suitable
196    because they can call malloc() themselves.  Maybe it would work if
197    the hash table were preallocated to a huge size, and if we set the
198    rehash threshold to 1.0.  */
199
200 /* Register PTR in malloc_debug.  Abort if this is not possible
201    (presumably due to the number of current allocations exceeding the
202    size of malloc_debug.)  */
203
204 static void
205 register_ptr (void *ptr, const char *file, int line)
206 {
207   int i;
208   for (i = 0; i < countof (malloc_debug); i++)
209     if (malloc_debug[i].ptr == NULL)
210       {
211         malloc_debug[i].ptr = ptr;
212         malloc_debug[i].file = file;
213         malloc_debug[i].line = line;
214         return;
215       }
216   abort ();
217 }
218
219 /* Unregister PTR from malloc_debug.  Abort if PTR is not present in
220    malloc_debug.  (This catches calling free() with a bogus pointer.)  */
221
222 static void
223 unregister_ptr (void *ptr)
224 {
225   int i;
226   for (i = 0; i < countof (malloc_debug); i++)
227     if (malloc_debug[i].ptr == ptr)
228       {
229         malloc_debug[i].ptr = NULL;
230         return;
231       }
232   abort ();
233 }
234
235 /* Print the malloc debug stats that can be gathered from the above
236    information.  Currently this is the count of mallocs, frees, the
237    difference between the two, and the dump of the contents of
238    malloc_debug.  The last part are the memory leaks.  */
239
240 void
241 print_malloc_debug_stats (void)
242 {
243   int i;
244   printf ("\nMalloc:  %d\nFree:    %d\nBalance: %d\n\n",
245           malloc_count, free_count, malloc_count - free_count);
246   for (i = 0; i < countof (malloc_debug); i++)
247     if (malloc_debug[i].ptr != NULL)
248       printf ("0x%08ld: %s:%d\n", (long)malloc_debug[i].ptr,
249               malloc_debug[i].file, malloc_debug[i].line);
250 }
251
252 void *
253 xmalloc_debug (size_t size, const char *source_file, int source_line)
254 {
255   void *ptr = xmalloc_real (size);
256   ++malloc_count;
257   register_ptr (ptr, source_file, source_line);
258   return ptr;
259 }
260
261 void *
262 xmalloc0_debug (size_t size, const char *source_file, int source_line)
263 {
264   void *ptr = xmalloc0_real (size);
265   ++malloc_count;
266   register_ptr (ptr, source_file, source_line);
267   return ptr;
268 }
269
270 void *
271 xrealloc_debug (void *ptr, size_t newsize, const char *source_file, int source_line)
272 {
273   void *newptr = xrealloc_real (ptr, newsize);
274   if (!ptr)
275     {
276       ++malloc_count;
277       register_ptr (newptr, source_file, source_line);
278     }
279   else if (newptr != ptr)
280     {
281       unregister_ptr (ptr);
282       register_ptr (newptr, source_file, source_line);
283     }
284   return newptr;
285 }
286
287 char *
288 xstrdup_debug (const char *s, const char *source_file, int source_line)
289 {
290   char *copy = xstrdup_real (s);
291   ++malloc_count;
292   register_ptr (copy, source_file, source_line);
293   return copy;
294 }
295
296 void
297 xfree_debug (void *ptr, const char *source_file, int source_line)
298 {
299   assert (ptr != NULL);
300   ++free_count;
301   unregister_ptr (ptr);
302   free (ptr);
303 }
304
305 #endif /* DEBUG_MALLOC */