Line | Count | Source |
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 | | #include "curl_setup.h" |
25 | | |
26 | | #ifdef CURL_MEMDEBUG |
27 | | |
28 | | #include <stddef.h> /* for offsetof() */ |
29 | | |
30 | | #include "urldata.h" |
31 | | #include "curl_threads.h" |
32 | | #include "curlx/fopen.h" /* for CURLX_FOPEN_LOW(), CURLX_FREOPEN_LOW() */ |
33 | | |
34 | | #ifdef USE_BACKTRACE |
35 | | #include <backtrace.h> |
36 | | #endif |
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 multi-threaded 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 | | #ifdef USE_BACKTRACE |
62 | | static struct backtrace_state *btstate; |
63 | | #endif |
64 | | |
65 | | static char membuf[10000]; |
66 | | static size_t memwidx = 0; /* write index */ |
67 | | |
68 | | #ifdef USE_MUTEX |
69 | | static bool dbg_mutex_init = 0; |
70 | | static curl_mutex_t dbg_mutex; |
71 | | #endif |
72 | | |
73 | | static bool curl_dbg_lock(void) |
74 | 8.55M | { |
75 | 8.55M | #ifdef USE_MUTEX |
76 | 8.55M | if(dbg_mutex_init) { |
77 | 0 | Curl_mutex_acquire(&dbg_mutex); |
78 | 0 | return TRUE; |
79 | 0 | } |
80 | 8.55M | #endif |
81 | 8.55M | return FALSE; |
82 | 8.55M | } |
83 | | |
84 | | static void curl_dbg_unlock(bool was_locked) |
85 | 8.55M | { |
86 | 8.55M | #ifdef USE_MUTEX |
87 | 8.55M | if(was_locked) |
88 | 0 | Curl_mutex_release(&dbg_mutex); |
89 | | #else |
90 | | (void)was_locked; |
91 | | #endif |
92 | 8.55M | } |
93 | | |
94 | | static void curl_dbg_log_locked(const char *format, ...) CURL_PRINTF(1, 2); |
95 | | |
96 | | /* LeakSantizier (LSAN) calls _exit() instead of exit() when a leak is detected |
97 | | on exit so the logfile must be closed explicitly or data could be lost. |
98 | | Though _exit() does not call atexit handlers such as this, LSAN's call to |
99 | | _exit() comes after the atexit handlers are called. curl/curl#6620 */ |
100 | | static void curl_dbg_cleanup(void) |
101 | 0 | { |
102 | 0 | bool locked = curl_dbg_lock(); |
103 | 0 | if(curl_dbg_logfile && |
104 | 0 | curl_dbg_logfile != stderr && |
105 | 0 | curl_dbg_logfile != stdout) { |
106 | 0 | if(memwidx) |
107 | 0 | fwrite(membuf, 1, memwidx, curl_dbg_logfile); |
108 | | /* !checksrc! disable BANNEDFUNC 1 */ |
109 | 0 | fclose(curl_dbg_logfile); |
110 | 0 | } |
111 | 0 | curl_dbg_logfile = NULL; |
112 | 0 | curl_dbg_unlock(locked); |
113 | 0 | #ifdef USE_MUTEX |
114 | 0 | if(dbg_mutex_init) { |
115 | 0 | Curl_mutex_destroy(&dbg_mutex); |
116 | 0 | dbg_mutex_init = FALSE; |
117 | 0 | } |
118 | 0 | #endif |
119 | 0 | } |
120 | | |
121 | | #ifdef USE_BACKTRACE |
122 | | static void error_bt_callback(void *data, const char *message, |
123 | | int error_number) |
124 | | { |
125 | | (void)data; |
126 | | if(error_number == -1) |
127 | | curl_dbg_log("compile with -g\n\n"); |
128 | | else |
129 | | curl_dbg_log("Backtrace error %d: %s\n", error_number, message); |
130 | | } |
131 | | |
132 | | static int full_callback(void *data, uintptr_t pc, const char *pathname, |
133 | | int line_number, const char *function) |
134 | | { |
135 | | (void)data; |
136 | | (void)pc; |
137 | | if(pathname || function || line_number) |
138 | | curl_dbg_log("BT %s:%d -- %s\n", pathname, line_number, function); |
139 | | return 0; |
140 | | } |
141 | | |
142 | | static void dump_bt(void) |
143 | | { |
144 | | backtrace_full(btstate, 0, full_callback, error_bt_callback, NULL); |
145 | | } |
146 | | #else |
147 | | #define dump_bt() /* nothing to do */ |
148 | | #endif |
149 | | |
150 | | /* this sets the log filename */ |
151 | | void curl_dbg_memdebug(const char *logname) |
152 | 0 | { |
153 | 0 | if(!curl_dbg_logfile) { |
154 | 0 | if(logname && *logname) |
155 | 0 | curl_dbg_logfile = CURLX_FOPEN_LOW(logname, FOPEN_WRITETEXT); |
156 | | #ifdef MEMDEBUG_LOG_SYNC |
157 | | /* Flush the log file after every line so the log is not lost in a crash */ |
158 | | if(curl_dbg_logfile) |
159 | | setbuf(curl_dbg_logfile, (char *)NULL); |
160 | | #endif |
161 | 0 | } |
162 | 0 | #ifdef USE_MUTEX |
163 | 0 | if(!dbg_mutex_init) { |
164 | 0 | dbg_mutex_init = TRUE; |
165 | 0 | Curl_mutex_init(&dbg_mutex); |
166 | 0 | } |
167 | 0 | #endif |
168 | | #ifdef USE_BACKTRACE |
169 | | btstate = backtrace_create_state(NULL, 0, error_bt_callback, NULL); |
170 | | #endif |
171 | 0 | if(!registered_cleanup) |
172 | 0 | registered_cleanup = !atexit(curl_dbg_cleanup); |
173 | 0 | } |
174 | | |
175 | | /* This function sets the number of malloc() calls that should return |
176 | | successfully! */ |
177 | | void curl_dbg_memlimit(long limit) |
178 | 0 | { |
179 | 0 | if(!memlimit) { |
180 | 0 | memlimit = TRUE; |
181 | 0 | memsize = limit; |
182 | 0 | } |
183 | 0 | } |
184 | | |
185 | | /* returns TRUE if this is not allowed! */ |
186 | | static bool countcheck(const char *func, int line, const char *source) |
187 | 38.2M | { |
188 | | /* if source is NULL, then the call is made internally and this check |
189 | | should not be made */ |
190 | 38.2M | if(memlimit && source) { |
191 | 0 | if(!memsize) { |
192 | | /* log to file */ |
193 | 0 | curl_dbg_log("LIMIT %s:%d %s reached memlimit\n", source, line, func); |
194 | | /* log to stderr also */ |
195 | 0 | curl_mfprintf(stderr, "LIMIT %s:%d %s reached memlimit\n", |
196 | 0 | source, line, func); |
197 | 0 | dump_bt(); |
198 | 0 | fflush(curl_dbg_logfile); /* because it might crash now */ |
199 | | /* !checksrc! disable ERRNOVAR 1 */ |
200 | 0 | errno = ENOMEM; |
201 | 0 | return TRUE; /* RETURN ERROR! */ |
202 | 0 | } |
203 | 0 | else |
204 | 0 | memsize--; /* countdown */ |
205 | 0 | } |
206 | | |
207 | 38.2M | return FALSE; /* allow this */ |
208 | 38.2M | } |
209 | | |
210 | | ALLOC_FUNC |
211 | | void *curl_dbg_malloc(size_t wantedsize, int line, const char *source) |
212 | 14.4M | { |
213 | 14.4M | struct memdebug *mem; |
214 | 14.4M | size_t size; |
215 | | |
216 | 14.4M | DEBUGASSERT(wantedsize != 0); |
217 | | |
218 | 14.4M | if(countcheck("malloc", line, source)) |
219 | 0 | return NULL; |
220 | | |
221 | | /* alloc at least 64 bytes */ |
222 | 14.4M | size = sizeof(struct memdebug) + wantedsize; |
223 | | |
224 | 14.4M | mem = Curl_cmalloc(size); |
225 | 14.4M | if(mem) { |
226 | 14.4M | mem->size = wantedsize; |
227 | 14.4M | } |
228 | | |
229 | 14.4M | if(source) |
230 | 9.39M | curl_dbg_log("MEM %s:%d malloc(%zu) = %p\n", |
231 | 9.39M | source, line, wantedsize, |
232 | 9.39M | mem ? (void *)mem->mem : (void *)0); |
233 | | |
234 | 14.4M | return mem ? mem->mem : NULL; |
235 | 14.4M | } |
236 | | |
237 | | ALLOC_FUNC |
238 | | void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size, |
239 | | int line, const char *source) |
240 | 10.0M | { |
241 | 10.0M | struct memdebug *mem; |
242 | 10.0M | size_t size, user_size; |
243 | | |
244 | 10.0M | DEBUGASSERT(wanted_elements != 0); |
245 | 10.0M | DEBUGASSERT(wanted_size != 0); |
246 | | |
247 | 10.0M | if(countcheck("calloc", line, source)) |
248 | 0 | return NULL; |
249 | | |
250 | | /* alloc at least 64 bytes */ |
251 | 10.0M | user_size = wanted_size * wanted_elements; |
252 | 10.0M | size = sizeof(struct memdebug) + user_size; |
253 | | |
254 | 10.0M | mem = Curl_ccalloc(1, size); |
255 | 10.0M | if(mem) |
256 | 10.0M | mem->size = user_size; |
257 | | |
258 | 10.0M | if(source) |
259 | 10.0M | curl_dbg_log("MEM %s:%d calloc(%zu,%zu) = %p\n", |
260 | 10.0M | source, line, wanted_elements, wanted_size, |
261 | 10.0M | mem ? (void *)mem->mem : (void *)0); |
262 | | |
263 | 10.0M | return mem ? mem->mem : NULL; |
264 | 10.0M | } |
265 | | |
266 | | ALLOC_FUNC |
267 | | char *curl_dbg_strdup(const char *str, int line, const char *source) |
268 | 5.01M | { |
269 | 5.01M | char *mem; |
270 | 5.01M | size_t len; |
271 | | |
272 | 5.01M | DEBUGASSERT(str != NULL); |
273 | | |
274 | 5.01M | if(countcheck("strdup", line, source)) |
275 | 0 | return NULL; |
276 | | |
277 | 5.01M | len = strlen(str) + 1; |
278 | | |
279 | 5.01M | mem = curl_dbg_malloc(len, 0, NULL); /* NULL prevents logging */ |
280 | 5.01M | if(mem) |
281 | 5.01M | memcpy(mem, str, len); |
282 | | |
283 | 5.01M | if(source) |
284 | 5.01M | curl_dbg_log("MEM %s:%d strdup(%p) (%zu) = %p\n", |
285 | 5.01M | source, line, (const void *)str, len, (const void *)mem); |
286 | | |
287 | 5.01M | return mem; |
288 | 5.01M | } |
289 | | |
290 | | #if defined(_WIN32) && defined(UNICODE) |
291 | | ALLOC_FUNC |
292 | | wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source) |
293 | | { |
294 | | wchar_t *mem; |
295 | | size_t wsiz, bsiz; |
296 | | |
297 | | DEBUGASSERT(str != NULL); |
298 | | |
299 | | if(countcheck("wcsdup", line, source)) |
300 | | return NULL; |
301 | | |
302 | | wsiz = wcslen(str) + 1; |
303 | | bsiz = wsiz * sizeof(wchar_t); |
304 | | |
305 | | mem = curl_dbg_malloc(bsiz, 0, NULL); /* NULL prevents logging */ |
306 | | if(mem) |
307 | | memcpy(mem, str, bsiz); |
308 | | |
309 | | if(source) |
310 | | curl_dbg_log("MEM %s:%d wcsdup(%p) (%zu) = %p\n", |
311 | | source, line, (const void *)str, bsiz, (void *)mem); |
312 | | |
313 | | return mem; |
314 | | } |
315 | | #endif |
316 | | |
317 | | /* We provide a realloc() that accepts a NULL as pointer, which then |
318 | | performs a malloc(). In order to work with ares. */ |
319 | | void *curl_dbg_realloc(void *ptr, size_t wantedsize, |
320 | | int line, const char *source) |
321 | 8.55M | { |
322 | 8.55M | struct memdebug *mem = NULL; |
323 | 8.55M | bool was_locked; |
324 | | |
325 | 8.55M | size_t size = sizeof(struct memdebug) + wantedsize; |
326 | | |
327 | 8.55M | DEBUGASSERT(wantedsize != 0); |
328 | | |
329 | 8.55M | if(countcheck("realloc", line, source)) |
330 | 0 | return NULL; |
331 | | |
332 | | /* need to realloc under lock, as we get out-of-order log |
333 | | * entries otherwise, since another thread might alloc the |
334 | | * memory released by realloc() before otherwise would log it. */ |
335 | 8.55M | was_locked = curl_dbg_lock(); |
336 | | #ifdef __INTEL_COMPILER |
337 | | # pragma warning(push) |
338 | | # pragma warning(disable:1684) |
339 | | /* 1684: conversion from pointer to same-sized integral type */ |
340 | | #endif |
341 | | |
342 | 8.55M | if(ptr) |
343 | 1.40M | mem = (void *)((char *)ptr - offsetof(struct memdebug, mem)); |
344 | | |
345 | | #ifdef __INTEL_COMPILER |
346 | | # pragma warning(pop) |
347 | | #endif |
348 | | |
349 | 8.55M | mem = Curl_crealloc(mem, size); |
350 | 8.55M | if(source) |
351 | 8.55M | curl_dbg_log_locked("MEM %s:%d realloc(%p, %zu) = %p\n", |
352 | 8.55M | source, line, (void *)ptr, wantedsize, |
353 | 8.55M | mem ? (void *)mem->mem : (void *)0); |
354 | | |
355 | 8.55M | curl_dbg_unlock(was_locked); |
356 | 8.55M | if(mem) { |
357 | 8.55M | mem->size = wantedsize; |
358 | 8.55M | return mem->mem; |
359 | 8.55M | } |
360 | | |
361 | 0 | return NULL; |
362 | 8.55M | } |
363 | | |
364 | | void curl_dbg_free(void *ptr, int line, const char *source) |
365 | 151M | { |
366 | 151M | if(ptr) { |
367 | 31.6M | struct memdebug *mem; |
368 | | |
369 | 31.6M | if(source) |
370 | 31.6M | curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr); |
371 | | |
372 | | #ifdef __INTEL_COMPILER |
373 | | # pragma warning(push) |
374 | | # pragma warning(disable:1684) |
375 | | /* 1684: conversion from pointer to same-sized integral type */ |
376 | | #endif |
377 | | |
378 | 31.6M | mem = (void *)((char *)ptr - offsetof(struct memdebug, mem)); |
379 | | |
380 | | #ifdef __INTEL_COMPILER |
381 | | # pragma warning(pop) |
382 | | #endif |
383 | | |
384 | | /* free for real */ |
385 | 31.6M | Curl_cfree(mem); |
386 | 31.6M | } |
387 | 151M | } |
388 | | |
389 | | curl_socket_t curl_dbg_socket(int domain, int type, int protocol, |
390 | | int line, const char *source) |
391 | 181k | { |
392 | 181k | curl_socket_t sockfd; |
393 | | |
394 | 181k | if(countcheck("socket", line, source)) |
395 | 0 | return CURL_SOCKET_BAD; |
396 | | |
397 | | /* !checksrc! disable BANNEDFUNC 1 */ |
398 | 181k | sockfd = socket(domain, type, protocol); |
399 | | |
400 | 181k | if(source && (sockfd != CURL_SOCKET_BAD)) |
401 | 181k | curl_dbg_log("FD %s:%d socket() = %" FMT_SOCKET_T "\n", |
402 | 181k | source, line, sockfd); |
403 | | |
404 | 181k | return sockfd; |
405 | 181k | } |
406 | | |
407 | | #ifdef HAVE_SOCKETPAIR |
408 | | int curl_dbg_socketpair(int domain, int type, int protocol, |
409 | | curl_socket_t socket_vector[2], |
410 | | int line, const char *source) |
411 | 0 | { |
412 | | /* !checksrc! disable BANNEDFUNC 1 */ |
413 | 0 | int res = socketpair(domain, type, protocol, socket_vector); |
414 | |
|
415 | 0 | if(source && (res == 0)) |
416 | 0 | curl_dbg_log("FD %s:%d socketpair() = " |
417 | 0 | "%" FMT_SOCKET_T " %" FMT_SOCKET_T "\n", |
418 | 0 | source, line, socket_vector[0], socket_vector[1]); |
419 | |
|
420 | 0 | return res; |
421 | 0 | } |
422 | | #endif |
423 | | |
424 | | curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen, |
425 | | int line, const char *source) |
426 | 0 | { |
427 | 0 | struct sockaddr *addr = (struct sockaddr *)saddr; |
428 | 0 | curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; |
429 | | |
430 | | /* !checksrc! disable BANNEDFUNC 1 */ |
431 | 0 | curl_socket_t sockfd = accept(s, addr, addrlen); |
432 | |
|
433 | 0 | if(source && (sockfd != CURL_SOCKET_BAD)) |
434 | 0 | curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n", |
435 | 0 | source, line, sockfd); |
436 | |
|
437 | 0 | return sockfd; |
438 | 0 | } |
439 | | |
440 | | #ifdef HAVE_ACCEPT4 |
441 | | curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr, void *saddrlen, |
442 | | int flags, |
443 | | int line, const char *source) |
444 | 0 | { |
445 | 0 | struct sockaddr *addr = (struct sockaddr *)saddr; |
446 | 0 | curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; |
447 | | |
448 | | /* !checksrc! disable BANNEDFUNC 1 */ |
449 | 0 | curl_socket_t sockfd = accept4(s, addr, addrlen, flags); |
450 | |
|
451 | 0 | if(source && (sockfd != CURL_SOCKET_BAD)) |
452 | 0 | curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n", |
453 | 0 | source, line, sockfd); |
454 | |
|
455 | 0 | return sockfd; |
456 | 0 | } |
457 | | #endif |
458 | | |
459 | | /* separate function to allow libcurl to mark a "faked" close */ |
460 | | void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source) |
461 | 291k | { |
462 | 291k | if(source) |
463 | 291k | curl_dbg_log("FD %s:%d sclose(%" FMT_SOCKET_T ")\n", |
464 | 291k | source, line, sockfd); |
465 | 291k | } |
466 | | |
467 | | /* this is our own defined way to close sockets on *ALL* platforms */ |
468 | | int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source) |
469 | 291k | { |
470 | 291k | curl_dbg_mark_sclose(sockfd, line, source); |
471 | 291k | return CURL_SCLOSE(sockfd); |
472 | 291k | } |
473 | | |
474 | | ALLOC_FUNC |
475 | | FILE *curl_dbg_fopen(const char *file, const char *mode, |
476 | | int line, const char *source) |
477 | 968k | { |
478 | 968k | FILE *res = CURLX_FOPEN_LOW(file, mode); |
479 | 968k | if(source) |
480 | 968k | curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n", |
481 | 968k | source, line, file, mode, (void *)res); |
482 | | |
483 | 968k | return res; |
484 | 968k | } |
485 | | |
486 | | ALLOC_FUNC |
487 | | FILE *curl_dbg_freopen(const char *file, const char *mode, FILE *fh, |
488 | | int line, const char *source) |
489 | 0 | { |
490 | 0 | FILE *res = CURLX_FREOPEN_LOW(file, mode, fh); |
491 | 0 | if(source) |
492 | 0 | curl_dbg_log("FILE %s:%d freopen(\"%s\",\"%s\",%p) = %p\n", |
493 | 0 | source, line, file, mode, (void *)fh, (void *)res); |
494 | |
|
495 | 0 | return res; |
496 | 0 | } |
497 | | |
498 | | ALLOC_FUNC |
499 | | FILE *curl_dbg_fdopen(int filedes, const char *mode, |
500 | | int line, const char *source) |
501 | 0 | { |
502 | 0 | FILE *res = CURLX_FDOPEN_LOW(filedes, mode); |
503 | 0 | if(source) |
504 | 0 | curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n", |
505 | 0 | source, line, filedes, mode, (void *)res); |
506 | 0 | return res; |
507 | 0 | } |
508 | | |
509 | | int curl_dbg_fclose(FILE *file, int line, const char *source) |
510 | 968k | { |
511 | 968k | int res; |
512 | | |
513 | 968k | DEBUGASSERT(file != NULL); |
514 | | |
515 | 968k | if(source) |
516 | 968k | curl_dbg_log("FILE %s:%d fclose(%p)\n", source, line, (void *)file); |
517 | | |
518 | | /* !checksrc! disable BANNEDFUNC 1 */ |
519 | 968k | res = fclose(file); |
520 | | |
521 | 968k | return res; |
522 | 968k | } |
523 | | |
524 | | static void curl_dbg_vlog(const char * const fmt, |
525 | | va_list ap) CURL_PRINTF(1, 0); |
526 | | |
527 | | static void curl_dbg_vlog(const char * const fmt, va_list ap) |
528 | 0 | { |
529 | 0 | char buf[1024]; |
530 | 0 | size_t nchars = curl_mvsnprintf(buf, sizeof(buf), fmt, ap); |
531 | |
|
532 | 0 | if(nchars > (int)sizeof(buf) - 1) |
533 | 0 | nchars = (int)sizeof(buf) - 1; |
534 | |
|
535 | 0 | if(nchars > 0) { |
536 | 0 | if(sizeof(membuf) - nchars < memwidx) { |
537 | | /* flush */ |
538 | 0 | fwrite(membuf, 1, memwidx, curl_dbg_logfile); |
539 | 0 | fflush(curl_dbg_logfile); |
540 | 0 | memwidx = 0; |
541 | 0 | } |
542 | 0 | if(memwidx) { |
543 | | /* the previous line ends with a newline */ |
544 | 0 | DEBUGASSERT(membuf[memwidx - 1] == '\n'); |
545 | 0 | } |
546 | 0 | memcpy(&membuf[memwidx], buf, nchars); |
547 | 0 | memwidx += nchars; |
548 | 0 | } |
549 | 0 | } |
550 | | |
551 | | static void curl_dbg_log_locked(const char *format, ...) |
552 | 8.55M | { |
553 | 8.55M | va_list ap; |
554 | | |
555 | 8.55M | if(!curl_dbg_logfile) |
556 | 8.55M | return; |
557 | | |
558 | 8.55M | va_start(ap, format); |
559 | 0 | curl_dbg_vlog(format, ap); |
560 | 0 | va_end(ap); |
561 | 0 | } |
562 | | |
563 | | /* this does the writing to the memory tracking log file */ |
564 | | void curl_dbg_log(const char *format, ...) |
565 | 58.5M | { |
566 | 58.5M | bool was_locked; |
567 | 58.5M | va_list ap; |
568 | | |
569 | 58.5M | if(!curl_dbg_logfile) |
570 | 58.5M | return; |
571 | | |
572 | 1 | was_locked = curl_dbg_lock(); |
573 | 1 | va_start(ap, format); |
574 | 1 | curl_dbg_vlog(format, ap); |
575 | | va_end(ap); |
576 | 1 | curl_dbg_unlock(was_locked); |
577 | 1 | } |
578 | | |
579 | | #endif /* CURL_MEMDEBUG */ |