quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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