1 /* $NetBSD: mem.c,v 1.17 2025/01/26 16:25:37 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 /*! \file */ 17 18 #include <inttypes.h> 19 #include <limits.h> 20 #include <stdbool.h> 21 #include <stddef.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 25 #include <isc/hash.h> 26 #include <isc/magic.h> 27 #include <isc/mem.h> 28 #include <isc/mutex.h> 29 #include <isc/once.h> 30 #include <isc/os.h> 31 #include <isc/overflow.h> 32 #include <isc/refcount.h> 33 #include <isc/strerr.h> 34 #include <isc/string.h> 35 #include <isc/types.h> 36 #include <isc/urcu.h> 37 #include <isc/util.h> 38 39 #ifdef HAVE_LIBXML2 40 #include <libxml/xmlwriter.h> 41 #define ISC_XMLCHAR (const xmlChar *) 42 #endif /* HAVE_LIBXML2 */ 43 44 #ifdef HAVE_JSON_C 45 #include <json_object.h> 46 #endif /* HAVE_JSON_C */ 47 48 /* On DragonFly BSD the header does not provide jemalloc API */ 49 #if defined(HAVE_MALLOC_NP_H) && !defined(__DragonFly__) 50 #include <malloc_np.h> 51 #define JEMALLOC_API_SUPPORTED 1 52 #elif defined(HAVE_JEMALLOC) 53 #include <jemalloc/jemalloc.h> 54 #define JEMALLOC_API_SUPPORTED 1 55 #else 56 #include "jemalloc_shim.h" 57 #endif 58 59 #include "mem_p.h" 60 61 #define MCTXLOCK(m) LOCK(&m->lock) 62 #define MCTXUNLOCK(m) UNLOCK(&m->lock) 63 64 #ifndef ISC_MEM_DEBUGGING 65 #define ISC_MEM_DEBUGGING 0 66 #endif /* ifndef ISC_MEM_DEBUGGING */ 67 unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING; 68 unsigned int isc_mem_defaultflags = ISC_MEMFLAG_DEFAULT; 69 70 #define ISC_MEM_ILLEGAL_ARENA (UINT_MAX) 71 72 volatile void *isc__mem_malloc = mallocx; 73 74 /* 75 * Constants. 76 */ 77 78 #define ZERO_ALLOCATION_SIZE sizeof(void *) 79 #define ALIGNMENT 8U /*%< must be a power of 2 */ 80 #define ALIGNMENT_SIZE sizeof(size_info) 81 #define DEBUG_TABLE_COUNT 512U 82 83 /* 84 * Types. 85 */ 86 #if ISC_MEM_TRACKLINES 87 typedef struct debuglink debuglink_t; 88 struct debuglink { 89 ISC_LINK(debuglink_t) link; 90 const void *ptr; 91 size_t size; 92 const char *file; 93 unsigned int line; 94 }; 95 96 typedef ISC_LIST(debuglink_t) debuglist_t; 97 98 #define FLARG_PASS , file, line 99 #define FLARG , const char *file, unsigned int line 100 #else /* if ISC_MEM_TRACKLINES */ 101 #define FLARG_PASS 102 #define FLARG 103 #endif /* if ISC_MEM_TRACKLINES */ 104 105 typedef struct element element; 106 struct element { 107 element *next; 108 }; 109 110 #define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C') 111 #define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC) 112 113 /* List of all active memory contexts. */ 114 115 static ISC_LIST(isc_mem_t) contexts; 116 117 static isc_once_t init_once = ISC_ONCE_INIT; 118 static isc_once_t shut_once = ISC_ONCE_INIT; 119 static isc_mutex_t contextslock; 120 121 struct isc_mem { 122 unsigned int magic; 123 unsigned int flags; 124 unsigned int jemalloc_flags; 125 unsigned int jemalloc_arena; 126 unsigned int debugging; 127 isc_mutex_t lock; 128 bool checkfree; 129 isc_refcount_t references; 130 char name[16]; 131 atomic_size_t inuse; 132 atomic_bool hi_called; 133 atomic_bool is_overmem; 134 atomic_size_t hi_water; 135 atomic_size_t lo_water; 136 ISC_LIST(isc_mempool_t) pools; 137 unsigned int poolcnt; 138 139 #if ISC_MEM_TRACKLINES 140 debuglist_t *debuglist; 141 size_t debuglistcnt; 142 #endif /* if ISC_MEM_TRACKLINES */ 143 144 ISC_LINK(isc_mem_t) link; 145 }; 146 147 #define MEMPOOL_MAGIC ISC_MAGIC('M', 'E', 'M', 'p') 148 #define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC) 149 150 struct isc_mempool { 151 /* always unlocked */ 152 unsigned int magic; 153 isc_mem_t *mctx; /*%< our memory context */ 154 ISC_LINK(isc_mempool_t) link; /*%< next pool in this mem context */ 155 element *items; /*%< low water item list */ 156 size_t size; /*%< size of each item on this pool */ 157 size_t allocated; /*%< # of items currently given out */ 158 size_t freecount; /*%< # of items on reserved list */ 159 size_t freemax; /*%< # of items allowed on free list */ 160 size_t fillcount; /*%< # of items to fetch on each fill */ 161 /*%< Stats only. */ 162 size_t gets; /*%< # of requests to this pool */ 163 /*%< Debugging only. */ 164 char name[16]; /*%< printed name in stats reports */ 165 }; 166 167 /* 168 * Private Inline-able. 169 */ 170 171 #if !ISC_MEM_TRACKLINES 172 #define ADD_TRACE(mctx, ptr, size, file, line) 173 #define DELETE_TRACE(mctx, ptr, size, file, line) 174 #define ISC_MEMFUNC_SCOPE 175 #else /* if !ISC_MEM_TRACKLINES */ 176 #define TRACE_OR_RECORD (ISC_MEM_DEBUGTRACE | ISC_MEM_DEBUGRECORD) 177 178 #define SHOULD_TRACE_OR_RECORD(mctx, ptr) \ 179 (((mctx)->debugging & TRACE_OR_RECORD) != 0 && ptr != NULL) 180 181 #define ADD_TRACE(mctx, ptr, size, file, line) \ 182 if (SHOULD_TRACE_OR_RECORD(mctx, ptr)) { \ 183 add_trace_entry(mctx, ptr, size, file, line); \ 184 } 185 186 #define DELETE_TRACE(mctx, ptr, size, file, line) \ 187 if (SHOULD_TRACE_OR_RECORD(mctx, ptr)) { \ 188 delete_trace_entry(mctx, ptr, size, file, line); \ 189 } 190 191 static void 192 print_active(isc_mem_t *ctx, FILE *out); 193 #endif /* ISC_MEM_TRACKLINES */ 194 195 #if ISC_MEM_TRACKLINES 196 /*! 197 * mctx must not be locked. 198 */ 199 static void 200 add_trace_entry(isc_mem_t *mctx, const void *ptr, size_t size FLARG) { 201 debuglink_t *dl = NULL; 202 uint32_t hash; 203 uint32_t idx; 204 205 MCTXLOCK(mctx); 206 207 if ((mctx->debugging & ISC_MEM_DEBUGTRACE) != 0) { 208 fprintf(stderr, "add %p size %zu file %s line %u mctx %p\n", 209 ptr, size, file, line, mctx); 210 } 211 212 if (mctx->debuglist == NULL) { 213 goto unlock; 214 } 215 216 #ifdef __COVERITY__ 217 /* 218 * Use simple conversion from pointer to hash to avoid 219 * tainting 'ptr' due to byte swap in isc_hash32. 220 */ 221 hash = (uintptr_t)ptr >> 3; 222 #else 223 hash = isc_hash32(&ptr, sizeof(ptr), true); 224 #endif 225 idx = hash % DEBUG_TABLE_COUNT; 226 227 dl = mallocx(sizeof(debuglink_t), mctx->jemalloc_flags); 228 INSIST(dl != NULL); 229 230 ISC_LINK_INIT(dl, link); 231 dl->ptr = ptr; 232 dl->size = size; 233 dl->file = file; 234 dl->line = line; 235 236 ISC_LIST_PREPEND(mctx->debuglist[idx], dl, link); 237 mctx->debuglistcnt++; 238 unlock: 239 MCTXUNLOCK(mctx); 240 } 241 242 static void 243 delete_trace_entry(isc_mem_t *mctx, const void *ptr, size_t size, 244 const char *file, unsigned int line) { 245 debuglink_t *dl = NULL; 246 uint32_t hash; 247 uint32_t idx; 248 249 MCTXLOCK(mctx); 250 251 if ((mctx->debugging & ISC_MEM_DEBUGTRACE) != 0) { 252 fprintf(stderr, "del %p size %zu file %s line %u mctx %p\n", 253 ptr, size, file, line, mctx); 254 } 255 256 if (mctx->debuglist == NULL) { 257 goto unlock; 258 } 259 260 #ifdef __COVERITY__ 261 /* 262 * Use simple conversion from pointer to hash to avoid 263 * tainting 'ptr' due to byte swap in isc_hash32. 264 */ 265 hash = (uintptr_t)ptr >> 3; 266 #else 267 hash = isc_hash32(&ptr, sizeof(ptr), true); 268 #endif 269 idx = hash % DEBUG_TABLE_COUNT; 270 271 dl = ISC_LIST_HEAD(mctx->debuglist[idx]); 272 while (dl != NULL) { 273 if (dl->ptr == ptr) { 274 ISC_LIST_UNLINK(mctx->debuglist[idx], dl, link); 275 sdallocx(dl, sizeof(*dl), mctx->jemalloc_flags); 276 goto unlock; 277 } 278 dl = ISC_LIST_NEXT(dl, link); 279 } 280 281 /* 282 * If we get here, we didn't find the item on the list. We're 283 * screwed. 284 */ 285 UNREACHABLE(); 286 unlock: 287 MCTXUNLOCK(mctx); 288 } 289 #endif /* ISC_MEM_TRACKLINES */ 290 291 #define ADJUST_ZERO_ALLOCATION_SIZE(s) \ 292 if (s == 0) { \ 293 s = ZERO_ALLOCATION_SIZE; \ 294 } 295 296 /*! 297 * Perform a malloc, doing memory filling and overrun detection as necessary. 298 */ 299 static void * 300 mem_get(isc_mem_t *ctx, size_t size, int flags) { 301 char *ret = NULL; 302 303 ADJUST_ZERO_ALLOCATION_SIZE(size); 304 305 ret = mallocx(size, flags | ctx->jemalloc_flags); 306 INSIST(ret != NULL); 307 308 if ((flags & ISC__MEM_ZERO) == 0 && 309 (ctx->flags & ISC_MEMFLAG_FILL) != 0) 310 { 311 memset(ret, 0xbe, size); /* Mnemonic for "beef". */ 312 } 313 314 return ret; 315 } 316 317 /*! 318 * Perform a free, doing memory filling and overrun detection as necessary. 319 */ 320 /* coverity[+free : arg-1] */ 321 static void 322 mem_put(isc_mem_t *ctx, void *mem, size_t size, int flags) { 323 ADJUST_ZERO_ALLOCATION_SIZE(size); 324 325 if ((ctx->flags & ISC_MEMFLAG_FILL) != 0) { 326 memset(mem, 0xde, size); /* Mnemonic for "dead". */ 327 } 328 sdallocx(mem, size, flags | ctx->jemalloc_flags); 329 } 330 331 static void * 332 mem_realloc(isc_mem_t *ctx, void *old_ptr, size_t old_size, size_t new_size, 333 int flags) { 334 void *new_ptr = NULL; 335 336 ADJUST_ZERO_ALLOCATION_SIZE(new_size); 337 338 new_ptr = rallocx(old_ptr, new_size, flags | ctx->jemalloc_flags); 339 INSIST(new_ptr != NULL); 340 341 if ((flags & ISC__MEM_ZERO) == 0 && 342 (ctx->flags & ISC_MEMFLAG_FILL) != 0) 343 { 344 ssize_t diff_size = new_size - old_size; 345 void *diff_ptr = (uint8_t *)new_ptr + old_size; 346 if (diff_size > 0) { 347 /* Mnemonic for "beef". */ 348 memset(diff_ptr, 0xbe, diff_size); 349 } 350 } 351 352 return new_ptr; 353 } 354 355 /*! 356 * Update internal counters after a memory get. 357 */ 358 static void 359 mem_getstats(isc_mem_t *ctx, size_t size) { 360 atomic_fetch_add_relaxed(&ctx->inuse, size); 361 } 362 363 /*! 364 * Update internal counters after a memory put. 365 */ 366 static void 367 mem_putstats(isc_mem_t *ctx, size_t size) { 368 atomic_size_t s = atomic_fetch_sub_relaxed(&ctx->inuse, size); 369 INSIST(s >= size); 370 } 371 372 /* 373 * Private. 374 */ 375 376 static bool 377 mem_jemalloc_arena_create(unsigned int *pnew_arenano) { 378 REQUIRE(pnew_arenano != NULL); 379 380 #ifdef JEMALLOC_API_SUPPORTED 381 unsigned int arenano = 0; 382 size_t len = sizeof(arenano); 383 int res = 0; 384 385 res = mallctl("arenas.create", &arenano, &len, NULL, 0); 386 if (res != 0) { 387 return false; 388 } 389 390 *pnew_arenano = arenano; 391 392 return true; 393 #else 394 *pnew_arenano = ISC_MEM_ILLEGAL_ARENA; 395 return true; 396 #endif /* JEMALLOC_API_SUPPORTED */ 397 } 398 399 static bool 400 mem_jemalloc_arena_destroy(unsigned int arenano) { 401 #ifdef JEMALLOC_API_SUPPORTED 402 int res = 0; 403 char buf[256] = { 0 }; 404 405 (void)snprintf(buf, sizeof(buf), "arena.%u.destroy", arenano); 406 res = mallctl(buf, NULL, NULL, NULL, 0); 407 if (res != 0) { 408 return false; 409 } 410 411 return true; 412 #else 413 UNUSED(arenano); 414 return true; 415 #endif /* JEMALLOC_API_SUPPORTED */ 416 } 417 418 static void 419 mem_initialize(void) { 420 /* 421 * Check if the values copied from jemalloc still match 422 */ 423 #ifdef JEMALLOC_API_SUPPORTED 424 RUNTIME_CHECK(ISC__MEM_ZERO == MALLOCX_ZERO); 425 #endif /* JEMALLOC_API_SUPPORTED */ 426 427 isc_mutex_init(&contextslock); 428 ISC_LIST_INIT(contexts); 429 } 430 431 void 432 isc__mem_initialize(void) { 433 isc_once_do(&init_once, mem_initialize); 434 } 435 436 static void 437 mem_shutdown(void) { 438 bool empty; 439 440 isc__mem_checkdestroyed(); 441 442 LOCK(&contextslock); 443 empty = ISC_LIST_EMPTY(contexts); 444 UNLOCK(&contextslock); 445 446 if (empty) { 447 isc_mutex_destroy(&contextslock); 448 } 449 } 450 451 void 452 isc__mem_shutdown(void) { 453 isc_once_do(&shut_once, mem_shutdown); 454 } 455 456 static void 457 mem_create(isc_mem_t **ctxp, unsigned int debugging, unsigned int flags, 458 unsigned int jemalloc_flags) { 459 isc_mem_t *ctx = NULL; 460 461 REQUIRE(ctxp != NULL && *ctxp == NULL); 462 463 ctx = mallocx(sizeof(*ctx), jemalloc_flags); 464 INSIST(ctx != NULL); 465 466 *ctx = (isc_mem_t){ 467 .magic = MEM_MAGIC, 468 .debugging = debugging, 469 .flags = flags, 470 .jemalloc_flags = jemalloc_flags, 471 .jemalloc_arena = ISC_MEM_ILLEGAL_ARENA, 472 .checkfree = true, 473 }; 474 475 isc_mutex_init(&ctx->lock); 476 isc_refcount_init(&ctx->references, 1); 477 478 atomic_init(&ctx->inuse, 0); 479 atomic_init(&ctx->hi_water, 0); 480 atomic_init(&ctx->lo_water, 0); 481 atomic_init(&ctx->hi_called, false); 482 atomic_init(&ctx->is_overmem, false); 483 484 ISC_LIST_INIT(ctx->pools); 485 486 #if ISC_MEM_TRACKLINES 487 if ((ctx->debugging & ISC_MEM_DEBUGRECORD) != 0) { 488 unsigned int i; 489 490 ctx->debuglist = mallocx( 491 ISC_CHECKED_MUL(DEBUG_TABLE_COUNT, sizeof(debuglist_t)), 492 jemalloc_flags); 493 INSIST(ctx->debuglist != NULL); 494 495 for (i = 0; i < DEBUG_TABLE_COUNT; i++) { 496 ISC_LIST_INIT(ctx->debuglist[i]); 497 } 498 } 499 #endif /* if ISC_MEM_TRACKLINES */ 500 501 LOCK(&contextslock); 502 ISC_LIST_INITANDAPPEND(contexts, ctx, link); 503 UNLOCK(&contextslock); 504 505 *ctxp = ctx; 506 } 507 508 /* 509 * Public. 510 */ 511 512 static void 513 destroy(isc_mem_t *ctx) { 514 unsigned int arena_no; 515 LOCK(&contextslock); 516 ISC_LIST_UNLINK(contexts, ctx, link); 517 UNLOCK(&contextslock); 518 519 ctx->magic = 0; 520 521 arena_no = ctx->jemalloc_arena; 522 523 INSIST(ISC_LIST_EMPTY(ctx->pools)); 524 525 #if ISC_MEM_TRACKLINES 526 if (ctx->debuglist != NULL) { 527 debuglink_t *dl; 528 for (size_t i = 0; i < DEBUG_TABLE_COUNT; i++) { 529 for (dl = ISC_LIST_HEAD(ctx->debuglist[i]); dl != NULL; 530 dl = ISC_LIST_HEAD(ctx->debuglist[i])) 531 { 532 if (ctx->checkfree && dl->ptr != NULL) { 533 print_active(ctx, stderr); 534 } 535 INSIST(!ctx->checkfree || dl->ptr == NULL); 536 537 ISC_LIST_UNLINK(ctx->debuglist[i], dl, link); 538 sdallocx(dl, sizeof(*dl), ctx->jemalloc_flags); 539 } 540 } 541 542 sdallocx( 543 ctx->debuglist, 544 ISC_CHECKED_MUL(DEBUG_TABLE_COUNT, sizeof(debuglist_t)), 545 ctx->jemalloc_flags); 546 } 547 #endif /* if ISC_MEM_TRACKLINES */ 548 549 isc_mutex_destroy(&ctx->lock); 550 551 if (ctx->checkfree) { 552 INSIST(atomic_load(&ctx->inuse) == 0); 553 } 554 sdallocx(ctx, sizeof(*ctx), ctx->jemalloc_flags); 555 556 if (arena_no != ISC_MEM_ILLEGAL_ARENA) { 557 RUNTIME_CHECK(mem_jemalloc_arena_destroy(arena_no) == true); 558 } 559 } 560 561 void 562 isc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) { 563 REQUIRE(VALID_CONTEXT(source)); 564 REQUIRE(targetp != NULL && *targetp == NULL); 565 566 isc_refcount_increment(&source->references); 567 568 *targetp = source; 569 } 570 571 void 572 isc__mem_detach(isc_mem_t **ctxp FLARG) { 573 isc_mem_t *ctx = NULL; 574 575 REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp)); 576 577 ctx = *ctxp; 578 *ctxp = NULL; 579 580 if (isc_refcount_decrement(&ctx->references) == 1) { 581 isc_refcount_destroy(&ctx->references); 582 #if ISC_MEM_TRACKLINES 583 if ((ctx->debugging & ISC_MEM_DEBUGTRACE) != 0) { 584 fprintf(stderr, "destroy mctx %p file %s line %u\n", 585 ctx, file, line); 586 } 587 #endif 588 destroy(ctx); 589 } 590 } 591 592 /* 593 * isc_mem_putanddetach() is the equivalent of: 594 * 595 * mctx = NULL; 596 * isc_mem_attach(ptr->mctx, &mctx); 597 * isc_mem_detach(&ptr->mctx); 598 * isc_mem_put(mctx, ptr, sizeof(*ptr); 599 * isc_mem_detach(&mctx); 600 */ 601 602 void 603 isc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size, 604 int flags FLARG) { 605 REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp)); 606 REQUIRE(ptr != NULL); 607 REQUIRE(size != 0); 608 609 isc_mem_t *ctx = *ctxp; 610 *ctxp = NULL; 611 612 isc__mem_put(ctx, ptr, size, flags FLARG_PASS); 613 isc__mem_detach(&ctx FLARG_PASS); 614 } 615 616 void 617 isc__mem_destroy(isc_mem_t **ctxp FLARG) { 618 isc_mem_t *ctx = NULL; 619 620 /* 621 * This routine provides legacy support for callers who use mctxs 622 * without attaching/detaching. 623 */ 624 625 REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp)); 626 627 ctx = *ctxp; 628 *ctxp = NULL; 629 630 rcu_barrier(); 631 632 #if ISC_MEM_TRACKLINES 633 if ((ctx->debugging & ISC_MEM_DEBUGTRACE) != 0) { 634 fprintf(stderr, "destroy mctx %p file %s line %u\n", ctx, file, 635 line); 636 } 637 638 if (isc_refcount_decrement(&ctx->references) > 1) { 639 print_active(ctx, stderr); 640 } 641 #else /* if ISC_MEM_TRACKLINES */ 642 isc_refcount_decrementz(&ctx->references); 643 #endif /* if ISC_MEM_TRACKLINES */ 644 isc_refcount_destroy(&ctx->references); 645 destroy(ctx); 646 647 *ctxp = NULL; 648 } 649 650 void * 651 isc__mem_get(isc_mem_t *ctx, size_t size, int flags FLARG) { 652 void *ptr = NULL; 653 654 REQUIRE(VALID_CONTEXT(ctx)); 655 656 ptr = mem_get(ctx, size, flags); 657 658 mem_getstats(ctx, size); 659 ADD_TRACE(ctx, ptr, size, file, line); 660 661 return ptr; 662 } 663 664 void 665 isc__mem_put(isc_mem_t *ctx, void *ptr, size_t size, int flags FLARG) { 666 REQUIRE(VALID_CONTEXT(ctx)); 667 668 DELETE_TRACE(ctx, ptr, size, file, line); 669 670 mem_putstats(ctx, size); 671 mem_put(ctx, ptr, size, flags); 672 } 673 674 #if ISC_MEM_TRACKLINES 675 static void 676 print_active(isc_mem_t *mctx, FILE *out) { 677 if (mctx->debuglist != NULL) { 678 debuglink_t *dl; 679 unsigned int i; 680 bool found; 681 682 fprintf(out, "Dump of all outstanding memory " 683 "allocations:\n"); 684 found = false; 685 for (i = 0; i < DEBUG_TABLE_COUNT; i++) { 686 dl = ISC_LIST_HEAD(mctx->debuglist[i]); 687 688 if (dl != NULL) { 689 found = true; 690 } 691 692 while (dl != NULL) { 693 if (dl->ptr != NULL) { 694 fprintf(out, 695 "\tptr %p size %zu " 696 "file %s " 697 "line %u\n", 698 dl->ptr, dl->size, dl->file, 699 dl->line); 700 } 701 dl = ISC_LIST_NEXT(dl, link); 702 } 703 } 704 705 if (!found) { 706 fprintf(out, "\tNone.\n"); 707 } 708 } 709 } 710 #endif /* if ISC_MEM_TRACKLINES */ 711 712 /* 713 * Print the stats[] on the stream "out" with suitable formatting. 714 */ 715 void 716 isc_mem_stats(isc_mem_t *ctx, FILE *out) { 717 isc_mempool_t *pool = NULL; 718 719 REQUIRE(VALID_CONTEXT(ctx)); 720 721 MCTXLOCK(ctx); 722 723 /* 724 * Note that since a pool can be locked now, these stats might 725 * be somewhat off if the pool is in active use at the time the 726 * stats are dumped. The link fields are protected by the 727 * isc_mem_t's lock, however, so walking this list and 728 * extracting integers from stats fields is always safe. 729 */ 730 pool = ISC_LIST_HEAD(ctx->pools); 731 if (pool != NULL) { 732 fprintf(out, "[Pool statistics]\n"); 733 fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %1s\n", "name", 734 "size", "allocated", "freecount", "freemax", 735 "fillcount", "gets", "L"); 736 } 737 while (pool != NULL) { 738 fprintf(out, 739 "%15s %10zu %10zu %10zu %10zu %10zu %10zu %10zu %s\n", 740 pool->name, pool->size, (size_t)0, pool->allocated, 741 pool->freecount, pool->freemax, pool->fillcount, 742 pool->gets, "N"); 743 pool = ISC_LIST_NEXT(pool, link); 744 } 745 746 #if ISC_MEM_TRACKLINES 747 print_active(ctx, out); 748 #endif /* if ISC_MEM_TRACKLINES */ 749 750 MCTXUNLOCK(ctx); 751 } 752 753 void * 754 isc__mem_allocate(isc_mem_t *ctx, size_t size, int flags FLARG) { 755 void *ptr = NULL; 756 757 REQUIRE(VALID_CONTEXT(ctx)); 758 759 ptr = mem_get(ctx, size, flags); 760 761 /* Recalculate the real allocated size */ 762 size = sallocx(ptr, flags | ctx->jemalloc_flags); 763 764 mem_getstats(ctx, size); 765 ADD_TRACE(ctx, ptr, size, file, line); 766 767 return ptr; 768 } 769 770 void * 771 isc__mem_reget(isc_mem_t *ctx, void *old_ptr, size_t old_size, size_t new_size, 772 int flags FLARG) { 773 void *new_ptr = NULL; 774 775 if (old_ptr == NULL) { 776 REQUIRE(old_size == 0); 777 new_ptr = isc__mem_get(ctx, new_size, flags FLARG_PASS); 778 } else if (new_size == 0) { 779 isc__mem_put(ctx, old_ptr, old_size, flags FLARG_PASS); 780 } else { 781 DELETE_TRACE(ctx, old_ptr, old_size, file, line); 782 mem_putstats(ctx, old_size); 783 784 new_ptr = mem_realloc(ctx, old_ptr, old_size, new_size, flags); 785 786 mem_getstats(ctx, new_size); 787 ADD_TRACE(ctx, new_ptr, new_size, file, line); 788 789 /* 790 * We want to postpone the call to water in edge case 791 * where the realloc will exactly hit on the boundary of 792 * the water and we would call water twice. 793 */ 794 } 795 796 return new_ptr; 797 } 798 799 void * 800 isc__mem_reallocate(isc_mem_t *ctx, void *old_ptr, size_t new_size, 801 int flags FLARG) { 802 void *new_ptr = NULL; 803 804 REQUIRE(VALID_CONTEXT(ctx)); 805 806 if (old_ptr == NULL) { 807 new_ptr = isc__mem_allocate(ctx, new_size, flags FLARG_PASS); 808 } else if (new_size == 0) { 809 isc__mem_free(ctx, old_ptr, flags FLARG_PASS); 810 } else { 811 size_t old_size = sallocx(old_ptr, flags | ctx->jemalloc_flags); 812 813 DELETE_TRACE(ctx, old_ptr, old_size, file, line); 814 mem_putstats(ctx, old_size); 815 816 new_ptr = mem_realloc(ctx, old_ptr, old_size, new_size, flags); 817 818 /* Recalculate the real allocated size */ 819 new_size = sallocx(new_ptr, flags | ctx->jemalloc_flags); 820 821 mem_getstats(ctx, new_size); 822 ADD_TRACE(ctx, new_ptr, new_size, file, line); 823 824 /* 825 * We want to postpone the call to water in edge case 826 * where the realloc will exactly hit on the boundary of 827 * the water and we would call water twice. 828 */ 829 } 830 831 return new_ptr; 832 } 833 834 void 835 isc__mem_free(isc_mem_t *ctx, void *ptr, int flags FLARG) { 836 size_t size = 0; 837 838 REQUIRE(VALID_CONTEXT(ctx)); 839 REQUIRE(ptr != NULL); 840 841 size = sallocx(ptr, flags | ctx->jemalloc_flags); 842 843 DELETE_TRACE(ctx, ptr, size, file, line); 844 845 mem_putstats(ctx, size); 846 mem_put(ctx, ptr, size, flags); 847 } 848 849 /* 850 * Other useful things. 851 */ 852 853 char * 854 isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) { 855 size_t len; 856 char *ns = NULL; 857 858 REQUIRE(VALID_CONTEXT(mctx)); 859 REQUIRE(s != NULL); 860 861 len = strlen(s) + 1; 862 863 ns = isc__mem_allocate(mctx, len, 0 FLARG_PASS); 864 865 strlcpy(ns, s, len); 866 867 return ns; 868 } 869 870 char * 871 isc__mem_strndup(isc_mem_t *mctx, const char *s, size_t size FLARG) { 872 size_t len; 873 char *ns = NULL; 874 875 REQUIRE(VALID_CONTEXT(mctx)); 876 REQUIRE(s != NULL); 877 REQUIRE(size != 0); 878 879 len = strlen(s) + 1; 880 if (len > size) { 881 len = size; 882 } 883 884 ns = isc__mem_allocate(mctx, len, 0 FLARG_PASS); 885 886 strlcpy(ns, s, len); 887 888 return ns; 889 } 890 891 void 892 isc_mem_setdestroycheck(isc_mem_t *ctx, bool flag) { 893 REQUIRE(VALID_CONTEXT(ctx)); 894 895 MCTXLOCK(ctx); 896 897 ctx->checkfree = flag; 898 899 MCTXUNLOCK(ctx); 900 } 901 902 size_t 903 isc_mem_inuse(isc_mem_t *ctx) { 904 REQUIRE(VALID_CONTEXT(ctx)); 905 906 return atomic_load_relaxed(&ctx->inuse); 907 } 908 909 void 910 isc_mem_clearwater(isc_mem_t *mctx) { 911 isc_mem_setwater(mctx, 0, 0); 912 } 913 914 void 915 isc_mem_setwater(isc_mem_t *ctx, size_t hiwater, size_t lowater) { 916 REQUIRE(VALID_CONTEXT(ctx)); 917 REQUIRE(hiwater >= lowater); 918 919 atomic_store_release(&ctx->hi_water, hiwater); 920 atomic_store_release(&ctx->lo_water, lowater); 921 922 return; 923 } 924 925 bool 926 isc_mem_isovermem(isc_mem_t *ctx) { 927 REQUIRE(VALID_CONTEXT(ctx)); 928 929 bool is_overmem = atomic_load_relaxed(&ctx->is_overmem); 930 931 if (!is_overmem) { 932 /* We are not overmem, check whether we should be? */ 933 size_t hiwater = atomic_load_relaxed(&ctx->hi_water); 934 if (hiwater == 0) { 935 return false; 936 } 937 938 size_t inuse = atomic_load_relaxed(&ctx->inuse); 939 if (inuse <= hiwater) { 940 return false; 941 } 942 943 if ((isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) { 944 fprintf(stderr, 945 "overmem mctx %p inuse %zu hi_water %zu\n", ctx, 946 inuse, hiwater); 947 } 948 949 atomic_store_relaxed(&ctx->is_overmem, true); 950 return true; 951 } else { 952 /* We are overmem, check whether we should not be? */ 953 size_t lowater = atomic_load_relaxed(&ctx->lo_water); 954 if (lowater == 0) { 955 return false; 956 } 957 958 size_t inuse = atomic_load_relaxed(&ctx->inuse); 959 if (inuse >= lowater) { 960 return true; 961 } 962 963 if ((isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) { 964 fprintf(stderr, 965 "overmem mctx %p inuse %zu lo_water %zu\n", ctx, 966 inuse, lowater); 967 } 968 atomic_store_relaxed(&ctx->is_overmem, false); 969 return false; 970 } 971 } 972 973 void 974 isc_mem_setname(isc_mem_t *ctx, const char *name) { 975 REQUIRE(VALID_CONTEXT(ctx)); 976 977 LOCK(&ctx->lock); 978 strlcpy(ctx->name, name, sizeof(ctx->name)); 979 UNLOCK(&ctx->lock); 980 } 981 982 const char * 983 isc_mem_getname(isc_mem_t *ctx) { 984 REQUIRE(VALID_CONTEXT(ctx)); 985 986 if (ctx->name[0] == 0) { 987 return ""; 988 } 989 990 return ctx->name; 991 } 992 993 /* 994 * Memory pool stuff 995 */ 996 997 void 998 isc__mempool_create(isc_mem_t *restrict mctx, const size_t element_size, 999 isc_mempool_t **restrict mpctxp FLARG) { 1000 isc_mempool_t *restrict mpctx = NULL; 1001 size_t size = element_size; 1002 1003 REQUIRE(VALID_CONTEXT(mctx)); 1004 REQUIRE(size > 0U); 1005 REQUIRE(mpctxp != NULL && *mpctxp == NULL); 1006 1007 /* 1008 * Mempools are stored as a linked list of element. 1009 */ 1010 if (size < sizeof(element)) { 1011 size = sizeof(element); 1012 } 1013 1014 /* 1015 * Allocate space for this pool, initialize values, and if all 1016 * works well, attach to the memory context. 1017 */ 1018 mpctx = isc_mem_get(mctx, sizeof(isc_mempool_t)); 1019 1020 *mpctx = (isc_mempool_t){ 1021 .size = size, 1022 .freemax = 1, 1023 .fillcount = 1, 1024 }; 1025 1026 #if ISC_MEM_TRACKLINES 1027 if ((mctx->debugging & ISC_MEM_DEBUGTRACE) != 0) { 1028 fprintf(stderr, "create pool %p file %s line %u mctx %p\n", 1029 mpctx, file, line, mctx); 1030 } 1031 #endif /* ISC_MEM_TRACKLINES */ 1032 1033 isc_mem_attach(mctx, &mpctx->mctx); 1034 mpctx->magic = MEMPOOL_MAGIC; 1035 1036 *mpctxp = (isc_mempool_t *)mpctx; 1037 1038 MCTXLOCK(mctx); 1039 ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link); 1040 mctx->poolcnt++; 1041 MCTXUNLOCK(mctx); 1042 } 1043 1044 void 1045 isc_mempool_setname(isc_mempool_t *restrict mpctx, const char *name) { 1046 REQUIRE(VALID_MEMPOOL(mpctx)); 1047 REQUIRE(name != NULL); 1048 1049 strlcpy(mpctx->name, name, sizeof(mpctx->name)); 1050 } 1051 1052 void 1053 isc__mempool_destroy(isc_mempool_t **restrict mpctxp FLARG) { 1054 isc_mempool_t *restrict mpctx = NULL; 1055 isc_mem_t *mctx = NULL; 1056 element *restrict item = NULL; 1057 1058 REQUIRE(mpctxp != NULL); 1059 REQUIRE(VALID_MEMPOOL(*mpctxp)); 1060 1061 mpctx = *mpctxp; 1062 *mpctxp = NULL; 1063 1064 mctx = mpctx->mctx; 1065 1066 #if ISC_MEM_TRACKLINES 1067 if ((mctx->debugging & ISC_MEM_DEBUGTRACE) != 0) { 1068 fprintf(stderr, "destroy pool %p file %s line %u mctx %p\n", 1069 mpctx, file, line, mctx); 1070 } 1071 #endif 1072 1073 if (mpctx->allocated > 0) { 1074 UNEXPECTED_ERROR("mempool %s leaked memory", mpctx->name); 1075 } 1076 REQUIRE(mpctx->allocated == 0); 1077 1078 /* 1079 * Return any items on the free list 1080 */ 1081 while (mpctx->items != NULL) { 1082 INSIST(mpctx->freecount > 0); 1083 mpctx->freecount--; 1084 1085 item = mpctx->items; 1086 mpctx->items = item->next; 1087 1088 mem_putstats(mctx, mpctx->size); 1089 mem_put(mctx, item, mpctx->size, 0); 1090 } 1091 1092 /* 1093 * Remove our linked list entry from the memory context. 1094 */ 1095 MCTXLOCK(mctx); 1096 ISC_LIST_UNLINK(mctx->pools, mpctx, link); 1097 mctx->poolcnt--; 1098 MCTXUNLOCK(mctx); 1099 1100 mpctx->magic = 0; 1101 1102 isc_mem_putanddetach(&mpctx->mctx, mpctx, sizeof(isc_mempool_t)); 1103 } 1104 1105 void * 1106 isc__mempool_get(isc_mempool_t *restrict mpctx FLARG) { 1107 element *restrict item = NULL; 1108 1109 REQUIRE(VALID_MEMPOOL(mpctx)); 1110 1111 mpctx->allocated++; 1112 1113 if (mpctx->items == NULL) { 1114 isc_mem_t *mctx = mpctx->mctx; 1115 #if !__SANITIZE_ADDRESS__ 1116 const size_t fillcount = mpctx->fillcount; 1117 #else 1118 const size_t fillcount = 1; 1119 #endif 1120 /* 1121 * We need to dip into the well. Fill up our free list. 1122 */ 1123 for (size_t i = 0; i < fillcount; i++) { 1124 item = mem_get(mctx, mpctx->size, 0); 1125 mem_getstats(mctx, mpctx->size); 1126 item->next = mpctx->items; 1127 mpctx->items = item; 1128 mpctx->freecount++; 1129 } 1130 } 1131 1132 INSIST(mpctx->items != NULL); 1133 item = mpctx->items; 1134 1135 mpctx->items = item->next; 1136 1137 INSIST(mpctx->freecount > 0); 1138 mpctx->freecount--; 1139 mpctx->gets++; 1140 1141 ADD_TRACE(mpctx->mctx, item, mpctx->size, file, line); 1142 1143 return item; 1144 } 1145 1146 /* coverity[+free : arg-1] */ 1147 void 1148 isc__mempool_put(isc_mempool_t *restrict mpctx, void *mem FLARG) { 1149 element *restrict item = NULL; 1150 1151 REQUIRE(VALID_MEMPOOL(mpctx)); 1152 REQUIRE(mem != NULL); 1153 1154 isc_mem_t *mctx = mpctx->mctx; 1155 const size_t freecount = mpctx->freecount; 1156 #if !__SANITIZE_ADDRESS__ 1157 const size_t freemax = mpctx->freemax; 1158 #else 1159 const size_t freemax = 0; 1160 #endif 1161 1162 INSIST(mpctx->allocated > 0); 1163 mpctx->allocated--; 1164 1165 DELETE_TRACE(mctx, mem, mpctx->size, file, line); 1166 1167 /* 1168 * If our free list is full, return this to the mctx directly. 1169 */ 1170 if (freecount >= freemax) { 1171 mem_putstats(mctx, mpctx->size); 1172 mem_put(mctx, mem, mpctx->size, 0); 1173 return; 1174 } 1175 1176 /* 1177 * Otherwise, attach it to our free list and bump the counter. 1178 */ 1179 item = (element *)mem; 1180 item->next = mpctx->items; 1181 mpctx->items = item; 1182 mpctx->freecount++; 1183 } 1184 1185 /* 1186 * Quotas 1187 */ 1188 1189 void 1190 isc_mempool_setfreemax(isc_mempool_t *restrict mpctx, 1191 const unsigned int limit) { 1192 REQUIRE(VALID_MEMPOOL(mpctx)); 1193 mpctx->freemax = limit; 1194 } 1195 1196 unsigned int 1197 isc_mempool_getfreemax(isc_mempool_t *restrict mpctx) { 1198 REQUIRE(VALID_MEMPOOL(mpctx)); 1199 1200 return mpctx->freemax; 1201 } 1202 1203 unsigned int 1204 isc_mempool_getfreecount(isc_mempool_t *restrict mpctx) { 1205 REQUIRE(VALID_MEMPOOL(mpctx)); 1206 1207 return mpctx->freecount; 1208 } 1209 1210 unsigned int 1211 isc_mempool_getallocated(isc_mempool_t *restrict mpctx) { 1212 REQUIRE(VALID_MEMPOOL(mpctx)); 1213 1214 return mpctx->allocated; 1215 } 1216 1217 void 1218 isc_mempool_setfillcount(isc_mempool_t *restrict mpctx, 1219 unsigned int const limit) { 1220 REQUIRE(VALID_MEMPOOL(mpctx)); 1221 REQUIRE(limit > 0); 1222 1223 mpctx->fillcount = limit; 1224 } 1225 1226 unsigned int 1227 isc_mempool_getfillcount(isc_mempool_t *restrict mpctx) { 1228 REQUIRE(VALID_MEMPOOL(mpctx)); 1229 1230 return mpctx->fillcount; 1231 } 1232 1233 /* 1234 * Requires contextslock to be held by caller. 1235 */ 1236 #if ISC_MEM_TRACKLINES 1237 static void 1238 print_contexts(FILE *file) { 1239 isc_mem_t *ctx; 1240 1241 for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL; 1242 ctx = ISC_LIST_NEXT(ctx, link)) 1243 { 1244 fprintf(file, "context: %p (%s): %" PRIuFAST32 " references\n", 1245 ctx, ctx->name[0] == 0 ? "<unknown>" : ctx->name, 1246 isc_refcount_current(&ctx->references)); 1247 print_active(ctx, file); 1248 } 1249 fflush(file); 1250 } 1251 #endif 1252 1253 static atomic_uintptr_t checkdestroyed = 0; 1254 1255 void 1256 isc_mem_checkdestroyed(FILE *file) { 1257 atomic_store_release(&checkdestroyed, (uintptr_t)file); 1258 } 1259 1260 void 1261 isc__mem_checkdestroyed(void) { 1262 FILE *file = (FILE *)atomic_load_acquire(&checkdestroyed); 1263 1264 if (file == NULL) { 1265 return; 1266 } 1267 1268 LOCK(&contextslock); 1269 if (!ISC_LIST_EMPTY(contexts)) { 1270 #if ISC_MEM_TRACKLINES 1271 if ((isc_mem_debugging & TRACE_OR_RECORD) != 0) { 1272 print_contexts(file); 1273 } 1274 #endif /* if ISC_MEM_TRACKLINES */ 1275 UNREACHABLE(); 1276 } 1277 UNLOCK(&contextslock); 1278 } 1279 1280 unsigned int 1281 isc_mem_references(isc_mem_t *ctx) { 1282 return isc_refcount_current(&ctx->references); 1283 } 1284 1285 #ifdef HAVE_LIBXML2 1286 #define TRY0(a) \ 1287 do { \ 1288 xmlrc = (a); \ 1289 if (xmlrc < 0) \ 1290 goto error; \ 1291 } while (0) 1292 static int 1293 xml_renderctx(isc_mem_t *ctx, size_t *inuse, xmlTextWriterPtr writer) { 1294 REQUIRE(VALID_CONTEXT(ctx)); 1295 1296 int xmlrc; 1297 1298 MCTXLOCK(ctx); 1299 1300 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "context")); 1301 1302 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id")); 1303 TRY0(xmlTextWriterWriteFormatString(writer, "%p", ctx)); 1304 TRY0(xmlTextWriterEndElement(writer)); /* id */ 1305 1306 if (ctx->name[0] != 0) { 1307 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name")); 1308 TRY0(xmlTextWriterWriteFormatString(writer, "%s", ctx->name)); 1309 TRY0(xmlTextWriterEndElement(writer)); /* name */ 1310 } 1311 1312 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references")); 1313 TRY0(xmlTextWriterWriteFormatString( 1314 writer, "%" PRIuFAST32, 1315 isc_refcount_current(&ctx->references))); 1316 TRY0(xmlTextWriterEndElement(writer)); /* references */ 1317 1318 *inuse += isc_mem_inuse(ctx); 1319 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse")); 1320 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 1321 (uint64_t)isc_mem_inuse(ctx))); 1322 TRY0(xmlTextWriterEndElement(writer)); /* inuse */ 1323 1324 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "malloced")); 1325 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 1326 (uint64_t)isc_mem_inuse(ctx))); 1327 TRY0(xmlTextWriterEndElement(writer)); /* malloced */ 1328 1329 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools")); 1330 TRY0(xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt)); 1331 TRY0(xmlTextWriterEndElement(writer)); /* pools */ 1332 1333 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater")); 1334 TRY0(xmlTextWriterWriteFormatString( 1335 writer, "%" PRIu64 "", 1336 (uint64_t)atomic_load_relaxed(&ctx->hi_water))); 1337 TRY0(xmlTextWriterEndElement(writer)); /* hiwater */ 1338 1339 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater")); 1340 TRY0(xmlTextWriterWriteFormatString( 1341 writer, "%" PRIu64 "", 1342 (uint64_t)atomic_load_relaxed(&ctx->lo_water))); 1343 TRY0(xmlTextWriterEndElement(writer)); /* lowater */ 1344 1345 TRY0(xmlTextWriterEndElement(writer)); /* context */ 1346 1347 error: 1348 MCTXUNLOCK(ctx); 1349 1350 return xmlrc; 1351 } 1352 1353 int 1354 isc_mem_renderxml(void *writer0) { 1355 isc_mem_t *ctx; 1356 size_t inuse = 0; 1357 int xmlrc; 1358 xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0; 1359 1360 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts")); 1361 1362 LOCK(&contextslock); 1363 for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL; 1364 ctx = ISC_LIST_NEXT(ctx, link)) 1365 { 1366 xmlrc = xml_renderctx(ctx, &inuse, writer); 1367 if (xmlrc < 0) { 1368 UNLOCK(&contextslock); 1369 goto error; 1370 } 1371 } 1372 UNLOCK(&contextslock); 1373 1374 TRY0(xmlTextWriterEndElement(writer)); /* contexts */ 1375 1376 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary")); 1377 1378 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Malloced")); 1379 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 1380 (uint64_t)inuse)); 1381 TRY0(xmlTextWriterEndElement(writer)); /* malloced */ 1382 1383 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse")); 1384 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 1385 (uint64_t)inuse)); 1386 TRY0(xmlTextWriterEndElement(writer)); /* InUse */ 1387 1388 TRY0(xmlTextWriterEndElement(writer)); /* summary */ 1389 error: 1390 return xmlrc; 1391 } 1392 1393 #endif /* HAVE_LIBXML2 */ 1394 1395 #ifdef HAVE_JSON_C 1396 #define CHECKMEM(m) RUNTIME_CHECK(m != NULL) 1397 1398 static isc_result_t 1399 json_renderctx(isc_mem_t *ctx, size_t *inuse, json_object *array) { 1400 REQUIRE(VALID_CONTEXT(ctx)); 1401 REQUIRE(array != NULL); 1402 1403 json_object *ctxobj, *obj; 1404 char buf[1024]; 1405 1406 MCTXLOCK(ctx); 1407 1408 *inuse += isc_mem_inuse(ctx); 1409 1410 ctxobj = json_object_new_object(); 1411 CHECKMEM(ctxobj); 1412 1413 snprintf(buf, sizeof(buf), "%p", ctx); 1414 obj = json_object_new_string(buf); 1415 CHECKMEM(obj); 1416 json_object_object_add(ctxobj, "id", obj); 1417 1418 if (ctx->name[0] != 0) { 1419 obj = json_object_new_string(ctx->name); 1420 CHECKMEM(obj); 1421 json_object_object_add(ctxobj, "name", obj); 1422 } 1423 1424 obj = json_object_new_int64(isc_refcount_current(&ctx->references)); 1425 CHECKMEM(obj); 1426 json_object_object_add(ctxobj, "references", obj); 1427 1428 obj = json_object_new_int64(isc_mem_inuse(ctx)); 1429 CHECKMEM(obj); 1430 json_object_object_add(ctxobj, "malloced", obj); 1431 1432 obj = json_object_new_int64(isc_mem_inuse(ctx)); 1433 CHECKMEM(obj); 1434 json_object_object_add(ctxobj, "inuse", obj); 1435 1436 obj = json_object_new_int64(ctx->poolcnt); 1437 CHECKMEM(obj); 1438 json_object_object_add(ctxobj, "pools", obj); 1439 1440 obj = json_object_new_int64(atomic_load_relaxed(&ctx->hi_water)); 1441 CHECKMEM(obj); 1442 json_object_object_add(ctxobj, "hiwater", obj); 1443 1444 obj = json_object_new_int64(atomic_load_relaxed(&ctx->lo_water)); 1445 CHECKMEM(obj); 1446 json_object_object_add(ctxobj, "lowater", obj); 1447 1448 MCTXUNLOCK(ctx); 1449 json_object_array_add(array, ctxobj); 1450 return ISC_R_SUCCESS; 1451 } 1452 1453 isc_result_t 1454 isc_mem_renderjson(void *memobj0) { 1455 isc_result_t result = ISC_R_SUCCESS; 1456 isc_mem_t *ctx; 1457 size_t inuse = 0; 1458 json_object *ctxarray, *obj; 1459 json_object *memobj = (json_object *)memobj0; 1460 1461 ctxarray = json_object_new_array(); 1462 CHECKMEM(ctxarray); 1463 1464 LOCK(&contextslock); 1465 for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL; 1466 ctx = ISC_LIST_NEXT(ctx, link)) 1467 { 1468 result = json_renderctx(ctx, &inuse, ctxarray); 1469 if (result != ISC_R_SUCCESS) { 1470 UNLOCK(&contextslock); 1471 goto error; 1472 } 1473 } 1474 UNLOCK(&contextslock); 1475 1476 obj = json_object_new_int64(inuse); 1477 CHECKMEM(obj); 1478 json_object_object_add(memobj, "InUse", obj); 1479 1480 obj = json_object_new_int64(inuse); 1481 CHECKMEM(obj); 1482 json_object_object_add(memobj, "Malloced", obj); 1483 1484 json_object_object_add(memobj, "contexts", ctxarray); 1485 return ISC_R_SUCCESS; 1486 1487 error: 1488 if (ctxarray != NULL) { 1489 json_object_put(ctxarray); 1490 } 1491 return result; 1492 } 1493 #endif /* HAVE_JSON_C */ 1494 1495 void 1496 isc__mem_create(isc_mem_t **mctxp FLARG) { 1497 mem_create(mctxp, isc_mem_debugging, isc_mem_defaultflags, 0); 1498 #if ISC_MEM_TRACKLINES 1499 if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) { 1500 fprintf(stderr, "create mctx %p file %s line %u\n", *mctxp, 1501 file, line); 1502 } 1503 #endif /* ISC_MEM_TRACKLINES */ 1504 } 1505 1506 void 1507 isc__mem_create_arena(isc_mem_t **mctxp FLARG) { 1508 unsigned int arena_no = ISC_MEM_ILLEGAL_ARENA; 1509 1510 RUNTIME_CHECK(mem_jemalloc_arena_create(&arena_no)); 1511 1512 /* 1513 * We use MALLOCX_TCACHE_NONE to bypass the tcache and route 1514 * allocations directly to the arena. That is a recommendation 1515 * from jemalloc developers: 1516 * 1517 * https://github.com/jemalloc/jemalloc/issues/2483#issuecomment-1698173849 1518 */ 1519 mem_create(mctxp, isc_mem_debugging, isc_mem_defaultflags, 1520 arena_no == ISC_MEM_ILLEGAL_ARENA 1521 ? 0 1522 : MALLOCX_ARENA(arena_no) | MALLOCX_TCACHE_NONE); 1523 (*mctxp)->jemalloc_arena = arena_no; 1524 #if ISC_MEM_TRACKLINES 1525 if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) { 1526 fprintf(stderr, 1527 "create mctx %p file %s line %u for jemalloc arena " 1528 "%u\n", 1529 *mctxp, file, line, arena_no); 1530 } 1531 #endif /* ISC_MEM_TRACKLINES */ 1532 } 1533 1534 #ifdef JEMALLOC_API_SUPPORTED 1535 static bool 1536 jemalloc_set_ssize_value(const char *valname, ssize_t newval) { 1537 int ret; 1538 1539 ret = mallctl(valname, NULL, NULL, &newval, sizeof(newval)); 1540 return ret == 0; 1541 } 1542 #endif /* JEMALLOC_API_SUPPORTED */ 1543 1544 static isc_result_t 1545 mem_set_arena_ssize_value(isc_mem_t *mctx, const char *arena_valname, 1546 const ssize_t newval) { 1547 REQUIRE(VALID_CONTEXT(mctx)); 1548 #ifdef JEMALLOC_API_SUPPORTED 1549 bool ret; 1550 char buf[256] = { 0 }; 1551 1552 if (mctx->jemalloc_arena == ISC_MEM_ILLEGAL_ARENA) { 1553 return ISC_R_UNEXPECTED; 1554 } 1555 1556 (void)snprintf(buf, sizeof(buf), "arena.%u.%s", mctx->jemalloc_arena, 1557 arena_valname); 1558 1559 ret = jemalloc_set_ssize_value(buf, newval); 1560 1561 if (!ret) { 1562 return ISC_R_FAILURE; 1563 } 1564 1565 return ISC_R_SUCCESS; 1566 #else 1567 UNUSED(arena_valname); 1568 UNUSED(newval); 1569 return ISC_R_NOTIMPLEMENTED; 1570 #endif 1571 } 1572 1573 isc_result_t 1574 isc_mem_arena_set_muzzy_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms) { 1575 return mem_set_arena_ssize_value(mctx, "muzzy_decay_ms", decay_ms); 1576 } 1577 1578 isc_result_t 1579 isc_mem_arena_set_dirty_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms) { 1580 return mem_set_arena_ssize_value(mctx, "dirty_decay_ms", decay_ms); 1581 } 1582 1583 void 1584 isc__mem_printactive(isc_mem_t *ctx, FILE *file) { 1585 #if ISC_MEM_TRACKLINES 1586 REQUIRE(VALID_CONTEXT(ctx)); 1587 REQUIRE(file != NULL); 1588 1589 print_active(ctx, file); 1590 #else /* if ISC_MEM_TRACKLINES */ 1591 UNUSED(ctx); 1592 UNUSED(file); 1593 #endif /* if ISC_MEM_TRACKLINES */ 1594 } 1595