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