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