050c5d4b2f1e2a18a7d6f6bc8e629c2906bdcc43
[platform/upstream/cmake.git] / Utilities / cmcurl / lib / memdebug.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2021, 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  ***************************************************************************/
22
23 #include "curl_setup.h"
24
25 #ifdef CURLDEBUG
26
27 #include <curl/curl.h>
28
29 #include "urldata.h"
30
31 #define MEMDEBUG_NODEFINES /* don't redefine the standard functions */
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'm 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 very simple and they are meant to
51  * remain so. For advanced analysis, record a log file and write perl scripts
52  * to analyze them!
53  *
54  * Don't 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 file name */
77 void curl_dbg_memdebug(const char *logname)
78 {
79   if(!curl_dbg_logfile) {
80     if(logname && *logname)
81       curl_dbg_logfile = fopen(logname, FOPEN_WRITETEXT);
82     else
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 */
86     if(curl_dbg_logfile)
87       setbuf(curl_dbg_logfile, (char *)NULL);
88 #endif
89   }
90   if(!registered_cleanup)
91     registered_cleanup = !atexit(curl_dbg_cleanup);
92 }
93
94 /* This function sets the number of malloc() calls that should return
95    successfully! */
96 void curl_dbg_memlimit(long limit)
97 {
98   if(!memlimit) {
99     memlimit = TRUE;
100     memsize = limit;
101   }
102 }
103
104 /* returns TRUE if this isn't allowed! */
105 static bool countcheck(const char *func, int line, const char *source)
106 {
107   /* if source is NULL, then the call is made internally and this check
108      should not be made */
109   if(memlimit && source) {
110     if(!memsize) {
111       /* log to file */
112       curl_dbg_log("LIMIT %s:%d %s reached memlimit\n",
113                    source, line, func);
114       /* log to stderr also */
115       fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
116               source, line, func);
117       fflush(curl_dbg_logfile); /* because it might crash now */
118       errno = ENOMEM;
119       return TRUE; /* RETURN ERROR! */
120     }
121     else
122       memsize--; /* countdown */
123
124
125   }
126
127   return FALSE; /* allow this */
128 }
129
130 void *curl_dbg_malloc(size_t wantedsize, int line, const char *source)
131 {
132   struct memdebug *mem;
133   size_t size;
134
135   DEBUGASSERT(wantedsize != 0);
136
137   if(countcheck("malloc", line, source))
138     return NULL;
139
140   /* alloc at least 64 bytes */
141   size = sizeof(struct memdebug) + wantedsize;
142
143   mem = (Curl_cmalloc)(size);
144   if(mem) {
145     mem->size = wantedsize;
146   }
147
148   if(source)
149     curl_dbg_log("MEM %s:%d malloc(%zu) = %p\n",
150                  source, line, wantedsize,
151                  mem ? (void *)mem->mem : (void *)0);
152
153   return (mem ? mem->mem : NULL);
154 }
155
156 void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
157                       int line, const char *source)
158 {
159   struct memdebug *mem;
160   size_t size, user_size;
161
162   DEBUGASSERT(wanted_elements != 0);
163   DEBUGASSERT(wanted_size != 0);
164
165   if(countcheck("calloc", line, source))
166     return NULL;
167
168   /* alloc at least 64 bytes */
169   user_size = wanted_size * wanted_elements;
170   size = sizeof(struct memdebug) + user_size;
171
172   mem = (Curl_ccalloc)(1, size);
173   if(mem)
174     mem->size = user_size;
175
176   if(source)
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);
180
181   return (mem ? mem->mem : NULL);
182 }
183
184 char *curl_dbg_strdup(const char *str, int line, const char *source)
185 {
186   char *mem;
187   size_t len;
188
189   DEBUGASSERT(str != NULL);
190
191   if(countcheck("strdup", line, source))
192     return NULL;
193
194   len = strlen(str) + 1;
195
196   mem = curl_dbg_malloc(len, 0, NULL); /* NULL prevents logging */
197   if(mem)
198     memcpy(mem, str, len);
199
200   if(source)
201     curl_dbg_log("MEM %s:%d strdup(%p) (%zu) = %p\n",
202                  source, line, (const void *)str, len, (const void *)mem);
203
204   return mem;
205 }
206
207 #if defined(WIN32) && defined(UNICODE)
208 wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source)
209 {
210   wchar_t *mem;
211   size_t wsiz, bsiz;
212
213   DEBUGASSERT(str != NULL);
214
215   if(countcheck("wcsdup", line, source))
216     return NULL;
217
218   wsiz = wcslen(str) + 1;
219   bsiz = wsiz * sizeof(wchar_t);
220
221   mem = curl_dbg_malloc(bsiz, 0, NULL); /* NULL prevents logging */
222   if(mem)
223     memcpy(mem, str, bsiz);
224
225   if(source)
226     curl_dbg_log("MEM %s:%d wcsdup(%p) (%zu) = %p\n",
227                 source, line, (void *)str, bsiz, (void *)mem);
228
229   return mem;
230 }
231 #endif
232
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)
237 {
238   struct memdebug *mem = NULL;
239
240   size_t size = sizeof(struct memdebug) + wantedsize;
241
242   DEBUGASSERT(wantedsize != 0);
243
244   if(countcheck("realloc", line, source))
245     return NULL;
246
247 #ifdef __INTEL_COMPILER
248 #  pragma warning(push)
249 #  pragma warning(disable:1684)
250    /* 1684: conversion from pointer to same-sized integral type */
251 #endif
252
253   if(ptr)
254     mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
255
256 #ifdef __INTEL_COMPILER
257 #  pragma warning(pop)
258 #endif
259
260   mem = (Curl_crealloc)(mem, size);
261   if(source)
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);
265
266   if(mem) {
267     mem->size = wantedsize;
268     return mem->mem;
269   }
270
271   return NULL;
272 }
273
274 void curl_dbg_free(void *ptr, int line, const char *source)
275 {
276   if(ptr) {
277     struct memdebug *mem;
278
279 #ifdef __INTEL_COMPILER
280 #  pragma warning(push)
281 #  pragma warning(disable:1684)
282    /* 1684: conversion from pointer to same-sized integral type */
283 #endif
284
285     mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
286
287 #ifdef __INTEL_COMPILER
288 #  pragma warning(pop)
289 #endif
290
291     /* free for real */
292     (Curl_cfree)(mem);
293   }
294
295   if(source && ptr)
296     curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr);
297 }
298
299 curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
300                              int line, const char *source)
301 {
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";
307
308   curl_socket_t sockfd;
309
310   if(countcheck("socket", line, source))
311     return CURL_SOCKET_BAD;
312
313   sockfd = socket(domain, type, protocol);
314
315   if(source && (sockfd != CURL_SOCKET_BAD))
316     curl_dbg_log(fmt, source, line, sockfd);
317
318   return sockfd;
319 }
320
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,
324                             const char *source)
325 {
326   SEND_TYPE_RETV rc;
327   if(countcheck("send", line, source))
328     return -1;
329   rc = send(sockfd, buf, len, flags);
330   if(source)
331     curl_dbg_log("SEND %s:%d send(%lu) = %ld\n",
332                 source, line, (unsigned long)len, (long)rc);
333   return rc;
334 }
335
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,
338                             const char *source)
339 {
340   RECV_TYPE_RETV rc;
341   if(countcheck("recv", line, source))
342     return -1;
343   rc = recv(sockfd, buf, len, flags);
344   if(source)
345     curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n",
346                 source, line, (unsigned long)len, (long)rc);
347   return rc;
348 }
349
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)
354 {
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";
360
361   int res = socketpair(domain, type, protocol, socket_vector);
362
363   if(source && (0 == res))
364     curl_dbg_log(fmt, source, line, socket_vector[0], socket_vector[1]);
365
366   return res;
367 }
368 #endif
369
370 curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
371                              int line, const char *source)
372 {
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";
378
379   struct sockaddr *addr = (struct sockaddr *)saddr;
380   curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
381
382   curl_socket_t sockfd = accept(s, addr, addrlen);
383
384   if(source && (sockfd != CURL_SOCKET_BAD))
385     curl_dbg_log(fmt, source, line, sockfd);
386
387   return sockfd;
388 }
389
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)
392 {
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";
398
399   if(source)
400     curl_dbg_log(fmt, source, line, sockfd);
401 }
402
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)
405 {
406   int res = sclose(sockfd);
407   curl_dbg_mark_sclose(sockfd, line, source);
408   return res;
409 }
410
411 FILE *curl_dbg_fopen(const char *file, const char *mode,
412                     int line, const char *source)
413 {
414   FILE *res = fopen(file, mode);
415
416   if(source)
417     curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
418                 source, line, file, mode, (void *)res);
419
420   return res;
421 }
422
423 FILE *curl_dbg_fdopen(int filedes, const char *mode,
424                       int line, const char *source)
425 {
426   FILE *res = fdopen(filedes, mode);
427   if(source)
428     curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
429                  source, line, filedes, mode, (void *)res);
430   return res;
431 }
432
433 int curl_dbg_fclose(FILE *file, int line, const char *source)
434 {
435   int res;
436
437   DEBUGASSERT(file != NULL);
438
439   if(source)
440     curl_dbg_log("FILE %s:%d fclose(%p)\n",
441                  source, line, (void *)file);
442
443   res = fclose(file);
444
445   return res;
446 }
447
448 #define LOGLINE_BUFSIZE  1024
449
450 /* this does the writing to the memory tracking log file */
451 void curl_dbg_log(const char *format, ...)
452 {
453   char *buf;
454   int nchars;
455   va_list ap;
456
457   if(!curl_dbg_logfile)
458     return;
459
460   buf = (Curl_cmalloc)(LOGLINE_BUFSIZE);
461   if(!buf)
462     return;
463
464   va_start(ap, format);
465   nchars = mvsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
466   va_end(ap);
467
468   if(nchars > LOGLINE_BUFSIZE - 1)
469     nchars = LOGLINE_BUFSIZE - 1;
470
471   if(nchars > 0)
472     fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile);
473
474   (Curl_cfree)(buf);
475 }
476
477 #endif /* CURLDEBUG */