]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/jemalloc/src/util.c
MFV r302423:
[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         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
33 static void     wrtmessage(void *cbopaque, const char *s);
34 #define U2S_BUFSIZE     ((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1)
35 static char     *u2s(uintmax_t x, unsigned base, bool uppercase, char *s,
36     size_t *slen_p);
37 #define D2S_BUFSIZE     (1 + U2S_BUFSIZE)
38 static char     *d2s(intmax_t x, char sign, char *s, size_t *slen_p);
39 #define O2S_BUFSIZE     (1 + U2S_BUFSIZE)
40 static char     *o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p);
41 #define X2S_BUFSIZE     (2 + U2S_BUFSIZE)
42 static char     *x2s(uintmax_t x, bool alt_form, bool uppercase, char *s,
43     size_t *slen_p);
44
45 /******************************************************************************/
46
47 /* malloc_message() setup. */
48 static void
49 wrtmessage(void *cbopaque, const char *s)
50 {
51
52 #ifdef 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
68 JEMALLOC_EXPORT void    (*je_malloc_message)(void *, const char *s);
69
70 JEMALLOC_ATTR(visibility("hidden"))
71 void
72 wrtmessage_1_0(const char *s1, const char *s2, const char *s3,
73     const char *s4)
74 {
75
76         wrtmessage(NULL, s1);
77         wrtmessage(NULL, s2);
78         wrtmessage(NULL, s3);
79         wrtmessage(NULL, s4);
80 }
81
82 void    (*__malloc_message_1_0)(const char *s1, const char *s2, const char *s3,
83     const char *s4) = wrtmessage_1_0;
84 __sym_compat(_malloc_message, __malloc_message_1_0, FBSD_1.0);
85
86 /*
87  * Wrapper around malloc_message() that avoids the need for
88  * je_malloc_message(...) throughout the code.
89  */
90 void
91 malloc_write(const char *s)
92 {
93
94         if (je_malloc_message != NULL)
95                 je_malloc_message(NULL, s);
96         else
97                 wrtmessage(NULL, s);
98 }
99
100 /*
101  * glibc provides a non-standard strerror_r() when _GNU_SOURCE is defined, so
102  * provide a wrapper.
103  */
104 int
105 buferror(int err, char *buf, size_t buflen)
106 {
107
108 #ifdef _WIN32
109         FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0,
110             (LPSTR)buf, (DWORD)buflen, NULL);
111         return (0);
112 #elif defined(__GLIBC__) && defined(_GNU_SOURCE)
113         char *b = strerror_r(err, buf, buflen);
114         if (b != buf) {
115                 strncpy(buf, b, buflen);
116                 buf[buflen-1] = '\0';
117         }
118         return (0);
119 #else
120         return (strerror_r(err, buf, buflen));
121 #endif
122 }
123
124 uintmax_t
125 malloc_strtoumax(const char *restrict nptr, char **restrict endptr, int base)
126 {
127         uintmax_t ret, digit;
128         unsigned b;
129         bool neg;
130         const char *p, *ns;
131
132         p = nptr;
133         if (base < 0 || base == 1 || base > 36) {
134                 ns = p;
135                 set_errno(EINVAL);
136                 ret = UINTMAX_MAX;
137                 goto label_return;
138         }
139         b = base;
140
141         /* Swallow leading whitespace and get sign, if any. */
142         neg = false;
143         while (true) {
144                 switch (*p) {
145                 case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
146                         p++;
147                         break;
148                 case '-':
149                         neg = true;
150                         /* Fall through. */
151                 case '+':
152                         p++;
153                         /* Fall through. */
154                 default:
155                         goto label_prefix;
156                 }
157         }
158
159         /* Get prefix, if any. */
160         label_prefix:
161         /*
162          * Note where the first non-whitespace/sign character is so that it is
163          * possible to tell whether any digits are consumed (e.g., "  0" vs.
164          * "  -x").
165          */
166         ns = p;
167         if (*p == '0') {
168                 switch (p[1]) {
169                 case '0': case '1': case '2': case '3': case '4': case '5':
170                 case '6': case '7':
171                         if (b == 0)
172                                 b = 8;
173                         if (b == 8)
174                                 p++;
175                         break;
176                 case 'X': case 'x':
177                         switch (p[2]) {
178                         case '0': case '1': case '2': case '3': case '4':
179                         case '5': case '6': case '7': case '8': case '9':
180                         case 'A': case 'B': case 'C': case 'D': case 'E':
181                         case 'F':
182                         case 'a': case 'b': case 'c': case 'd': case 'e':
183                         case 'f':
184                                 if (b == 0)
185                                         b = 16;
186                                 if (b == 16)
187                                         p += 2;
188                                 break;
189                         default:
190                                 break;
191                         }
192                         break;
193                 default:
194                         p++;
195                         ret = 0;
196                         goto label_return;
197                 }
198         }
199         if (b == 0)
200                 b = 10;
201
202         /* Convert. */
203         ret = 0;
204         while ((*p >= '0' && *p <= '9' && (digit = *p - '0') < b)
205             || (*p >= 'A' && *p <= 'Z' && (digit = 10 + *p - 'A') < b)
206             || (*p >= 'a' && *p <= 'z' && (digit = 10 + *p - 'a') < b)) {
207                 uintmax_t pret = ret;
208                 ret *= b;
209                 ret += digit;
210                 if (ret < pret) {
211                         /* Overflow. */
212                         set_errno(ERANGE);
213                         ret = UINTMAX_MAX;
214                         goto label_return;
215                 }
216                 p++;
217         }
218         if (neg)
219                 ret = -ret;
220
221         if (p == ns) {
222                 /* No conversion performed. */
223                 set_errno(EINVAL);
224                 ret = UINTMAX_MAX;
225                 goto label_return;
226         }
227
228 label_return:
229         if (endptr != NULL) {
230                 if (p == ns) {
231                         /* No characters were converted. */
232                         *endptr = (char *)nptr;
233                 } else
234                         *endptr = (char *)p;
235         }
236         return (ret);
237 }
238
239 static char *
240 u2s(uintmax_t x, unsigned base, bool uppercase, char *s, size_t *slen_p)
241 {
242         unsigned i;
243
244         i = U2S_BUFSIZE - 1;
245         s[i] = '\0';
246         switch (base) {
247         case 10:
248                 do {
249                         i--;
250                         s[i] = "0123456789"[x % (uint64_t)10];
251                         x /= (uint64_t)10;
252                 } while (x > 0);
253                 break;
254         case 16: {
255                 const char *digits = (uppercase)
256                     ? "0123456789ABCDEF"
257                     : "0123456789abcdef";
258
259                 do {
260                         i--;
261                         s[i] = digits[x & 0xf];
262                         x >>= 4;
263                 } while (x > 0);
264                 break;
265         } default: {
266                 const char *digits = (uppercase)
267                     ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
268                     : "0123456789abcdefghijklmnopqrstuvwxyz";
269
270                 assert(base >= 2 && base <= 36);
271                 do {
272                         i--;
273                         s[i] = digits[x % (uint64_t)base];
274                         x /= (uint64_t)base;
275                 } while (x > 0);
276         }}
277
278         *slen_p = U2S_BUFSIZE - 1 - i;
279         return (&s[i]);
280 }
281
282 static char *
283 d2s(intmax_t x, char sign, char *s, size_t *slen_p)
284 {
285         bool neg;
286
287         if ((neg = (x < 0)))
288                 x = -x;
289         s = u2s(x, 10, false, s, slen_p);
290         if (neg)
291                 sign = '-';
292         switch (sign) {
293         case '-':
294                 if (!neg)
295                         break;
296                 /* Fall through. */
297         case ' ':
298         case '+':
299                 s--;
300                 (*slen_p)++;
301                 *s = sign;
302                 break;
303         default: not_reached();
304         }
305         return (s);
306 }
307
308 static char *
309 o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p)
310 {
311
312         s = u2s(x, 8, false, s, slen_p);
313         if (alt_form && *s != '0') {
314                 s--;
315                 (*slen_p)++;
316                 *s = '0';
317         }
318         return (s);
319 }
320
321 static char *
322 x2s(uintmax_t x, bool alt_form, bool uppercase, char *s, size_t *slen_p)
323 {
324
325         s = u2s(x, 16, uppercase, s, slen_p);
326         if (alt_form) {
327                 s -= 2;
328                 (*slen_p) += 2;
329                 memcpy(s, uppercase ? "0X" : "0x", 2);
330         }
331         return (s);
332 }
333
334 size_t
335 malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
336 {
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                         char *s;
428                         size_t slen;
429
430                         f++;
431                         /* Flags. */
432                         while (true) {
433                                 switch (*f) {
434                                 case '#':
435                                         assert(!alt_form);
436                                         alt_form = true;
437                                         break;
438                                 case '-':
439                                         assert(!left_justify);
440                                         left_justify = true;
441                                         break;
442                                 case ' ':
443                                         assert(!plus_space);
444                                         plus_space = true;
445                                         break;
446                                 case '+':
447                                         assert(!plus_plus);
448                                         plus_plus = true;
449                                         break;
450                                 default: goto label_width;
451                                 }
452                                 f++;
453                         }
454                         /* Width. */
455                         label_width:
456                         switch (*f) {
457                         case '*':
458                                 width = va_arg(ap, int);
459                                 f++;
460                                 if (width < 0) {
461                                         left_justify = true;
462                                         width = -width;
463                                 }
464                                 break;
465                         case '0': case '1': case '2': case '3': case '4':
466                         case '5': case '6': case '7': case '8': case '9': {
467                                 uintmax_t uwidth;
468                                 set_errno(0);
469                                 uwidth = malloc_strtoumax(f, (char **)&f, 10);
470                                 assert(uwidth != UINTMAX_MAX || get_errno() !=
471                                     ERANGE);
472                                 width = (int)uwidth;
473                                 break;
474                         } default:
475                                 break;
476                         }
477                         /* Width/precision separator. */
478                         if (*f == '.')
479                                 f++;
480                         else
481                                 goto label_length;
482                         /* Precision. */
483                         switch (*f) {
484                         case '*':
485                                 prec = va_arg(ap, int);
486                                 f++;
487                                 break;
488                         case '0': case '1': case '2': case '3': case '4':
489                         case '5': case '6': case '7': case '8': case '9': {
490                                 uintmax_t uprec;
491                                 set_errno(0);
492                                 uprec = malloc_strtoumax(f, (char **)&f, 10);
493                                 assert(uprec != UINTMAX_MAX || get_errno() !=
494                                     ERANGE);
495                                 prec = (int)uprec;
496                                 break;
497                         }
498                         default: break;
499                         }
500                         /* Length. */
501                         label_length:
502                         switch (*f) {
503                         case 'l':
504                                 f++;
505                                 if (*f == 'l') {
506                                         len = 'q';
507                                         f++;
508                                 } else
509                                         len = 'l';
510                                 break;
511                         case 'q': case 'j': case 't': case 'z':
512                                 len = *f;
513                                 f++;
514                                 break;
515                         default: break;
516                         }
517                         /* Conversion specifier. */
518                         switch (*f) {
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
605 #undef APPEND_C
606 #undef APPEND_S
607 #undef APPEND_PADDED_S
608 #undef GET_ARG_NUMERIC
609         return (i);
610 }
611
612 JEMALLOC_FORMAT_PRINTF(3, 4)
613 size_t
614 malloc_snprintf(char *str, size_t size, const char *format, ...)
615 {
616         size_t ret;
617         va_list ap;
618
619         va_start(ap, format);
620         ret = malloc_vsnprintf(str, size, format, ap);
621         va_end(ap);
622
623         return (ret);
624 }
625
626 void
627 malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
628     const char *format, va_list ap)
629 {
630         char buf[MALLOC_PRINTF_BUFSIZE];
631
632         if (write_cb == NULL) {
633                 /*
634                  * The caller did not provide an alternate write_cb callback
635                  * function, so use the default one.  malloc_write() is an
636                  * inline function, so use malloc_message() directly here.
637                  */
638                 write_cb = (je_malloc_message != NULL) ? je_malloc_message :
639                     wrtmessage;
640                 cbopaque = NULL;
641         }
642
643         malloc_vsnprintf(buf, sizeof(buf), format, ap);
644         write_cb(cbopaque, buf);
645 }
646
647 /*
648  * Print to a callback function in such a way as to (hopefully) avoid memory
649  * allocation.
650  */
651 JEMALLOC_FORMAT_PRINTF(3, 4)
652 void
653 malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
654     const char *format, ...)
655 {
656         va_list ap;
657
658         va_start(ap, format);
659         malloc_vcprintf(write_cb, cbopaque, format, ap);
660         va_end(ap);
661 }
662
663 /* Print to stderr in such a way as to avoid memory allocation. */
664 JEMALLOC_FORMAT_PRINTF(1, 2)
665 void
666 malloc_printf(const char *format, ...)
667 {
668         va_list ap;
669
670         va_start(ap, format);
671         malloc_vcprintf(NULL, NULL, format, ap);
672         va_end(ap);
673 }
674
675 /*
676  * Restore normal assertion macros, in order to make it possible to compile all
677  * C files as a single concatenation.
678  */
679 #undef assert
680 #undef not_reached
681 #undef not_implemented
682 #include "jemalloc/internal/assert.h"