source: trunk/essentials/net-misc/wget/src/xmalloc.c

Last change on this file was 3440, checked in by bird, 18 years ago

wget 1.10.2

File size: 11.1 KB
Line 
1/* Wrappers around malloc and memory debugging support.
2 Copyright (C) 2005 Free Software Foundation, Inc.
3
4This file is part of GNU Wget.
5
6GNU Wget is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11GNU Wget is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with Wget; if not, write to the Free Software
18Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20In addition, as a special exception, the Free Software Foundation
21gives permission to link the code of its release of Wget with the
22OpenSSL project's "OpenSSL" library (or with modified versions of it
23that use the same license as the "OpenSSL" library), and distribute
24the linked executables. You must obey the GNU General Public License
25in all respects for all of the code used other than "OpenSSL". If you
26modify this file, you may extend this exception to your version of the
27file, but you are not obligated to do so. If you do not wish to do
28so, 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
48extern 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
65static void
66memfatal (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
110STATIC_IF_DEBUG void *
111checking_malloc (size_t size)
112{
113 void *ptr = malloc (size);
114 if (!ptr)
115 memfatal ("malloc", size);
116 return ptr;
117}
118
119STATIC_IF_DEBUG void *
120checking_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
131STATIC_IF_DEBUG void *
132checking_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
148STATIC_IF_DEBUG char *
149checking_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
168STATIC_IF_DEBUG void
169checking_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
195
196#ifdef DEBUG_MALLOC
197
198/* Crude home-grown routines for debugging some malloc-related
199 problems. Featured:
200
201 * Counting the number of malloc and free invocations, and reporting
202 the "balance", i.e. how many times more malloc was called than it
203 was the case with free.
204
205 * Making malloc store its entry into a simple array and free remove
206 stuff from that array. At the end, print the pointers which have
207 not been freed, along with the source file and the line number.
208
209 * Checking for "invalid frees", where free is called on a pointer
210 not obtained with malloc, or where the same pointer is freed
211 twice.
212
213 Note that this kind of memory leak checking strongly depends on
214 every malloc() being followed by a free(), even if the program is
215 about to finish. Wget is careful to free the data structure it
216 allocated in init.c. */
217
218static int malloc_count, free_count;
219
220/* Home-grown hash table of mallocs: */
221
222#define SZ 100003 /* Prime just over 100,000. Increase
223 it to debug larger Wget runs. */
224
225static struct {
226 const void *ptr;
227 const char *file;
228 int line;
229} malloc_table[SZ];
230
231/* Find PTR's position in malloc_table. If PTR is not found, return
232 the next available position. */
233
234static inline int
235ptr_position (const void *ptr)
236{
237 int i = hash_pointer (ptr) % SZ;
238 for (; malloc_table[i].ptr != NULL; i = (i + 1) % SZ)
239 if (malloc_table[i].ptr == ptr)
240 return i;
241 return i;
242}
243
244/* Register PTR in malloc_table. Abort if this is not possible
245 (presumably due to the number of current allocations exceeding the
246 size of malloc_table.) */
247
248static void
249register_ptr (const void *ptr, const char *file, int line)
250{
251 int i;
252 if (malloc_count - free_count > SZ)
253 {
254 fprintf (stderr, "Increase SZ to a larger value and recompile.\n");
255 fflush (stderr);
256 abort ();
257 }
258
259 i = ptr_position (ptr);
260 malloc_table[i].ptr = ptr;
261 malloc_table[i].file = file;
262 malloc_table[i].line = line;
263}
264
265/* Unregister PTR from malloc_table. Return 0 if PTR is not present
266 in malloc_table. */
267
268static int
269unregister_ptr (void *ptr)
270{
271 int i = ptr_position (ptr);
272 if (malloc_table[i].ptr == NULL)
273 return 0;
274 malloc_table[i].ptr = NULL;
275
276 /* Relocate malloc_table entries immediately following PTR. */
277 for (i = (i + 1) % SZ; malloc_table[i].ptr != NULL; i = (i + 1) % SZ)
278 {
279 const void *ptr2 = malloc_table[i].ptr;
280 /* Find the new location for the key. */
281 int j = hash_pointer (ptr2) % SZ;
282 for (; malloc_table[j].ptr != NULL; j = (j + 1) % SZ)
283 if (ptr2 == malloc_table[j].ptr)
284 /* No need to relocate entry at [i]; it's already at or near
285 its hash position. */
286 goto cont_outer;
287 malloc_table[j] = malloc_table[i];
288 malloc_table[i].ptr = NULL;
289 cont_outer:
290 ;
291 }
292 return 1;
293}
294
295/* Print the malloc debug stats gathered from the above information.
296 Currently this is the count of mallocs, frees, the difference
297 between the two, and the dump of the contents of malloc_table. The
298 last part are the memory leaks. */
299
300void
301print_malloc_debug_stats (void)
302{
303 int i;
304 printf ("\nMalloc: %d\nFree: %d\nBalance: %d\n\n",
305 malloc_count, free_count, malloc_count - free_count);
306 for (i = 0; i < SZ; i++)
307 if (malloc_table[i].ptr != NULL)
308 printf ("0x%0*lx: %s:%d\n", PTR_FORMAT (malloc_table[i].ptr),
309 malloc_table[i].file, malloc_table[i].line);
310}
311
312void *
313debugging_malloc (size_t size, const char *source_file, int source_line)
314{
315 void *ptr = checking_malloc (size);
316 ++malloc_count;
317 register_ptr (ptr, source_file, source_line);
318 return ptr;
319}
320
321void *
322debugging_malloc0 (size_t size, const char *source_file, int source_line)
323{
324 void *ptr = checking_malloc0 (size);
325 ++malloc_count;
326 register_ptr (ptr, source_file, source_line);
327 return ptr;
328}
329
330void *
331debugging_realloc (void *ptr, size_t newsize, const char *source_file, int source_line)
332{
333 void *newptr = checking_realloc (ptr, newsize);
334 if (!ptr)
335 {
336 ++malloc_count;
337 register_ptr (newptr, source_file, source_line);
338 }
339 else if (newptr != ptr)
340 {
341 unregister_ptr (ptr);
342 register_ptr (newptr, source_file, source_line);
343 }
344 return newptr;
345}
346
347char *
348debugging_strdup (const char *s, const char *source_file, int source_line)
349{
350 char *copy = checking_strdup (s);
351 ++malloc_count;
352 register_ptr (copy, source_file, source_line);
353 return copy;
354}
355
356void
357debugging_free (void *ptr, const char *source_file, int source_line)
358{
359 /* See checking_free for rationale of this abort. We repeat it here
360 because we can print the file and the line where the offending
361 free occurred. */
362 if (ptr == NULL)
363 {
364 fprintf (stderr, "%s: xfree(NULL) at %s:%d\n",
365 exec_name, source_file, source_line);
366 abort ();
367 }
368 if (!unregister_ptr (ptr))
369 {
370 fprintf (stderr, "%s: bad xfree(0x%0*lx) at %s:%d\n",
371 exec_name, PTR_FORMAT (ptr), source_file, source_line);
372 abort ();
373 }
374 ++free_count;
375
376 checking_free (ptr);
377}
378
379#endif /* DEBUG_MALLOC */
Note: See TracBrowser for help on using the repository browser.