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