diff --git a/lib/curl_setup.h b/lib/curl_setup.h index db438a8b0a..915cb4dae0 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -1005,6 +1005,8 @@ CURL_EXTERN ALLOC_FUNC CURL_EXTERN void curl_dbg_memdebug(const char *logname); CURL_EXTERN void curl_dbg_memlimit(long limit); CURL_EXTERN void curl_dbg_log(const char *format, ...) CURL_PRINTF(1, 2); +CURL_EXTERN void curl_dbg_allow(int line, const char *source, int index); +CURL_EXTERN void curl_dbg_clear(int line, const char *source); /* file descriptor manipulators */ CURL_EXTERN curl_socket_t curl_dbg_socket(int domain, int type, int protocol, @@ -1091,6 +1093,9 @@ CURL_EXTERN ALLOC_FUNC #define CURL_SEND send #define CURL_RECV recv +#define curl_dbg_overlook(x) +#define curl_dbg_restart() + #endif /* CURLDEBUG */ /* Some versions of the Android NDK is missing the declaration */ diff --git a/lib/easy.c b/lib/easy.c index 54896a8d55..112ec99edf 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -196,9 +196,11 @@ static CURLcode global_init(long flags, bool memoryfuncs) easy_init_flags = flags; #ifdef DEBUGBUILD - if(getenv("CURL_GLOBAL_INIT")) + if(getenv("CURL_GLOBAL_INIT")) { /* alloc data that will leak if *cleanup() is not called! */ leakpointer = malloc(1); + curl_dbg_restart(); + } #endif return CURLE_OK; diff --git a/lib/getenv.c b/lib/getenv.c index a2d8056fcc..8e42b1b6c7 100644 --- a/lib/getenv.c +++ b/lib/getenv.c @@ -70,6 +70,7 @@ static char *GetEnv(const char *variable) } #else char *env = getenv(variable); + curl_dbg_overlook(1); return (env && env[0]) ? strdup(env) : NULL; #endif } diff --git a/lib/memdebug.c b/lib/memdebug.c index 758c7b6aa7..68155bbadb 100644 --- a/lib/memdebug.c +++ b/lib/memdebug.c @@ -110,8 +110,7 @@ static bool countcheck(const char *func, int line, const char *source) if(memlimit && source) { if(!memsize) { /* log to file */ - curl_dbg_log("LIMIT %s:%d %s reached memlimit\n", - source, line, func); + curl_dbg_log("LIMIT %s:%d %s reached memlimit\n", source, line, func); /* log to stderr also */ curl_mfprintf(stderr, "LIMIT %s:%d %s reached memlimit\n", source, line, func); @@ -135,8 +134,11 @@ void *curl_dbg_malloc(size_t wantedsize, int line, const char *source) DEBUGASSERT(wantedsize != 0); - if(countcheck("malloc", line, source)) + if(countcheck("malloc", line, source)) { + curl_dbg_log("FAIL %s:%d malloc(%zu) = NULL\n", + source, line, wantedsize); return NULL; + } /* alloc at least 64 bytes */ size = sizeof(struct memdebug) + wantedsize; @@ -164,8 +166,11 @@ void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size, DEBUGASSERT(wanted_elements != 0); DEBUGASSERT(wanted_size != 0); - if(countcheck("calloc", line, source)) + if(countcheck("calloc", line, source)) { + curl_dbg_log("FAIL %s:%d calloc(%zu,%zu) = NULL\n", + source, line, wanted_elements, wanted_size); return NULL; + } /* alloc at least 64 bytes */ user_size = wanted_size * wanted_elements; @@ -191,11 +196,14 @@ char *curl_dbg_strdup(const char *str, int line, const char *source) DEBUGASSERT(str != NULL); - if(countcheck("strdup", line, source)) - return NULL; - len = strlen(str) + 1; + if(countcheck("strdup", line, source)) { + curl_dbg_log("FAIL %s:%d strdup(%zu bytes) = NULL\n", source, line, + len); + return NULL; + } + mem = curl_dbg_malloc(len, 0, NULL); /* NULL prevents logging */ if(mem) memcpy(mem, str, len); @@ -216,8 +224,10 @@ wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source) DEBUGASSERT(str != NULL); - if(countcheck("wcsdup", line, source)) + if(countcheck("wcsdup", line, source)) { + curl_dbg_log("FAIL %s:%d wcdup() = NULL\n", source, line); return NULL; + } wsiz = wcslen(str) + 1; bsiz = wsiz * sizeof(wchar_t); @@ -245,8 +255,11 @@ void *curl_dbg_realloc(void *ptr, size_t wantedsize, DEBUGASSERT(wantedsize != 0); - if(countcheck("realloc", line, source)) + if(countcheck("realloc", line, source)) { + curl_dbg_log("FAIL %s:%d realloc(%zu) = NULL\n", + source, line, wantedsize); return NULL; + } #ifdef __INTEL_COMPILER # pragma warning(push) @@ -305,8 +318,11 @@ curl_socket_t curl_dbg_socket(int domain, int type, int protocol, { curl_socket_t sockfd; - if(countcheck("socket", line, source)) + if(countcheck("socket", line, source)) { + curl_dbg_log("FAIL %s:%d socket() = [bad]\n", + source, line); return CURL_SOCKET_BAD; + } /* !checksrc! disable BANNEDFUNC 1 */ sockfd = socket(domain, type, protocol); @@ -324,8 +340,10 @@ SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd, int line, const char *source) { SEND_TYPE_RETV rc; - if(countcheck("send", line, source)) + if(countcheck("send", line, source)) { + curl_dbg_log("FAIL %s:%d send() = -1\n", source, line); return -1; + } /* !checksrc! disable BANNEDFUNC 1 */ rc = send(sockfd, buf, len, flags); if(source) @@ -339,8 +357,10 @@ RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf, int line, const char *source) { RECV_TYPE_RETV rc; - if(countcheck("recv", line, source)) + if(countcheck("recv", line, source)) { + curl_dbg_log("FAIL %s:%d recv() = -1\n", source, line); return -1; + } /* !checksrc! disable BANNEDFUNC 1 */ rc = recv(sockfd, buf, len, flags); if(source) @@ -490,4 +510,15 @@ void curl_dbg_log(const char *format, ...) (fwrite)(buf, 1, (size_t)nchars, curl_dbg_logfile); } +void curl_dbg_allow(int line, const char *source, int index) +{ + curl_dbg_log("OVERLOOK %s:%d\n", source, line + index); +} + +void curl_dbg_clear(int line, const char *source) +{ + curl_dbg_log("RESTART %s:%d\n", source, line); +} + + #endif /* CURLDEBUG */ diff --git a/lib/memdebug.h b/lib/memdebug.h index c2b7fad952..0e15d27fa4 100644 --- a/lib/memdebug.h +++ b/lib/memdebug.h @@ -52,5 +52,8 @@ #endif #endif /* _WIN32 */ +#define curl_dbg_overlook(x) curl_dbg_allow(__LINE__, __FILE__, x) +#define curl_dbg_restart() curl_dbg_clear(__LINE__, __FILE__) + #endif /* CURLDEBUG */ #endif /* HEADER_CURL_MEMDEBUG_H */ diff --git a/src/tool_operate.c b/src/tool_operate.c index 69cfef96d7..c56ce8a63e 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -776,6 +776,7 @@ skip: free(per->uploadfile); curl_slist_free_all(per->hdrcbdata.headlist); per->hdrcbdata.headlist = NULL; + curl_dbg_restart(); return result; } diff --git a/src/tool_writeout.c b/src/tool_writeout.c index cdfa23a752..c56d6a2b1b 100644 --- a/src/tool_writeout.c +++ b/src/tool_writeout.c @@ -733,6 +733,7 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per, if(!writeinfo) return; + curl_dbg_restart(); curlx_dyn_init(&name, MAX_WRITEOUT_NAME_LENGTH); while(ptr && *ptr && !done) { if('%' == *ptr && ptr[1]) { diff --git a/tests/data/test558 b/tests/data/test558 index 359d690cf9..f66b71d3f5 100644 --- a/tests/data/test558 +++ b/tests/data/test558 @@ -31,6 +31,7 @@ libtest memory tracking operational MEM easy.c: malloc() +RESTART easy.c: MEM lib%TESTNUMBER.c: malloc() MEM lib%TESTNUMBER.c: free() MEM dynbuf.c: realloc() @@ -43,10 +44,11 @@ s/^MEM escape.c:\d+ free\(\(nil\)\)[\n]$// s/ =.*// s/\(.*\)/()/ s/:\d+/:/ -s:^(MEM |FD )(.*[/\\])(.*):$1$3: +s:^(MEM |FD |OVERLOOK |RESTART )(.*[/\\])(.*):$1$3: s/\r\n/\n/ s/^MEM getenv.c: realloc\(\)[\n]$// s/^MEM getenv.c: free\(\)[\n]$// +s/^OVERLOOK getenv.c:[\n]$// diff --git a/tests/memanalyze.pl b/tests/memanalyze.pl index e35b2e7aba..be44650f0b 100755 --- a/tests/memanalyze.pl +++ b/tests/memanalyze.pl @@ -43,6 +43,7 @@ my $recvs=0; my $sockets=0; my $verbose=0; my $trace=0; +my $strict; while(@ARGV) { if($ARGV[0] eq "-v") { @@ -53,6 +54,11 @@ while(@ARGV) { $trace=1; shift @ARGV; } + elsif($ARGV[0] eq "-s") { + $strict= $ARGV[1]; + shift @ARGV; + shift @ARGV; + } elsif($ARGV[0] eq "-l") { # only show what alloc that caused a memlimit failure $showlimit=1; @@ -79,10 +85,11 @@ my $file = $ARGV[0] || ''; if(! -f $file) { print "Usage: memanalyze.pl [options] \n", - "Options:\n", - " -l memlimit failure displayed\n", - " -v Verbose\n", - " -t Trace\n"; + "Options:\n", + " -l memlimit failure displayed\n", + " -v Verbose\n", + " -s [path] Strict - warn on mallocs after memlimit is reached\n", + " -t Trace\n"; exit; } @@ -124,18 +131,37 @@ my $addrinfos = 0; my $source; my $linenum; my $function; +my $memwarn = 0; my $lnum = 0; + +my %overlook; + while(<$fileh>) { chomp $_; my $line = $_; $lnum++; - if($line =~ /^LIMIT ([^ ]*):(\d*) (.*)/) { + if($line =~ /^FAIL (.*)/) { + # for informational purposes + } + elsif($line =~ /^RESTART ([^ ]*):(\d*)/) { + # allow a new LIMIT after this + $memwarn = 0; + } + elsif($line =~ /^OVERLOOK ([^ ]*):(\d*)/) { + $overlook{$1, $2} = 1; + } + elsif($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"; + if($i =~ /([^ ]*) reached memlimit/) { + if($trace) { + print "LIMIT: $1 returned error at $source:$linenum\n"; + } + if($strict && !$overlook{$source, $linenum}) { + $memwarn++; + } } } elsif($line =~ /^MEM ([^ ]*):(\d*) (.*)/) { diff --git a/tests/runner.pm b/tests/runner.pm index a73f9d5999..0eef26241c 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -549,7 +549,7 @@ sub torture { $fail=1; } else { - my @memdata=`$memanalyze "$LOGDIR/$MEMDUMP"`; + my @memdata=`$memanalyze -s "$srcdir" "$LOGDIR/$MEMDUMP"`; my $leak=0; for(@memdata) { if($_ ne "") {