1/*
2 * Define simple versions of assertion macros that won't recurse in case
3 * of assertion failures in malloc_*printf().
4 */
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 } \
17 unreachable(); \
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
33static void wrtmessage(void *cbopaque, const char *s);
34#define U2S_BUFSIZE ((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1)
35static char *u2s(uintmax_t x, unsigned base, bool uppercase, char *s,
36 size_t *slen_p);
37#define D2S_BUFSIZE (1 + U2S_BUFSIZE)
38static char *d2s(intmax_t x, char sign, char *s, size_t *slen_p);
39#define O2S_BUFSIZE (1 + U2S_BUFSIZE)
40static char *o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p);
41#define X2S_BUFSIZE (2 + U2S_BUFSIZE)
42static char *x2s(uintmax_t x, bool alt_form, bool uppercase, char *s,
43 size_t *slen_p);
44
45/******************************************************************************/
46
47/* malloc_message() setup. */
48static void
49wrtmessage(void *cbopaque, const char *s)
50{
51
52#ifdef SYS_write
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.
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.
61 */
62 UNUSED long result = syscall(SYS_write, STDERR_FILENO, s, strlen(s));
63#else
64 UNUSED ssize_t result = write(STDERR_FILENO, s, strlen(s));
65#endif
66}
67
68JEMALLOC_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 */
74void
75malloc_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 */
88int
89buferror(int err, char *buf, size_t buflen)
90{
91
92#ifdef _WIN32
93 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0,
94 (LPSTR)buf, (DWORD)buflen, NULL);
95 return (0);
96#elif defined(__GLIBC__) && defined(_GNU_SOURCE)
97 char *b = strerror_r(err, buf, buflen);
98 if (b != buf) {
99 strncpy(buf, b, buflen);
100 buf[buflen-1] = '\0';
101 }
102 return (0);
103#else
104 return (strerror_r(err, buf, buflen));
105#endif
106}
107
108uintmax_t
109malloc_strtoumax(const char *restrict nptr, char **restrict endptr, int base)
110{
111 uintmax_t ret, digit;
112 unsigned b;
113 bool neg;
114 const char *p, *ns;
115
116 p = nptr;
117 if (base < 0 || base == 1 || base > 36) {
118 ns = p;
119 set_errno(EINVAL);
120 ret = UINTMAX_MAX;
121 goto label_return;
122 }
123 b = base;
124
125 /* Swallow leading whitespace and get sign, if any. */
126 neg = false;
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;
160 case 'X': case 'x':
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:
178 p++;
179 ret = 0;
180 goto label_return;
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);
197 ret = UINTMAX_MAX;
198 goto label_return;
199 }
200 p++;
201 }
202 if (neg)
203 ret = -ret;
204
205 if (p == ns) {
206 /* No conversion performed. */
207 set_errno(EINVAL);
208 ret = UINTMAX_MAX;
209 goto label_return;
210 }
211
212label_return:
213 if (endptr != NULL) {
214 if (p == ns) {
215 /* No characters were converted. */
216 *endptr = (char *)nptr;
217 } else
218 *endptr = (char *)p;
219 }
220 return (ret);
221}
222
223static char *
224u2s(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
266static char *
267d2s(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 '-':
278 if (!neg)
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
292static char *
293o2s(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
305static char *
306x2s(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
318size_t
319malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
320{
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); \
340 if (!left_justify && pad_len != 0) { \
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)
354#define GET_ARG_NUMERIC(val, len) do { \
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; \
377 case 'j' | 0x80: \
378 val = va_arg(ap, uintmax_t); \
379 break; \
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; \
392 default: \
393 not_reached(); \
394 val = 0; \
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 = '?';
411 char *s;
412 size_t slen;
413
414 f++;
415 /* Flags. */
416 while (true) {
417 switch (*f) {
418 case '#':
419 assert(!alt_form);
420 alt_form = true;
421 break;
422 case '-':
423 assert(!left_justify);
424 left_justify = true;
425 break;
426 case ' ':
427 assert(!plus_space);
428 plus_space = true;
429 break;
430 case '+':
431 assert(!plus_plus);
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++;
444 if (width < 0) {
445 left_justify = true;
446 width = -width;
447 }
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;
457 break;
458 } default:
459 break;
460 }
461 /* Width/precision separator. */
462 if (*f == '.')
463 f++;
464 else
465 goto label_length;
466 /* Precision. */
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;
495 case 'q': case 'j': case 't': case 'z':
496 len = *f;
497 f++;
498 break;
499 default: break;
500 }
501 /* Conversion specifier. */
502 switch (*f) {
503 case '%':
504 /* %% */
505 APPEND_C(*f);
506 f++;
507 break;
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 *);
561 slen = (prec < 0) ? strlen(s) : (size_t)prec;
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;
574 } default: not_reached();
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';
588
589#undef APPEND_C
590#undef APPEND_S
591#undef APPEND_PADDED_S
592#undef GET_ARG_NUMERIC
593 return (i);
594}
595
596JEMALLOC_FORMAT_PRINTF(3, 4)
597size_t
598malloc_snprintf(char *str, size_t size, const char *format, ...)
599{
600 size_t ret;
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
610void
611malloc_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 */
635JEMALLOC_FORMAT_PRINTF(3, 4)
636void
637malloc_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. */
648JEMALLOC_FORMAT_PRINTF(1, 2)
649void
650malloc_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}
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"
667