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