mirror of
https://github.com/moparisthebest/wget
synced 2024-07-03 16:38:41 -04:00
[svn] Committed memory debugging stuff.
Published in <sxs1yw34pt4.fsf@florida.arsdigita.de>.
This commit is contained in:
parent
d8c9ce30aa
commit
1cddc05edb
@ -1,3 +1,12 @@
|
||||
2000-11-22 Hrvoje Niksic <hniksic@arsdigita.com>
|
||||
|
||||
* ftp.c (getftp): ftp_getaddress() returns a malloc'ed copy of the
|
||||
string; no need to strdup() it.
|
||||
(getftp): Make pwd_len a local variable.
|
||||
(ftp_loop): Free PWD before returning.
|
||||
|
||||
* init.c (cleanup): Free opt.ftp_pass only if it's non-NULL.
|
||||
|
||||
2000-11-22 Hrvoje Niksic <hniksic@arsdigita.com>
|
||||
|
||||
* all: Use xfree() instead of free.
|
||||
|
10
src/ftp.c
10
src/ftp.c
@ -57,9 +57,11 @@ extern int h_errno;
|
||||
|
||||
extern char ftp_last_respline[];
|
||||
|
||||
/* #### Global variables?? These two should be members of struct
|
||||
ccon! */
|
||||
|
||||
static enum stype host_type=ST_UNIX;
|
||||
static char *pwd;
|
||||
static int pwd_len;
|
||||
|
||||
/* Look for regexp "( *[0-9]+ *byte" (literal parenthesis) anywhere in
|
||||
the string S, and return the number converted to long, if found, 0
|
||||
@ -133,7 +135,7 @@ getftp (const struct urlinfo *u, long *len, long restval, ccon *con)
|
||||
search_netrc (u->host, (const char **)&user, (const char **)&passwd, 1);
|
||||
user = user ? user : opt.ftp_acc;
|
||||
if (!opt.ftp_pass)
|
||||
opt.ftp_pass = xstrdup (ftp_getaddress ());
|
||||
opt.ftp_pass = ftp_getaddress ();
|
||||
passwd = passwd ? passwd : opt.ftp_pass;
|
||||
assert (user && passwd);
|
||||
|
||||
@ -279,7 +281,6 @@ Error in server response, closing control connection.\n"));
|
||||
if (!opt.server_response)
|
||||
logprintf (LOG_VERBOSE, "==> PWD ... ");
|
||||
err = ftp_pwd(&con->rbuf, &pwd);
|
||||
pwd_len = strlen(pwd);
|
||||
/* FTPRERR */
|
||||
switch (err)
|
||||
{
|
||||
@ -354,6 +355,7 @@ Error in server response, closing control connection.\n"));
|
||||
it to VMS style as VMS does not like leading slashes */
|
||||
if (*(u->dir) == '/')
|
||||
{
|
||||
int pwd_len = strlen (pwd);
|
||||
char *result = (char *)alloca (strlen (u->dir) + pwd_len + 10);
|
||||
*result = '\0';
|
||||
switch (host_type)
|
||||
@ -1634,6 +1636,8 @@ ftp_loop (struct urlinfo *u, int *dt)
|
||||
/* If a connection was left, quench it. */
|
||||
if (rbuf_initialized_p (&con.rbuf))
|
||||
CLOSE (RBUF_FD (&con.rbuf));
|
||||
FREE_MAYBE (pwd);
|
||||
pwd = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -372,6 +372,9 @@ sufmatch (const char **list, const char *what)
|
||||
function returns a short `username@' form, accepted by most
|
||||
anonymous servers.
|
||||
|
||||
The returned string is generated by malloc() and should be freed
|
||||
using free().
|
||||
|
||||
If not even the username cannot be divined, it means things are
|
||||
seriously fucked up, and Wget exits. */
|
||||
char *
|
||||
|
@ -566,3 +566,10 @@ get_urls_html (const char *file, const char *this_url, int dash_p_leaf_HTML,
|
||||
read_file_free (fm);
|
||||
return closure.head;
|
||||
}
|
||||
|
||||
void
|
||||
cleanup_html_url (void)
|
||||
{
|
||||
FREE_MAYBE (interesting_tags);
|
||||
FREE_MAYBE (interesting_attributes);
|
||||
}
|
||||
|
@ -981,6 +981,10 @@ check_user_specified_header (const char *s)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cleanup_html_url PARAMS ((void));
|
||||
void downloaded_files_free PARAMS ((void));
|
||||
|
||||
|
||||
/* Free the memory allocated by global variables. */
|
||||
void
|
||||
cleanup (void)
|
||||
@ -992,6 +996,8 @@ cleanup (void)
|
||||
free_netrc (netrc_list);
|
||||
if (opt.dfp)
|
||||
fclose (opt.dfp);
|
||||
cleanup_html_url ();
|
||||
downloaded_files_free ();
|
||||
FREE_MAYBE (opt.lfilename);
|
||||
xfree (opt.dir_prefix);
|
||||
FREE_MAYBE (opt.input_filename);
|
||||
@ -1004,7 +1010,7 @@ cleanup (void)
|
||||
free_vec (opt.follow_tags);
|
||||
free_vec (opt.ignore_tags);
|
||||
xfree (opt.ftp_acc);
|
||||
xfree (opt.ftp_pass);
|
||||
FREE_MAYBE (opt.ftp_pass);
|
||||
FREE_MAYBE (opt.ftp_proxy);
|
||||
FREE_MAYBE (opt.http_proxy);
|
||||
free_vec (opt.no_proxy);
|
||||
|
@ -809,6 +809,9 @@ Can't timestamp and not clobber old files at the same time.\n"));
|
||||
}
|
||||
log_close ();
|
||||
cleanup ();
|
||||
#ifdef DEBUG_MALLOC
|
||||
print_malloc_debug_stats ();
|
||||
#endif
|
||||
if (status == RETROK)
|
||||
return 0;
|
||||
else
|
||||
|
29
src/url.c
29
src/url.c
@ -1577,6 +1577,14 @@ write_backup_file (const char *file, downloaded_file_t downloaded_file_return)
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct _downloaded_file_list {
|
||||
char* file;
|
||||
downloaded_file_t download_type;
|
||||
struct _downloaded_file_list* next;
|
||||
} downloaded_file_list;
|
||||
|
||||
static downloaded_file_list *downloaded_files;
|
||||
|
||||
/* Remembers which files have been downloaded. In the standard case, should be
|
||||
called with mode == FILE_DOWNLOADED_NORMALLY for each file we actually
|
||||
download successfully (i.e. not for ones we have failures on or that we skip
|
||||
@ -1592,15 +1600,7 @@ write_backup_file (const char *file, downloaded_file_t downloaded_file_return)
|
||||
downloaded_file_t
|
||||
downloaded_file (downloaded_file_t mode, const char* file)
|
||||
{
|
||||
typedef struct _downloaded_file_list
|
||||
{
|
||||
char* file;
|
||||
downloaded_file_t download_type;
|
||||
struct _downloaded_file_list* next;
|
||||
} downloaded_file_list;
|
||||
|
||||
boolean found_file = FALSE;
|
||||
static downloaded_file_list* downloaded_files = NULL;
|
||||
downloaded_file_list* rover = downloaded_files;
|
||||
|
||||
while (rover != NULL)
|
||||
@ -1628,6 +1628,19 @@ downloaded_file (downloaded_file_t mode, const char* file)
|
||||
return FILE_NOT_ALREADY_DOWNLOADED;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
downloaded_files_free (void)
|
||||
{
|
||||
downloaded_file_list* rover = downloaded_files;
|
||||
while (rover)
|
||||
{
|
||||
downloaded_file_list *next = rover->next;
|
||||
xfree (rover->file);
|
||||
xfree (rover);
|
||||
rover = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialization of static stuff. */
|
||||
void
|
||||
|
227
src/utils.c
227
src/utils.c
@ -60,80 +60,249 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
/* This section implements several wrappers around the basic
|
||||
allocation routines. This is done for two reasons: first, so that
|
||||
the callers of these functions need not consistently check for
|
||||
errors. 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
|
||||
interface that checks memory leaks. */
|
||||
|
||||
/* Croak the fatal memory error and bail out with non-zero exit
|
||||
status. */
|
||||
static void
|
||||
memfatal (const char *s)
|
||||
memfatal (const char *what)
|
||||
{
|
||||
/* HACK: expose save_log_p from log.c, so we can turn it off in
|
||||
order to prevent saving the log. Saving the log is dangerous
|
||||
because logprintf() and logputs() can call malloc(), so this
|
||||
could infloop. When logging is turned off, infloop can no longer
|
||||
happen. */
|
||||
happen.
|
||||
|
||||
#### This is no longer really necessary because the new routines
|
||||
in log.c cons only if the line exceeds eighty characters. But
|
||||
this can come at the end of a line, so it's OK to be careful.
|
||||
|
||||
On a more serious note, it would be good to have a
|
||||
log_forced_shutdown() routine that exposes this cleanly. */
|
||||
extern int save_log_p;
|
||||
|
||||
save_log_p = 0;
|
||||
logprintf (LOG_ALWAYS, _("%s: %s: Not enough memory.\n"), exec_name, s);
|
||||
logprintf (LOG_ALWAYS, _("%s: %s: Not enough memory.\n"), exec_name, what);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* xmalloc, xrealloc and xstrdup exit the program if there is not
|
||||
enough memory. xstrdup also implements strdup on systems that do
|
||||
not have it. xfree is provided to make leak-tracking easier.
|
||||
Currently it's a no-op. */
|
||||
/* These functions end with _real because they need to be
|
||||
distinguished from the debugging functions, and from the macros.
|
||||
Explanation follows:
|
||||
|
||||
If memory debugging is not turned on, wget.h defines these:
|
||||
|
||||
#define xmalloc xmalloc_real
|
||||
#define xfree xfree_real
|
||||
#define xrealloc xrealloc_real
|
||||
#define xstrdup xstrdup_real
|
||||
|
||||
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 xfree(a) xfree_debug (a, __FILE__, __LINE__)
|
||||
#define xrealloc(a, b) xrealloc_debug (a, b, __FILE__, __LINE__)
|
||||
#define xstrdup(a) xstrdup_debug (a, __FILE__, __LINE__)
|
||||
|
||||
Each of the *_debug function does its magic and calls the real one. */
|
||||
|
||||
void *
|
||||
xmalloc (size_t size)
|
||||
xmalloc_real (size_t size)
|
||||
{
|
||||
void *res;
|
||||
|
||||
res = malloc (size);
|
||||
if (!res)
|
||||
void *ptr = malloc (size);
|
||||
if (!ptr)
|
||||
memfatal ("malloc");
|
||||
return res;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void
|
||||
xfree (void *ptr)
|
||||
xfree_real (void *ptr)
|
||||
{
|
||||
free (ptr);
|
||||
}
|
||||
|
||||
void *
|
||||
xrealloc (void *obj, size_t size)
|
||||
xrealloc_real (void *ptr, size_t newsize)
|
||||
{
|
||||
void *res;
|
||||
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 (obj)
|
||||
res = realloc (obj, size);
|
||||
if (ptr)
|
||||
newptr = realloc (ptr, newsize);
|
||||
else
|
||||
res = malloc (size);
|
||||
if (!res)
|
||||
newptr = malloc (newsize);
|
||||
if (!newptr)
|
||||
memfatal ("realloc");
|
||||
return res;
|
||||
return newptr;
|
||||
}
|
||||
|
||||
char *
|
||||
xstrdup (const char *s)
|
||||
xstrdup_real (const char *s)
|
||||
{
|
||||
char *copy;
|
||||
|
||||
#ifndef HAVE_STRDUP
|
||||
int l = strlen (s);
|
||||
char *s1 = malloc (l + 1);
|
||||
if (!s1)
|
||||
copy = malloc (l + 1);
|
||||
if (!copy)
|
||||
memfatal ("strdup");
|
||||
memcpy (s1, s, l + 1);
|
||||
return s1;
|
||||
memcpy (copy, s, l + 1);
|
||||
#else /* HAVE_STRDUP */
|
||||
char *s1 = strdup (s);
|
||||
if (!s1)
|
||||
copy = strdup (s);
|
||||
if (!copy)
|
||||
memfatal ("strdup");
|
||||
return s1;
|
||||
#endif /* HAVE_STRDUP */
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
#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 < ARRAY_SIZE (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 < ARRAY_SIZE (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 < ARRAY_SIZE (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
|
||||
xfree_debug (void *ptr, const char *source_file, int source_line)
|
||||
{
|
||||
assert (ptr != NULL);
|
||||
++free_count;
|
||||
unregister_ptr (ptr);
|
||||
xfree_real (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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#endif /* DEBUG_MALLOC */
|
||||
|
||||
/* Copy the string formed by two pointers (one on the beginning, other
|
||||
on the char after the last char) to a new, malloc-ed location.
|
||||
|
@ -42,6 +42,10 @@ struct file_memory {
|
||||
char *time_str PARAMS ((time_t *));
|
||||
const char *uerrmsg PARAMS ((uerr_t));
|
||||
|
||||
#ifdef DEBUG_MALLOC
|
||||
void print_malloc_debug_stats ();
|
||||
#endif
|
||||
|
||||
char *strdupdelim PARAMS ((const char *, const char *));
|
||||
char **sepstring PARAMS ((const char *));
|
||||
int frontcmp PARAMS ((const char *, const char *));
|
||||
|
29
src/wget.h
29
src/wget.h
@ -98,10 +98,31 @@ void debug_logprintf ();
|
||||
void logputs PARAMS ((enum log_options, const char *));
|
||||
|
||||
/* Defined in `utils.c', but used literally everywhere. */
|
||||
void *xmalloc PARAMS ((size_t));
|
||||
void xfree PARAMS ((void *));
|
||||
void *xrealloc PARAMS ((void *, size_t));
|
||||
char *xstrdup PARAMS ((const char *));
|
||||
#ifndef DEBUG_MALLOC
|
||||
|
||||
#define xmalloc xmalloc_real
|
||||
#define xfree xfree_real
|
||||
#define xrealloc xrealloc_real
|
||||
#define xstrdup xstrdup_real
|
||||
|
||||
void *xmalloc_real PARAMS ((size_t));
|
||||
void xfree_real PARAMS ((void *));
|
||||
void *xrealloc_real PARAMS ((void *, size_t));
|
||||
char *xstrdup_real PARAMS ((const char *));
|
||||
|
||||
#else /* DEBUG_MALLOC */
|
||||
|
||||
#define xmalloc(s) xmalloc_debug (s, __FILE__, __LINE__)
|
||||
#define xfree(p) xfree_debug (p, __FILE__, __LINE__)
|
||||
#define xrealloc(p, s) xrealloc_debug (p, s, __FILE__, __LINE__)
|
||||
#define xstrdup(p) xstrdup_debug (p, __FILE__, __LINE__)
|
||||
|
||||
void *xmalloc_debug PARAMS ((size_t, const char *, int));
|
||||
void xfree_debug PARAMS ((void *, const char *, int));
|
||||
void *xrealloc_debug PARAMS ((void *, size_t, const char *, int));
|
||||
char *xstrdup_debug PARAMS ((const char *, const char *, int));
|
||||
|
||||
#endif /* DEBUG_MALLOC */
|
||||
|
||||
/* #### Find a better place for this. */
|
||||
/* The log file to which Wget writes to after HUP. */
|
||||
|
Loading…
Reference in New Issue
Block a user