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