+
+#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 */