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