• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 } while (0)
18 
19 #define	not_implemented() do {						\
20 	if (config_debug) {						\
21 		malloc_write("<jemalloc>: Not implemented\n");		\
22 		abort();						\
23 	}								\
24 } while (0)
25 
26 #define	JEMALLOC_UTIL_C_
27 #include "jemalloc/internal/jemalloc_internal.h"
28 
29 /******************************************************************************/
30 /* Function prototypes for non-inline static functions. */
31 
32 static void	wrtmessage(void *cbopaque, const char *s);
33 #define	U2S_BUFSIZE	((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1)
34 static char	*u2s(uintmax_t x, unsigned base, bool uppercase, char *s,
35     size_t *slen_p);
36 #define	D2S_BUFSIZE	(1 + U2S_BUFSIZE)
37 static char	*d2s(intmax_t x, char sign, char *s, size_t *slen_p);
38 #define	O2S_BUFSIZE	(1 + U2S_BUFSIZE)
39 static char	*o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p);
40 #define	X2S_BUFSIZE	(2 + U2S_BUFSIZE)
41 static char	*x2s(uintmax_t x, bool alt_form, bool uppercase, char *s,
42     size_t *slen_p);
43 
44 /******************************************************************************/
45 
46 /* malloc_message() setup. */
47 static void
wrtmessage(void * cbopaque,const char * s)48 wrtmessage(void *cbopaque, const char *s)
49 {
50 
51 #ifdef SYS_write
52 	/*
53 	 * Use syscall(2) rather than write(2) when possible in order to avoid
54 	 * the possibility of memory allocation within libc.  This is necessary
55 	 * on FreeBSD; most operating systems do not have this problem though.
56 	 *
57 	 * syscall() returns long or int, depending on platform, so capture the
58 	 * unused result in the widest plausible type to avoid compiler
59 	 * warnings.
60 	 */
61 	UNUSED long result = syscall(SYS_write, STDERR_FILENO, s, strlen(s));
62 #else
63 	UNUSED ssize_t result = write(STDERR_FILENO, s, strlen(s));
64 #endif
65 }
66 
67 JEMALLOC_EXPORT void	(*je_malloc_message)(void *, const char *s);
68 
69 /*
70  * Wrapper around malloc_message() that avoids the need for
71  * je_malloc_message(...) throughout the code.
72  */
73 void
malloc_write(const char * s)74 malloc_write(const char *s)
75 {
76 
77 	if (je_malloc_message != NULL)
78 		je_malloc_message(NULL, s);
79 	else
80 		wrtmessage(NULL, s);
81 }
82 
83 /*
84  * glibc provides a non-standard strerror_r() when _GNU_SOURCE is defined, so
85  * provide a wrapper.
86  */
87 int
buferror(int err,char * buf,size_t buflen)88 buferror(int err, char *buf, size_t buflen)
89 {
90 
91 #ifdef _WIN32
92 	FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0,
93 	    (LPSTR)buf, (DWORD)buflen, NULL);
94 	return (0);
95 #elif defined(__GLIBC__) && defined(_GNU_SOURCE)
96 	char *b = strerror_r(err, buf, buflen);
97 	if (b != buf) {
98 		strncpy(buf, b, buflen);
99 		buf[buflen-1] = '\0';
100 	}
101 	return (0);
102 #else
103 	return (strerror_r(err, buf, buflen));
104 #endif
105 }
106 
107 uintmax_t
malloc_strtoumax(const char * restrict nptr,char ** restrict endptr,int base)108 malloc_strtoumax(const char *restrict nptr, char **restrict endptr, int base)
109 {
110 	uintmax_t ret, digit;
111 	unsigned b;
112 	bool neg;
113 	const char *p, *ns;
114 
115 	p = nptr;
116 	if (base < 0 || base == 1 || base > 36) {
117 		ns = p;
118 		set_errno(EINVAL);
119 		ret = UINTMAX_MAX;
120 		goto label_return;
121 	}
122 	b = base;
123 
124 	/* Swallow leading whitespace and get sign, if any. */
125 	neg = false;
126 	while (true) {
127 		switch (*p) {
128 		case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
129 			p++;
130 			break;
131 		case '-':
132 			neg = true;
133 			/* Fall through. */
134 		case '+':
135 			p++;
136 			/* Fall through. */
137 		default:
138 			goto label_prefix;
139 		}
140 	}
141 
142 	/* Get prefix, if any. */
143 	label_prefix:
144 	/*
145 	 * Note where the first non-whitespace/sign character is so that it is
146 	 * possible to tell whether any digits are consumed (e.g., "  0" vs.
147 	 * "  -x").
148 	 */
149 	ns = p;
150 	if (*p == '0') {
151 		switch (p[1]) {
152 		case '0': case '1': case '2': case '3': case '4': case '5':
153 		case '6': case '7':
154 			if (b == 0)
155 				b = 8;
156 			if (b == 8)
157 				p++;
158 			break;
159 		case 'X': case 'x':
160 			switch (p[2]) {
161 			case '0': case '1': case '2': case '3': case '4':
162 			case '5': case '6': case '7': case '8': case '9':
163 			case 'A': case 'B': case 'C': case 'D': case 'E':
164 			case 'F':
165 			case 'a': case 'b': case 'c': case 'd': case 'e':
166 			case 'f':
167 				if (b == 0)
168 					b = 16;
169 				if (b == 16)
170 					p += 2;
171 				break;
172 			default:
173 				break;
174 			}
175 			break;
176 		default:
177 			p++;
178 			ret = 0;
179 			goto label_return;
180 		}
181 	}
182 	if (b == 0)
183 		b = 10;
184 
185 	/* Convert. */
186 	ret = 0;
187 	while ((*p >= '0' && *p <= '9' && (digit = *p - '0') < b)
188 	    || (*p >= 'A' && *p <= 'Z' && (digit = 10 + *p - 'A') < b)
189 	    || (*p >= 'a' && *p <= 'z' && (digit = 10 + *p - 'a') < b)) {
190 		uintmax_t pret = ret;
191 		ret *= b;
192 		ret += digit;
193 		if (ret < pret) {
194 			/* Overflow. */
195 			set_errno(ERANGE);
196 			ret = UINTMAX_MAX;
197 			goto label_return;
198 		}
199 		p++;
200 	}
201 	if (neg)
202 		ret = -ret;
203 
204 	if (p == ns) {
205 		/* No conversion performed. */
206 		set_errno(EINVAL);
207 		ret = UINTMAX_MAX;
208 		goto label_return;
209 	}
210 
211 label_return:
212 	if (endptr != NULL) {
213 		if (p == ns) {
214 			/* No characters were converted. */
215 			*endptr = (char *)nptr;
216 		} else
217 			*endptr = (char *)p;
218 	}
219 	return (ret);
220 }
221 
222 static char *
u2s(uintmax_t x,unsigned base,bool uppercase,char * s,size_t * slen_p)223 u2s(uintmax_t x, unsigned base, bool uppercase, char *s, size_t *slen_p)
224 {
225 	unsigned i;
226 
227 	i = U2S_BUFSIZE - 1;
228 	s[i] = '\0';
229 	switch (base) {
230 	case 10:
231 		do {
232 			i--;
233 			s[i] = "0123456789"[x % (uint64_t)10];
234 			x /= (uint64_t)10;
235 		} while (x > 0);
236 		break;
237 	case 16: {
238 		const char *digits = (uppercase)
239 		    ? "0123456789ABCDEF"
240 		    : "0123456789abcdef";
241 
242 		do {
243 			i--;
244 			s[i] = digits[x & 0xf];
245 			x >>= 4;
246 		} while (x > 0);
247 		break;
248 	} default: {
249 		const char *digits = (uppercase)
250 		    ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
251 		    : "0123456789abcdefghijklmnopqrstuvwxyz";
252 
253 		assert(base >= 2 && base <= 36);
254 		do {
255 			i--;
256 			s[i] = digits[x % (uint64_t)base];
257 			x /= (uint64_t)base;
258 		} while (x > 0);
259 	}}
260 
261 	*slen_p = U2S_BUFSIZE - 1 - i;
262 	return (&s[i]);
263 }
264 
265 static char *
d2s(intmax_t x,char sign,char * s,size_t * slen_p)266 d2s(intmax_t x, char sign, char *s, size_t *slen_p)
267 {
268 	bool neg;
269 
270 	if ((neg = (x < 0)))
271 		x = -x;
272 	s = u2s(x, 10, false, s, slen_p);
273 	if (neg)
274 		sign = '-';
275 	switch (sign) {
276 	case '-':
277 		if (!neg)
278 			break;
279 		/* Fall through. */
280 	case ' ':
281 	case '+':
282 		s--;
283 		(*slen_p)++;
284 		*s = sign;
285 		break;
286 	default: not_reached();
287 	}
288 	return (s);
289 }
290 
291 static char *
o2s(uintmax_t x,bool alt_form,char * s,size_t * slen_p)292 o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p)
293 {
294 
295 	s = u2s(x, 8, false, s, slen_p);
296 	if (alt_form && *s != '0') {
297 		s--;
298 		(*slen_p)++;
299 		*s = '0';
300 	}
301 	return (s);
302 }
303 
304 static char *
x2s(uintmax_t x,bool alt_form,bool uppercase,char * s,size_t * slen_p)305 x2s(uintmax_t x, bool alt_form, bool uppercase, char *s, size_t *slen_p)
306 {
307 
308 	s = u2s(x, 16, uppercase, s, slen_p);
309 	if (alt_form) {
310 		s -= 2;
311 		(*slen_p) += 2;
312 		memcpy(s, uppercase ? "0X" : "0x", 2);
313 	}
314 	return (s);
315 }
316 
317 int
malloc_vsnprintf(char * str,size_t size,const char * format,va_list ap)318 malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
319 {
320 	int ret;
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 
412 			f++;
413 			/* Flags. */
414 			while (true) {
415 				switch (*f) {
416 				case '#':
417 					assert(!alt_form);
418 					alt_form = true;
419 					break;
420 				case '-':
421 					assert(!left_justify);
422 					left_justify = true;
423 					break;
424 				case ' ':
425 					assert(!plus_space);
426 					plus_space = true;
427 					break;
428 				case '+':
429 					assert(!plus_plus);
430 					plus_plus = true;
431 					break;
432 				default: goto label_width;
433 				}
434 				f++;
435 			}
436 			/* Width. */
437 			label_width:
438 			switch (*f) {
439 			case '*':
440 				width = va_arg(ap, int);
441 				f++;
442 				if (width < 0) {
443 					left_justify = true;
444 					width = -width;
445 				}
446 				break;
447 			case '0': case '1': case '2': case '3': case '4':
448 			case '5': case '6': case '7': case '8': case '9': {
449 				uintmax_t uwidth;
450 				set_errno(0);
451 				uwidth = malloc_strtoumax(f, (char **)&f, 10);
452 				assert(uwidth != UINTMAX_MAX || get_errno() !=
453 				    ERANGE);
454 				width = (int)uwidth;
455 				break;
456 			} default:
457 				break;
458 			}
459 			/* Width/precision separator. */
460 			if (*f == '.')
461 				f++;
462 			else
463 				goto label_length;
464 			/* Precision. */
465 			switch (*f) {
466 			case '*':
467 				prec = va_arg(ap, int);
468 				f++;
469 				break;
470 			case '0': case '1': case '2': case '3': case '4':
471 			case '5': case '6': case '7': case '8': case '9': {
472 				uintmax_t uprec;
473 				set_errno(0);
474 				uprec = malloc_strtoumax(f, (char **)&f, 10);
475 				assert(uprec != UINTMAX_MAX || get_errno() !=
476 				    ERANGE);
477 				prec = (int)uprec;
478 				break;
479 			}
480 			default: break;
481 			}
482 			/* Length. */
483 			label_length:
484 			switch (*f) {
485 			case 'l':
486 				f++;
487 				if (*f == 'l') {
488 					len = 'q';
489 					f++;
490 				} else
491 					len = 'l';
492 				break;
493 			case 'q': case 'j': case 't': case 'z':
494 				len = *f;
495 				f++;
496 				break;
497 			default: break;
498 			}
499 			/* Conversion specifier. */
500 			switch (*f) {
501 				char *s;
502 				size_t slen;
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 	assert(i < INT_MAX);
589 	ret = (int)i;
590 
591 #undef APPEND_C
592 #undef APPEND_S
593 #undef APPEND_PADDED_S
594 #undef GET_ARG_NUMERIC
595 	return (ret);
596 }
597 
598 JEMALLOC_FORMAT_PRINTF(3, 4)
599 int
malloc_snprintf(char * str,size_t size,const char * format,...)600 malloc_snprintf(char *str, size_t size, const char *format, ...)
601 {
602 	int ret;
603 	va_list ap;
604 
605 	va_start(ap, format);
606 	ret = malloc_vsnprintf(str, size, format, ap);
607 	va_end(ap);
608 
609 	return (ret);
610 }
611 
612 void
malloc_vcprintf(void (* write_cb)(void *,const char *),void * cbopaque,const char * format,va_list ap)613 malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
614     const char *format, va_list ap)
615 {
616 	char buf[MALLOC_PRINTF_BUFSIZE];
617 
618 	if (write_cb == NULL) {
619 		/*
620 		 * The caller did not provide an alternate write_cb callback
621 		 * function, so use the default one.  malloc_write() is an
622 		 * inline function, so use malloc_message() directly here.
623 		 */
624 		write_cb = (je_malloc_message != NULL) ? je_malloc_message :
625 		    wrtmessage;
626 		cbopaque = NULL;
627 	}
628 
629 	malloc_vsnprintf(buf, sizeof(buf), format, ap);
630 	write_cb(cbopaque, buf);
631 }
632 
633 /*
634  * Print to a callback function in such a way as to (hopefully) avoid memory
635  * allocation.
636  */
637 JEMALLOC_FORMAT_PRINTF(3, 4)
638 void
malloc_cprintf(void (* write_cb)(void *,const char *),void * cbopaque,const char * format,...)639 malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
640     const char *format, ...)
641 {
642 	va_list ap;
643 
644 	va_start(ap, format);
645 	malloc_vcprintf(write_cb, cbopaque, format, ap);
646 	va_end(ap);
647 }
648 
649 /* Print to stderr in such a way as to avoid memory allocation. */
650 JEMALLOC_FORMAT_PRINTF(1, 2)
651 void
malloc_printf(const char * format,...)652 malloc_printf(const char *format, ...)
653 {
654 	va_list ap;
655 
656 	va_start(ap, format);
657 	malloc_vcprintf(NULL, NULL, format, ap);
658 	va_end(ap);
659 }
660 
661 /*
662  * Restore normal assertion macros, in order to make it possible to compile all
663  * C files as a single concatenation.
664  */
665 #undef assert
666 #undef not_reached
667 #undef not_implemented
668 #include "jemalloc/internal/assert.h"
669