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