memdebug.c (12653B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 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. 13 * 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. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25 #include "curl_setup.h" 26 27 #ifdef CURLDEBUG 28 29 #include <curl/curl.h> 30 31 #include "urldata.h" 32 33 /* The last 3 #include files should be in this order */ 34 #include "curl_printf.h" 35 #include "curl_memory.h" 36 #include "memdebug.h" 37 38 struct memdebug { 39 size_t size; 40 union { 41 curl_off_t o; 42 double d; 43 void *p; 44 } mem[1]; 45 /* I am hoping this is the thing with the strictest alignment 46 * requirements. That also means we waste some space :-( */ 47 }; 48 49 /* 50 * Note that these debug functions are simple and they are meant to remain so. 51 * For advanced analysis, record a log file and write perl scripts to analyze 52 * them! 53 * 54 * Do not use these with multithreaded test programs! 55 */ 56 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 */ 61 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) 67 { 68 if(curl_dbg_logfile && 69 curl_dbg_logfile != stderr && 70 curl_dbg_logfile != stdout) { 71 (fclose)(curl_dbg_logfile); 72 } 73 curl_dbg_logfile = NULL; 74 } 75 76 /* this sets the log filename */ 77 void curl_dbg_memdebug(const char *logname) 78 { 79 if(!curl_dbg_logfile) { 80 if(logname && *logname) 81 #ifdef CURL_FOPEN 82 curl_dbg_logfile = CURL_FOPEN(logname, FOPEN_WRITETEXT); 83 #else 84 curl_dbg_logfile = (fopen)(logname, FOPEN_WRITETEXT); 85 #endif 86 else 87 curl_dbg_logfile = stderr; 88 #ifdef MEMDEBUG_LOG_SYNC 89 /* Flush the log file after every line so the log is not lost in a crash */ 90 if(curl_dbg_logfile) 91 setbuf(curl_dbg_logfile, (char *)NULL); 92 #endif 93 } 94 if(!registered_cleanup) 95 registered_cleanup = !atexit(curl_dbg_cleanup); 96 } 97 98 /* This function sets the number of malloc() calls that should return 99 successfully! */ 100 void curl_dbg_memlimit(long limit) 101 { 102 if(!memlimit) { 103 memlimit = TRUE; 104 memsize = limit; 105 } 106 } 107 108 /* returns TRUE if this is not allowed! */ 109 static bool countcheck(const char *func, int line, const char *source) 110 { 111 /* if source is NULL, then the call is made internally and this check 112 should not be made */ 113 if(memlimit && source) { 114 if(!memsize) { 115 /* log to file */ 116 curl_dbg_log("LIMIT %s:%d %s reached memlimit\n", 117 source, line, func); 118 /* log to stderr also */ 119 fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n", 120 source, line, func); 121 fflush(curl_dbg_logfile); /* because it might crash now */ 122 /* !checksrc! disable ERRNOVAR 1 */ 123 CURL_SETERRNO(ENOMEM); 124 return TRUE; /* RETURN ERROR! */ 125 } 126 else 127 memsize--; /* countdown */ 128 } 129 130 return FALSE; /* allow this */ 131 } 132 133 ALLOC_FUNC 134 void *curl_dbg_malloc(size_t wantedsize, int line, const char *source) 135 { 136 struct memdebug *mem; 137 size_t size; 138 139 DEBUGASSERT(wantedsize != 0); 140 141 if(countcheck("malloc", line, source)) 142 return NULL; 143 144 /* alloc at least 64 bytes */ 145 size = sizeof(struct memdebug) + wantedsize; 146 147 mem = (Curl_cmalloc)(size); 148 if(mem) { 149 mem->size = wantedsize; 150 } 151 152 if(source) 153 curl_dbg_log("MEM %s:%d malloc(%zu) = %p\n", 154 source, line, wantedsize, 155 mem ? (void *)mem->mem : (void *)0); 156 157 return mem ? mem->mem : NULL; 158 } 159 160 ALLOC_FUNC 161 void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size, 162 int line, const char *source) 163 { 164 struct memdebug *mem; 165 size_t size, user_size; 166 167 DEBUGASSERT(wanted_elements != 0); 168 DEBUGASSERT(wanted_size != 0); 169 170 if(countcheck("calloc", line, source)) 171 return NULL; 172 173 /* alloc at least 64 bytes */ 174 user_size = wanted_size * wanted_elements; 175 size = sizeof(struct memdebug) + user_size; 176 177 mem = (Curl_ccalloc)(1, size); 178 if(mem) 179 mem->size = user_size; 180 181 if(source) 182 curl_dbg_log("MEM %s:%d calloc(%zu,%zu) = %p\n", 183 source, line, wanted_elements, wanted_size, 184 mem ? (void *)mem->mem : (void *)0); 185 186 return mem ? mem->mem : NULL; 187 } 188 189 ALLOC_FUNC 190 char *curl_dbg_strdup(const char *str, int line, const char *source) 191 { 192 char *mem; 193 size_t len; 194 195 DEBUGASSERT(str != NULL); 196 197 if(countcheck("strdup", line, source)) 198 return NULL; 199 200 len = strlen(str) + 1; 201 202 mem = curl_dbg_malloc(len, 0, NULL); /* NULL prevents logging */ 203 if(mem) 204 memcpy(mem, str, len); 205 206 if(source) 207 curl_dbg_log("MEM %s:%d strdup(%p) (%zu) = %p\n", 208 source, line, (const void *)str, len, (const void *)mem); 209 210 return mem; 211 } 212 213 #if defined(_WIN32) && defined(UNICODE) 214 ALLOC_FUNC 215 wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source) 216 { 217 wchar_t *mem; 218 size_t wsiz, bsiz; 219 220 DEBUGASSERT(str != NULL); 221 222 if(countcheck("wcsdup", line, source)) 223 return NULL; 224 225 wsiz = wcslen(str) + 1; 226 bsiz = wsiz * sizeof(wchar_t); 227 228 mem = curl_dbg_malloc(bsiz, 0, NULL); /* NULL prevents logging */ 229 if(mem) 230 memcpy(mem, str, bsiz); 231 232 if(source) 233 curl_dbg_log("MEM %s:%d wcsdup(%p) (%zu) = %p\n", 234 source, line, (const void *)str, bsiz, (void *)mem); 235 236 return mem; 237 } 238 #endif 239 240 /* We provide a realloc() that accepts a NULL as pointer, which then 241 performs a malloc(). In order to work with ares. */ 242 void *curl_dbg_realloc(void *ptr, size_t wantedsize, 243 int line, const char *source) 244 { 245 struct memdebug *mem = NULL; 246 247 size_t size = sizeof(struct memdebug) + wantedsize; 248 249 DEBUGASSERT(wantedsize != 0); 250 251 if(countcheck("realloc", line, source)) 252 return NULL; 253 254 #ifdef __INTEL_COMPILER 255 # pragma warning(push) 256 # pragma warning(disable:1684) 257 /* 1684: conversion from pointer to same-sized integral type */ 258 #endif 259 260 if(ptr) 261 mem = (void *)((char *)ptr - offsetof(struct memdebug, mem)); 262 263 #ifdef __INTEL_COMPILER 264 # pragma warning(pop) 265 #endif 266 267 mem = (Curl_crealloc)(mem, size); 268 if(source) 269 curl_dbg_log("MEM %s:%d realloc(%p, %zu) = %p\n", 270 source, line, (void *)ptr, wantedsize, 271 mem ? (void *)mem->mem : (void *)0); 272 273 if(mem) { 274 mem->size = wantedsize; 275 return mem->mem; 276 } 277 278 return NULL; 279 } 280 281 void curl_dbg_free(void *ptr, int line, const char *source) 282 { 283 if(ptr) { 284 struct memdebug *mem; 285 286 #ifdef __INTEL_COMPILER 287 # pragma warning(push) 288 # pragma warning(disable:1684) 289 /* 1684: conversion from pointer to same-sized integral type */ 290 #endif 291 292 mem = (void *)((char *)ptr - offsetof(struct memdebug, mem)); 293 294 #ifdef __INTEL_COMPILER 295 # pragma warning(pop) 296 #endif 297 298 /* free for real */ 299 (Curl_cfree)(mem); 300 } 301 302 if(source && ptr) 303 curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr); 304 } 305 306 curl_socket_t curl_dbg_socket(int domain, int type, int protocol, 307 int line, const char *source) 308 { 309 curl_socket_t sockfd; 310 311 if(countcheck("socket", line, source)) 312 return CURL_SOCKET_BAD; 313 314 sockfd = (socket)(domain, type, protocol); 315 316 if(source && (sockfd != CURL_SOCKET_BAD)) 317 curl_dbg_log("FD %s:%d socket() = %" FMT_SOCKET_T "\n", 318 source, line, sockfd); 319 320 return sockfd; 321 } 322 323 SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd, 324 SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf, 325 SEND_TYPE_ARG3 len, SEND_TYPE_ARG4 flags, int line, 326 const char *source) 327 { 328 SEND_TYPE_RETV rc; 329 if(countcheck("send", line, source)) 330 return -1; 331 rc = (send)(sockfd, buf, len, flags); 332 if(source) 333 curl_dbg_log("SEND %s:%d send(%lu) = %ld\n", 334 source, line, (unsigned long)len, (long)rc); 335 return rc; 336 } 337 338 RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf, 339 RECV_TYPE_ARG3 len, RECV_TYPE_ARG4 flags, int line, 340 const char *source) 341 { 342 RECV_TYPE_RETV rc; 343 if(countcheck("recv", line, source)) 344 return -1; 345 rc = (recv)(sockfd, buf, len, flags); 346 if(source) 347 curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n", 348 source, line, (unsigned long)len, (long)rc); 349 return rc; 350 } 351 352 #ifdef HAVE_SOCKETPAIR 353 int curl_dbg_socketpair(int domain, int type, int protocol, 354 curl_socket_t socket_vector[2], 355 int line, const char *source) 356 { 357 int res = (socketpair)(domain, type, protocol, socket_vector); 358 359 if(source && (0 == res)) 360 curl_dbg_log("FD %s:%d socketpair() = " 361 "%" FMT_SOCKET_T " %" FMT_SOCKET_T "\n", 362 source, line, socket_vector[0], socket_vector[1]); 363 364 return res; 365 } 366 #endif 367 368 curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen, 369 int line, const char *source) 370 { 371 struct sockaddr *addr = (struct sockaddr *)saddr; 372 curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; 373 374 curl_socket_t sockfd = (accept)(s, addr, addrlen); 375 376 if(source && (sockfd != CURL_SOCKET_BAD)) 377 curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n", 378 source, line, sockfd); 379 380 return sockfd; 381 } 382 383 #ifdef HAVE_ACCEPT4 384 curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr, void *saddrlen, 385 int flags, 386 int line, const char *source) 387 { 388 struct sockaddr *addr = (struct sockaddr *)saddr; 389 curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; 390 391 curl_socket_t sockfd = (accept4)(s, addr, addrlen, flags); 392 393 if(source && (sockfd != CURL_SOCKET_BAD)) 394 curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n", 395 source, line, sockfd); 396 397 return sockfd; 398 } 399 #endif 400 401 /* separate function to allow libcurl to mark a "faked" close */ 402 void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source) 403 { 404 if(source) 405 curl_dbg_log("FD %s:%d sclose(%" FMT_SOCKET_T ")\n", 406 source, line, sockfd); 407 } 408 409 /* this is our own defined way to close sockets on *ALL* platforms */ 410 int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source) 411 { 412 int res = CURL_SCLOSE(sockfd); 413 curl_dbg_mark_sclose(sockfd, line, source); 414 return res; 415 } 416 417 ALLOC_FUNC 418 FILE *curl_dbg_fopen(const char *file, const char *mode, 419 int line, const char *source) 420 { 421 FILE *res; 422 #ifdef CURL_FOPEN 423 res = CURL_FOPEN(file, mode); 424 #else 425 res = (fopen)(file, mode); 426 #endif 427 428 if(source) 429 curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n", 430 source, line, file, mode, (void *)res); 431 432 return res; 433 } 434 435 ALLOC_FUNC 436 FILE *curl_dbg_fdopen(int filedes, const char *mode, 437 int line, const char *source) 438 { 439 FILE *res = (fdopen)(filedes, mode); 440 if(source) 441 curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n", 442 source, line, filedes, mode, (void *)res); 443 return res; 444 } 445 446 int curl_dbg_fclose(FILE *file, int line, const char *source) 447 { 448 int res; 449 450 DEBUGASSERT(file != NULL); 451 452 if(source) 453 curl_dbg_log("FILE %s:%d fclose(%p)\n", 454 source, line, (void *)file); 455 456 res = (fclose)(file); 457 458 return res; 459 } 460 461 /* this does the writing to the memory tracking log file */ 462 void curl_dbg_log(const char *format, ...) 463 { 464 char buf[1024]; 465 int nchars; 466 va_list ap; 467 468 if(!curl_dbg_logfile) 469 return; 470 471 va_start(ap, format); 472 nchars = mvsnprintf(buf, sizeof(buf), format, ap); 473 va_end(ap); 474 475 if(nchars > (int)sizeof(buf) - 1) 476 nchars = (int)sizeof(buf) - 1; 477 478 if(nchars > 0) 479 (fwrite)(buf, 1, (size_t)nchars, curl_dbg_logfile); 480 } 481 482 #endif /* CURLDEBUG */