2003-10-31 09:31:56 -05:00
|
|
|
|
/* Wrappers around malloc and memory debugging support.
|
|
|
|
|
Copyright (C) 2003 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
This file is part of GNU Wget.
|
|
|
|
|
|
|
|
|
|
GNU Wget is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
GNU Wget is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with Wget; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
|
|
|
|
|
In addition, as a special exception, the Free Software Foundation
|
|
|
|
|
gives permission to link the code of its release of Wget with the
|
|
|
|
|
OpenSSL project's "OpenSSL" library (or with modified versions of it
|
|
|
|
|
that use the same license as the "OpenSSL" library), and distribute
|
|
|
|
|
the linked executables. You must obey the GNU General Public License
|
|
|
|
|
in all respects for all of the code used other than "OpenSSL". If you
|
|
|
|
|
modify this file, you may extend this exception to your version of the
|
|
|
|
|
file, but you are not obligated to do so. If you do not wish to do
|
|
|
|
|
so, delete this exception statement from your version. */
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
|
# include <string.h>
|
|
|
|
|
#else /* not HAVE_STRING_H */
|
|
|
|
|
# include <strings.h>
|
|
|
|
|
#endif /* not HAVE_STRING_H */
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
|
|
#include "wget.h"
|
|
|
|
|
#include "xmalloc.h"
|
|
|
|
|
|
|
|
|
|
#ifndef errno
|
|
|
|
|
extern int errno;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* This file implements several wrappers around the basic allocation
|
|
|
|
|
routines. This is done for two reasons: first, so that the callers
|
|
|
|
|
of these functions need not check for errors, which is easy to
|
|
|
|
|
forget. If there is not enough virtual memory for running Wget,
|
|
|
|
|
something is seriously wrong, and Wget exits with an appropriate
|
|
|
|
|
error message.
|
|
|
|
|
|
|
|
|
|
The second reason why these are useful is that, if DEBUG_MALLOC is
|
|
|
|
|
defined, they also provide a handy (if crude) malloc debugging
|
2003-10-31 21:51:47 -05:00
|
|
|
|
interface that checks for memory leaks. */
|
2003-10-31 09:31:56 -05:00
|
|
|
|
|
|
|
|
|
/* Croak the fatal memory error and bail out with non-zero exit
|
|
|
|
|
status. */
|
|
|
|
|
|
|
|
|
|
static void
|
2003-10-31 21:51:47 -05:00
|
|
|
|
memfatal (const char *context, long attempted_size)
|
2003-10-31 09:31:56 -05:00
|
|
|
|
{
|
|
|
|
|
/* Make sure we don't try to store part of the log line, and thus
|
|
|
|
|
call malloc. */
|
|
|
|
|
log_set_save_context (0);
|
2003-10-31 21:51:47 -05:00
|
|
|
|
logprintf (LOG_ALWAYS,
|
|
|
|
|
_("%s: %s: Failed to allocate %ld bytes; memory exhausted.\n"),
|
|
|
|
|
exec_name, context, attempted_size);
|
2003-10-31 09:31:56 -05:00
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* These functions end with _real because they need to be
|
|
|
|
|
distinguished from the debugging functions, and from the macros.
|
|
|
|
|
Explanation follows:
|
|
|
|
|
|
2003-10-31 21:51:47 -05:00
|
|
|
|
If memory debugging is not turned on, xmalloc.h defines these:
|
2003-10-31 09:31:56 -05:00
|
|
|
|
|
|
|
|
|
#define xmalloc xmalloc_real
|
|
|
|
|
#define xmalloc0 xmalloc0_real
|
|
|
|
|
#define xrealloc xrealloc_real
|
|
|
|
|
#define xstrdup xstrdup_real
|
2003-11-02 16:12:49 -05:00
|
|
|
|
#define xfree xfree_real
|
2003-10-31 09:31:56 -05:00
|
|
|
|
|
|
|
|
|
In case of memory debugging, the definitions are a bit more
|
|
|
|
|
complex, because we want to provide more information, *and* we want
|
|
|
|
|
to call the debugging code. (The former is the reason why xmalloc
|
|
|
|
|
and friends need to be macros in the first place.) Then it looks
|
|
|
|
|
like this:
|
|
|
|
|
|
|
|
|
|
#define xmalloc(a) xmalloc_debug (a, __FILE__, __LINE__)
|
|
|
|
|
#define xmalloc0(a) xmalloc0_debug (a, __FILE__, __LINE__)
|
|
|
|
|
#define xrealloc(a, b) xrealloc_debug (a, b, __FILE__, __LINE__)
|
|
|
|
|
#define xstrdup(a) xstrdup_debug (a, __FILE__, __LINE__)
|
2003-11-02 16:12:49 -05:00
|
|
|
|
#define xfree(a) xfree_debug (a, __FILE__, __LINE__)
|
2003-10-31 09:31:56 -05:00
|
|
|
|
|
|
|
|
|
Each of the *_debug function does its magic and calls the real one. */
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_MALLOC
|
|
|
|
|
# define STATIC_IF_DEBUG static
|
|
|
|
|
#else
|
|
|
|
|
# define STATIC_IF_DEBUG
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
STATIC_IF_DEBUG void *
|
|
|
|
|
xmalloc_real (size_t size)
|
|
|
|
|
{
|
|
|
|
|
void *ptr = malloc (size);
|
|
|
|
|
if (!ptr)
|
|
|
|
|
memfatal ("malloc", size);
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
STATIC_IF_DEBUG void *
|
|
|
|
|
xmalloc0_real (size_t size)
|
|
|
|
|
{
|
|
|
|
|
/* Using calloc can be faster than malloc+memset because some calloc
|
|
|
|
|
implementations know when they're dealing with zeroed-out memory
|
|
|
|
|
from the system and can avoid unnecessary memset. */
|
|
|
|
|
void *ptr = calloc (1, size);
|
|
|
|
|
if (!ptr)
|
|
|
|
|
memfatal ("calloc", size);
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
STATIC_IF_DEBUG void *
|
|
|
|
|
xrealloc_real (void *ptr, size_t newsize)
|
|
|
|
|
{
|
|
|
|
|
void *newptr;
|
|
|
|
|
|
|
|
|
|
/* Not all Un*xes have the feature of realloc() that calling it with
|
|
|
|
|
a NULL-pointer is the same as malloc(), but it is easy to
|
|
|
|
|
simulate. */
|
|
|
|
|
if (ptr)
|
|
|
|
|
newptr = realloc (ptr, newsize);
|
|
|
|
|
else
|
|
|
|
|
newptr = malloc (newsize);
|
|
|
|
|
if (!newptr)
|
|
|
|
|
memfatal ("realloc", newsize);
|
|
|
|
|
return newptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
STATIC_IF_DEBUG char *
|
|
|
|
|
xstrdup_real (const char *s)
|
|
|
|
|
{
|
|
|
|
|
char *copy;
|
|
|
|
|
|
|
|
|
|
#ifndef HAVE_STRDUP
|
|
|
|
|
int l = strlen (s);
|
|
|
|
|
copy = malloc (l + 1);
|
|
|
|
|
if (!copy)
|
|
|
|
|
memfatal ("strdup", l + 1);
|
|
|
|
|
memcpy (copy, s, l + 1);
|
|
|
|
|
#else /* HAVE_STRDUP */
|
|
|
|
|
copy = strdup (s);
|
|
|
|
|
if (!copy)
|
|
|
|
|
memfatal ("strdup", 1 + strlen (s));
|
|
|
|
|
#endif /* HAVE_STRDUP */
|
|
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
|
2003-11-02 16:12:49 -05:00
|
|
|
|
STATIC_IF_DEBUG void
|
|
|
|
|
xfree_real (void *ptr)
|
|
|
|
|
{
|
|
|
|
|
/* Wget's xfree() must not be passed a NULL pointer. This is for
|
|
|
|
|
historical reasons: many pre-C89 systems were known to bomb at
|
|
|
|
|
free(NULL), and Wget was careful to use xfree_null when there is
|
|
|
|
|
a possibility of PTR being NULL. (It might have been better to
|
|
|
|
|
simply have xfree() do nothing if ptr==NULL.)
|
|
|
|
|
|
|
|
|
|
Since the code is already written that way, this assert simply
|
|
|
|
|
enforces that constraint. Code that thinks it doesn't deal with
|
|
|
|
|
NULL, and it in fact does, aborts immediately. If you trip on
|
|
|
|
|
this, either the code has a pointer handling bug or should have
|
|
|
|
|
called xfree_null instead of xfree. Correctly written code
|
|
|
|
|
should never trigger this assertion.
|
|
|
|
|
|
|
|
|
|
If the assertion proves to be too much of a hassle, it can be
|
|
|
|
|
removed and a check that makes NULL a no-op placed in its stead.
|
|
|
|
|
If that is done, xfree_null is no longer needed and should be
|
|
|
|
|
removed. */
|
|
|
|
|
assert (ptr != NULL);
|
|
|
|
|
free (ptr);
|
|
|
|
|
}
|
|
|
|
|
|
2003-10-31 21:51:47 -05:00
|
|
|
|
/* xfree_real is unnecessary because free doesn't require any special
|
|
|
|
|
functionality. */
|
|
|
|
|
|
2003-10-31 09:31:56 -05:00
|
|
|
|
#ifdef DEBUG_MALLOC
|
|
|
|
|
|
|
|
|
|
/* Crude home-grown routines for debugging some malloc-related
|
|
|
|
|
problems. Featured:
|
|
|
|
|
|
|
|
|
|
* Counting the number of malloc and free invocations, and reporting
|
|
|
|
|
the "balance", i.e. how many times more malloc was called than it
|
|
|
|
|
was the case with free.
|
|
|
|
|
|
|
|
|
|
* Making malloc store its entry into a simple array and free remove
|
|
|
|
|
stuff from that array. At the end, print the pointers which have
|
|
|
|
|
not been freed, along with the source file and the line number.
|
|
|
|
|
This also has the side-effect of detecting freeing memory that
|
|
|
|
|
was never allocated.
|
|
|
|
|
|
|
|
|
|
Note that this kind of memory leak checking strongly depends on
|
|
|
|
|
every malloc() being followed by a free(), even if the program is
|
|
|
|
|
about to finish. Wget is careful to free the data structure it
|
|
|
|
|
allocated in init.c. */
|
|
|
|
|
|
|
|
|
|
static int malloc_count, free_count;
|
|
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
|
char *ptr;
|
|
|
|
|
const char *file;
|
|
|
|
|
int line;
|
|
|
|
|
} malloc_debug[100000];
|
|
|
|
|
|
|
|
|
|
/* Both register_ptr and unregister_ptr take O(n) operations to run,
|
|
|
|
|
which can be a real problem. It would be nice to use a hash table
|
|
|
|
|
for malloc_debug, but the functions in hash.c are not suitable
|
|
|
|
|
because they can call malloc() themselves. Maybe it would work if
|
|
|
|
|
the hash table were preallocated to a huge size, and if we set the
|
|
|
|
|
rehash threshold to 1.0. */
|
|
|
|
|
|
|
|
|
|
/* Register PTR in malloc_debug. Abort if this is not possible
|
|
|
|
|
(presumably due to the number of current allocations exceeding the
|
|
|
|
|
size of malloc_debug.) */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
register_ptr (void *ptr, const char *file, int line)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < countof (malloc_debug); i++)
|
|
|
|
|
if (malloc_debug[i].ptr == NULL)
|
|
|
|
|
{
|
|
|
|
|
malloc_debug[i].ptr = ptr;
|
|
|
|
|
malloc_debug[i].file = file;
|
|
|
|
|
malloc_debug[i].line = line;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Unregister PTR from malloc_debug. Abort if PTR is not present in
|
|
|
|
|
malloc_debug. (This catches calling free() with a bogus pointer.) */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
unregister_ptr (void *ptr)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < countof (malloc_debug); i++)
|
|
|
|
|
if (malloc_debug[i].ptr == ptr)
|
|
|
|
|
{
|
|
|
|
|
malloc_debug[i].ptr = NULL;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print the malloc debug stats that can be gathered from the above
|
|
|
|
|
information. Currently this is the count of mallocs, frees, the
|
|
|
|
|
difference between the two, and the dump of the contents of
|
|
|
|
|
malloc_debug. The last part are the memory leaks. */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
print_malloc_debug_stats (void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
printf ("\nMalloc: %d\nFree: %d\nBalance: %d\n\n",
|
|
|
|
|
malloc_count, free_count, malloc_count - free_count);
|
|
|
|
|
for (i = 0; i < countof (malloc_debug); i++)
|
|
|
|
|
if (malloc_debug[i].ptr != NULL)
|
|
|
|
|
printf ("0x%08ld: %s:%d\n", (long)malloc_debug[i].ptr,
|
|
|
|
|
malloc_debug[i].file, malloc_debug[i].line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
xmalloc_debug (size_t size, const char *source_file, int source_line)
|
|
|
|
|
{
|
|
|
|
|
void *ptr = xmalloc_real (size);
|
|
|
|
|
++malloc_count;
|
|
|
|
|
register_ptr (ptr, source_file, source_line);
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
xmalloc0_debug (size_t size, const char *source_file, int source_line)
|
|
|
|
|
{
|
|
|
|
|
void *ptr = xmalloc0_real (size);
|
|
|
|
|
++malloc_count;
|
|
|
|
|
register_ptr (ptr, source_file, source_line);
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
xrealloc_debug (void *ptr, size_t newsize, const char *source_file, int source_line)
|
|
|
|
|
{
|
|
|
|
|
void *newptr = xrealloc_real (ptr, newsize);
|
|
|
|
|
if (!ptr)
|
|
|
|
|
{
|
|
|
|
|
++malloc_count;
|
|
|
|
|
register_ptr (newptr, source_file, source_line);
|
|
|
|
|
}
|
|
|
|
|
else if (newptr != ptr)
|
|
|
|
|
{
|
|
|
|
|
unregister_ptr (ptr);
|
|
|
|
|
register_ptr (newptr, source_file, source_line);
|
|
|
|
|
}
|
|
|
|
|
return newptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
xstrdup_debug (const char *s, const char *source_file, int source_line)
|
|
|
|
|
{
|
|
|
|
|
char *copy = xstrdup_real (s);
|
|
|
|
|
++malloc_count;
|
|
|
|
|
register_ptr (copy, source_file, source_line);
|
|
|
|
|
return copy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
xfree_debug (void *ptr, const char *source_file, int source_line)
|
|
|
|
|
{
|
2003-11-02 16:12:49 -05:00
|
|
|
|
/* See xfree_real for rationale of this abort. We repeat it here
|
|
|
|
|
because we can print the file and the line where the offending
|
|
|
|
|
free occurred. */
|
|
|
|
|
if (ptr == NULL)
|
|
|
|
|
{
|
|
|
|
|
fprintf ("%s: xfree(NULL) at %s:%d\n",
|
|
|
|
|
exec_name, source_file, source_line);
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
2003-10-31 09:31:56 -05:00
|
|
|
|
++free_count;
|
|
|
|
|
unregister_ptr (ptr);
|
2003-11-02 16:12:49 -05:00
|
|
|
|
xfree_real (ptr);
|
2003-10-31 09:31:56 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* DEBUG_MALLOC */
|