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#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 68JEMALLOC_EXPORT void (*je_malloc_message)(void *, const char *s); 69 70JEMALLOC_ATTR(visibility("hidden")) 71void 72wrtmessage_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 82void (*__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 */ 90void 91malloc_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 */ 104int 105buferror(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 124uintmax_t 125malloc_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 228label_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 239static char * 240u2s(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 282static char * 283d2s(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 308static char * 309o2s(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 321static char * 322x2s(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 334size_t 335malloc_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 612JEMALLOC_FORMAT_PRINTF(3, 4) 613size_t 614malloc_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 626void 627malloc_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 */ 651JEMALLOC_FORMAT_PRINTF(3, 4) 652void 653malloc_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. */ 664JEMALLOC_FORMAT_PRINTF(1, 2) 665void 666malloc_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" 683