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