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