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