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