1
0
mirror of https://github.com/moparisthebest/wget synced 2024-07-03 16:38:41 -04:00

[svn] Improve built-in memory debugger.

This commit is contained in:
hniksic 2005-03-20 07:07:40 -08:00
parent 6da4142b3b
commit 7c044778bc
15 changed files with 321 additions and 196 deletions

View File

@ -1,3 +1,38 @@
2005-03-20 Hrvoje Niksic <hniksic@xemacs.org>
* url.c (unescape_single_char): New function.
(url_escape_dir): Use it to unescape slashes in directory
components.
(url_string): Escape unsafe chars in host name, except for the ':'
charaters, which can appear in IPv6 addresses.
* main.c (main): Don't access the cookie jar directly.
* log.c (escnonprint_internal): Correctly calculate the needed
string size. Don't forget the buffer's new size after having
reallocated it.
(log_cleanup): New function. Free the escnonprint ring data.
* init.c (cleanup): Don't free the cookie jar explicitly, it is
now done by http_cleanup.
(cleanup): opt.user_headers is now a vector, free it with
free_vec.
* http.c (gethttp): Make sure to free the request data, the status
message, and the response data before returning from the function.
(save_cookies): New function.
(http_cleanup): Free the cookie jar here.
* hash.c: Renamed string_hash to hash_string and ptrhash to
hash_pointer. Exported hash_pointer.
* xmalloc.c: Organized malloc_table (previously malloc_debug) as a
simple EQ hash table. register_ptr and unregister_ptr are now of
O(1) complexity.
* xmalloc.c: Renamed "*_debug" to debugging_* and "*_real" to
checking_*.
2005-03-12 Hrvoje Niksic <hniksic@xemacs.org> 2005-03-12 Hrvoje Niksic <hniksic@xemacs.org>
* utils.c (debug_test_md5): Moved to gen-md5.c. * utils.c (debug_test_md5): Moved to gen-md5.c.

View File

@ -96,5 +96,6 @@ void register_redirection PARAMS ((const char *, const char *));
void register_html PARAMS ((const char *, const char *)); void register_html PARAMS ((const char *, const char *));
void register_delete_file PARAMS ((const char *)); void register_delete_file PARAMS ((const char *));
void convert_all_links PARAMS ((void)); void convert_all_links PARAMS ((void));
void convert_cleanup PARAMS ((void));
#endif /* CONVERT_H */ #endif /* CONVERT_H */

View File

@ -244,8 +244,7 @@ prime_size (int size, int *prime_offset)
return 0; return 0;
} }
static unsigned long ptrhash PARAMS ((const void *)); static int cmp_pointer PARAMS ((const void *, const void *));
static int ptrcmp PARAMS ((const void *, const void *));
/* Create a hash table with hash function HASH_FUNCTION and test /* Create a hash table with hash function HASH_FUNCTION and test
function TEST_FUNCTION. The table is empty (its count is 0), but function TEST_FUNCTION. The table is empty (its count is 0), but
@ -279,8 +278,8 @@ hash_table_new (int items,
int size; int size;
struct hash_table *ht = xnew (struct hash_table); struct hash_table *ht = xnew (struct hash_table);
ht->hash_function = hash_function ? hash_function : ptrhash; ht->hash_function = hash_function ? hash_function : hash_pointer;
ht->test_function = test_function ? test_function : ptrcmp; ht->test_function = test_function ? test_function : cmp_pointer;
/* If the size of struct hash_table ever becomes a concern, this /* If the size of struct hash_table ever becomes a concern, this
field can go. (Wget doesn't create many hashes.) */ field can go. (Wget doesn't create many hashes.) */
@ -596,8 +595,8 @@ hash_table_count (const struct hash_table *ht)
We used to use the popular hash function from the Dragon Book, but We used to use the popular hash function from the Dragon Book, but
this one seems to perform much better. */ this one seems to perform much better. */
unsigned long static unsigned long
string_hash (const void *key) hash_string (const void *key)
{ {
const char *p = key; const char *p = key;
unsigned int h = *p; unsigned int h = *p;
@ -611,8 +610,8 @@ string_hash (const void *key)
/* Frontend for strcmp usable for hash tables. */ /* Frontend for strcmp usable for hash tables. */
int static int
string_cmp (const void *s1, const void *s2) cmp_string (const void *s1, const void *s2)
{ {
return !strcmp ((const char *)s1, (const char *)s2); return !strcmp ((const char *)s1, (const char *)s2);
} }
@ -623,7 +622,7 @@ string_cmp (const void *s1, const void *s2)
struct hash_table * struct hash_table *
make_string_hash_table (int items) make_string_hash_table (int items)
{ {
return hash_table_new (items, string_hash, string_cmp); return hash_table_new (items, hash_string, cmp_string);
} }
/* /*
@ -632,10 +631,10 @@ make_string_hash_table (int items)
* *
*/ */
/* Like string_hash, but produce the same hash regardless of the case. */ /* Like hash_string, but produce the same hash regardless of the case. */
static unsigned long static unsigned long
string_hash_nocase (const void *key) hash_string_nocase (const void *key)
{ {
const char *p = key; const char *p = key;
unsigned int h = TOLOWER (*p); unsigned int h = TOLOWER (*p);
@ -661,7 +660,7 @@ string_cmp_nocase (const void *s1, const void *s2)
struct hash_table * struct hash_table *
make_nocase_string_hash_table (int items) make_nocase_string_hash_table (int items)
{ {
return hash_table_new (items, string_hash_nocase, string_cmp_nocase); return hash_table_new (items, hash_string_nocase, string_cmp_nocase);
} }
/* Hashing of numeric values, such as pointers and integers. /* Hashing of numeric values, such as pointers and integers.
@ -671,8 +670,8 @@ make_nocase_string_hash_table (int items)
spreading of values and doesn't need to know the hash table size to spreading of values and doesn't need to know the hash table size to
work (unlike the very popular Knuth's multiplication hash). */ work (unlike the very popular Knuth's multiplication hash). */
static unsigned long unsigned long
ptrhash (const void *ptr) hash_pointer (const void *ptr)
{ {
unsigned long key = (unsigned long)ptr; unsigned long key = (unsigned long)ptr;
key += (key << 12); key += (key << 12);
@ -697,7 +696,7 @@ ptrhash (const void *ptr)
} }
static int static int
ptrcmp (const void *ptr1, const void *ptr2) cmp_pointer (const void *ptr1, const void *ptr2)
{ {
return ptr1 == ptr2; return ptr1 == ptr2;
} }

View File

@ -30,18 +30,6 @@ so, delete this exception statement from your version. */
#ifndef HASH_H #ifndef HASH_H
#define HASH_H #define HASH_H
/* From XEmacs, and hence from Dragon book. */
#define GOOD_HASH 65599 /* prime number just over 2^16; Dragon book, p. 435 */
#define HASH2(a,b) (GOOD_HASH * (a) + (b))
#define HASH3(a,b,c) (GOOD_HASH * HASH2 (a,b) + (c))
#define HASH4(a,b,c,d) (GOOD_HASH * HASH3 (a,b,c) + (d))
#define HASH5(a,b,c,d,e) (GOOD_HASH * HASH4 (a,b,c,d) + (e))
#define HASH6(a,b,c,d,e,f) (GOOD_HASH * HASH5 (a,b,c,d,e) + (f))
#define HASH7(a,b,c,d,e,f,g) (GOOD_HASH * HASH6 (a,b,c,d,e,f) + (g))
#define HASH8(a,b,c,d,e,f,g,h) (GOOD_HASH * HASH7 (a,b,c,d,e,f,g) + (h))
#define HASH9(a,b,c,d,e,f,g,h,i) (GOOD_HASH * HASH8 (a,b,c,d,e,f,g,h) + (i))
struct hash_table; struct hash_table;
struct hash_table *hash_table_new PARAMS ((int, struct hash_table *hash_table_new PARAMS ((int,
@ -64,9 +52,21 @@ void hash_table_map PARAMS ((struct hash_table *,
void *)); void *));
int hash_table_count PARAMS ((const struct hash_table *)); int hash_table_count PARAMS ((const struct hash_table *));
unsigned long string_hash PARAMS ((const void *));
int string_cmp PARAMS ((const void *, const void *));
struct hash_table *make_string_hash_table PARAMS ((int)); struct hash_table *make_string_hash_table PARAMS ((int));
struct hash_table *make_nocase_string_hash_table PARAMS ((int)); struct hash_table *make_nocase_string_hash_table PARAMS ((int));
unsigned long hash_pointer PARAMS ((const void *));
/* From XEmacs, and hence from Dragon book. */
#define GOOD_HASH 65599 /* prime number just over 2^16; Dragon book, p. 435 */
#define HASH2(a,b) (GOOD_HASH * (a) + (b))
#define HASH3(a,b,c) (GOOD_HASH * HASH2 (a,b) + (c))
#define HASH4(a,b,c,d) (GOOD_HASH * HASH3 (a,b,c) + (d))
#define HASH5(a,b,c,d,e) (GOOD_HASH * HASH4 (a,b,c,d) + (e))
#define HASH6(a,b,c,d,e,f) (GOOD_HASH * HASH5 (a,b,c,d,e) + (f))
#define HASH7(a,b,c,d,e,f,g) (GOOD_HASH * HASH6 (a,b,c,d,e,f) + (g))
#define HASH8(a,b,c,d,e,f,g,h) (GOOD_HASH * HASH7 (a,b,c,d,e,f,g) + (h))
#define HASH9(a,b,c,d,e,f,g,h,i) (GOOD_HASH * HASH8 (a,b,c,d,e,f,g,h) + (i))
#endif /* HASH_H */ #endif /* HASH_H */

View File

@ -84,7 +84,7 @@ extern int output_stream_regular;
static int cookies_loaded_p; static int cookies_loaded_p;
struct cookie_jar *wget_cookie_jar; static struct cookie_jar *wget_cookie_jar;
#define TEXTHTML_S "text/html" #define TEXTHTML_S "text/html"
#define TEXTXHTML_S "application/xhtml+xml" #define TEXTXHTML_S "application/xhtml+xml"
@ -1349,14 +1349,23 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
look up conn->host in some cases. If that lookup failed, we look up conn->host in some cases. If that lookup failed, we
don't need to bother with connect_to_host. */ don't need to bother with connect_to_host. */
if (host_lookup_failed) if (host_lookup_failed)
{
request_free (req);
return HOSTERR; return HOSTERR;
}
sock = connect_to_host (conn->host, conn->port); sock = connect_to_host (conn->host, conn->port);
if (sock == E_HOST) if (sock == E_HOST)
{
request_free (req);
return HOSTERR; return HOSTERR;
}
else if (sock < 0) else if (sock < 0)
{
request_free (req);
return (retryable_socket_connect_error (errno) return (retryable_socket_connect_error (errno)
? CONERROR : CONIMPOSSIBLE); ? CONERROR : CONIMPOSSIBLE);
}
#ifdef HAVE_SSL #ifdef HAVE_SSL
if (proxy && u->scheme == SCHEME_HTTPS) if (proxy && u->scheme == SCHEME_HTTPS)
@ -1405,6 +1414,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
resp = resp_new (head); resp = resp_new (head);
statcode = resp_status (resp, &message); statcode = resp_status (resp, &message);
resp_free (resp); resp_free (resp);
xfree (head);
if (statcode != 200) if (statcode != 200)
{ {
failed_tunnel: failed_tunnel:
@ -1582,6 +1592,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
hs->error = xstrdup (_("(no description)")); hs->error = xstrdup (_("(no description)"));
else else
hs->error = xstrdup (message); hs->error = xstrdup (message);
xfree (message);
type = resp_header_strdup (resp, "Content-Type"); type = resp_header_strdup (resp, "Content-Type");
if (type) if (type)
@ -1623,6 +1634,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
contrange = first_byte_pos; contrange = first_byte_pos;
} }
resp_free (resp); resp_free (resp);
xfree (head);
/* 20x responses are counted among successful by default. */ /* 20x responses are counted among successful by default. */
if (H_20X (statcode)) if (H_20X (statcode))
@ -2832,7 +2844,17 @@ create_authorization_line (const char *au, const char *user,
return NULL; return NULL;
} }
void
save_cookies (void)
{
if (wget_cookie_jar)
cookie_jar_save (wget_cookie_jar, opt.cookies_output);
}
void void
http_cleanup (void) http_cleanup (void)
{ {
xfree_null (pconn.host);
if (wget_cookie_jar)
cookie_jar_delete (wget_cookie_jar);
} }

View File

@ -1,6 +1,5 @@
/* Reading/parsing the initialization file. /* Reading/parsing the initialization file.
Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2003, 2004 Copyright (C) 2005 Free Software Foundation, Inc.
Free Software Foundation, Inc.
This file is part of GNU Wget. This file is part of GNU Wget.
@ -53,16 +52,14 @@ so, delete this exception statement from your version. */
#include "init.h" #include "init.h"
#include "host.h" #include "host.h"
#include "netrc.h" #include "netrc.h"
#include "cookies.h" /* for cookie_jar_delete */
#include "progress.h" #include "progress.h"
#include "recur.h" /* for INFINITE_RECURSION */ #include "recur.h" /* for INFINITE_RECURSION */
#include "convert.h" /* for convert_cleanup */
#ifndef errno #ifndef errno
extern int errno; extern int errno;
#endif #endif
extern struct cookie_jar *wget_cookie_jar;
/* We want tilde expansion enabled only when reading `.wgetrc' lines; /* We want tilde expansion enabled only when reading `.wgetrc' lines;
otherwise, it will be performed by the shell. This variable will otherwise, it will be performed by the shell. This variable will
be set by the wgetrc-reading function. */ be set by the wgetrc-reading function. */
@ -1337,8 +1334,7 @@ cleanup (void)
cleanup_html_url (); cleanup_html_url ();
downloaded_files_free (); downloaded_files_free ();
host_cleanup (); host_cleanup ();
if (wget_cookie_jar) log_cleanup ();
cookie_jar_delete (wget_cookie_jar);
{ {
extern acc_t *netrc_list; extern acc_t *netrc_list;
@ -1366,7 +1362,7 @@ cleanup (void)
xfree_null (opt.referer); xfree_null (opt.referer);
xfree_null (opt.http_user); xfree_null (opt.http_user);
xfree_null (opt.http_passwd); xfree_null (opt.http_passwd);
xfree_null (opt.user_header); free_vec (opt.user_headers);
#ifdef HAVE_SSL #ifdef HAVE_SSL
xfree_null (opt.sslcertkey); xfree_null (opt.sslcertkey);
xfree_null (opt.sslcertfile); xfree_null (opt.sslcertfile);

View File

@ -1,5 +1,5 @@
/* Messages logging. /* Messages logging.
Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc. Copyright (C) 2005 Free Software Foundation, Inc.
This file is part of GNU Wget. This file is part of GNU Wget.
@ -684,11 +684,11 @@ struct ringel {
char *buffer; char *buffer;
int size; int size;
}; };
static struct ringel ring[RING_SIZE]; /* ring data */
static const char * static const char *
escnonprint_internal (const char *str, int for_uri) escnonprint_internal (const char *str, int for_uri)
{ {
static struct ringel ring[RING_SIZE]; /* ring data */
static int ringpos; /* current ring position */ static int ringpos; /* current ring position */
int nprcnt = count_nonprint (str); int nprcnt = count_nonprint (str);
@ -706,12 +706,15 @@ escnonprint_internal (const char *str, int for_uri)
i.e. with three *additional* chars (two in URI-mode). Size i.e. with three *additional* chars (two in URI-mode). Size
must also include the length of the original string and an must also include the length of the original string and an
additional char for the terminating \0. */ additional char for the terminating \0. */
int needed_size = strlen (str) + 1 + for_uri ? (2 * nprcnt) : (3 * nprcnt); int needed_size = strlen (str) + 1 + (for_uri ? 2 * nprcnt : 3 * nprcnt);
/* If the current buffer is uninitialized or too small, /* If the current buffer is uninitialized or too small,
(re)allocate it. */ (re)allocate it. */
if (r->buffer == NULL || r->size < needed_size) if (r->buffer == NULL || r->size < needed_size)
{
r->buffer = xrealloc (r->buffer, needed_size); r->buffer = xrealloc (r->buffer, needed_size);
r->size = needed_size;
}
copy_and_escape (str, r->buffer, for_uri); copy_and_escape (str, r->buffer, for_uri);
ringpos = (ringpos + 1) % RING_SIZE; ringpos = (ringpos + 1) % RING_SIZE;
@ -748,6 +751,14 @@ escnonprint_uri (const char *str)
{ {
return escnonprint_internal (str, 1); return escnonprint_internal (str, 1);
} }
void
log_cleanup (void)
{
int i;
for (i = 0; i < countof (ring); i++)
xfree_null (ring[i].buffer);
}
/* When SIGHUP or SIGUSR1 are received, the output is redirected /* When SIGHUP or SIGUSR1 are received, the output is redirected
elsewhere. Such redirection is only allowed once. */ elsewhere. Such redirection is only allowed once. */

View File

@ -50,6 +50,7 @@ int log_set_save_context PARAMS ((int));
void log_init PARAMS ((const char *, int)); void log_init PARAMS ((const char *, int));
void log_close PARAMS ((void)); void log_close PARAMS ((void));
void log_cleanup PARAMS ((void));
void log_request_redirect_output PARAMS ((const char *)); void log_request_redirect_output PARAMS ((const char *));
const char *escnonprint PARAMS ((const char *)); const char *escnonprint PARAMS ((const char *));

View File

@ -61,7 +61,6 @@ extern int errno;
#include "retr.h" #include "retr.h"
#include "recur.h" #include "recur.h"
#include "host.h" #include "host.h"
#include "cookies.h"
#include "url.h" #include "url.h"
#include "progress.h" /* for progress_handle_sigwinch */ #include "progress.h" /* for progress_handle_sigwinch */
#include "convert.h" #include "convert.h"
@ -953,8 +952,8 @@ Can't timestamp and not clobber old files at the same time.\n"));
legible (opt.quota)); legible (opt.quota));
} }
if (opt.cookies_output && wget_cookie_jar) if (opt.cookies_output)
cookie_jar_save (wget_cookie_jar, opt.cookies_output); save_cookies ();
if (opt.convert_links && !opt.delete_after) if (opt.convert_links && !opt.delete_after)
convert_all_links (); convert_all_links ();

View File

@ -214,8 +214,7 @@ fd_read_body (int fd, FILE *out, wgint toread, wgint startpos,
data arrives slowly. */ data arrives slowly. */
int progress_interactive = 0; int progress_interactive = 0;
/*int exact = flags & rb_read_exactly;*/ int exact = flags & rb_read_exactly;
int exact = 1;
wgint skip = 0; wgint skip = 0;
/* How much data we've read/written. */ /* How much data we've read/written. */

View File

@ -62,6 +62,7 @@ struct url;
uerr_t http_loop PARAMS ((struct url *, char **, char **, const char *, uerr_t http_loop PARAMS ((struct url *, char **, char **, const char *,
int *, struct url *)); int *, struct url *));
void save_cookies PARAMS ((void));
#endif /* RETR_H */ #endif /* RETR_H */

View File

@ -1,6 +1,5 @@
/* URL handling. /* URL handling.
Copyright (C) 1995, 1996, 1997, 2000, 2001, 2003, 2003 Copyright (C) 2005 Free Software Foundation, Inc.
Free Software Foundation, Inc.
This file is part of GNU Wget. This file is part of GNU Wget.
@ -1017,6 +1016,30 @@ url_full_path (const struct url *url)
return full_path; return full_path;
} }
/* Unescape CHR in an otherwise escaped STR. Used to selectively
escaping of certain characters, such as "/" and ":". Returns a
count of unescaped chars. */
static void
unescape_single_char (char *str, char chr)
{
const char c1 = XNUM_TO_DIGIT (chr >> 4);
const char c2 = XNUM_TO_DIGIT (chr & 0xf);
char *h = str; /* hare */
char *t = str; /* tortoise */
for (; *h; h++, t++)
{
if (h[0] == '%' && h[1] == c1 && h[2] == c2)
{
*t = chr;
h += 2;
}
else
*t = *h;
}
*t = '\0';
}
/* Escape unsafe and reserved characters, except for the slash /* Escape unsafe and reserved characters, except for the slash
characters. */ characters. */
@ -1024,28 +1047,10 @@ static char *
url_escape_dir (const char *dir) url_escape_dir (const char *dir)
{ {
char *newdir = url_escape_1 (dir, urlchr_unsafe | urlchr_reserved, 1); char *newdir = url_escape_1 (dir, urlchr_unsafe | urlchr_reserved, 1);
char *h, *t;
if (newdir == dir) if (newdir == dir)
return (char *)dir; return (char *)dir;
/* Unescape slashes in NEWDIR. */ unescape_single_char (newdir, '/');
h = newdir; /* hare */
t = newdir; /* tortoise */
for (; *h; h++, t++)
{
/* url_escape_1 having converted '/' to "%2F" exactly. */
if (*h == '%' && h[1] == '2' && h[2] == 'F')
{
*t = '/';
h += 2;
}
else
*t = *h;
}
*t = '\0';
return newdir; return newdir;
} }
@ -1845,7 +1850,7 @@ url_string (const struct url *url, int hide_password)
{ {
int size; int size;
char *result, *p; char *result, *p;
char *quoted_user = NULL, *quoted_passwd = NULL; char *quoted_host, *quoted_user = NULL, *quoted_passwd = NULL;
int scheme_port = supported_schemes[url->scheme].default_port; int scheme_port = supported_schemes[url->scheme].default_port;
const char *scheme_str = supported_schemes[url->scheme].leading_string; const char *scheme_str = supported_schemes[url->scheme].leading_string;
@ -1868,12 +1873,19 @@ url_string (const struct url *url, int hide_password)
} }
} }
/* Numeric IPv6 addresses can contain ':' and need to be quoted with /* In the unlikely event that the host name contains non-printable
brackets. */ characters, quote it for displaying to the user. */
brackets_around_host = strchr (url->host, ':') != NULL; quoted_host = url_escape_allow_passthrough (url->host);
/* Undo the quoting of colons that URL escaping performs. IPv6
addresses may legally contain colons, and in that case must be
placed in square brackets. */
if (quoted_host != url->host)
unescape_single_char (quoted_host, ':');
brackets_around_host = strchr (quoted_host, ':') != NULL;
size = (strlen (scheme_str) size = (strlen (scheme_str)
+ strlen (url->host) + strlen (quoted_host)
+ (brackets_around_host ? 2 : 0) + (brackets_around_host ? 2 : 0)
+ fplen + fplen
+ 1); + 1);
@ -1902,7 +1914,7 @@ url_string (const struct url *url, int hide_password)
if (brackets_around_host) if (brackets_around_host)
*p++ = '['; *p++ = '[';
APPEND (p, url->host); APPEND (p, quoted_host);
if (brackets_around_host) if (brackets_around_host)
*p++ = ']'; *p++ = ']';
if (url->port != scheme_port) if (url->port != scheme_port)
@ -1919,9 +1931,10 @@ url_string (const struct url *url, int hide_password)
if (quoted_user && quoted_user != url->user) if (quoted_user && quoted_user != url->user)
xfree (quoted_user); xfree (quoted_user);
if (quoted_passwd && !hide_password if (quoted_passwd && !hide_password && quoted_passwd != url->passwd)
&& quoted_passwd != url->passwd)
xfree (quoted_passwd); xfree (quoted_passwd);
if (quoted_host != url->host)
xfree (quoted_host);
return result; return result;
} }

View File

@ -558,6 +558,14 @@ fopen_excl (const char *fname, int binary)
return NULL; return NULL;
return fdopen (fd, binary ? "wb" : "w"); return fdopen (fd, binary ? "wb" : "w");
#else /* not O_EXCL */ #else /* not O_EXCL */
/* Manually check whether the file exists. This is prone to race
conditions, but systems without O_EXCL haven't deserved
better. */
if (file_exists_p (fname))
{
errno = EEXIST;
return NULL;
}
return fopen (fname, binary ? "wb" : "w"); return fopen (fname, binary ? "wb" : "w");
#endif /* not O_EXCL */ #endif /* not O_EXCL */
} }

View File

@ -1,5 +1,5 @@
/* Wrappers around malloc and memory debugging support. /* Wrappers around malloc and memory debugging support.
Copyright (C) 2003 Free Software Foundation, Inc. Copyright (C) 2005 Free Software Foundation, Inc.
This file is part of GNU Wget. This file is part of GNU Wget.
@ -42,6 +42,7 @@ so, delete this exception statement from your version. */
#include "wget.h" #include "wget.h"
#include "xmalloc.h" #include "xmalloc.h"
#include "hash.h" /* for hash_pointer */
#ifndef errno #ifndef errno
extern int errno; extern int errno;
@ -79,11 +80,11 @@ memfatal (const char *context, long attempted_size)
If memory debugging is not turned on, xmalloc.h defines these: If memory debugging is not turned on, xmalloc.h defines these:
#define xmalloc xmalloc_real #define xmalloc checking_malloc
#define xmalloc0 xmalloc0_real #define xmalloc0 checking_malloc0
#define xrealloc xrealloc_real #define xrealloc checking_realloc
#define xstrdup xstrdup_real #define xstrdup checking_strdup
#define xfree xfree_real #define xfree checking_free
In case of memory debugging, the definitions are a bit more In case of memory debugging, the definitions are a bit more
complex, because we want to provide more information, *and* we want complex, because we want to provide more information, *and* we want
@ -91,13 +92,14 @@ memfatal (const char *context, long attempted_size)
and friends need to be macros in the first place.) Then it looks and friends need to be macros in the first place.) Then it looks
like this: like this:
#define xmalloc(a) xmalloc_debug (a, __FILE__, __LINE__) #define xmalloc(a) debugging_malloc (a, __FILE__, __LINE__)
#define xmalloc0(a) xmalloc0_debug (a, __FILE__, __LINE__) #define xmalloc0(a) debugging_malloc0 (a, __FILE__, __LINE__)
#define xrealloc(a, b) xrealloc_debug (a, b, __FILE__, __LINE__) #define xrealloc(a, b) debugging_realloc (a, b, __FILE__, __LINE__)
#define xstrdup(a) xstrdup_debug (a, __FILE__, __LINE__) #define xstrdup(a) debugging_strdup (a, __FILE__, __LINE__)
#define xfree(a) xfree_debug (a, __FILE__, __LINE__) #define xfree(a) debugging_free (a, __FILE__, __LINE__)
Each of the *_debug function does its magic and calls the real one. */ Each of the debugging_* functions does its magic and calls the
corresponding checking_* one. */
#ifdef DEBUG_MALLOC #ifdef DEBUG_MALLOC
# define STATIC_IF_DEBUG static # define STATIC_IF_DEBUG static
@ -106,7 +108,7 @@ memfatal (const char *context, long attempted_size)
#endif #endif
STATIC_IF_DEBUG void * STATIC_IF_DEBUG void *
xmalloc_real (size_t size) checking_malloc (size_t size)
{ {
void *ptr = malloc (size); void *ptr = malloc (size);
if (!ptr) if (!ptr)
@ -115,7 +117,7 @@ xmalloc_real (size_t size)
} }
STATIC_IF_DEBUG void * STATIC_IF_DEBUG void *
xmalloc0_real (size_t size) checking_malloc0 (size_t size)
{ {
/* Using calloc can be faster than malloc+memset because some calloc /* Using calloc can be faster than malloc+memset because some calloc
implementations know when they're dealing with zeroed-out memory implementations know when they're dealing with zeroed-out memory
@ -127,7 +129,7 @@ xmalloc0_real (size_t size)
} }
STATIC_IF_DEBUG void * STATIC_IF_DEBUG void *
xrealloc_real (void *ptr, size_t newsize) checking_realloc (void *ptr, size_t newsize)
{ {
void *newptr; void *newptr;
@ -144,7 +146,7 @@ xrealloc_real (void *ptr, size_t newsize)
} }
STATIC_IF_DEBUG char * STATIC_IF_DEBUG char *
xstrdup_real (const char *s) checking_strdup (const char *s)
{ {
char *copy; char *copy;
@ -164,31 +166,31 @@ xstrdup_real (const char *s)
} }
STATIC_IF_DEBUG void STATIC_IF_DEBUG void
xfree_real (void *ptr) checking_free (void *ptr)
{ {
/* Wget's xfree() must not be passed a NULL pointer. This is for /* Wget's xfree() must not be passed a NULL pointer. This is for
historical reasons: many pre-C89 systems were known to bomb at historical reasons: pre-C89 systems were reported to bomb at
free(NULL), and Wget was careful to use xfree_null when there is free(NULL), and Wget was careful to not call xfree when there was
a possibility of PTR being NULL. (It might have been better to a possibility of PTR being NULL. (It might have been better to
simply have xfree() do nothing if ptr==NULL.) simply have xfree() do nothing if ptr==NULL.)
Since the code is already written that way, this assert simply Since the code is already written that way, this assert simply
enforces that constraint. Code that thinks it doesn't deal with enforces the existing constraint. The benefit is double-checking
NULL, and it in fact does, aborts immediately. If you trip on the logic: code that thinks it can't be passed a NULL pointer,
this, either the code has a pointer handling bug or should have while it in fact can, aborts here. If you trip on this, either
called xfree_null instead of xfree. Correctly written code the code has a pointer handling bug or should have called
should never trigger this assertion. 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 The downside is that the uninitiated might not expect xfree(NULL)
removed and a check that makes NULL a no-op placed in its stead. to abort. If the assertion proves to be too much of a hassle, it
If that is done, xfree_null is no longer needed and should be can be removed and a check that makes NULL a no-op placed in its
removed. */ stead. If that is done, xfree_null is no longer needed and
should be removed. */
assert (ptr != NULL); assert (ptr != NULL);
free (ptr); free (ptr);
} }
/* xfree_real is unnecessary because free doesn't require any special
functionality. */
#ifdef DEBUG_MALLOC #ifdef DEBUG_MALLOC
@ -202,8 +204,10 @@ xfree_real (void *ptr)
* Making malloc store its entry into a simple array and free remove * Making malloc store its entry into a simple array and free remove
stuff from that array. At the end, print the pointers which have stuff from that array. At the end, print the pointers which have
not been freed, along with the source file and the line number. 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. * Checking for "invalid frees", where free is called on a pointer
not obtained with malloc, or where the same pointer is freed
twice.
Note that this kind of memory leak checking strongly depends on Note that this kind of memory leak checking strongly depends on
every malloc() being followed by a free(), even if the program is every malloc() being followed by a free(), even if the program is
@ -212,58 +216,82 @@ xfree_real (void *ptr)
static int malloc_count, free_count; static int malloc_count, free_count;
/* Home-grown hash table of mallocs: */
#define SZ 100003 /* Prime number a little over 100,000.
Increase the table size if you need
to debug larger Wget runs. */
static struct { static struct {
char *ptr; const void *ptr;
const char *file; const char *file;
int line; int line;
} malloc_debug[100000]; } malloc_table[SZ];
/* Both register_ptr and unregister_ptr take O(n) operations to run, /* Find PTR's position in malloc_table. If PTR is not found, return
which can be a real problem. It would be nice to use a hash table the next available position. */
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 static inline int
ptr_position (const void *ptr)
{
int i = hash_pointer (ptr) % SZ;
for (; malloc_table[i].ptr != NULL; i = (i + 1) % SZ)
if (malloc_table[i].ptr == ptr)
return i;
return i;
}
/* Register PTR in malloc_table. Abort if this is not possible
(presumably due to the number of current allocations exceeding the (presumably due to the number of current allocations exceeding the
size of malloc_debug.) */ size of malloc_table.) */
static void static void
register_ptr (void *ptr, const char *file, int line) register_ptr (const void *ptr, const char *file, int line)
{ {
int i; int i;
for (i = 0; i < countof (malloc_debug); i++) if (malloc_count - free_count > SZ)
if (malloc_debug[i].ptr == NULL)
{
malloc_debug[i].ptr = ptr;
malloc_debug[i].file = file;
malloc_debug[i].line = line;
return;
}
abort (); abort ();
i = ptr_position (ptr);
malloc_table[i].ptr = ptr;
malloc_table[i].file = file;
malloc_table[i].line = line;
} }
/* Unregister PTR from malloc_debug. Abort if PTR is not present in /* Unregister PTR from malloc_table. Return 0 if PTR is not present
malloc_debug. (This catches calling free() with a bogus pointer.) */ in malloc_table. */
static void static int
unregister_ptr (void *ptr) unregister_ptr (void *ptr)
{ {
int i; int i = ptr_position (ptr);
for (i = 0; i < countof (malloc_debug); i++) if (malloc_table[i].ptr == NULL)
if (malloc_debug[i].ptr == ptr) return 0;
malloc_table[i].ptr = NULL;
/* Relocate malloc_table entries immediately following PTR. */
for (i = (i + 1) % SZ; malloc_table[i].ptr != NULL; i = (i + 1) % SZ)
{ {
malloc_debug[i].ptr = NULL; const void *ptr2 = malloc_table[i].ptr;
return; /* Find the new location for the key. */
int j = hash_pointer (ptr2) % SZ;
for (; malloc_table[j].ptr != NULL; j = (j + 1) % SZ)
if (ptr2 == malloc_table[j].ptr)
/* No need to relocate entry at [i]; it's already at or near
its hash position. */
goto cont_outer;
malloc_table[j] = malloc_table[i];
malloc_table[i].ptr = NULL;
cont_outer:
;
} }
abort (); return 1;
} }
/* Print the malloc debug stats that can be gathered from the above /* Print the malloc debug stats gathered from the above information.
information. Currently this is the count of mallocs, frees, the Currently this is the count of mallocs, frees, the difference
difference between the two, and the dump of the contents of between the two, and the dump of the contents of malloc_table. The
malloc_debug. The last part are the memory leaks. */ last part are the memory leaks. */
void void
print_malloc_debug_stats (void) print_malloc_debug_stats (void)
@ -271,34 +299,35 @@ print_malloc_debug_stats (void)
int i; int i;
printf ("\nMalloc: %d\nFree: %d\nBalance: %d\n\n", printf ("\nMalloc: %d\nFree: %d\nBalance: %d\n\n",
malloc_count, free_count, malloc_count - free_count); malloc_count, free_count, malloc_count - free_count);
for (i = 0; i < countof (malloc_debug); i++) for (i = 0; i < SZ; i++)
if (malloc_debug[i].ptr != NULL) if (malloc_table[i].ptr != NULL)
printf ("0x%08ld: %s:%d\n", (long)malloc_debug[i].ptr, printf ("0x%0*lx: %s:%d\n", 2 * sizeof (void *),
malloc_debug[i].file, malloc_debug[i].line); (long) malloc_table[i].ptr,
malloc_table[i].file, malloc_table[i].line);
} }
void * void *
xmalloc_debug (size_t size, const char *source_file, int source_line) debugging_malloc (size_t size, const char *source_file, int source_line)
{ {
void *ptr = xmalloc_real (size); void *ptr = checking_malloc (size);
++malloc_count; ++malloc_count;
register_ptr (ptr, source_file, source_line); register_ptr (ptr, source_file, source_line);
return ptr; return ptr;
} }
void * void *
xmalloc0_debug (size_t size, const char *source_file, int source_line) debugging_malloc0 (size_t size, const char *source_file, int source_line)
{ {
void *ptr = xmalloc0_real (size); void *ptr = checking_malloc0 (size);
++malloc_count; ++malloc_count;
register_ptr (ptr, source_file, source_line); register_ptr (ptr, source_file, source_line);
return ptr; return ptr;
} }
void * void *
xrealloc_debug (void *ptr, size_t newsize, const char *source_file, int source_line) debugging_realloc (void *ptr, size_t newsize, const char *source_file, int source_line)
{ {
void *newptr = xrealloc_real (ptr, newsize); void *newptr = checking_realloc (ptr, newsize);
if (!ptr) if (!ptr)
{ {
++malloc_count; ++malloc_count;
@ -313,29 +342,36 @@ xrealloc_debug (void *ptr, size_t newsize, const char *source_file, int source_l
} }
char * char *
xstrdup_debug (const char *s, const char *source_file, int source_line) debugging_strdup (const char *s, const char *source_file, int source_line)
{ {
char *copy = xstrdup_real (s); char *copy = checking_strdup (s);
++malloc_count; ++malloc_count;
register_ptr (copy, source_file, source_line); register_ptr (copy, source_file, source_line);
return copy; return copy;
} }
void void
xfree_debug (void *ptr, const char *source_file, int source_line) debugging_free (void *ptr, const char *source_file, int source_line)
{ {
/* See xfree_real for rationale of this abort. We repeat it here /* See checking_free for rationale of this abort. We repeat it here
because we can print the file and the line where the offending because we can print the file and the line where the offending
free occurred. */ free occurred. */
if (ptr == NULL) if (ptr == NULL)
{ {
fprintf ("%s: xfree(NULL) at %s:%d\n", fprintf (stderr, "%s: xfree(NULL) at %s:%d\n",
exec_name, source_file, source_line); exec_name, source_file, source_line);
abort (); abort ();
} }
if (!unregister_ptr (ptr))
{
fprintf (stderr, "%s: bad xfree(%0*lx) at %s:%d\n",
exec_name, 2 * sizeof (void *), (long) ptr,
source_file, source_line);
abort ();
}
++free_count; ++free_count;
unregister_ptr (ptr);
xfree_real (ptr); checking_free (ptr);
} }
#endif /* DEBUG_MALLOC */ #endif /* DEBUG_MALLOC */

View File

@ -30,46 +30,50 @@ so, delete this exception statement from your version. */
#ifndef XMALLOC_H #ifndef XMALLOC_H
#define XMALLOC_H #define XMALLOC_H
/* Define this if you want primitive but extensive malloc debugging. /* Define this to get Wget's builtin malloc debugging, which is
It will make Wget extremely slow, so only do it in development primitive but fairly extensive. It will make Wget somewhat slower
builds. */ and larger, so it should be used by developers only. */
#undef DEBUG_MALLOC #undef DEBUG_MALLOC
/* When DEBUG_MALLOC is not defined (which is normally the case), the /* When DEBUG_MALLOC is not defined (which is normally the case), the
allocation functions directly map to *_real wrappers. In the allocator identifiers are mapped to checking_* wrappers, which exit
DEBUG_MALLOC mode, they also record the file and line where the Wget if malloc/realloc/strdup return NULL
offending malloc/free/... was invoked.
*Note*: xfree(NULL) aborts. If the pointer you're freeing can be In DEBUG_MALLOC mode, the allocators are mapped to debugging_*
NULL, use xfree_null instead. */ wrappers, which also record the file and line from which the
allocation was attempted. At the end of the program, a detailed
summary of unfreed allocations is displayed.
*Note*: xfree(NULL) aborts in both modes. If the pointer you're
freeing can be NULL, use xfree_null instead. */
#ifndef DEBUG_MALLOC #ifndef DEBUG_MALLOC
#define xmalloc xmalloc_real #define xmalloc checking_malloc
#define xmalloc0 xmalloc0_real #define xmalloc0 checking_malloc0
#define xrealloc xrealloc_real #define xrealloc checking_realloc
#define xstrdup xstrdup_real #define xstrdup checking_strdup
#define xfree xfree_real #define xfree checking_free
void *xmalloc_real PARAMS ((size_t)); void *checking_malloc PARAMS ((size_t));
void *xmalloc0_real PARAMS ((size_t)); void *checking_malloc0 PARAMS ((size_t));
void *xrealloc_real PARAMS ((void *, size_t)); void *checking_realloc PARAMS ((void *, size_t));
char *xstrdup_real PARAMS ((const char *)); char *checking_strdup PARAMS ((const char *));
void xfree_real PARAMS ((void *)); void checking_free PARAMS ((void *));
#else /* DEBUG_MALLOC */ #else /* DEBUG_MALLOC */
#define xmalloc(s) xmalloc_debug (s, __FILE__, __LINE__) #define xmalloc(s) debugging_malloc (s, __FILE__, __LINE__)
#define xmalloc0(s) xmalloc0_debug (s, __FILE__, __LINE__) #define xmalloc0(s) debugging_malloc0 (s, __FILE__, __LINE__)
#define xrealloc(p, s) xrealloc_debug (p, s, __FILE__, __LINE__) #define xrealloc(p, s) debugging_realloc (p, s, __FILE__, __LINE__)
#define xstrdup(p) xstrdup_debug (p, __FILE__, __LINE__) #define xstrdup(p) debugging_strdup (p, __FILE__, __LINE__)
#define xfree(p) xfree_debug (p, __FILE__, __LINE__) #define xfree(p) debugging_free (p, __FILE__, __LINE__)
void *xmalloc_debug PARAMS ((size_t, const char *, int)); void *debugging_malloc PARAMS ((size_t, const char *, int));
void *xmalloc0_debug PARAMS ((size_t, const char *, int)); void *debugging_malloc0 PARAMS ((size_t, const char *, int));
void *xrealloc_debug PARAMS ((void *, size_t, const char *, int)); void *debugging_realloc PARAMS ((void *, size_t, const char *, int));
char *xstrdup_debug PARAMS ((const char *, const char *, int)); char *debugging_strdup PARAMS ((const char *, const char *, int));
void xfree_debug PARAMS ((void *, const char *, int)); void debugging_free PARAMS ((void *, const char *, int));
#endif /* DEBUG_MALLOC */ #endif /* DEBUG_MALLOC */