Home | History | Annotate | Download | only in src
      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 
     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 
     52 #if defined(JEMALLOC_USE_SYSCALL) && defined(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 
     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
     89 buferror(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 
    108 uintmax_t
    109 malloc_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 = (uintmax_t)(-((intmax_t)ret));
    204 
    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:
    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 
    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 '-':
    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 
    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 
    318 size_t
    319 malloc_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 
    596 JEMALLOC_FORMAT_PRINTF(3, 4)
    597 size_t
    598 malloc_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 
    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  */
    635 JEMALLOC_FORMAT_PRINTF(3, 4)
    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. */
    648 JEMALLOC_FORMAT_PRINTF(1, 2)
    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 }
    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