summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorJames Bursa <james@netsurf-browser.org>2004-07-28 22:35:02 +0000
committerJames Bursa <james@netsurf-browser.org>2004-07-28 22:35:02 +0000
commit7114ceadd573d7e8c8bfe6be95a3cae379e5c27e (patch)
treef150ba806bf758bb024876ab7118535594bfb553 /utils
parent3244e3440d06a283fd23cc7bf159d2785a3f46bc (diff)
downloadnetsurf-7114ceadd573d7e8c8bfe6be95a3cae379e5c27e.tar.gz
netsurf-7114ceadd573d7e8c8bfe6be95a3cae379e5c27e.tar.bz2
[project @ 2004-07-28 22:35:02 by bursa]
Heap debugging functions and log analysis script. svn path=/import/netsurf/; revision=1156
Diffstat (limited to 'utils')
-rwxr-xr-xutils/memanalyze.pl380
-rw-r--r--utils/memdebug.c368
-rw-r--r--utils/memdebug.h106
3 files changed, 854 insertions, 0 deletions
diff --git a/utils/memanalyze.pl b/utils/memanalyze.pl
new file mode 100755
index 000000000..57e107d11
--- /dev/null
+++ b/utils/memanalyze.pl
@@ -0,0 +1,380 @@
+#!/usr/bin/env perl
+#
+# Example input:
+#
+# MEM mprintf.c:1094 malloc(32) = e5718
+# MEM mprintf.c:1103 realloc(e5718, 64) = e6118
+# MEM sendf.c:232 free(f6520)
+
+my $mallocs=0;
+my $callocs=0;
+my $reallocs=0;
+my $strdups=0;
+my $showlimit;
+
+while(1) {
+ if($ARGV[0] eq "-v") {
+ $verbose=1;
+ shift @ARGV;
+ }
+ elsif($ARGV[0] eq "-t") {
+ $trace=1;
+ shift @ARGV;
+ }
+ elsif($ARGV[0] eq "-l") {
+ # only show what alloc that caused a memlimit failure
+ $showlimit=1;
+ shift @ARGV;
+ }
+ else {
+ last;
+ }
+}
+
+my $maxmem;
+
+sub newtotal {
+ my ($newtot)=@_;
+ # count a max here
+
+ if($newtot > $maxmem) {
+ $maxmem= $newtot;
+ }
+}
+
+my $file = $ARGV[0];
+
+if(! -f $file) {
+ print "Usage: memanalyze.pl [options] <dump file>\n",
+ "Options:\n",
+ " -l memlimit failure displayed\n",
+ " -v Verbose\n",
+ " -t Trace\n";
+ exit;
+}
+
+open(FILE, "<$file");
+
+if($showlimit) {
+ while(<FILE>) {
+ if(/^LIMIT.*memlimit$/) {
+ print $_;
+ last;
+ }
+ }
+ close(FILE);
+ exit;
+}
+
+
+
+while(<FILE>) {
+ chomp $_;
+ $line = $_;
+
+ if($line =~ /^LIMIT ([^ ]*):(\d*) (.*)/) {
+ # new memory limit test prefix
+ my $i = $3;
+ my ($source, $linenum) = ($1, $2);
+ if($trace && ($i =~ /([^ ]*) reached memlimit/)) {
+ print "LIMIT: $1 returned error at $source:$linenum\n";
+ }
+ }
+ elsif($line =~ /^MEM ([^ ]*):(\d*) (.*)/) {
+ # generic match for the filename+linenumber
+ $source = $1;
+ $linenum = $2;
+ $function = $3;
+
+ if($function =~ /free\(0x([0-9a-f]*)/) {
+ $addr = $1;
+ if(!exists $sizeataddr{$addr}) {
+ print "FREE ERROR: No memory allocated: $line\n";
+ }
+ elsif(-1 == $sizeataddr{$addr}) {
+ print "FREE ERROR: Memory freed twice: $line\n";
+ print "FREE ERROR: Previously freed at: ".$getmem{$addr}."\n";
+ }
+ else {
+ $totalmem -= $sizeataddr{$addr};
+ if($trace) {
+ print "FREE: malloc at ".$getmem{$addr}." is freed again at $source:$linenum\n";
+ printf("FREE: %d bytes freed, left allocated: $totalmem bytes\n", $sizeataddr{$addr});
+ }
+
+ newtotal($totalmem);
+ $frees++;
+
+ $sizeataddr{$addr}=-1; # set -1 to mark as freed
+ $getmem{$addr}="$source:$linenum";
+
+ }
+ }
+ elsif($function =~ /malloc\((\d*)\) = 0x([0-9a-f]*)/) {
+ $size = $1;
+ $addr = $2;
+
+ if($sizeataddr{$addr}>0) {
+ # this means weeeeeirdo
+ print "Mixed debug compile, rebuild curl now\n";
+ }
+
+ $sizeataddr{$addr}=$size;
+ $totalmem += $size;
+
+ if($trace) {
+ print "MALLOC: malloc($size) at $source:$linenum",
+ " makes totally $totalmem bytes\n";
+ }
+
+ newtotal($totalmem);
+ $mallocs++;
+
+ $getmem{$addr}="$source:$linenum";
+ }
+ elsif($function =~ /calloc\((\d*),(\d*)\) = 0x([0-9a-f]*)/) {
+ $size = $1*$2;
+ $addr = $3;
+
+ $arg1 = $1;
+ $arg2 = $2;
+
+ if($sizeataddr{$addr}>0) {
+ # this means weeeeeirdo
+ print "Mixed debug compile, rebuild curl now\n";
+ }
+
+ $sizeataddr{$addr}=$size;
+ $totalmem += $size;
+
+ if($trace) {
+ print "CALLOC: calloc($arg1,$arg2) at $source:$linenum",
+ " makes totally $totalmem bytes\n";
+ }
+
+ newtotal($totalmem);
+ $callocs++;
+
+ $getmem{$addr}="$source:$linenum";
+ }
+ elsif($function =~ /realloc\(0x([0-9a-f]*), (\d*)\) = 0x([0-9a-f]*)/) {
+ $oldaddr = $1;
+ $newsize = $2;
+ $newaddr = $3;
+
+ $totalmem -= $sizeataddr{$oldaddr};
+ if($trace) {
+ printf("REALLOC: %d less bytes and ", $sizeataddr{$oldaddr});
+ }
+ $sizeataddr{$oldaddr}=0;
+
+ $totalmem += $newsize;
+ $sizeataddr{$newaddr}=$newsize;
+
+ if($trace) {
+ printf("%d more bytes ($source:$linenum)\n", $newsize);
+ }
+
+ newtotal($totalmem);
+ $reallocs++;
+
+ $getmem{$oldaddr}="";
+ $getmem{$newaddr}="$source:$linenum";
+ }
+ elsif($function =~ /strdup\(0x([0-9a-f]*)\) \((\d*)\) = 0x([0-9a-f]*)/) {
+ # strdup(a5b50) (8) = df7c0
+
+ $dup = $1;
+ $size = $2;
+ $addr = $3;
+ $getmem{$addr}="$source:$linenum";
+ $sizeataddr{$addr}=$size;
+
+ $totalmem += $size;
+
+ if($trace) {
+ printf("STRDUP: $size bytes at %s, makes totally: %d bytes\n",
+ $getmem{$addr}, $totalmem);
+ }
+
+ newtotal($totalmem);
+ $strdups++;
+ }
+ elsif($function =~ /strndup\(0x([0-9a-f]*), (\d*)\) \((\d*)\) = 0x([0-9a-f]*)/) {
+ # strndup(a5b50, 20) (8) = df7c0
+
+ $dup = $1;
+ $limit = $2;
+ $size = $3;
+ $addr = $4;
+ $getmem{$addr}="$source:$linenum";
+ $sizeataddr{$addr}=$size;
+
+ $totalmem += $size;
+
+ if($trace) {
+ printf("STRDUP: $size bytes at %s, makes totally: %d bytes\n",
+ $getmem{$addr}, $totalmem);
+ }
+
+ newtotal($totalmem);
+ $strdups++;
+ }
+ else {
+ print "Not recognized input line: $function\n";
+ }
+ }
+ # FD url.c:1282 socket() = 5
+ elsif($_ =~ /^FD ([^ ]*):(\d*) (.*)/) {
+ # generic match for the filename+linenumber
+ $source = $1;
+ $linenum = $2;
+ $function = $3;
+
+ if($function =~ /socket\(\) = (\d*)/) {
+ $filedes{$1}=1;
+ $getfile{$1}="$source:$linenum";
+ $openfile++;
+ }
+ elsif($function =~ /accept\(\) = (\d*)/) {
+ $filedes{$1}=1;
+ $getfile{$1}="$source:$linenum";
+ $openfile++;
+ }
+ elsif($function =~ /sclose\((\d*)\)/) {
+ if($filedes{$1} != 1) {
+ print "Close without open: $line\n";
+ }
+ else {
+ $filedes{$1}=0; # closed now
+ $openfile--;
+ }
+ }
+ }
+ # FILE url.c:1282 fopen("blabla") = 0x5ddd
+ elsif($_ =~ /^FILE ([^ ]*):(\d*) (.*)/) {
+ # generic match for the filename+linenumber
+ $source = $1;
+ $linenum = $2;
+ $function = $3;
+
+ if($function =~ /fopen\(\"([^\"]*)\",\"([^\"]*)\"\) = (\(nil\)|0x([0-9a-f]*))/) {
+ if($3 eq "(nil)") {
+ ;
+ }
+ else {
+ $fopen{$4}=1;
+ $fopenfile{$4}="$source:$linenum";
+ $fopens++;
+ }
+ }
+ # fclose(0x1026c8)
+ elsif($function =~ /fclose\(0x([0-9a-f]*)\)/) {
+ if(!$fopen{$1}) {
+ print "fclose() without fopen(): $line\n";
+ }
+ else {
+ $fopen{$1}=0;
+ $fopens--;
+ }
+ }
+ }
+ # GETNAME url.c:1901 getnameinfo()
+ elsif($_ =~ /^GETNAME ([^ ]*):(\d*) (.*)/) {
+ # not much to do
+ }
+
+ # ADDR url.c:1282 getaddrinfo() = 0x5ddd
+ elsif($_ =~ /^ADDR ([^ ]*):(\d*) (.*)/) {
+ # generic match for the filename+linenumber
+ $source = $1;
+ $linenum = $2;
+ $function = $3;
+
+ if($function =~ /getaddrinfo\(\) = (\(nil\)|0x([0-9a-f]*))/) {
+ my $add = $2;
+ if($add eq "(nil)") {
+ ;
+ }
+ else {
+ $addrinfo{$add}=1;
+ $addrinfofile{$add}="$source:$linenum";
+ $addrinfos++;
+ }
+ }
+ # fclose(0x1026c8)
+ elsif($function =~ /freeaddrinfo\(0x([0-9a-f]*)\)/) {
+ if(!$addrinfo{$1}) {
+ print "freeaddrinfo() without getaddrinfo(): $line\n";
+ }
+ else {
+ $addrinfo{$1}=0;
+ $addrinfos--;
+ }
+ }
+
+
+ }
+ else {
+ print "Not recognized prefix line: $line\n";
+ }
+}
+close(FILE);
+
+if($totalmem) {
+ print "Leak detected: memory still allocated: $totalmem bytes\n";
+
+ for(keys %sizeataddr) {
+ $addr = $_;
+ $size = $sizeataddr{$addr};
+ if($size > 0) {
+ print "At $addr, there's $size bytes.\t";
+ print " allocated by ".$getmem{$addr}."\n";
+ $allocs{$getmem{$addr}}++;
+ $amount{$getmem{$addr}} += $size;
+ }
+ }
+
+ print "Summary by location of allocation:\n";
+ print "Allocs\tBytes\tLocation\n";
+ for (sort { $amount{$b} <=> $amount{$a} } keys %allocs) {
+ print "$allocs{$_}\t$amount{$_}\t$_\n";
+ }
+}
+
+if($openfile) {
+ for(keys %filedes) {
+ if($filedes{$_} == 1) {
+ print "Open file descriptor created at ".$getfile{$_}."\n";
+ }
+ }
+}
+
+if($fopens) {
+ print "Open FILE handles left at:\n";
+ for(keys %fopen) {
+ if($fopen{$_} == 1) {
+ print "fopen() called at ".$fopenfile{$_}."\n";
+ }
+ }
+}
+
+if($addrinfos) {
+ print "IPv6-style name resolve data left at:\n";
+ for(keys %addrinfofile) {
+ if($addrinfo{$_} == 1) {
+ print "getaddrinfo() called at ".$addrinfofile{$_}."\n";
+ }
+ }
+}
+
+if($verbose) {
+ print "Mallocs: $mallocs\n",
+ "Reallocs: $reallocs\n",
+ "Callocs: $callocs\n",
+ "Strdups: $strdups\n",
+ "Frees: $frees\n",
+ "Allocations: ".($mallocs + $callocs + $reallocs + $strdups)."\n";
+
+ print "Maximum allocated: $maxmem\n";
+}
diff --git a/utils/memdebug.c b/utils/memdebug.c
new file mode 100644
index 000000000..312d66992
--- /dev/null
+++ b/utils/memdebug.c
@@ -0,0 +1,368 @@
+/** \file
+ * Heap debugging functions (implementation).
+ *
+ * Based on memdebug.c from curl (see below), with the following modifications:
+ *
+ * - renamed functions from curl_ to memdebug_
+ * - added memdebug_strndup
+ * - added guard bytes before and after each block to help detect overflows
+ * - if a guard byte is corrupted during free, dumps the DA to file
+ */
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id: memdebug.c,v 1.1 2004/07/28 22:35:02 bursa Exp $
+ ***************************************************************************/
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include "oslib/os.h"
+#include "oslib/osfile.h"
+
+#include "memdebug.h"
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#define MAGIC 0x34343434
+#define GUARD 0x34
+
+extern int __dynamic_num;
+
+struct memdebug {
+ size_t size;
+ unsigned int magic;
+ double mem[1];
+ /* I'm hoping this is the thing with the strictest alignment
+ * requirements. That also means we waste some space :-( */
+};
+
+/*
+ * Note that these debug functions are very simple and they are meant to
+ * remain so. For advanced analysis, record a log file and write perl scripts
+ * to analyze them!
+ *
+ * Don't use these with multithreaded test programs!
+ */
+
+#define logfile memdebug_debuglogfile
+FILE *memdebug_debuglogfile;
+static bool memlimit; /* enable memory limit */
+static long memsize; /* set number of mallocs allowed */
+
+/* this sets the log file name */
+void memdebug_memdebug(const char *logname)
+{
+ if(logname)
+ logfile = fopen(logname, "w");
+ else
+ logfile = stderr;
+}
+
+/* This function sets the number of malloc() calls that should return
+ successfully! */
+void memdebug_memlimit(long limit)
+{
+ memlimit = true;
+ memsize = limit;
+}
+
+/* returns true if this isn't allowed! */
+static bool countcheck(const char *func, int line, const char *source)
+{
+ /* if source is NULL, then the call is made internally and this check
+ should not be made */
+ if(memlimit && source) {
+ if(!memsize) {
+ if(logfile && source)
+ fprintf(logfile, "LIMIT %s:%d %s reached memlimit\n",
+ source, line, func);
+ if(source)
+ fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
+ source, line, func);
+ return true; /* RETURN ERROR! */
+ }
+ else
+ memsize--; /* countdown */
+
+ /* log the countdown */
+ if(logfile && source)
+ fprintf(logfile, "LIMIT %s:%d %ld ALLOCS left\n",
+ source, line, memsize);
+
+ }
+
+ return false; /* allow this */
+}
+
+void *memdebug_malloc(size_t wantedsize, int line, const char *source)
+{
+ struct memdebug *mem;
+ size_t size;
+
+ if(countcheck("malloc", line, source))
+ return NULL;
+
+ /* alloc at least 64 bytes */
+ size = sizeof(struct memdebug)+wantedsize + 8;
+
+ mem=(struct memdebug *)(malloc)(size);
+ if(mem) {
+ unsigned int i;
+ /* fill memory with junk */
+ memset(mem->mem, 0xA5, wantedsize);
+ mem->size = wantedsize;
+ mem->magic = MAGIC;
+ for (i = 0; i != 8; i++)
+ ((char *) mem->mem)[wantedsize + i] = GUARD;
+ }
+
+ if(logfile && source)
+ fprintf(logfile, "MEM %s:%d malloc(%u) = %p\n",
+ source, line, wantedsize, mem ? mem->mem : 0);
+ return (mem ? mem->mem : NULL);
+}
+
+void *memdebug_calloc(size_t wanted_elements, size_t wanted_size,
+ int line, const char *source)
+{
+ struct memdebug *mem;
+ size_t size, user_size;
+
+ if(countcheck("calloc", line, source))
+ return NULL;
+
+ /* alloc at least 64 bytes */
+ user_size = wanted_size * wanted_elements;
+ size = sizeof(struct memdebug) + user_size + 8;
+
+ mem = (struct memdebug *)(malloc)(size);
+ if(mem) {
+ unsigned int i;
+ /* fill memory with zeroes */
+ memset(mem->mem, 0, user_size);
+ mem->size = user_size;
+ mem->magic = MAGIC;
+ for (i = 0; i != 8; i++)
+ ((char *) mem->mem)[mem->size + i] = GUARD;
+ }
+
+ if(logfile && source)
+ fprintf(logfile, "MEM %s:%d calloc(%u,%u) = %p\n",
+ source, line, wanted_elements, wanted_size, mem ? mem->mem : 0);
+ return (mem ? mem->mem : NULL);
+}
+
+char *memdebug_strdup(const char *str, int line, const char *source)
+{
+ char *mem;
+ size_t len;
+
+ assert(str != NULL);
+
+ if(countcheck("strdup", line, source))
+ return NULL;
+
+ len=strlen(str)+1;
+
+ mem=memdebug_malloc(len, 0, NULL); /* NULL prevents logging */
+ if (mem)
+ memcpy(mem, str, len);
+
+ if(logfile)
+ fprintf(logfile, "MEM %s:%d strdup(%p) (%u) = %p\n",
+ source, line, str, len, mem);
+
+ return mem;
+}
+
+char *memdebug_strndup(const char *str, size_t size, int line, const char *source)
+{
+ char *mem;
+ size_t len;
+
+ assert(str != NULL);
+
+ if(countcheck("strndup", line, source))
+ return NULL;
+
+ len=strlen(str)+1;
+ if (size < len - 1)
+ len = size + 1;
+
+ mem=memdebug_malloc(len, 0, NULL); /* NULL prevents logging */
+ if (mem) {
+ memcpy(mem, str, len);
+ mem[len - 1] = 0;
+ }
+
+ if(logfile)
+ fprintf(logfile, "MEM %s:%d strndup(%p, %d) (%u) = %p\n",
+ source, line, str, size, len, mem);
+
+ return mem;
+}
+
+/* We provide a realloc() that accepts a NULL as pointer, which then
+ performs a malloc(). In order to work with ares. */
+void *memdebug_realloc(void *ptr, size_t wantedsize,
+ int line, const char *source)
+{
+ unsigned int i;
+ struct memdebug *mem=NULL;
+
+ size_t size = sizeof(struct memdebug)+wantedsize+8;
+
+ if(countcheck("realloc", line, source))
+ return NULL;
+
+ if(ptr) {
+ mem = (struct memdebug *)((char *)ptr - offsetof(struct memdebug, mem));
+ }
+
+ if(logfile) {
+ if (mem && mem->magic != MAGIC)
+ fprintf(logfile, "MAGIC match failed!\n");
+ for (i = 0; mem && i != 8; i++)
+ if (((char *) mem->mem)[mem->size + i] != GUARD)
+ fprintf(logfile, "GUARD %u match failed!\n", i);
+ fprintf(logfile, "MEM %s:%d realloc(%p, %u) = ",
+ source, line, ptr, wantedsize);
+ fflush(logfile);
+ }
+
+ mem=(struct memdebug *)(realloc)(mem, size);
+ if(logfile)
+ fprintf(logfile, "%p\n", mem?mem->mem:NULL);
+
+ if(mem) {
+ mem->size = wantedsize;
+ mem->magic = MAGIC;
+ for (i = 0; i != 8; i++)
+ ((char *) mem->mem)[wantedsize + i] = GUARD;
+ return mem->mem;
+ }
+
+ return NULL;
+}
+
+void memdebug_free(void *ptr, int line, const char *source)
+{
+ unsigned int i;
+ struct memdebug *mem;
+
+ if (!ptr)
+ return;
+
+ assert(ptr != NULL);
+
+ mem = (struct memdebug *)((char *)ptr - offsetof(struct memdebug, mem));
+ if(logfile) {
+ fprintf(logfile, "MEM %s:%d free(%p)\n", source, line, ptr);
+ if (mem->magic != MAGIC) {
+ fprintf(logfile, "MAGIC match failed!\n");
+ if (__dynamic_num != -1) {
+ int size;
+ byte *base_address;
+ xosdynamicarea_read(__dynamic_num, &size, &base_address,
+ 0, 0, 0, 0, 0);
+ fprintf(logfile, "saving DA %i %p %x\n", __dynamic_num, base_address,
+ size);
+ xosfile_save("core", (bits) base_address, 0, base_address,
+ base_address + size);
+ }
+ }
+ fflush(logfile);
+ for (i = 0; i != 8; i++)
+ if (((char *) mem->mem)[mem->size + i] != GUARD)
+ fprintf(logfile, "GUARD %u match failed!\n", i);
+ fflush(logfile);
+ }
+
+ /* destroy */
+ memset(mem->mem, 0x13, mem->size);
+ mem->magic = 0x13131313;
+ for (i = 0; i != 8; i++)
+ ((char *) mem->mem)[mem->size + i] = 0x13;
+
+ /* free for real */
+ (free)(mem);
+}
+
+int memdebug_socket(int domain, int type, int protocol, int line,
+ const char *source)
+{
+ int sockfd=(socket)(domain, type, protocol);
+ if(logfile && (sockfd!=-1))
+ fprintf(logfile, "FD %s:%d socket() = %d\n",
+ source, line, sockfd);
+ return sockfd;
+}
+
+int memdebug_accept(int s, void *saddr, void *saddrlen,
+ int line, const char *source)
+{
+ struct sockaddr *addr = (struct sockaddr *)saddr;
+ socklen_t *addrlen = (socklen_t *)saddrlen;
+ int sockfd=(accept)(s, addr, addrlen);
+ if(logfile)
+ fprintf(logfile, "FD %s:%d accept() = %d\n",
+ source, line, sockfd);
+ return sockfd;
+}
+
+/* this is our own defined way to close sockets on *ALL* platforms */
+int memdebug_sclose(int sockfd, int line, const char *source)
+{
+ int res=sclose(sockfd);
+ if(logfile)
+ fprintf(logfile, "FD %s:%d sclose(%d)\n",
+ source, line, sockfd);
+ return res;
+}
+
+FILE *memdebug_fopen(const char *file, const char *mode,
+ int line, const char *source)
+{
+ FILE *res=(fopen)(file, mode);
+ if(logfile)
+ fprintf(logfile, "FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
+ source, line, file, mode, res);
+ return res;
+}
+
+int memdebug_fclose(FILE *file, int line, const char *source)
+{
+ int res;
+
+ assert(file != NULL);
+
+ res=(fclose)(file);
+ if(logfile)
+ fprintf(logfile, "FILE %s:%d fclose(%p)\n",
+ source, line, file);
+ return res;
+}
diff --git a/utils/memdebug.h b/utils/memdebug.h
new file mode 100644
index 000000000..bdf933cc3
--- /dev/null
+++ b/utils/memdebug.h
@@ -0,0 +1,106 @@
+/** \file
+ * Heap debugging functions (interface).
+ *
+ * Based on memdebug.h from curl (see below), with the following modifications:
+ *
+ * - renamed functions from curl_ to memdebug_
+ * - added memdebug_strndup
+ * - added guard bytes before and after each block to help detect overflows
+ * - if a guard byte is corrupted during free, dumps the DA to file
+ */
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * $Id: memdebug.h,v 1.1 2004/07/28 22:35:02 bursa Exp $
+ ***************************************************************************/
+
+#ifndef _MEMDEBUG_H_
+#define _MEMDEBUG_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#define logfile memdebug_debuglogfile
+
+extern FILE *logfile;
+
+/* memory functions */
+void *memdebug_malloc(size_t size, int line, const char *source);
+void *memdebug_calloc(size_t elements, size_t size, int line, const char *source);
+void *memdebug_realloc(void *ptr, size_t size, int line, const char *source);
+void memdebug_free(void *ptr, int line, const char *source);
+char *memdebug_strdup(const char *str, int line, const char *source);
+char *memdebug_strndup(const char *str, size_t size, int line, const char *source);
+void memdebug_memdebug(const char *logname);
+void memdebug_memlimit(long limit);
+
+/* file descriptor manipulators */
+int memdebug_socket(int domain, int type, int protocol, int line , const char *);
+int memdebug_sclose(int sockfd, int, const char *source);
+int memdebug_accept(int s, void *addr, void *addrlen,
+ int line, const char *source);
+
+/* FILE functions */
+FILE *memdebug_fopen(const char *file, const char *mode, int line,
+ const char *source);
+int memdebug_fclose(FILE *file, int line, const char *source);
+
+#ifndef MEMDEBUG_NODEFINES
+
+#undef strdup
+#define strdup(ptr) memdebug_strdup(ptr, __LINE__, __FILE__)
+#define strndup(ptr,size) memdebug_strndup(ptr, size, __LINE__, __FILE__)
+#define malloc(size) memdebug_malloc(size, __LINE__, __FILE__)
+#define calloc(nbelem,size) memdebug_calloc(nbelem, size, __LINE__, __FILE__)
+#define realloc(ptr,size) memdebug_realloc(ptr, size, __LINE__, __FILE__)
+#define free(ptr) memdebug_free(ptr, __LINE__, __FILE__)
+
+#define socket(domain,type,protocol)\
+ memdebug_socket(domain,type,protocol,__LINE__,__FILE__)
+#undef accept /* for those with accept as a macro */
+#define accept(sock,addr,len)\
+ memdebug_accept(sock,addr,len,__LINE__,__FILE__)
+
+#define getaddrinfo(host,serv,hint,res) \
+ memdebug_getaddrinfo(host,serv,hint,res,__LINE__,__FILE__)
+#define getnameinfo(sa,salen,host,hostlen,serv,servlen,flags) \
+ memdebug_getnameinfo(sa,salen,host,hostlen,serv,servlen,flags, __LINE__, \
+ __FILE__)
+#define freeaddrinfo(data) \
+ memdebug_freeaddrinfo(data,__LINE__,__FILE__)
+
+/* sclose is probably already defined, redefine it! */
+#undef sclose
+#define sclose(sockfd) memdebug_sclose(sockfd,__LINE__,__FILE__)
+/* ares-adjusted define: */
+#undef closesocket
+#define closesocket(sockfd) memdebug_sclose(sockfd,__LINE__,__FILE__)
+
+#undef fopen
+#define fopen(file,mode) memdebug_fopen(file,mode,__LINE__,__FILE__)
+#define fclose(file) memdebug_fclose(file,__LINE__,__FILE__)
+
+#endif /* MEMDEBUG_NODEFINES */
+
+#endif