1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
27 #include <curl/curl.h>
31 #define MEMDEBUG_NODEFINES /* don't redefine the standard functions */
33 /* The last 3 #include files should be in this order */
34 #include "curl_printf.h"
35 #include "curl_memory.h"
45 /* I'm hoping this is the thing with the strictest alignment
46 * requirements. That also means we waste some space :-( */
50 * Note that these debug functions are very simple and they are meant to
51 * remain so. For advanced analysis, record a log file and write perl scripts
54 * Don't use these with multithreaded test programs!
57 FILE *curl_dbg_logfile = NULL;
58 static bool registered_cleanup = FALSE; /* atexit registered cleanup */
59 static bool memlimit = FALSE; /* enable memory limit */
60 static long memsize = 0; /* set number of mallocs allowed */
62 /* LeakSantizier (LSAN) calls _exit() instead of exit() when a leak is detected
63 on exit so the logfile must be closed explicitly or data could be lost.
64 Though _exit() does not call atexit handlers such as this, LSAN's call to
65 _exit() comes after the atexit handlers are called. curl/curl#6620 */
66 static void curl_dbg_cleanup(void)
68 if(curl_dbg_logfile &&
69 curl_dbg_logfile != stderr &&
70 curl_dbg_logfile != stdout) {
71 fclose(curl_dbg_logfile);
73 curl_dbg_logfile = NULL;
76 /* this sets the log file name */
77 void curl_dbg_memdebug(const char *logname)
79 if(!curl_dbg_logfile) {
80 if(logname && *logname)
81 curl_dbg_logfile = fopen(logname, FOPEN_WRITETEXT);
83 curl_dbg_logfile = stderr;
84 #ifdef MEMDEBUG_LOG_SYNC
85 /* Flush the log file after every line so the log isn't lost in a crash */
87 setbuf(curl_dbg_logfile, (char *)NULL);
90 if(!registered_cleanup)
91 registered_cleanup = !atexit(curl_dbg_cleanup);
94 /* This function sets the number of malloc() calls that should return
96 void curl_dbg_memlimit(long limit)
104 /* returns TRUE if this isn't allowed! */
105 static bool countcheck(const char *func, int line, const char *source)
107 /* if source is NULL, then the call is made internally and this check
108 should not be made */
109 if(memlimit && source) {
112 curl_dbg_log("LIMIT %s:%d %s reached memlimit\n",
114 /* log to stderr also */
115 fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
117 fflush(curl_dbg_logfile); /* because it might crash now */
119 return TRUE; /* RETURN ERROR! */
122 memsize--; /* countdown */
127 return FALSE; /* allow this */
130 void *curl_dbg_malloc(size_t wantedsize, int line, const char *source)
132 struct memdebug *mem;
135 DEBUGASSERT(wantedsize != 0);
137 if(countcheck("malloc", line, source))
140 /* alloc at least 64 bytes */
141 size = sizeof(struct memdebug) + wantedsize;
143 mem = (Curl_cmalloc)(size);
145 mem->size = wantedsize;
149 curl_dbg_log("MEM %s:%d malloc(%zu) = %p\n",
150 source, line, wantedsize,
151 mem ? (void *)mem->mem : (void *)0);
153 return (mem ? mem->mem : NULL);
156 void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
157 int line, const char *source)
159 struct memdebug *mem;
160 size_t size, user_size;
162 DEBUGASSERT(wanted_elements != 0);
163 DEBUGASSERT(wanted_size != 0);
165 if(countcheck("calloc", line, source))
168 /* alloc at least 64 bytes */
169 user_size = wanted_size * wanted_elements;
170 size = sizeof(struct memdebug) + user_size;
172 mem = (Curl_ccalloc)(1, size);
174 mem->size = user_size;
177 curl_dbg_log("MEM %s:%d calloc(%zu,%zu) = %p\n",
178 source, line, wanted_elements, wanted_size,
179 mem ? (void *)mem->mem : (void *)0);
181 return (mem ? mem->mem : NULL);
184 char *curl_dbg_strdup(const char *str, int line, const char *source)
189 DEBUGASSERT(str != NULL);
191 if(countcheck("strdup", line, source))
194 len = strlen(str) + 1;
196 mem = curl_dbg_malloc(len, 0, NULL); /* NULL prevents logging */
198 memcpy(mem, str, len);
201 curl_dbg_log("MEM %s:%d strdup(%p) (%zu) = %p\n",
202 source, line, (const void *)str, len, (const void *)mem);
207 #if defined(WIN32) && defined(UNICODE)
208 wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source)
213 DEBUGASSERT(str != NULL);
215 if(countcheck("wcsdup", line, source))
218 wsiz = wcslen(str) + 1;
219 bsiz = wsiz * sizeof(wchar_t);
221 mem = curl_dbg_malloc(bsiz, 0, NULL); /* NULL prevents logging */
223 memcpy(mem, str, bsiz);
226 curl_dbg_log("MEM %s:%d wcsdup(%p) (%zu) = %p\n",
227 source, line, (void *)str, bsiz, (void *)mem);
233 /* We provide a realloc() that accepts a NULL as pointer, which then
234 performs a malloc(). In order to work with ares. */
235 void *curl_dbg_realloc(void *ptr, size_t wantedsize,
236 int line, const char *source)
238 struct memdebug *mem = NULL;
240 size_t size = sizeof(struct memdebug) + wantedsize;
242 DEBUGASSERT(wantedsize != 0);
244 if(countcheck("realloc", line, source))
247 #ifdef __INTEL_COMPILER
248 # pragma warning(push)
249 # pragma warning(disable:1684)
250 /* 1684: conversion from pointer to same-sized integral type */
254 mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
256 #ifdef __INTEL_COMPILER
257 # pragma warning(pop)
260 mem = (Curl_crealloc)(mem, size);
262 curl_dbg_log("MEM %s:%d realloc(%p, %zu) = %p\n",
263 source, line, (void *)ptr, wantedsize,
264 mem ? (void *)mem->mem : (void *)0);
267 mem->size = wantedsize;
274 void curl_dbg_free(void *ptr, int line, const char *source)
277 struct memdebug *mem;
279 #ifdef __INTEL_COMPILER
280 # pragma warning(push)
281 # pragma warning(disable:1684)
282 /* 1684: conversion from pointer to same-sized integral type */
285 mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
287 #ifdef __INTEL_COMPILER
288 # pragma warning(pop)
296 curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr);
299 curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
300 int line, const char *source)
302 const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
303 "FD %s:%d socket() = %d\n" :
304 (sizeof(curl_socket_t) == sizeof(long)) ?
305 "FD %s:%d socket() = %ld\n" :
306 "FD %s:%d socket() = %zd\n";
308 curl_socket_t sockfd;
310 if(countcheck("socket", line, source))
311 return CURL_SOCKET_BAD;
313 sockfd = socket(domain, type, protocol);
315 if(source && (sockfd != CURL_SOCKET_BAD))
316 curl_dbg_log(fmt, source, line, sockfd);
321 SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd,
322 SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf,
323 SEND_TYPE_ARG3 len, SEND_TYPE_ARG4 flags, int line,
327 if(countcheck("send", line, source))
329 rc = send(sockfd, buf, len, flags);
331 curl_dbg_log("SEND %s:%d send(%lu) = %ld\n",
332 source, line, (unsigned long)len, (long)rc);
336 RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf,
337 RECV_TYPE_ARG3 len, RECV_TYPE_ARG4 flags, int line,
341 if(countcheck("recv", line, source))
343 rc = recv(sockfd, buf, len, flags);
345 curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n",
346 source, line, (unsigned long)len, (long)rc);
350 #ifdef HAVE_SOCKETPAIR
351 int curl_dbg_socketpair(int domain, int type, int protocol,
352 curl_socket_t socket_vector[2],
353 int line, const char *source)
355 const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
356 "FD %s:%d socketpair() = %d %d\n" :
357 (sizeof(curl_socket_t) == sizeof(long)) ?
358 "FD %s:%d socketpair() = %ld %ld\n" :
359 "FD %s:%d socketpair() = %zd %zd\n";
361 int res = socketpair(domain, type, protocol, socket_vector);
363 if(source && (0 == res))
364 curl_dbg_log(fmt, source, line, socket_vector[0], socket_vector[1]);
370 curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
371 int line, const char *source)
373 const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
374 "FD %s:%d accept() = %d\n" :
375 (sizeof(curl_socket_t) == sizeof(long)) ?
376 "FD %s:%d accept() = %ld\n" :
377 "FD %s:%d accept() = %zd\n";
379 struct sockaddr *addr = (struct sockaddr *)saddr;
380 curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
382 curl_socket_t sockfd = accept(s, addr, addrlen);
384 if(source && (sockfd != CURL_SOCKET_BAD))
385 curl_dbg_log(fmt, source, line, sockfd);
390 /* separate function to allow libcurl to mark a "faked" close */
391 void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source)
393 const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
394 "FD %s:%d sclose(%d)\n":
395 (sizeof(curl_socket_t) == sizeof(long)) ?
396 "FD %s:%d sclose(%ld)\n":
397 "FD %s:%d sclose(%zd)\n";
400 curl_dbg_log(fmt, source, line, sockfd);
403 /* this is our own defined way to close sockets on *ALL* platforms */
404 int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source)
406 int res = sclose(sockfd);
407 curl_dbg_mark_sclose(sockfd, line, source);
411 FILE *curl_dbg_fopen(const char *file, const char *mode,
412 int line, const char *source)
414 FILE *res = fopen(file, mode);
417 curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
418 source, line, file, mode, (void *)res);
423 FILE *curl_dbg_fdopen(int filedes, const char *mode,
424 int line, const char *source)
426 FILE *res = fdopen(filedes, mode);
428 curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
429 source, line, filedes, mode, (void *)res);
433 int curl_dbg_fclose(FILE *file, int line, const char *source)
437 DEBUGASSERT(file != NULL);
440 curl_dbg_log("FILE %s:%d fclose(%p)\n",
441 source, line, (void *)file);
448 #define LOGLINE_BUFSIZE 1024
450 /* this does the writing to the memory tracking log file */
451 void curl_dbg_log(const char *format, ...)
457 if(!curl_dbg_logfile)
460 buf = (Curl_cmalloc)(LOGLINE_BUFSIZE);
464 va_start(ap, format);
465 nchars = mvsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
468 if(nchars > LOGLINE_BUFSIZE - 1)
469 nchars = LOGLINE_BUFSIZE - 1;
472 fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile);
477 #endif /* CURLDEBUG */