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