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