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