]>
Commit | Line | Data |
---|---|---|
54a0048b SL |
1 | /* |
2 | * Define simple versions of assertion macros that won't recurse in case | |
3 | * of assertion failures in malloc_*printf(). | |
4 | */ | |
970d7e83 LB |
5 | #define assert(e) do { \ |
6 | if (config_debug && !(e)) { \ | |
7 | malloc_write("<jemalloc>: Failed assertion\n"); \ | |
8 | abort(); \ | |
9 | } \ | |
10 | } while (0) | |
11 | ||
12 | #define not_reached() do { \ | |
13 | if (config_debug) { \ | |
14 | malloc_write("<jemalloc>: Unreachable code reached\n"); \ | |
15 | abort(); \ | |
16 | } \ | |
3b2f2976 | 17 | unreachable(); \ |
970d7e83 LB |
18 | } while (0) |
19 | ||
20 | #define not_implemented() do { \ | |
21 | if (config_debug) { \ | |
22 | malloc_write("<jemalloc>: Not implemented\n"); \ | |
23 | abort(); \ | |
24 | } \ | |
25 | } while (0) | |
26 | ||
27 | #define JEMALLOC_UTIL_C_ | |
28 | #include "jemalloc/internal/jemalloc_internal.h" | |
29 | ||
30 | /******************************************************************************/ | |
31 | /* Function prototypes for non-inline static functions. */ | |
32 | ||
33 | static void wrtmessage(void *cbopaque, const char *s); | |
34 | #define U2S_BUFSIZE ((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1) | |
35 | static char *u2s(uintmax_t x, unsigned base, bool uppercase, char *s, | |
36 | size_t *slen_p); | |
37 | #define D2S_BUFSIZE (1 + U2S_BUFSIZE) | |
38 | static char *d2s(intmax_t x, char sign, char *s, size_t *slen_p); | |
39 | #define O2S_BUFSIZE (1 + U2S_BUFSIZE) | |
40 | static char *o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p); | |
41 | #define X2S_BUFSIZE (2 + U2S_BUFSIZE) | |
42 | static char *x2s(uintmax_t x, bool alt_form, bool uppercase, char *s, | |
43 | size_t *slen_p); | |
44 | ||
45 | /******************************************************************************/ | |
46 | ||
47 | /* malloc_message() setup. */ | |
48 | static void | |
49 | wrtmessage(void *cbopaque, const char *s) | |
50 | { | |
51 | ||
3b2f2976 | 52 | #if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_write) |
970d7e83 LB |
53 | /* |
54 | * Use syscall(2) rather than write(2) when possible in order to avoid | |
55 | * the possibility of memory allocation within libc. This is necessary | |
56 | * on FreeBSD; most operating systems do not have this problem though. | |
54a0048b SL |
57 | * |
58 | * syscall() returns long or int, depending on platform, so capture the | |
59 | * unused result in the widest plausible type to avoid compiler | |
60 | * warnings. | |
970d7e83 | 61 | */ |
54a0048b | 62 | UNUSED long result = syscall(SYS_write, STDERR_FILENO, s, strlen(s)); |
970d7e83 | 63 | #else |
54a0048b | 64 | UNUSED ssize_t result = write(STDERR_FILENO, s, strlen(s)); |
970d7e83 LB |
65 | #endif |
66 | } | |
67 | ||
68 | JEMALLOC_EXPORT void (*je_malloc_message)(void *, const char *s); | |
69 | ||
70 | /* | |
71 | * Wrapper around malloc_message() that avoids the need for | |
72 | * je_malloc_message(...) throughout the code. | |
73 | */ | |
74 | void | |
75 | malloc_write(const char *s) | |
76 | { | |
77 | ||
78 | if (je_malloc_message != NULL) | |
79 | je_malloc_message(NULL, s); | |
80 | else | |
81 | wrtmessage(NULL, s); | |
82 | } | |
83 | ||
84 | /* | |
85 | * glibc provides a non-standard strerror_r() when _GNU_SOURCE is defined, so | |
86 | * provide a wrapper. | |
87 | */ | |
88 | int | |
1a4d82fc | 89 | buferror(int err, char *buf, size_t buflen) |
970d7e83 LB |
90 | { |
91 | ||
92 | #ifdef _WIN32 | |
54a0048b SL |
93 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, |
94 | (LPSTR)buf, (DWORD)buflen, NULL); | |
970d7e83 | 95 | return (0); |
54a0048b | 96 | #elif defined(__GLIBC__) && defined(_GNU_SOURCE) |
1a4d82fc | 97 | char *b = strerror_r(err, buf, buflen); |
970d7e83 LB |
98 | if (b != buf) { |
99 | strncpy(buf, b, buflen); | |
100 | buf[buflen-1] = '\0'; | |
101 | } | |
102 | return (0); | |
103 | #else | |
1a4d82fc | 104 | return (strerror_r(err, buf, buflen)); |
970d7e83 LB |
105 | #endif |
106 | } | |
107 | ||
108 | uintmax_t | |
1a4d82fc | 109 | malloc_strtoumax(const char *restrict nptr, char **restrict endptr, int base) |
970d7e83 LB |
110 | { |
111 | uintmax_t ret, digit; | |
1a4d82fc | 112 | unsigned b; |
970d7e83 LB |
113 | bool neg; |
114 | const char *p, *ns; | |
115 | ||
1a4d82fc | 116 | p = nptr; |
970d7e83 | 117 | if (base < 0 || base == 1 || base > 36) { |
1a4d82fc | 118 | ns = p; |
970d7e83 | 119 | set_errno(EINVAL); |
1a4d82fc JJ |
120 | ret = UINTMAX_MAX; |
121 | goto label_return; | |
970d7e83 LB |
122 | } |
123 | b = base; | |
124 | ||
125 | /* Swallow leading whitespace and get sign, if any. */ | |
126 | neg = false; | |
970d7e83 LB |
127 | while (true) { |
128 | switch (*p) { | |
129 | case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': | |
130 | p++; | |
131 | break; | |
132 | case '-': | |
133 | neg = true; | |
134 | /* Fall through. */ | |
135 | case '+': | |
136 | p++; | |
137 | /* Fall through. */ | |
138 | default: | |
139 | goto label_prefix; | |
140 | } | |
141 | } | |
142 | ||
143 | /* Get prefix, if any. */ | |
144 | label_prefix: | |
145 | /* | |
146 | * Note where the first non-whitespace/sign character is so that it is | |
147 | * possible to tell whether any digits are consumed (e.g., " 0" vs. | |
148 | * " -x"). | |
149 | */ | |
150 | ns = p; | |
151 | if (*p == '0') { | |
152 | switch (p[1]) { | |
153 | case '0': case '1': case '2': case '3': case '4': case '5': | |
154 | case '6': case '7': | |
155 | if (b == 0) | |
156 | b = 8; | |
157 | if (b == 8) | |
158 | p++; | |
159 | break; | |
1a4d82fc | 160 | case 'X': case 'x': |
970d7e83 LB |
161 | switch (p[2]) { |
162 | case '0': case '1': case '2': case '3': case '4': | |
163 | case '5': case '6': case '7': case '8': case '9': | |
164 | case 'A': case 'B': case 'C': case 'D': case 'E': | |
165 | case 'F': | |
166 | case 'a': case 'b': case 'c': case 'd': case 'e': | |
167 | case 'f': | |
168 | if (b == 0) | |
169 | b = 16; | |
170 | if (b == 16) | |
171 | p += 2; | |
172 | break; | |
173 | default: | |
174 | break; | |
175 | } | |
176 | break; | |
177 | default: | |
1a4d82fc JJ |
178 | p++; |
179 | ret = 0; | |
180 | goto label_return; | |
970d7e83 LB |
181 | } |
182 | } | |
183 | if (b == 0) | |
184 | b = 10; | |
185 | ||
186 | /* Convert. */ | |
187 | ret = 0; | |
188 | while ((*p >= '0' && *p <= '9' && (digit = *p - '0') < b) | |
189 | || (*p >= 'A' && *p <= 'Z' && (digit = 10 + *p - 'A') < b) | |
190 | || (*p >= 'a' && *p <= 'z' && (digit = 10 + *p - 'a') < b)) { | |
191 | uintmax_t pret = ret; | |
192 | ret *= b; | |
193 | ret += digit; | |
194 | if (ret < pret) { | |
195 | /* Overflow. */ | |
196 | set_errno(ERANGE); | |
1a4d82fc JJ |
197 | ret = UINTMAX_MAX; |
198 | goto label_return; | |
970d7e83 LB |
199 | } |
200 | p++; | |
201 | } | |
202 | if (neg) | |
3b2f2976 | 203 | ret = (uintmax_t)(-((intmax_t)ret)); |
970d7e83 | 204 | |
1a4d82fc JJ |
205 | if (p == ns) { |
206 | /* No conversion performed. */ | |
207 | set_errno(EINVAL); | |
208 | ret = UINTMAX_MAX; | |
209 | goto label_return; | |
210 | } | |
211 | ||
212 | label_return: | |
970d7e83 LB |
213 | if (endptr != NULL) { |
214 | if (p == ns) { | |
215 | /* No characters were converted. */ | |
216 | *endptr = (char *)nptr; | |
217 | } else | |
218 | *endptr = (char *)p; | |
219 | } | |
970d7e83 LB |
220 | return (ret); |
221 | } | |
222 | ||
223 | static char * | |
224 | u2s(uintmax_t x, unsigned base, bool uppercase, char *s, size_t *slen_p) | |
225 | { | |
226 | unsigned i; | |
227 | ||
228 | i = U2S_BUFSIZE - 1; | |
229 | s[i] = '\0'; | |
230 | switch (base) { | |
231 | case 10: | |
232 | do { | |
233 | i--; | |
234 | s[i] = "0123456789"[x % (uint64_t)10]; | |
235 | x /= (uint64_t)10; | |
236 | } while (x > 0); | |
237 | break; | |
238 | case 16: { | |
239 | const char *digits = (uppercase) | |
240 | ? "0123456789ABCDEF" | |
241 | : "0123456789abcdef"; | |
242 | ||
243 | do { | |
244 | i--; | |
245 | s[i] = digits[x & 0xf]; | |
246 | x >>= 4; | |
247 | } while (x > 0); | |
248 | break; | |
249 | } default: { | |
250 | const char *digits = (uppercase) | |
251 | ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
252 | : "0123456789abcdefghijklmnopqrstuvwxyz"; | |
253 | ||
254 | assert(base >= 2 && base <= 36); | |
255 | do { | |
256 | i--; | |
257 | s[i] = digits[x % (uint64_t)base]; | |
258 | x /= (uint64_t)base; | |
259 | } while (x > 0); | |
260 | }} | |
261 | ||
262 | *slen_p = U2S_BUFSIZE - 1 - i; | |
263 | return (&s[i]); | |
264 | } | |
265 | ||
266 | static char * | |
267 | d2s(intmax_t x, char sign, char *s, size_t *slen_p) | |
268 | { | |
269 | bool neg; | |
270 | ||
271 | if ((neg = (x < 0))) | |
272 | x = -x; | |
273 | s = u2s(x, 10, false, s, slen_p); | |
274 | if (neg) | |
275 | sign = '-'; | |
276 | switch (sign) { | |
277 | case '-': | |
1a4d82fc | 278 | if (!neg) |
970d7e83 LB |
279 | break; |
280 | /* Fall through. */ | |
281 | case ' ': | |
282 | case '+': | |
283 | s--; | |
284 | (*slen_p)++; | |
285 | *s = sign; | |
286 | break; | |
287 | default: not_reached(); | |
288 | } | |
289 | return (s); | |
290 | } | |
291 | ||
292 | static char * | |
293 | o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p) | |
294 | { | |
295 | ||
296 | s = u2s(x, 8, false, s, slen_p); | |
297 | if (alt_form && *s != '0') { | |
298 | s--; | |
299 | (*slen_p)++; | |
300 | *s = '0'; | |
301 | } | |
302 | return (s); | |
303 | } | |
304 | ||
305 | static char * | |
306 | x2s(uintmax_t x, bool alt_form, bool uppercase, char *s, size_t *slen_p) | |
307 | { | |
308 | ||
309 | s = u2s(x, 16, uppercase, s, slen_p); | |
310 | if (alt_form) { | |
311 | s -= 2; | |
312 | (*slen_p) += 2; | |
313 | memcpy(s, uppercase ? "0X" : "0x", 2); | |
314 | } | |
315 | return (s); | |
316 | } | |
317 | ||
3b2f2976 | 318 | size_t |
970d7e83 LB |
319 | malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) |
320 | { | |
970d7e83 LB |
321 | size_t i; |
322 | const char *f; | |
323 | ||
324 | #define APPEND_C(c) do { \ | |
325 | if (i < size) \ | |
326 | str[i] = (c); \ | |
327 | i++; \ | |
328 | } while (0) | |
329 | #define APPEND_S(s, slen) do { \ | |
330 | if (i < size) { \ | |
331 | size_t cpylen = (slen <= size - i) ? slen : size - i; \ | |
332 | memcpy(&str[i], s, cpylen); \ | |
333 | } \ | |
334 | i += slen; \ | |
335 | } while (0) | |
336 | #define APPEND_PADDED_S(s, slen, width, left_justify) do { \ | |
337 | /* Left padding. */ \ | |
338 | size_t pad_len = (width == -1) ? 0 : ((slen < (size_t)width) ? \ | |
339 | (size_t)width - slen : 0); \ | |
1a4d82fc | 340 | if (!left_justify && pad_len != 0) { \ |
970d7e83 LB |
341 | size_t j; \ |
342 | for (j = 0; j < pad_len; j++) \ | |
343 | APPEND_C(' '); \ | |
344 | } \ | |
345 | /* Value. */ \ | |
346 | APPEND_S(s, slen); \ | |
347 | /* Right padding. */ \ | |
348 | if (left_justify && pad_len != 0) { \ | |
349 | size_t j; \ | |
350 | for (j = 0; j < pad_len; j++) \ | |
351 | APPEND_C(' '); \ | |
352 | } \ | |
353 | } while (0) | |
1a4d82fc | 354 | #define GET_ARG_NUMERIC(val, len) do { \ |
970d7e83 LB |
355 | switch (len) { \ |
356 | case '?': \ | |
357 | val = va_arg(ap, int); \ | |
358 | break; \ | |
359 | case '?' | 0x80: \ | |
360 | val = va_arg(ap, unsigned int); \ | |
361 | break; \ | |
362 | case 'l': \ | |
363 | val = va_arg(ap, long); \ | |
364 | break; \ | |
365 | case 'l' | 0x80: \ | |
366 | val = va_arg(ap, unsigned long); \ | |
367 | break; \ | |
368 | case 'q': \ | |
369 | val = va_arg(ap, long long); \ | |
370 | break; \ | |
371 | case 'q' | 0x80: \ | |
372 | val = va_arg(ap, unsigned long long); \ | |
373 | break; \ | |
374 | case 'j': \ | |
375 | val = va_arg(ap, intmax_t); \ | |
376 | break; \ | |
1a4d82fc JJ |
377 | case 'j' | 0x80: \ |
378 | val = va_arg(ap, uintmax_t); \ | |
379 | break; \ | |
970d7e83 LB |
380 | case 't': \ |
381 | val = va_arg(ap, ptrdiff_t); \ | |
382 | break; \ | |
383 | case 'z': \ | |
384 | val = va_arg(ap, ssize_t); \ | |
385 | break; \ | |
386 | case 'z' | 0x80: \ | |
387 | val = va_arg(ap, size_t); \ | |
388 | break; \ | |
389 | case 'p': /* Synthetic; used for %p. */ \ | |
390 | val = va_arg(ap, uintptr_t); \ | |
391 | break; \ | |
1a4d82fc JJ |
392 | default: \ |
393 | not_reached(); \ | |
394 | val = 0; \ | |
970d7e83 LB |
395 | } \ |
396 | } while (0) | |
397 | ||
398 | i = 0; | |
399 | f = format; | |
400 | while (true) { | |
401 | switch (*f) { | |
402 | case '\0': goto label_out; | |
403 | case '%': { | |
404 | bool alt_form = false; | |
405 | bool left_justify = false; | |
406 | bool plus_space = false; | |
407 | bool plus_plus = false; | |
408 | int prec = -1; | |
409 | int width = -1; | |
410 | unsigned char len = '?'; | |
3b2f2976 XL |
411 | char *s; |
412 | size_t slen; | |
970d7e83 LB |
413 | |
414 | f++; | |
970d7e83 LB |
415 | /* Flags. */ |
416 | while (true) { | |
417 | switch (*f) { | |
418 | case '#': | |
1a4d82fc | 419 | assert(!alt_form); |
970d7e83 LB |
420 | alt_form = true; |
421 | break; | |
422 | case '-': | |
1a4d82fc | 423 | assert(!left_justify); |
970d7e83 LB |
424 | left_justify = true; |
425 | break; | |
426 | case ' ': | |
1a4d82fc | 427 | assert(!plus_space); |
970d7e83 LB |
428 | plus_space = true; |
429 | break; | |
430 | case '+': | |
1a4d82fc | 431 | assert(!plus_plus); |
970d7e83 LB |
432 | plus_plus = true; |
433 | break; | |
434 | default: goto label_width; | |
435 | } | |
436 | f++; | |
437 | } | |
438 | /* Width. */ | |
439 | label_width: | |
440 | switch (*f) { | |
441 | case '*': | |
442 | width = va_arg(ap, int); | |
443 | f++; | |
1a4d82fc JJ |
444 | if (width < 0) { |
445 | left_justify = true; | |
446 | width = -width; | |
447 | } | |
970d7e83 LB |
448 | break; |
449 | case '0': case '1': case '2': case '3': case '4': | |
450 | case '5': case '6': case '7': case '8': case '9': { | |
451 | uintmax_t uwidth; | |
452 | set_errno(0); | |
453 | uwidth = malloc_strtoumax(f, (char **)&f, 10); | |
454 | assert(uwidth != UINTMAX_MAX || get_errno() != | |
455 | ERANGE); | |
456 | width = (int)uwidth; | |
970d7e83 | 457 | break; |
1a4d82fc JJ |
458 | } default: |
459 | break; | |
970d7e83 | 460 | } |
1a4d82fc JJ |
461 | /* Width/precision separator. */ |
462 | if (*f == '.') | |
463 | f++; | |
464 | else | |
465 | goto label_length; | |
970d7e83 | 466 | /* Precision. */ |
970d7e83 LB |
467 | switch (*f) { |
468 | case '*': | |
469 | prec = va_arg(ap, int); | |
470 | f++; | |
471 | break; | |
472 | case '0': case '1': case '2': case '3': case '4': | |
473 | case '5': case '6': case '7': case '8': case '9': { | |
474 | uintmax_t uprec; | |
475 | set_errno(0); | |
476 | uprec = malloc_strtoumax(f, (char **)&f, 10); | |
477 | assert(uprec != UINTMAX_MAX || get_errno() != | |
478 | ERANGE); | |
479 | prec = (int)uprec; | |
480 | break; | |
481 | } | |
482 | default: break; | |
483 | } | |
484 | /* Length. */ | |
485 | label_length: | |
486 | switch (*f) { | |
487 | case 'l': | |
488 | f++; | |
489 | if (*f == 'l') { | |
490 | len = 'q'; | |
491 | f++; | |
492 | } else | |
493 | len = 'l'; | |
494 | break; | |
1a4d82fc JJ |
495 | case 'q': case 'j': case 't': case 'z': |
496 | len = *f; | |
970d7e83 LB |
497 | f++; |
498 | break; | |
499 | default: break; | |
500 | } | |
501 | /* Conversion specifier. */ | |
502 | switch (*f) { | |
1a4d82fc JJ |
503 | case '%': |
504 | /* %% */ | |
505 | APPEND_C(*f); | |
506 | f++; | |
507 | break; | |
970d7e83 LB |
508 | case 'd': case 'i': { |
509 | intmax_t val JEMALLOC_CC_SILENCE_INIT(0); | |
510 | char buf[D2S_BUFSIZE]; | |
511 | ||
512 | GET_ARG_NUMERIC(val, len); | |
513 | s = d2s(val, (plus_plus ? '+' : (plus_space ? | |
514 | ' ' : '-')), buf, &slen); | |
515 | APPEND_PADDED_S(s, slen, width, left_justify); | |
516 | f++; | |
517 | break; | |
518 | } case 'o': { | |
519 | uintmax_t val JEMALLOC_CC_SILENCE_INIT(0); | |
520 | char buf[O2S_BUFSIZE]; | |
521 | ||
522 | GET_ARG_NUMERIC(val, len | 0x80); | |
523 | s = o2s(val, alt_form, buf, &slen); | |
524 | APPEND_PADDED_S(s, slen, width, left_justify); | |
525 | f++; | |
526 | break; | |
527 | } case 'u': { | |
528 | uintmax_t val JEMALLOC_CC_SILENCE_INIT(0); | |
529 | char buf[U2S_BUFSIZE]; | |
530 | ||
531 | GET_ARG_NUMERIC(val, len | 0x80); | |
532 | s = u2s(val, 10, false, buf, &slen); | |
533 | APPEND_PADDED_S(s, slen, width, left_justify); | |
534 | f++; | |
535 | break; | |
536 | } case 'x': case 'X': { | |
537 | uintmax_t val JEMALLOC_CC_SILENCE_INIT(0); | |
538 | char buf[X2S_BUFSIZE]; | |
539 | ||
540 | GET_ARG_NUMERIC(val, len | 0x80); | |
541 | s = x2s(val, alt_form, *f == 'X', buf, &slen); | |
542 | APPEND_PADDED_S(s, slen, width, left_justify); | |
543 | f++; | |
544 | break; | |
545 | } case 'c': { | |
546 | unsigned char val; | |
547 | char buf[2]; | |
548 | ||
549 | assert(len == '?' || len == 'l'); | |
550 | assert_not_implemented(len != 'l'); | |
551 | val = va_arg(ap, int); | |
552 | buf[0] = val; | |
553 | buf[1] = '\0'; | |
554 | APPEND_PADDED_S(buf, 1, width, left_justify); | |
555 | f++; | |
556 | break; | |
557 | } case 's': | |
558 | assert(len == '?' || len == 'l'); | |
559 | assert_not_implemented(len != 'l'); | |
560 | s = va_arg(ap, char *); | |
1a4d82fc | 561 | slen = (prec < 0) ? strlen(s) : (size_t)prec; |
970d7e83 LB |
562 | APPEND_PADDED_S(s, slen, width, left_justify); |
563 | f++; | |
564 | break; | |
565 | case 'p': { | |
566 | uintmax_t val; | |
567 | char buf[X2S_BUFSIZE]; | |
568 | ||
569 | GET_ARG_NUMERIC(val, 'p'); | |
570 | s = x2s(val, true, false, buf, &slen); | |
571 | APPEND_PADDED_S(s, slen, width, left_justify); | |
572 | f++; | |
573 | break; | |
1a4d82fc | 574 | } default: not_reached(); |
970d7e83 LB |
575 | } |
576 | break; | |
577 | } default: { | |
578 | APPEND_C(*f); | |
579 | f++; | |
580 | break; | |
581 | }} | |
582 | } | |
583 | label_out: | |
584 | if (i < size) | |
585 | str[i] = '\0'; | |
586 | else | |
587 | str[size - 1] = '\0'; | |
970d7e83 LB |
588 | |
589 | #undef APPEND_C | |
590 | #undef APPEND_S | |
591 | #undef APPEND_PADDED_S | |
592 | #undef GET_ARG_NUMERIC | |
3b2f2976 | 593 | return (i); |
970d7e83 LB |
594 | } |
595 | ||
54a0048b | 596 | JEMALLOC_FORMAT_PRINTF(3, 4) |
3b2f2976 | 597 | size_t |
970d7e83 LB |
598 | malloc_snprintf(char *str, size_t size, const char *format, ...) |
599 | { | |
3b2f2976 | 600 | size_t ret; |
970d7e83 LB |
601 | va_list ap; |
602 | ||
603 | va_start(ap, format); | |
604 | ret = malloc_vsnprintf(str, size, format, ap); | |
605 | va_end(ap); | |
606 | ||
607 | return (ret); | |
608 | } | |
609 | ||
610 | void | |
611 | malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque, | |
612 | const char *format, va_list ap) | |
613 | { | |
614 | char buf[MALLOC_PRINTF_BUFSIZE]; | |
615 | ||
616 | if (write_cb == NULL) { | |
617 | /* | |
618 | * The caller did not provide an alternate write_cb callback | |
619 | * function, so use the default one. malloc_write() is an | |
620 | * inline function, so use malloc_message() directly here. | |
621 | */ | |
622 | write_cb = (je_malloc_message != NULL) ? je_malloc_message : | |
623 | wrtmessage; | |
624 | cbopaque = NULL; | |
625 | } | |
626 | ||
627 | malloc_vsnprintf(buf, sizeof(buf), format, ap); | |
628 | write_cb(cbopaque, buf); | |
629 | } | |
630 | ||
631 | /* | |
632 | * Print to a callback function in such a way as to (hopefully) avoid memory | |
633 | * allocation. | |
634 | */ | |
54a0048b | 635 | JEMALLOC_FORMAT_PRINTF(3, 4) |
970d7e83 LB |
636 | void |
637 | malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque, | |
638 | const char *format, ...) | |
639 | { | |
640 | va_list ap; | |
641 | ||
642 | va_start(ap, format); | |
643 | malloc_vcprintf(write_cb, cbopaque, format, ap); | |
644 | va_end(ap); | |
645 | } | |
646 | ||
647 | /* Print to stderr in such a way as to avoid memory allocation. */ | |
54a0048b | 648 | JEMALLOC_FORMAT_PRINTF(1, 2) |
970d7e83 LB |
649 | void |
650 | malloc_printf(const char *format, ...) | |
651 | { | |
652 | va_list ap; | |
653 | ||
654 | va_start(ap, format); | |
655 | malloc_vcprintf(NULL, NULL, format, ap); | |
656 | va_end(ap); | |
657 | } | |
54a0048b SL |
658 | |
659 | /* | |
660 | * Restore normal assertion macros, in order to make it possible to compile all | |
661 | * C files as a single concatenation. | |
662 | */ | |
663 | #undef assert | |
664 | #undef not_reached | |
665 | #undef not_implemented | |
666 | #include "jemalloc/internal/assert.h" |