1 /* $NetBSD: mem.c,v 1.13 2023/01/25 21:43:31 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 <errno.h> 19 #include <inttypes.h> 20 #include <limits.h> 21 #include <stdbool.h> 22 #include <stddef.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 26 #include <isc/bind9.h> 27 #include <isc/hash.h> 28 #include <isc/lib.h> 29 #include <isc/magic.h> 30 #include <isc/mem.h> 31 #include <isc/mutex.h> 32 #include <isc/once.h> 33 #include <isc/print.h> 34 #include <isc/refcount.h> 35 #include <isc/strerr.h> 36 #include <isc/string.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 #include "mem_p.h" 49 50 #define MCTXLOCK(m) LOCK(&m->lock) 51 #define MCTXUNLOCK(m) UNLOCK(&m->lock) 52 53 #ifndef ISC_MEM_DEBUGGING 54 #define ISC_MEM_DEBUGGING 0 55 #endif /* ifndef ISC_MEM_DEBUGGING */ 56 LIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING; 57 LIBISC_EXTERNAL_DATA unsigned int isc_mem_defaultflags = ISC_MEMFLAG_DEFAULT; 58 59 /* 60 * Constants. 61 */ 62 63 #define DEF_MAX_SIZE 1100 64 #define DEF_MEM_TARGET 4096 65 #define ALIGNMENT_SIZE 8U /*%< must be a power of 2 */ 66 #define NUM_BASIC_BLOCKS 64 /*%< must be > 1 */ 67 #define TABLE_INCREMENT 1024 68 #define DEBUG_TABLE_COUNT 512U 69 70 /* 71 * Types. 72 */ 73 typedef struct isc__mem isc__mem_t; 74 typedef struct isc__mempool isc__mempool_t; 75 76 #if ISC_MEM_TRACKLINES 77 typedef struct debuglink debuglink_t; 78 struct debuglink { 79 ISC_LINK(debuglink_t) link; 80 const void *ptr; 81 size_t size; 82 const char *file; 83 unsigned int line; 84 }; 85 86 typedef ISC_LIST(debuglink_t) debuglist_t; 87 88 #define FLARG_PASS , file, line 89 #define FLARG , const char *file, unsigned int line 90 #else /* if ISC_MEM_TRACKLINES */ 91 #define FLARG_PASS 92 #define FLARG 93 #endif /* if ISC_MEM_TRACKLINES */ 94 95 typedef struct element element; 96 struct element { 97 element *next; 98 }; 99 100 typedef struct { 101 /*! 102 * This structure must be ALIGNMENT_SIZE bytes. 103 */ 104 union { 105 size_t size; 106 isc__mem_t *ctx; 107 char bytes[ALIGNMENT_SIZE]; 108 } u; 109 } size_info; 110 111 struct stats { 112 unsigned long gets; 113 unsigned long totalgets; 114 unsigned long blocks; 115 unsigned long freefrags; 116 }; 117 118 #define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C') 119 #define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC) 120 121 /* List of all active memory contexts. */ 122 123 static ISC_LIST(isc__mem_t) contexts; 124 125 static isc_once_t init_once = ISC_ONCE_INIT; 126 static isc_once_t shut_once = ISC_ONCE_INIT; 127 static isc_mutex_t contextslock; 128 129 /*% 130 * Total size of lost memory due to a bug of external library. 131 * Locked by the global lock. 132 */ 133 static uint64_t totallost; 134 135 /*% 136 * Memory allocation and free function definitions. 137 * isc__memalloc_t must deal with memory allocation failure 138 * and must never return NULL. 139 */ 140 typedef void *(*isc__memalloc_t)(size_t); 141 typedef void (*isc__memfree_t)(void *); 142 143 struct isc__mem { 144 isc_mem_t common; 145 unsigned int flags; 146 isc_mutex_t lock; 147 isc__memalloc_t memalloc; 148 isc__memfree_t memfree; 149 size_t max_size; 150 bool checkfree; 151 struct stats *stats; 152 isc_refcount_t references; 153 char name[16]; 154 void *tag; 155 size_t total; 156 size_t inuse; 157 size_t maxinuse; 158 size_t malloced; 159 size_t maxmalloced; 160 size_t hi_water; 161 size_t lo_water; 162 bool hi_called; 163 bool is_overmem; 164 isc_mem_water_t water; 165 void *water_arg; 166 ISC_LIST(isc__mempool_t) pools; 167 unsigned int poolcnt; 168 169 /* ISC_MEMFLAG_INTERNAL */ 170 size_t mem_target; 171 element **freelists; 172 element *basic_blocks; 173 unsigned char **basic_table; 174 unsigned int basic_table_count; 175 unsigned int basic_table_size; 176 unsigned char *lowest; 177 unsigned char *highest; 178 179 #if ISC_MEM_TRACKLINES 180 debuglist_t *debuglist; 181 size_t debuglistcnt; 182 #endif /* if ISC_MEM_TRACKLINES */ 183 184 ISC_LINK(isc__mem_t) link; 185 }; 186 187 #define MEMPOOL_MAGIC ISC_MAGIC('M', 'E', 'M', 'p') 188 #define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC) 189 190 struct isc__mempool { 191 /* always unlocked */ 192 isc_mempool_t common; /*%< common header of mempool's */ 193 isc__mem_t *mctx; /*%< our memory context */ 194 ISC_LINK(isc__mempool_t) link; /*%< next pool in this mem context */ 195 element *items; /*%< low water item list */ 196 size_t size; /*%< size of each item on this pool */ 197 unsigned int maxalloc; /*%< max number of items allowed */ 198 unsigned int allocated; /*%< # of items currently given out */ 199 unsigned int freecount; /*%< # of items on reserved list */ 200 unsigned int freemax; /*%< # of items allowed on free list */ 201 unsigned int fillcount; /*%< # of items to fetch on each fill */ 202 /*%< Stats only. */ 203 unsigned int gets; /*%< # of requests to this pool */ 204 /*%< Debugging only. */ 205 #if ISC_MEMPOOL_NAMES 206 char name[16]; /*%< printed name in stats reports */ 207 #endif /* if ISC_MEMPOOL_NAMES */ 208 }; 209 210 /* 211 * Private Inline-able. 212 */ 213 214 #if !ISC_MEM_TRACKLINES 215 #define ADD_TRACE(a, b, c, d, e) 216 #define DELETE_TRACE(a, b, c, d, e) 217 #define ISC_MEMFUNC_SCOPE 218 #else /* if !ISC_MEM_TRACKLINES */ 219 #define TRACE_OR_RECORD (ISC_MEM_DEBUGTRACE | ISC_MEM_DEBUGRECORD) 220 #define ADD_TRACE(a, b, c, d, e) \ 221 do { \ 222 if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \ 223 b != NULL)) \ 224 add_trace_entry(a, b, c, d, e); \ 225 } while (0) 226 #define DELETE_TRACE(a, b, c, d, e) \ 227 do { \ 228 if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \ 229 b != NULL)) \ 230 delete_trace_entry(a, b, c, d, e); \ 231 } while (0) 232 233 static void 234 print_active(isc__mem_t *ctx, FILE *out); 235 236 #endif /* ISC_MEM_TRACKLINES */ 237 238 static void * 239 isc___mem_get(isc_mem_t *ctx, size_t size FLARG); 240 static void 241 isc___mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG); 242 static void 243 isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG); 244 static void * 245 isc___mem_allocate(isc_mem_t *ctx, size_t size FLARG); 246 static void * 247 isc___mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG); 248 static char * 249 isc___mem_strdup(isc_mem_t *mctx, const char *s FLARG); 250 static char * 251 isc___mem_strndup(isc_mem_t *mctx0, const char *s, size_t size FLARG); 252 static void 253 isc___mem_free(isc_mem_t *ctx, void *ptr FLARG); 254 255 static isc_memmethods_t memmethods = { 256 isc___mem_get, isc___mem_put, isc___mem_putanddetach, 257 isc___mem_allocate, isc___mem_reallocate, isc___mem_strdup, 258 isc___mem_strndup, isc___mem_free, 259 }; 260 261 #if ISC_MEM_TRACKLINES 262 /*! 263 * mctx must be locked. 264 */ 265 static void 266 add_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size FLARG) { 267 debuglink_t *dl; 268 uint32_t hash; 269 uint32_t idx; 270 271 if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) { 272 fprintf(stderr, "add %p size %zu file %s line %u mctx %p\n", 273 ptr, size, file, line, mctx); 274 } 275 276 if (mctx->debuglist == NULL) { 277 return; 278 } 279 280 #ifdef __COVERITY__ 281 /* 282 * Use simple conversion from pointer to hash to avoid 283 * tainting 'ptr' due to byte swap in isc_hash_function. 284 */ 285 hash = (uintptr_t)ptr >> 3; 286 #else 287 hash = isc_hash_function(&ptr, sizeof(ptr), true); 288 #endif 289 idx = hash % DEBUG_TABLE_COUNT; 290 291 dl = malloc(sizeof(debuglink_t)); 292 INSIST(dl != NULL); 293 mctx->malloced += sizeof(debuglink_t); 294 if (mctx->malloced > mctx->maxmalloced) { 295 mctx->maxmalloced = mctx->malloced; 296 } 297 298 ISC_LINK_INIT(dl, link); 299 dl->ptr = ptr; 300 dl->size = size; 301 dl->file = file; 302 dl->line = line; 303 304 ISC_LIST_PREPEND(mctx->debuglist[idx], dl, link); 305 mctx->debuglistcnt++; 306 } 307 308 static void 309 delete_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size, 310 const char *file, unsigned int line) { 311 debuglink_t *dl; 312 uint32_t hash; 313 uint32_t idx; 314 315 if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) { 316 fprintf(stderr, "del %p size %zu file %s line %u mctx %p\n", 317 ptr, size, file, line, mctx); 318 } 319 320 if (mctx->debuglist == NULL) { 321 return; 322 } 323 324 #ifdef __COVERITY__ 325 /* 326 * Use simple conversion from pointer to hash to avoid 327 * tainting 'ptr' due to byte swap in isc_hash_function. 328 */ 329 hash = (uintptr_t)ptr >> 3; 330 #else 331 hash = isc_hash_function(&ptr, sizeof(ptr), true); 332 #endif 333 idx = hash % DEBUG_TABLE_COUNT; 334 335 dl = ISC_LIST_HEAD(mctx->debuglist[idx]); 336 while (ISC_LIKELY(dl != NULL)) { 337 if (ISC_UNLIKELY(dl->ptr == ptr)) { 338 ISC_LIST_UNLINK(mctx->debuglist[idx], dl, link); 339 mctx->malloced -= sizeof(*dl); 340 free(dl); 341 return; 342 } 343 dl = ISC_LIST_NEXT(dl, link); 344 } 345 346 /* 347 * If we get here, we didn't find the item on the list. We're 348 * screwed. 349 */ 350 UNREACHABLE(); 351 } 352 #endif /* ISC_MEM_TRACKLINES */ 353 354 static size_t 355 rmsize(size_t size) { 356 /* 357 * round down to ALIGNMENT_SIZE 358 */ 359 return (size & (~(ALIGNMENT_SIZE - 1))); 360 } 361 362 static size_t 363 quantize(size_t size) { 364 /*! 365 * Round up the result in order to get a size big 366 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE 367 * byte boundaries. 368 */ 369 370 if (size == 0U) { 371 return (ALIGNMENT_SIZE); 372 } 373 return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1))); 374 } 375 376 static void 377 more_basic_blocks(isc__mem_t *ctx) { 378 void *tmp; 379 unsigned char *curr, *next; 380 unsigned char *first, *last; 381 unsigned char **table; 382 unsigned int table_size; 383 384 /* Require: we hold the context lock. */ 385 386 INSIST(ctx->basic_table_count <= ctx->basic_table_size); 387 if (ctx->basic_table_count == ctx->basic_table_size) { 388 table_size = ctx->basic_table_size + TABLE_INCREMENT; 389 table = (ctx->memalloc)(table_size * sizeof(unsigned char *)); 390 ctx->malloced += table_size * sizeof(unsigned char *); 391 if (ctx->malloced > ctx->maxmalloced) { 392 ctx->maxmalloced = ctx->malloced; 393 } 394 if (ctx->basic_table_size != 0) { 395 memmove(table, ctx->basic_table, 396 ctx->basic_table_size * 397 sizeof(unsigned char *)); 398 (ctx->memfree)(ctx->basic_table); 399 ctx->malloced -= ctx->basic_table_size * 400 sizeof(unsigned char *); 401 } 402 ctx->basic_table = table; 403 ctx->basic_table_size = table_size; 404 } 405 406 tmp = (ctx->memalloc)(NUM_BASIC_BLOCKS * ctx->mem_target); 407 ctx->total += NUM_BASIC_BLOCKS * ctx->mem_target; 408 ctx->basic_table[ctx->basic_table_count] = tmp; 409 ctx->basic_table_count++; 410 ctx->malloced += NUM_BASIC_BLOCKS * ctx->mem_target; 411 if (ctx->malloced > ctx->maxmalloced) { 412 ctx->maxmalloced = ctx->malloced; 413 } 414 415 curr = tmp; 416 next = curr + ctx->mem_target; 417 for (int i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) { 418 ((element *)curr)->next = (element *)next; 419 curr = next; 420 next += ctx->mem_target; 421 } 422 /* 423 * curr is now pointing at the last block in the 424 * array. 425 */ 426 ((element *)curr)->next = NULL; 427 first = tmp; 428 last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1; 429 if (first < ctx->lowest || ctx->lowest == NULL) { 430 ctx->lowest = first; 431 } 432 if (last > ctx->highest) { 433 ctx->highest = last; 434 } 435 ctx->basic_blocks = tmp; 436 } 437 438 static void 439 more_frags(isc__mem_t *ctx, size_t new_size) { 440 int frags; 441 size_t total_size; 442 void *tmp; 443 unsigned char *curr, *next; 444 445 /*! 446 * Try to get more fragments by chopping up a basic block. 447 */ 448 449 if (ctx->basic_blocks == NULL) { 450 more_basic_blocks(ctx); 451 } 452 INSIST(ctx->basic_blocks != NULL); 453 454 total_size = ctx->mem_target; 455 tmp = ctx->basic_blocks; 456 ctx->basic_blocks = ctx->basic_blocks->next; 457 frags = (int)(total_size / new_size); 458 ctx->stats[new_size].blocks++; 459 ctx->stats[new_size].freefrags += frags; 460 /* 461 * Set up a linked-list of blocks of size 462 * "new_size". 463 */ 464 curr = tmp; 465 next = curr + new_size; 466 total_size -= new_size; 467 for (int i = 0; i < (frags - 1); i++) { 468 ((element *)curr)->next = (element *)next; 469 curr = next; 470 next += new_size; 471 total_size -= new_size; 472 } 473 /* 474 * Add the remaining fragment of the basic block to a free list. 475 */ 476 total_size = rmsize(total_size); 477 if (total_size > 0U) { 478 ((element *)next)->next = ctx->freelists[total_size]; 479 ctx->freelists[total_size] = (element *)next; 480 ctx->stats[total_size].freefrags++; 481 } 482 /* 483 * curr is now pointing at the last block in the 484 * array. 485 */ 486 ((element *)curr)->next = NULL; 487 ctx->freelists[new_size] = tmp; 488 } 489 490 static void * 491 mem_getunlocked(isc__mem_t *ctx, size_t size) { 492 size_t new_size = quantize(size); 493 void *ret; 494 495 if (new_size >= ctx->max_size) { 496 /* 497 * memget() was called on something beyond our upper limit. 498 */ 499 ret = (ctx->memalloc)(size); 500 ctx->total += size; 501 ctx->inuse += size; 502 ctx->stats[ctx->max_size].gets++; 503 ctx->stats[ctx->max_size].totalgets++; 504 ctx->malloced += size; 505 if (ctx->malloced > ctx->maxmalloced) { 506 ctx->maxmalloced = ctx->malloced; 507 } 508 /* 509 * If we don't set new_size to size, then the 510 * ISC_MEMFLAG_FILL code might write over bytes we don't 511 * own. 512 */ 513 new_size = size; 514 goto done; 515 } 516 /* 517 * If there are no blocks in the free list for this size, get a chunk 518 * of memory and then break it up into "new_size"-sized blocks, adding 519 * them to the free list. 520 */ 521 if (ctx->freelists[new_size] == NULL) { 522 more_frags(ctx, new_size); 523 } 524 INSIST(ctx->freelists[new_size] != NULL); 525 526 /* 527 * The free list uses the "rounded-up" size "new_size". 528 */ 529 530 ret = ctx->freelists[new_size]; 531 ctx->freelists[new_size] = ctx->freelists[new_size]->next; 532 533 /* 534 * The stats[] uses the _actual_ "size" requested by the 535 * caller, with the caveat (in the code above) that "size" >= the 536 * max. size (max_size) ends up getting recorded as a call to 537 * max_size. 538 */ 539 ctx->stats[size].gets++; 540 ctx->stats[size].totalgets++; 541 ctx->stats[new_size].freefrags--; 542 ctx->inuse += new_size; 543 544 done: 545 if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0) && 546 ISC_LIKELY(ret != NULL)) 547 { 548 memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */ 549 } 550 551 return (ret); 552 } 553 554 #if ISC_MEM_CHECKOVERRUN 555 static void 556 check_overrun(void *mem, size_t size, size_t new_size) { 557 unsigned char *cp; 558 559 cp = (unsigned char *)mem; 560 cp += size; 561 while (size < new_size) { 562 INSIST(*cp == 0xbe); 563 cp++; 564 size++; 565 } 566 } 567 #endif /* if ISC_MEM_CHECKOVERRUN */ 568 569 /* coverity[+free : arg-1] */ 570 static void 571 mem_putunlocked(isc__mem_t *ctx, void *mem, size_t size) { 572 size_t new_size = quantize(size); 573 574 if (new_size >= ctx->max_size) { 575 /* 576 * memput() called on something beyond our upper limit. 577 */ 578 if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) { 579 memset(mem, 0xde, size); /* Mnemonic for "dead". */ 580 } 581 582 (ctx->memfree)(mem); 583 INSIST(ctx->stats[ctx->max_size].gets != 0U); 584 ctx->stats[ctx->max_size].gets--; 585 INSIST(size <= ctx->inuse); 586 ctx->inuse -= size; 587 ctx->malloced -= size; 588 return; 589 } 590 591 if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) { 592 #if ISC_MEM_CHECKOVERRUN 593 check_overrun(mem, size, new_size); 594 #endif /* if ISC_MEM_CHECKOVERRUN */ 595 memset(mem, 0xde, new_size); /* Mnemonic for "dead". */ 596 } 597 598 /* 599 * The free list uses the "rounded-up" size "new_size". 600 */ 601 ((element *)mem)->next = ctx->freelists[new_size]; 602 ctx->freelists[new_size] = (element *)mem; 603 604 /* 605 * The stats[] uses the _actual_ "size" requested by the 606 * caller, with the caveat (in the code above) that "size" >= the 607 * max. size (max_size) ends up getting recorded as a call to 608 * max_size. 609 */ 610 INSIST(ctx->stats[size].gets != 0U); 611 ctx->stats[size].gets--; 612 ctx->stats[new_size].freefrags++; 613 ctx->inuse -= new_size; 614 } 615 616 /*! 617 * Perform a malloc, doing memory filling and overrun detection as necessary. 618 */ 619 static void * 620 mem_get(isc__mem_t *ctx, size_t size) { 621 char *ret; 622 623 #if ISC_MEM_CHECKOVERRUN 624 size += 1; 625 #endif /* if ISC_MEM_CHECKOVERRUN */ 626 ret = (ctx->memalloc)(size); 627 628 if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) { 629 if (ISC_LIKELY(ret != NULL)) { 630 memset(ret, 0xbe, size); /* Mnemonic for "beef". */ 631 } 632 } 633 #if ISC_MEM_CHECKOVERRUN 634 else 635 { 636 if (ISC_LIKELY(ret != NULL)) { 637 ret[size - 1] = 0xbe; 638 } 639 } 640 #endif /* if ISC_MEM_CHECKOVERRUN */ 641 642 return (ret); 643 } 644 645 /*! 646 * Perform a free, doing memory filling and overrun detection as necessary. 647 */ 648 /* coverity[+free : arg-1] */ 649 static void 650 mem_put(isc__mem_t *ctx, void *mem, size_t size) { 651 #if ISC_MEM_CHECKOVERRUN 652 INSIST(((unsigned char *)mem)[size] == 0xbe); 653 size += 1; 654 #endif /* if ISC_MEM_CHECKOVERRUN */ 655 if (ISC_UNLIKELY((ctx->flags & ISC_MEMFLAG_FILL) != 0)) { 656 memset(mem, 0xde, size); /* Mnemonic for "dead". */ 657 } 658 (ctx->memfree)(mem); 659 } 660 661 /*! 662 * Update internal counters after a memory get. 663 */ 664 static void 665 mem_getstats(isc__mem_t *ctx, size_t size) { 666 ctx->total += size; 667 ctx->inuse += size; 668 669 if (size > ctx->max_size) { 670 ctx->stats[ctx->max_size].gets++; 671 ctx->stats[ctx->max_size].totalgets++; 672 } else { 673 ctx->stats[size].gets++; 674 ctx->stats[size].totalgets++; 675 } 676 677 #if ISC_MEM_CHECKOVERRUN 678 size += 1; 679 #endif /* if ISC_MEM_CHECKOVERRUN */ 680 ctx->malloced += size; 681 if (ctx->malloced > ctx->maxmalloced) { 682 ctx->maxmalloced = ctx->malloced; 683 } 684 } 685 686 /*! 687 * Update internal counters after a memory put. 688 */ 689 static void 690 mem_putstats(isc__mem_t *ctx, void *ptr, size_t size) { 691 UNUSED(ptr); 692 693 INSIST(ctx->inuse >= size); 694 ctx->inuse -= size; 695 696 if (size > ctx->max_size) { 697 INSIST(ctx->stats[ctx->max_size].gets > 0U); 698 ctx->stats[ctx->max_size].gets--; 699 } else { 700 INSIST(ctx->stats[size].gets > 0U); 701 ctx->stats[size].gets--; 702 } 703 #if ISC_MEM_CHECKOVERRUN 704 size += 1; 705 #endif /* if ISC_MEM_CHECKOVERRUN */ 706 ctx->malloced -= size; 707 } 708 709 /* 710 * Private. 711 */ 712 713 static void * 714 default_memalloc(size_t size) { 715 void *ptr; 716 717 ptr = malloc(size); 718 719 /* 720 * If the space cannot be allocated, a null pointer is returned. If the 721 * size of the space requested is zero, the behavior is 722 * implementation-defined: either a null pointer is returned, or the 723 * behavior is as if the size were some nonzero value, except that the 724 * returned pointer shall not be used to access an object. 725 * [ISO9899 § 7.22.3] 726 * 727 * [ISO9899] 728 * ISO/IEC WG 9899:2011: Programming languages - C. 729 * International Organization for Standardization, Geneva, 730 * Switzerland. 731 * http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf 732 */ 733 734 if (ptr == NULL && size != 0) { 735 char strbuf[ISC_STRERRORSIZE]; 736 strerror_r(errno, strbuf, sizeof(strbuf)); 737 isc_error_fatal(__FILE__, __LINE__, "malloc failed: %s", 738 strbuf); 739 } 740 741 return (ptr); 742 } 743 744 static void 745 default_memfree(void *ptr) { 746 free(ptr); 747 } 748 749 static void 750 mem_initialize(void) { 751 isc_mutex_init(&contextslock); 752 ISC_LIST_INIT(contexts); 753 totallost = 0; 754 } 755 756 void 757 isc__mem_initialize(void) { 758 RUNTIME_CHECK(isc_once_do(&init_once, mem_initialize) == ISC_R_SUCCESS); 759 } 760 761 static void 762 mem_shutdown(void) { 763 isc__mem_checkdestroyed(); 764 765 isc_mutex_destroy(&contextslock); 766 } 767 768 void 769 isc__mem_shutdown(void) { 770 RUNTIME_CHECK(isc_once_do(&shut_once, mem_shutdown) == ISC_R_SUCCESS); 771 } 772 773 static void 774 mem_create(isc_mem_t **ctxp, unsigned int flags) { 775 REQUIRE(ctxp != NULL && *ctxp == NULL); 776 #if __SANITIZE_ADDRESS__ 777 REQUIRE((flags & ISC_MEMFLAG_INTERNAL) == 0); 778 #endif 779 780 isc__mem_t *ctx; 781 782 isc_enable_constructors(); 783 784 STATIC_ASSERT((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0, 785 "wrong alignment size"); 786 787 ctx = (default_memalloc)(sizeof(*ctx)); 788 789 isc_mutex_init(&ctx->lock); 790 791 ctx->max_size = DEF_MAX_SIZE; 792 ctx->flags = flags; 793 isc_refcount_init(&ctx->references, 1); 794 memset(ctx->name, 0, sizeof(ctx->name)); 795 ctx->tag = NULL; 796 ctx->total = 0; 797 ctx->inuse = 0; 798 ctx->maxinuse = 0; 799 ctx->malloced = sizeof(*ctx); 800 ctx->maxmalloced = sizeof(*ctx); 801 ctx->hi_water = 0; 802 ctx->lo_water = 0; 803 ctx->hi_called = false; 804 ctx->is_overmem = false; 805 ctx->water = NULL; 806 ctx->water_arg = NULL; 807 ctx->common.impmagic = MEM_MAGIC; 808 ctx->common.magic = ISCAPI_MCTX_MAGIC; 809 ctx->common.methods = (isc_memmethods_t *)&memmethods; 810 ctx->memalloc = default_memalloc; 811 ctx->memfree = default_memfree; 812 ctx->stats = NULL; 813 ctx->checkfree = true; 814 #if ISC_MEM_TRACKLINES 815 ctx->debuglist = NULL; 816 ctx->debuglistcnt = 0; 817 #endif /* if ISC_MEM_TRACKLINES */ 818 ISC_LIST_INIT(ctx->pools); 819 ctx->poolcnt = 0; 820 ctx->freelists = NULL; 821 ctx->basic_blocks = NULL; 822 ctx->basic_table = NULL; 823 ctx->basic_table_count = 0; 824 ctx->basic_table_size = 0; 825 ctx->lowest = NULL; 826 ctx->highest = NULL; 827 828 ctx->stats = 829 (ctx->memalloc)((ctx->max_size + 1) * sizeof(struct stats)); 830 831 memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats)); 832 ctx->malloced += (ctx->max_size + 1) * sizeof(struct stats); 833 ctx->maxmalloced += (ctx->max_size + 1) * sizeof(struct stats); 834 835 if ((flags & ISC_MEMFLAG_INTERNAL) != 0) { 836 ctx->mem_target = DEF_MEM_TARGET; 837 ctx->freelists = 838 (ctx->memalloc)(ctx->max_size * sizeof(element *)); 839 memset(ctx->freelists, 0, ctx->max_size * sizeof(element *)); 840 ctx->malloced += ctx->max_size * sizeof(element *); 841 ctx->maxmalloced += ctx->max_size * sizeof(element *); 842 } 843 844 #if ISC_MEM_TRACKLINES 845 if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0)) { 846 unsigned int i; 847 848 ctx->debuglist = (ctx->memalloc)( 849 (DEBUG_TABLE_COUNT * sizeof(debuglist_t))); 850 for (i = 0; i < DEBUG_TABLE_COUNT; i++) { 851 ISC_LIST_INIT(ctx->debuglist[i]); 852 } 853 ctx->malloced += DEBUG_TABLE_COUNT * sizeof(debuglist_t); 854 ctx->maxmalloced += DEBUG_TABLE_COUNT * sizeof(debuglist_t); 855 } 856 #endif /* if ISC_MEM_TRACKLINES */ 857 858 LOCK(&contextslock); 859 ISC_LIST_INITANDAPPEND(contexts, ctx, link); 860 UNLOCK(&contextslock); 861 862 *ctxp = (isc_mem_t *)ctx; 863 } 864 865 /* 866 * Public. 867 */ 868 869 static void 870 destroy(isc__mem_t *ctx) { 871 unsigned int i; 872 873 LOCK(&contextslock); 874 ISC_LIST_UNLINK(contexts, ctx, link); 875 totallost += ctx->inuse; 876 UNLOCK(&contextslock); 877 878 ctx->common.impmagic = 0; 879 ctx->common.magic = 0; 880 881 INSIST(ISC_LIST_EMPTY(ctx->pools)); 882 883 #if ISC_MEM_TRACKLINES 884 if (ISC_UNLIKELY(ctx->debuglist != NULL)) { 885 debuglink_t *dl; 886 for (i = 0; i < DEBUG_TABLE_COUNT; i++) { 887 for (dl = ISC_LIST_HEAD(ctx->debuglist[i]); dl != NULL; 888 dl = ISC_LIST_HEAD(ctx->debuglist[i])) 889 { 890 if (ctx->checkfree && dl->ptr != NULL) { 891 print_active(ctx, stderr); 892 } 893 INSIST(!ctx->checkfree || dl->ptr == NULL); 894 895 ISC_LIST_UNLINK(ctx->debuglist[i], dl, link); 896 free(dl); 897 ctx->malloced -= sizeof(*dl); 898 } 899 } 900 901 (ctx->memfree)(ctx->debuglist); 902 ctx->malloced -= DEBUG_TABLE_COUNT * sizeof(debuglist_t); 903 } 904 #endif /* if ISC_MEM_TRACKLINES */ 905 906 if (ctx->checkfree) { 907 for (i = 0; i <= ctx->max_size; i++) { 908 if (ctx->stats[i].gets != 0U) { 909 fprintf(stderr, 910 "Failing assertion due to probable " 911 "leaked memory in context %p (\"%s\") " 912 "(stats[%u].gets == %lu).\n", 913 ctx, ctx->name, i, ctx->stats[i].gets); 914 #if ISC_MEM_TRACKLINES 915 print_active(ctx, stderr); 916 #endif /* if ISC_MEM_TRACKLINES */ 917 INSIST(ctx->stats[i].gets == 0U); 918 } 919 } 920 } 921 922 (ctx->memfree)(ctx->stats); 923 ctx->malloced -= (ctx->max_size + 1) * sizeof(struct stats); 924 925 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { 926 for (i = 0; i < ctx->basic_table_count; i++) { 927 (ctx->memfree)(ctx->basic_table[i]); 928 ctx->malloced -= NUM_BASIC_BLOCKS * ctx->mem_target; 929 } 930 (ctx->memfree)(ctx->freelists); 931 ctx->malloced -= ctx->max_size * sizeof(element *); 932 if (ctx->basic_table != NULL) { 933 (ctx->memfree)(ctx->basic_table); 934 ctx->malloced -= ctx->basic_table_size * 935 sizeof(unsigned char *); 936 } 937 } 938 939 isc_mutex_destroy(&ctx->lock); 940 941 ctx->malloced -= sizeof(*ctx); 942 if (ctx->checkfree) { 943 INSIST(ctx->malloced == 0); 944 } 945 (ctx->memfree)(ctx); 946 } 947 948 void 949 isc_mem_attach(isc_mem_t *source0, isc_mem_t **targetp) { 950 REQUIRE(VALID_CONTEXT(source0)); 951 REQUIRE(targetp != NULL && *targetp == NULL); 952 953 isc__mem_t *source = (isc__mem_t *)source0; 954 955 isc_refcount_increment(&source->references); 956 957 *targetp = (isc_mem_t *)source; 958 } 959 960 void 961 isc_mem_detach(isc_mem_t **ctxp) { 962 REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp)); 963 964 isc__mem_t *ctx = (isc__mem_t *)*ctxp; 965 *ctxp = NULL; 966 967 if (isc_refcount_decrement(&ctx->references) == 1) { 968 isc_refcount_destroy(&ctx->references); 969 destroy(ctx); 970 } 971 } 972 973 /* 974 * isc_mem_putanddetach() is the equivalent of: 975 * 976 * mctx = NULL; 977 * isc_mem_attach(ptr->mctx, &mctx); 978 * isc_mem_detach(&ptr->mctx); 979 * isc_mem_put(mctx, ptr, sizeof(*ptr); 980 * isc_mem_detach(&mctx); 981 */ 982 983 void 984 isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) { 985 REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp)); 986 REQUIRE(ptr != NULL); 987 988 isc__mem_t *ctx = (isc__mem_t *)*ctxp; 989 *ctxp = NULL; 990 991 if (ISC_UNLIKELY((isc_mem_debugging & 992 (ISC_MEM_DEBUGSIZE | ISC_MEM_DEBUGCTX)) != 0)) 993 { 994 if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) { 995 size_info *si = &(((size_info *)ptr)[-1]); 996 size_t oldsize = si->u.size - ALIGNMENT_SIZE; 997 if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) { 998 oldsize -= ALIGNMENT_SIZE; 999 } 1000 INSIST(oldsize == size); 1001 } 1002 isc__mem_free((isc_mem_t *)ctx, ptr FLARG_PASS); 1003 1004 goto destroy; 1005 } 1006 1007 MCTXLOCK(ctx); 1008 1009 DELETE_TRACE(ctx, ptr, size, file, line); 1010 1011 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { 1012 mem_putunlocked(ctx, ptr, size); 1013 } else { 1014 mem_putstats(ctx, ptr, size); 1015 mem_put(ctx, ptr, size); 1016 } 1017 MCTXUNLOCK(ctx); 1018 1019 destroy: 1020 if (isc_refcount_decrement(&ctx->references) == 1) { 1021 isc_refcount_destroy(&ctx->references); 1022 destroy(ctx); 1023 } 1024 } 1025 1026 void 1027 isc_mem_destroy(isc_mem_t **ctxp) { 1028 /* 1029 * This routine provides legacy support for callers who use mctxs 1030 * without attaching/detaching. 1031 */ 1032 1033 REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp)); 1034 1035 isc__mem_t *ctx = (isc__mem_t *)*ctxp; 1036 1037 #if ISC_MEM_TRACKLINES 1038 if (isc_refcount_decrement(&ctx->references) > 1) { 1039 print_active(ctx, stderr); 1040 } 1041 #else /* if ISC_MEM_TRACKLINES */ 1042 isc_refcount_decrementz(&ctx->references); 1043 #endif /* if ISC_MEM_TRACKLINES */ 1044 isc_refcount_destroy(&ctx->references); 1045 destroy(ctx); 1046 1047 *ctxp = NULL; 1048 } 1049 1050 void * 1051 isc___mem_get(isc_mem_t *ctx0, size_t size FLARG) { 1052 REQUIRE(VALID_CONTEXT(ctx0)); 1053 1054 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1055 void *ptr; 1056 bool call_water = false; 1057 1058 if (ISC_UNLIKELY((isc_mem_debugging & 1059 (ISC_MEM_DEBUGSIZE | ISC_MEM_DEBUGCTX)) != 0)) 1060 { 1061 return (isc__mem_allocate(ctx0, size FLARG_PASS)); 1062 } 1063 1064 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { 1065 MCTXLOCK(ctx); 1066 ptr = mem_getunlocked(ctx, size); 1067 } else { 1068 ptr = mem_get(ctx, size); 1069 MCTXLOCK(ctx); 1070 if (ptr != NULL) { 1071 mem_getstats(ctx, size); 1072 } 1073 } 1074 1075 ADD_TRACE(ctx, ptr, size, file, line); 1076 1077 if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water) { 1078 ctx->is_overmem = true; 1079 if (!ctx->hi_called) { 1080 call_water = true; 1081 } 1082 } 1083 if (ctx->inuse > ctx->maxinuse) { 1084 ctx->maxinuse = ctx->inuse; 1085 if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water && 1086 (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) 1087 { 1088 fprintf(stderr, "maxinuse = %lu\n", 1089 (unsigned long)ctx->inuse); 1090 } 1091 } 1092 MCTXUNLOCK(ctx); 1093 1094 if (call_water && (ctx->water != NULL)) { 1095 (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER); 1096 } 1097 1098 return (ptr); 1099 } 1100 1101 void 1102 isc___mem_put(isc_mem_t *ctx0, void *ptr, size_t size FLARG) { 1103 REQUIRE(VALID_CONTEXT(ctx0)); 1104 REQUIRE(ptr != NULL); 1105 1106 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1107 bool call_water = false; 1108 size_info *si; 1109 size_t oldsize; 1110 1111 if (ISC_UNLIKELY((isc_mem_debugging & 1112 (ISC_MEM_DEBUGSIZE | ISC_MEM_DEBUGCTX)) != 0)) 1113 { 1114 if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) { 1115 si = &(((size_info *)ptr)[-1]); 1116 oldsize = si->u.size - ALIGNMENT_SIZE; 1117 if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) { 1118 oldsize -= ALIGNMENT_SIZE; 1119 } 1120 INSIST(oldsize == size); 1121 } 1122 isc__mem_free((isc_mem_t *)ctx, ptr FLARG_PASS); 1123 return; 1124 } 1125 1126 MCTXLOCK(ctx); 1127 1128 DELETE_TRACE(ctx, ptr, size, file, line); 1129 1130 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { 1131 mem_putunlocked(ctx, ptr, size); 1132 } else { 1133 mem_putstats(ctx, ptr, size); 1134 mem_put(ctx, ptr, size); 1135 } 1136 1137 /* 1138 * The check against ctx->lo_water == 0 is for the condition 1139 * when the context was pushed over hi_water but then had 1140 * isc_mem_setwater() called with 0 for hi_water and lo_water. 1141 */ 1142 if ((ctx->inuse < ctx->lo_water) || (ctx->lo_water == 0U)) { 1143 ctx->is_overmem = false; 1144 if (ctx->hi_called) { 1145 call_water = true; 1146 } 1147 } 1148 1149 MCTXUNLOCK(ctx); 1150 1151 if (call_water && (ctx->water != NULL)) { 1152 (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER); 1153 } 1154 } 1155 1156 void 1157 isc_mem_waterack(isc_mem_t *ctx0, int flag) { 1158 REQUIRE(VALID_CONTEXT(ctx0)); 1159 1160 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1161 1162 MCTXLOCK(ctx); 1163 if (flag == ISC_MEM_LOWATER) { 1164 ctx->hi_called = false; 1165 } else if (flag == ISC_MEM_HIWATER) { 1166 ctx->hi_called = true; 1167 } 1168 MCTXUNLOCK(ctx); 1169 } 1170 1171 #if ISC_MEM_TRACKLINES 1172 static void 1173 print_active(isc__mem_t *mctx, FILE *out) { 1174 if (mctx->debuglist != NULL) { 1175 debuglink_t *dl; 1176 unsigned int i; 1177 bool found; 1178 1179 fputs("Dump of all outstanding memory allocations:\n", out); 1180 found = false; 1181 for (i = 0; i < DEBUG_TABLE_COUNT; i++) { 1182 dl = ISC_LIST_HEAD(mctx->debuglist[i]); 1183 1184 if (dl != NULL) { 1185 found = true; 1186 } 1187 1188 while (dl != NULL) { 1189 if (dl->ptr != NULL) { 1190 fprintf(out, 1191 "\tptr %p size %zu file %s " 1192 "line %u\n", 1193 dl->ptr, dl->size, dl->file, 1194 dl->line); 1195 } 1196 dl = ISC_LIST_NEXT(dl, link); 1197 } 1198 } 1199 1200 if (!found) { 1201 fputs("\tNone.\n", out); 1202 } 1203 } 1204 } 1205 #endif /* if ISC_MEM_TRACKLINES */ 1206 1207 /* 1208 * Print the stats[] on the stream "out" with suitable formatting. 1209 */ 1210 void 1211 isc_mem_stats(isc_mem_t *ctx0, FILE *out) { 1212 REQUIRE(VALID_CONTEXT(ctx0)); 1213 1214 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1215 size_t i; 1216 const struct stats *s; 1217 const isc__mempool_t *pool; 1218 1219 MCTXLOCK(ctx); 1220 1221 for (i = 0; i <= ctx->max_size; i++) { 1222 s = &ctx->stats[i]; 1223 1224 if (s->totalgets == 0U && s->gets == 0U) { 1225 continue; 1226 } 1227 fprintf(out, "%s%5lu: %11lu gets, %11lu rem", 1228 (i == ctx->max_size) ? ">=" : " ", (unsigned long)i, 1229 s->totalgets, s->gets); 1230 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0 && 1231 (s->blocks != 0U || s->freefrags != 0U)) 1232 { 1233 fprintf(out, " (%lu bl, %lu ff)", s->blocks, 1234 s->freefrags); 1235 } 1236 fputc('\n', out); 1237 } 1238 1239 /* 1240 * Note that since a pool can be locked now, these stats might be 1241 * somewhat off if the pool is in active use at the time the stats 1242 * are dumped. The link fields are protected by the isc_mem_t's 1243 * lock, however, so walking this list and extracting integers from 1244 * stats fields is always safe. 1245 */ 1246 pool = ISC_LIST_HEAD(ctx->pools); 1247 if (pool != NULL) { 1248 fputs("[Pool statistics]\n", out); 1249 fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %10s %1s\n", 1250 "name", "size", "maxalloc", "allocated", "freecount", 1251 "freemax", "fillcount", "gets", "L"); 1252 } 1253 while (pool != NULL) { 1254 fprintf(out, "%15s %10lu %10u %10u %10u %10u %10u %10u %s\n", 1255 #if ISC_MEMPOOL_NAMES 1256 pool->name, 1257 #else /* if ISC_MEMPOOL_NAMES */ 1258 "(not tracked)", 1259 #endif /* if ISC_MEMPOOL_NAMES */ 1260 (unsigned long)pool->size, pool->maxalloc, 1261 pool->allocated, pool->freecount, pool->freemax, 1262 pool->fillcount, pool->gets, "N"); 1263 pool = ISC_LIST_NEXT(pool, link); 1264 } 1265 1266 #if ISC_MEM_TRACKLINES 1267 print_active(ctx, out); 1268 #endif /* if ISC_MEM_TRACKLINES */ 1269 1270 MCTXUNLOCK(ctx); 1271 } 1272 1273 /* 1274 * Replacements for malloc() and free() -- they implicitly remember the 1275 * size of the object allocated (with some additional overhead). 1276 */ 1277 1278 static void * 1279 mem_allocateunlocked(isc_mem_t *ctx0, size_t size) { 1280 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1281 size_info *si; 1282 1283 size += ALIGNMENT_SIZE; 1284 if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)) { 1285 size += ALIGNMENT_SIZE; 1286 } 1287 1288 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { 1289 si = mem_getunlocked(ctx, size); 1290 } else { 1291 si = mem_get(ctx, size); 1292 } 1293 1294 if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)) { 1295 si->u.ctx = ctx; 1296 si++; 1297 } 1298 si->u.size = size; 1299 return (&si[1]); 1300 } 1301 1302 void * 1303 isc___mem_allocate(isc_mem_t *ctx0, size_t size FLARG) { 1304 REQUIRE(VALID_CONTEXT(ctx0)); 1305 1306 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1307 size_info *si; 1308 bool call_water = false; 1309 1310 MCTXLOCK(ctx); 1311 si = mem_allocateunlocked((isc_mem_t *)ctx, size); 1312 if (((ctx->flags & ISC_MEMFLAG_INTERNAL) == 0)) { 1313 mem_getstats(ctx, si[-1].u.size); 1314 } 1315 1316 ADD_TRACE(ctx, si, si[-1].u.size, file, line); 1317 if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water && 1318 !ctx->is_overmem) 1319 { 1320 ctx->is_overmem = true; 1321 } 1322 1323 if (ctx->hi_water != 0U && !ctx->hi_called && 1324 ctx->inuse > ctx->hi_water) 1325 { 1326 ctx->hi_called = true; 1327 call_water = true; 1328 } 1329 if (ctx->inuse > ctx->maxinuse) { 1330 ctx->maxinuse = ctx->inuse; 1331 if (ISC_UNLIKELY(ctx->hi_water != 0U && 1332 ctx->inuse > ctx->hi_water && 1333 (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)) 1334 { 1335 fprintf(stderr, "maxinuse = %lu\n", 1336 (unsigned long)ctx->inuse); 1337 } 1338 } 1339 MCTXUNLOCK(ctx); 1340 1341 if (call_water) { 1342 (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER); 1343 } 1344 1345 return (si); 1346 } 1347 1348 void * 1349 isc___mem_reallocate(isc_mem_t *ctx0, void *ptr, size_t size FLARG) { 1350 REQUIRE(VALID_CONTEXT(ctx0)); 1351 1352 void *new_ptr = NULL; 1353 size_t oldsize, copysize; 1354 1355 /* 1356 * This function emulates the realloc(3) standard library function: 1357 * - if size > 0, allocate new memory; and if ptr is non NULL, copy 1358 * as much of the old contents to the new buffer and free the old one. 1359 * Note that when allocation fails the original pointer is intact; 1360 * the caller must free it. 1361 * - if size is 0 and ptr is non NULL, simply free the given ptr. 1362 * - this function returns: 1363 * pointer to the newly allocated memory, or 1364 * NULL if allocation fails or doesn't happen. 1365 */ 1366 if (size > 0U) { 1367 new_ptr = isc__mem_allocate(ctx0, size FLARG_PASS); 1368 if (new_ptr != NULL && ptr != NULL) { 1369 oldsize = (((size_info *)ptr)[-1]).u.size; 1370 INSIST(oldsize >= ALIGNMENT_SIZE); 1371 oldsize -= ALIGNMENT_SIZE; 1372 if (ISC_UNLIKELY((isc_mem_debugging & 1373 ISC_MEM_DEBUGCTX) != 0)) 1374 { 1375 INSIST(oldsize >= ALIGNMENT_SIZE); 1376 oldsize -= ALIGNMENT_SIZE; 1377 } 1378 copysize = (oldsize > size) ? size : oldsize; 1379 memmove(new_ptr, ptr, copysize); 1380 isc__mem_free(ctx0, ptr FLARG_PASS); 1381 } 1382 } else if (ptr != NULL) { 1383 isc__mem_free(ctx0, ptr FLARG_PASS); 1384 } 1385 1386 return (new_ptr); 1387 } 1388 1389 void 1390 isc___mem_free(isc_mem_t *ctx0, void *ptr FLARG) { 1391 REQUIRE(VALID_CONTEXT(ctx0)); 1392 REQUIRE(ptr != NULL); 1393 1394 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1395 size_info *si; 1396 size_t size; 1397 bool call_water = false; 1398 1399 if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)) { 1400 si = &(((size_info *)ptr)[-2]); 1401 REQUIRE(si->u.ctx == ctx); 1402 size = si[1].u.size; 1403 } else { 1404 si = &(((size_info *)ptr)[-1]); 1405 size = si->u.size; 1406 } 1407 1408 MCTXLOCK(ctx); 1409 1410 DELETE_TRACE(ctx, ptr, size, file, line); 1411 1412 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { 1413 mem_putunlocked(ctx, si, size); 1414 } else { 1415 mem_putstats(ctx, si, size); 1416 mem_put(ctx, si, size); 1417 } 1418 1419 /* 1420 * The check against ctx->lo_water == 0 is for the condition 1421 * when the context was pushed over hi_water but then had 1422 * isc_mem_setwater() called with 0 for hi_water and lo_water. 1423 */ 1424 if (ctx->is_overmem && 1425 (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) 1426 { 1427 ctx->is_overmem = false; 1428 } 1429 1430 if (ctx->hi_called && 1431 (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) 1432 { 1433 ctx->hi_called = false; 1434 1435 if (ctx->water != NULL) { 1436 call_water = true; 1437 } 1438 } 1439 MCTXUNLOCK(ctx); 1440 1441 if (call_water) { 1442 (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER); 1443 } 1444 } 1445 1446 /* 1447 * Other useful things. 1448 */ 1449 1450 char * 1451 isc___mem_strdup(isc_mem_t *mctx0, const char *s FLARG) { 1452 REQUIRE(VALID_CONTEXT(mctx0)); 1453 REQUIRE(s != NULL); 1454 1455 isc__mem_t *mctx = (isc__mem_t *)mctx0; 1456 size_t len; 1457 char *ns; 1458 1459 len = strlen(s) + 1; 1460 1461 ns = isc__mem_allocate((isc_mem_t *)mctx, len FLARG_PASS); 1462 1463 if (ns != NULL) { 1464 strlcpy(ns, s, len); 1465 } 1466 1467 return (ns); 1468 } 1469 1470 char * 1471 isc___mem_strndup(isc_mem_t *mctx0, const char *s, size_t size FLARG) { 1472 REQUIRE(VALID_CONTEXT(mctx0)); 1473 REQUIRE(s != NULL); 1474 1475 isc__mem_t *mctx = (isc__mem_t *)mctx0; 1476 size_t len; 1477 char *ns; 1478 1479 len = strlen(s) + 1; 1480 if (len > size) { 1481 len = size; 1482 } 1483 1484 ns = isc__mem_allocate((isc_mem_t *)mctx, len FLARG_PASS); 1485 1486 if (ns != NULL) { 1487 strlcpy(ns, s, len); 1488 } 1489 1490 return (ns); 1491 } 1492 1493 void 1494 isc_mem_setdestroycheck(isc_mem_t *ctx0, bool flag) { 1495 REQUIRE(VALID_CONTEXT(ctx0)); 1496 1497 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1498 1499 MCTXLOCK(ctx); 1500 1501 ctx->checkfree = flag; 1502 1503 MCTXUNLOCK(ctx); 1504 } 1505 1506 size_t 1507 isc_mem_inuse(isc_mem_t *ctx0) { 1508 REQUIRE(VALID_CONTEXT(ctx0)); 1509 1510 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1511 size_t inuse; 1512 1513 MCTXLOCK(ctx); 1514 1515 inuse = ctx->inuse; 1516 1517 MCTXUNLOCK(ctx); 1518 1519 return (inuse); 1520 } 1521 1522 size_t 1523 isc_mem_maxinuse(isc_mem_t *ctx0) { 1524 REQUIRE(VALID_CONTEXT(ctx0)); 1525 1526 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1527 size_t maxinuse; 1528 1529 MCTXLOCK(ctx); 1530 1531 maxinuse = ctx->maxinuse; 1532 1533 MCTXUNLOCK(ctx); 1534 1535 return (maxinuse); 1536 } 1537 1538 size_t 1539 isc_mem_total(isc_mem_t *ctx0) { 1540 REQUIRE(VALID_CONTEXT(ctx0)); 1541 1542 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1543 size_t total; 1544 1545 MCTXLOCK(ctx); 1546 1547 total = ctx->total; 1548 1549 MCTXUNLOCK(ctx); 1550 1551 return (total); 1552 } 1553 1554 void 1555 isc_mem_setwater(isc_mem_t *ctx0, isc_mem_water_t water, void *water_arg, 1556 size_t hiwater, size_t lowater) { 1557 REQUIRE(VALID_CONTEXT(ctx0)); 1558 REQUIRE(hiwater >= lowater); 1559 1560 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1561 bool callwater = false; 1562 isc_mem_water_t oldwater; 1563 void *oldwater_arg; 1564 1565 MCTXLOCK(ctx); 1566 oldwater = ctx->water; 1567 oldwater_arg = ctx->water_arg; 1568 if (water == NULL) { 1569 callwater = ctx->hi_called; 1570 ctx->water = NULL; 1571 ctx->water_arg = NULL; 1572 ctx->hi_water = 0; 1573 ctx->lo_water = 0; 1574 } else { 1575 if (ctx->hi_called && 1576 (ctx->water != water || ctx->water_arg != water_arg || 1577 ctx->inuse < lowater || lowater == 0U)) 1578 { 1579 callwater = true; 1580 } 1581 ctx->water = water; 1582 ctx->water_arg = water_arg; 1583 ctx->hi_water = hiwater; 1584 ctx->lo_water = lowater; 1585 } 1586 MCTXUNLOCK(ctx); 1587 1588 if (callwater && oldwater != NULL) { 1589 (oldwater)(oldwater_arg, ISC_MEM_LOWATER); 1590 } 1591 } 1592 1593 ISC_NO_SANITIZE_THREAD bool 1594 isc_mem_isovermem(isc_mem_t *ctx0) { 1595 REQUIRE(VALID_CONTEXT(ctx0)); 1596 1597 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1598 1599 /* 1600 * We don't bother to lock the context because 100% accuracy isn't 1601 * necessary (and even if we locked the context the returned value 1602 * could be different from the actual state when it's used anyway) 1603 */ 1604 return (ctx->is_overmem); 1605 } 1606 1607 void 1608 isc_mem_setname(isc_mem_t *ctx0, const char *name, void *tag) { 1609 REQUIRE(VALID_CONTEXT(ctx0)); 1610 1611 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1612 1613 LOCK(&ctx->lock); 1614 strlcpy(ctx->name, name, sizeof(ctx->name)); 1615 ctx->tag = tag; 1616 UNLOCK(&ctx->lock); 1617 } 1618 1619 const char * 1620 isc_mem_getname(isc_mem_t *ctx0) { 1621 REQUIRE(VALID_CONTEXT(ctx0)); 1622 1623 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1624 1625 if (ctx->name[0] == 0) { 1626 return (""); 1627 } 1628 1629 return (ctx->name); 1630 } 1631 1632 void * 1633 isc_mem_gettag(isc_mem_t *ctx0) { 1634 REQUIRE(VALID_CONTEXT(ctx0)); 1635 1636 isc__mem_t *ctx = (isc__mem_t *)ctx0; 1637 1638 return (ctx->tag); 1639 } 1640 1641 /* 1642 * Memory pool stuff 1643 */ 1644 1645 void 1646 isc_mempool_create(isc_mem_t *mctx0, size_t size, isc_mempool_t **mpctxp) { 1647 REQUIRE(VALID_CONTEXT(mctx0)); 1648 REQUIRE(size > 0U); 1649 REQUIRE(mpctxp != NULL && *mpctxp == NULL); 1650 1651 isc__mem_t *mctx = (isc__mem_t *)mctx0; 1652 isc__mempool_t *mpctx; 1653 1654 /* 1655 * Allocate space for this pool, initialize values, and if all works 1656 * well, attach to the memory context. 1657 */ 1658 mpctx = isc_mem_get((isc_mem_t *)mctx, sizeof(isc__mempool_t)); 1659 1660 mpctx->common.impmagic = MEMPOOL_MAGIC; 1661 mpctx->common.magic = ISCAPI_MPOOL_MAGIC; 1662 mpctx->mctx = NULL; 1663 isc_mem_attach((isc_mem_t *)mctx, (isc_mem_t **)&mpctx->mctx); 1664 /* 1665 * Mempools are stored as a linked list of element. 1666 */ 1667 if (size < sizeof(element)) { 1668 size = sizeof(element); 1669 } 1670 mpctx->size = size; 1671 mpctx->maxalloc = UINT_MAX; 1672 mpctx->allocated = 0; 1673 mpctx->freecount = 0; 1674 mpctx->freemax = 1; 1675 mpctx->fillcount = 1; 1676 mpctx->gets = 0; 1677 #if ISC_MEMPOOL_NAMES 1678 mpctx->name[0] = 0; 1679 #endif /* if ISC_MEMPOOL_NAMES */ 1680 mpctx->items = NULL; 1681 1682 *mpctxp = (isc_mempool_t *)mpctx; 1683 1684 MCTXLOCK(mctx); 1685 ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link); 1686 mctx->poolcnt++; 1687 MCTXUNLOCK(mctx); 1688 } 1689 1690 void 1691 isc_mempool_setname(isc_mempool_t *mpctx0, const char *name) { 1692 REQUIRE(VALID_MEMPOOL(mpctx0)); 1693 REQUIRE(name != NULL); 1694 1695 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1696 1697 #if ISC_MEMPOOL_NAMES 1698 strlcpy(mpctx->name, name, sizeof(mpctx->name)); 1699 #else /* if ISC_MEMPOOL_NAMES */ 1700 UNUSED(mpctx); 1701 UNUSED(name); 1702 #endif /* if ISC_MEMPOOL_NAMES */ 1703 } 1704 1705 void 1706 isc_mempool_destroy(isc_mempool_t **mpctxp) { 1707 REQUIRE(mpctxp != NULL); 1708 REQUIRE(VALID_MEMPOOL(*mpctxp)); 1709 1710 isc__mempool_t *mpctx; 1711 isc__mem_t *mctx; 1712 element *item; 1713 1714 mpctx = (isc__mempool_t *)*mpctxp; 1715 #if ISC_MEMPOOL_NAMES 1716 if (mpctx->allocated > 0) { 1717 UNEXPECTED_ERROR(__FILE__, __LINE__, 1718 "isc_mempool_destroy(): mempool %s " 1719 "leaked memory", 1720 mpctx->name); 1721 } 1722 #endif /* if ISC_MEMPOOL_NAMES */ 1723 REQUIRE(mpctx->allocated == 0); 1724 1725 mctx = mpctx->mctx; 1726 1727 /* 1728 * Return any items on the free list 1729 */ 1730 MCTXLOCK(mctx); 1731 while (mpctx->items != NULL) { 1732 INSIST(mpctx->freecount > 0); 1733 mpctx->freecount--; 1734 item = mpctx->items; 1735 mpctx->items = item->next; 1736 mem_putstats(mctx, item, mpctx->size); 1737 mem_put(mctx, item, mpctx->size); 1738 } 1739 MCTXUNLOCK(mctx); 1740 1741 /* 1742 * Remove our linked list entry from the memory context. 1743 */ 1744 MCTXLOCK(mctx); 1745 ISC_LIST_UNLINK(mctx->pools, mpctx, link); 1746 mctx->poolcnt--; 1747 MCTXUNLOCK(mctx); 1748 1749 mpctx->common.impmagic = 0; 1750 mpctx->common.magic = 0; 1751 1752 isc_mem_putanddetach((isc_mem_t **)&mpctx->mctx, mpctx, 1753 sizeof(isc__mempool_t)); 1754 1755 *mpctxp = NULL; 1756 } 1757 1758 #if __SANITIZE_ADDRESS__ 1759 void * 1760 isc__mempool_get(isc_mempool_t *mpctx0 FLARG) { 1761 void *item = NULL; 1762 1763 REQUIRE(VALID_MEMPOOL(mpctx0)); 1764 1765 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1766 isc_mem_t *mctx = (isc_mem_t *)mpctx->mctx; 1767 1768 /* 1769 * Don't let the caller go over quota 1770 */ 1771 if (ISC_UNLIKELY(mpctx->allocated >= mpctx->maxalloc)) { 1772 goto out; 1773 } 1774 1775 item = isc__mem_get(mctx, mpctx->size FLARG_PASS); 1776 mpctx->gets++; 1777 mpctx->allocated++; 1778 1779 out: 1780 return (item); 1781 } 1782 1783 void 1784 isc__mempool_put(isc_mempool_t *mpctx0, void *mem FLARG) { 1785 REQUIRE(VALID_MEMPOOL(mpctx0)); 1786 1787 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1788 isc_mem_t *mctx = (isc_mem_t *)mpctx->mctx; 1789 1790 REQUIRE(mem != NULL); 1791 1792 INSIST(mpctx->allocated > 0); 1793 mpctx->allocated--; 1794 1795 isc__mem_put(mctx, mem, mpctx->size FLARG_PASS); 1796 } 1797 1798 #else /* __SANITIZE_ADDRESS__ */ 1799 void * 1800 isc__mempool_get(isc_mempool_t *mpctx0 FLARG) { 1801 REQUIRE(VALID_MEMPOOL(mpctx0)); 1802 1803 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1804 element *item; 1805 isc__mem_t *mctx; 1806 unsigned int i; 1807 1808 mctx = mpctx->mctx; 1809 1810 /* 1811 * Don't let the caller go over quota 1812 */ 1813 if (ISC_UNLIKELY(mpctx->allocated >= mpctx->maxalloc)) { 1814 item = NULL; 1815 goto out; 1816 } 1817 1818 if (ISC_UNLIKELY(mpctx->items == NULL)) { 1819 /* 1820 * We need to dip into the well. Lock the memory context 1821 * here and fill up our free list. 1822 */ 1823 MCTXLOCK(mctx); 1824 for (i = 0; i < mpctx->fillcount; i++) { 1825 item = mem_get(mctx, mpctx->size); 1826 mem_getstats(mctx, mpctx->size); 1827 item->next = mpctx->items; 1828 mpctx->items = item; 1829 mpctx->freecount++; 1830 } 1831 MCTXUNLOCK(mctx); 1832 } 1833 1834 /* 1835 * If we didn't get any items, return NULL. 1836 */ 1837 item = mpctx->items; 1838 if (ISC_UNLIKELY(item == NULL)) { 1839 goto out; 1840 } 1841 1842 mpctx->items = item->next; 1843 INSIST(mpctx->freecount > 0); 1844 mpctx->freecount--; 1845 mpctx->gets++; 1846 mpctx->allocated++; 1847 1848 out: 1849 #if ISC_MEM_TRACKLINES 1850 if (ISC_UNLIKELY(((isc_mem_debugging & TRACE_OR_RECORD) != 0) && 1851 item != NULL)) 1852 { 1853 MCTXLOCK(mctx); 1854 ADD_TRACE(mctx, item, mpctx->size, file, line); 1855 MCTXUNLOCK(mctx); 1856 } 1857 #endif /* ISC_MEM_TRACKLINES */ 1858 1859 return (item); 1860 } 1861 1862 /* coverity[+free : arg-1] */ 1863 void 1864 isc__mempool_put(isc_mempool_t *mpctx0, void *mem FLARG) { 1865 REQUIRE(VALID_MEMPOOL(mpctx0)); 1866 REQUIRE(mem != NULL); 1867 1868 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1869 isc__mem_t *mctx = mpctx->mctx; 1870 element *item; 1871 1872 INSIST(mpctx->allocated > 0); 1873 mpctx->allocated--; 1874 1875 #if ISC_MEM_TRACKLINES 1876 if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0)) { 1877 MCTXLOCK(mctx); 1878 DELETE_TRACE(mctx, mem, mpctx->size, file, line); 1879 MCTXUNLOCK(mctx); 1880 } 1881 #endif /* ISC_MEM_TRACKLINES */ 1882 1883 /* 1884 * If our free list is full, return this to the mctx directly. 1885 */ 1886 if (mpctx->freecount >= mpctx->freemax) { 1887 MCTXLOCK(mctx); 1888 mem_putstats(mctx, mem, mpctx->size); 1889 mem_put(mctx, mem, mpctx->size); 1890 MCTXUNLOCK(mctx); 1891 return; 1892 } 1893 1894 /* 1895 * Otherwise, attach it to our free list and bump the counter. 1896 */ 1897 mpctx->freecount++; 1898 item = (element *)mem; 1899 item->next = mpctx->items; 1900 mpctx->items = item; 1901 } 1902 1903 #endif /* __SANITIZE_ADDRESS__ */ 1904 1905 /* 1906 * Quotas 1907 */ 1908 1909 void 1910 isc_mempool_setfreemax(isc_mempool_t *mpctx0, unsigned int limit) { 1911 REQUIRE(VALID_MEMPOOL(mpctx0)); 1912 1913 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1914 1915 mpctx->freemax = limit; 1916 } 1917 1918 unsigned int 1919 isc_mempool_getfreemax(isc_mempool_t *mpctx0) { 1920 REQUIRE(VALID_MEMPOOL(mpctx0)); 1921 1922 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1923 1924 return (mpctx->freemax); 1925 } 1926 1927 unsigned int 1928 isc_mempool_getfreecount(isc_mempool_t *mpctx0) { 1929 REQUIRE(VALID_MEMPOOL(mpctx0)); 1930 1931 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1932 1933 return (mpctx->freecount); 1934 } 1935 1936 void 1937 isc_mempool_setmaxalloc(isc_mempool_t *mpctx0, unsigned int limit) { 1938 REQUIRE(VALID_MEMPOOL(mpctx0)); 1939 REQUIRE(limit > 0); 1940 1941 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1942 1943 mpctx->maxalloc = limit; 1944 } 1945 1946 unsigned int 1947 isc_mempool_getmaxalloc(isc_mempool_t *mpctx0) { 1948 REQUIRE(VALID_MEMPOOL(mpctx0)); 1949 1950 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1951 1952 return (mpctx->maxalloc); 1953 } 1954 1955 unsigned int 1956 isc_mempool_getallocated(isc_mempool_t *mpctx0) { 1957 REQUIRE(VALID_MEMPOOL(mpctx0)); 1958 1959 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1960 1961 return (mpctx->allocated); 1962 } 1963 1964 void 1965 isc_mempool_setfillcount(isc_mempool_t *mpctx0, unsigned int limit) { 1966 REQUIRE(VALID_MEMPOOL(mpctx0)); 1967 REQUIRE(limit > 0); 1968 1969 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1970 1971 mpctx->fillcount = limit; 1972 } 1973 1974 unsigned int 1975 isc_mempool_getfillcount(isc_mempool_t *mpctx0) { 1976 REQUIRE(VALID_MEMPOOL(mpctx0)); 1977 1978 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; 1979 1980 return (mpctx->fillcount); 1981 } 1982 1983 /* 1984 * Requires contextslock to be held by caller. 1985 */ 1986 static void 1987 print_contexts(FILE *file) { 1988 isc__mem_t *ctx; 1989 1990 for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL; 1991 ctx = ISC_LIST_NEXT(ctx, link)) 1992 { 1993 fprintf(file, "context: %p (%s): %" PRIuFAST32 " references\n", 1994 ctx, ctx->name[0] == 0 ? "<unknown>" : ctx->name, 1995 isc_refcount_current(&ctx->references)); 1996 print_active(ctx, file); 1997 } 1998 fflush(file); 1999 } 2000 2001 static atomic_uintptr_t checkdestroyed = 0; 2002 2003 void 2004 isc_mem_checkdestroyed(FILE *file) { 2005 atomic_store_release(&checkdestroyed, (uintptr_t)file); 2006 } 2007 2008 void 2009 isc__mem_checkdestroyed(void) { 2010 FILE *file = (FILE *)atomic_load_acquire(&checkdestroyed); 2011 2012 if (file == NULL) { 2013 return; 2014 } 2015 2016 LOCK(&contextslock); 2017 if (!ISC_LIST_EMPTY(contexts)) { 2018 #if ISC_MEM_TRACKLINES 2019 if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0)) { 2020 print_contexts(file); 2021 } 2022 #endif /* if ISC_MEM_TRACKLINES */ 2023 UNREACHABLE(); 2024 } 2025 UNLOCK(&contextslock); 2026 } 2027 2028 unsigned int 2029 isc_mem_references(isc_mem_t *ctx0) { 2030 isc__mem_t *ctx = (isc__mem_t *)ctx0; 2031 return (isc_refcount_current(&ctx->references)); 2032 } 2033 2034 typedef struct summarystat { 2035 uint64_t total; 2036 uint64_t inuse; 2037 uint64_t malloced; 2038 uint64_t blocksize; 2039 uint64_t contextsize; 2040 } summarystat_t; 2041 2042 #ifdef HAVE_LIBXML2 2043 #define TRY0(a) \ 2044 do { \ 2045 xmlrc = (a); \ 2046 if (xmlrc < 0) \ 2047 goto error; \ 2048 } while (0) 2049 static int 2050 xml_renderctx(isc__mem_t *ctx, summarystat_t *summary, 2051 xmlTextWriterPtr writer) { 2052 REQUIRE(VALID_CONTEXT(ctx)); 2053 2054 int xmlrc; 2055 2056 MCTXLOCK(ctx); 2057 2058 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "context")); 2059 2060 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id")); 2061 TRY0(xmlTextWriterWriteFormatString(writer, "%p", ctx)); 2062 TRY0(xmlTextWriterEndElement(writer)); /* id */ 2063 2064 if (ctx->name[0] != 0) { 2065 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name")); 2066 TRY0(xmlTextWriterWriteFormatString(writer, "%s", ctx->name)); 2067 TRY0(xmlTextWriterEndElement(writer)); /* name */ 2068 } 2069 2070 summary->contextsize += sizeof(*ctx) + 2071 (ctx->max_size + 1) * sizeof(struct stats) + 2072 ctx->max_size * sizeof(element *) + 2073 ctx->basic_table_count * sizeof(char *); 2074 #if ISC_MEM_TRACKLINES 2075 if (ctx->debuglist != NULL) { 2076 summary->contextsize += DEBUG_TABLE_COUNT * 2077 sizeof(debuglist_t) + 2078 ctx->debuglistcnt * sizeof(debuglink_t); 2079 } 2080 #endif /* if ISC_MEM_TRACKLINES */ 2081 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references")); 2082 TRY0(xmlTextWriterWriteFormatString( 2083 writer, "%" PRIuFAST32, 2084 isc_refcount_current(&ctx->references))); 2085 TRY0(xmlTextWriterEndElement(writer)); /* references */ 2086 2087 summary->total += ctx->total; 2088 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "total")); 2089 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2090 (uint64_t)ctx->total)); 2091 TRY0(xmlTextWriterEndElement(writer)); /* total */ 2092 2093 summary->inuse += ctx->inuse; 2094 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse")); 2095 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2096 (uint64_t)ctx->inuse)); 2097 TRY0(xmlTextWriterEndElement(writer)); /* inuse */ 2098 2099 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse")); 2100 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2101 (uint64_t)ctx->maxinuse)); 2102 TRY0(xmlTextWriterEndElement(writer)); /* maxinuse */ 2103 2104 summary->malloced += ctx->malloced; 2105 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "malloced")); 2106 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2107 (uint64_t)ctx->malloced)); 2108 TRY0(xmlTextWriterEndElement(writer)); /* malloced */ 2109 2110 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxmalloced")); 2111 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2112 (uint64_t)ctx->maxmalloced)); 2113 TRY0(xmlTextWriterEndElement(writer)); /* maxmalloced */ 2114 2115 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "blocksize")); 2116 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { 2117 summary->blocksize += ctx->basic_table_count * 2118 NUM_BASIC_BLOCKS * ctx->mem_target; 2119 TRY0(xmlTextWriterWriteFormatString( 2120 writer, "%" PRIu64 "", 2121 (uint64_t)ctx->basic_table_count * NUM_BASIC_BLOCKS * 2122 ctx->mem_target)); 2123 } else { 2124 TRY0(xmlTextWriterWriteFormatString(writer, "%s", "-")); 2125 } 2126 TRY0(xmlTextWriterEndElement(writer)); /* blocksize */ 2127 2128 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools")); 2129 TRY0(xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt)); 2130 TRY0(xmlTextWriterEndElement(writer)); /* pools */ 2131 summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t); 2132 2133 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater")); 2134 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2135 (uint64_t)ctx->hi_water)); 2136 TRY0(xmlTextWriterEndElement(writer)); /* hiwater */ 2137 2138 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater")); 2139 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2140 (uint64_t)ctx->lo_water)); 2141 TRY0(xmlTextWriterEndElement(writer)); /* lowater */ 2142 2143 TRY0(xmlTextWriterEndElement(writer)); /* context */ 2144 2145 error: 2146 MCTXUNLOCK(ctx); 2147 2148 return (xmlrc); 2149 } 2150 2151 int 2152 isc_mem_renderxml(void *writer0) { 2153 isc__mem_t *ctx; 2154 summarystat_t summary; 2155 uint64_t lost; 2156 int xmlrc; 2157 xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0; 2158 2159 memset(&summary, 0, sizeof(summary)); 2160 2161 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts")); 2162 2163 LOCK(&contextslock); 2164 lost = totallost; 2165 for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL; 2166 ctx = ISC_LIST_NEXT(ctx, link)) 2167 { 2168 xmlrc = xml_renderctx(ctx, &summary, writer); 2169 if (xmlrc < 0) { 2170 UNLOCK(&contextslock); 2171 goto error; 2172 } 2173 } 2174 UNLOCK(&contextslock); 2175 2176 TRY0(xmlTextWriterEndElement(writer)); /* contexts */ 2177 2178 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary")); 2179 2180 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse")); 2181 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2182 summary.total)); 2183 TRY0(xmlTextWriterEndElement(writer)); /* TotalUse */ 2184 2185 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse")); 2186 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2187 summary.inuse)); 2188 TRY0(xmlTextWriterEndElement(writer)); /* InUse */ 2189 2190 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Malloced")); 2191 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2192 summary.malloced)); 2193 TRY0(xmlTextWriterEndElement(writer)); /* InUse */ 2194 2195 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "BlockSize")); 2196 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2197 summary.blocksize)); 2198 TRY0(xmlTextWriterEndElement(writer)); /* BlockSize */ 2199 2200 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize")); 2201 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", 2202 summary.contextsize)); 2203 TRY0(xmlTextWriterEndElement(writer)); /* ContextSize */ 2204 2205 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost")); 2206 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", lost)); 2207 TRY0(xmlTextWriterEndElement(writer)); /* Lost */ 2208 2209 TRY0(xmlTextWriterEndElement(writer)); /* summary */ 2210 error: 2211 return (xmlrc); 2212 } 2213 2214 #endif /* HAVE_LIBXML2 */ 2215 2216 #ifdef HAVE_JSON_C 2217 #define CHECKMEM(m) RUNTIME_CHECK(m != NULL) 2218 2219 static isc_result_t 2220 json_renderctx(isc__mem_t *ctx, summarystat_t *summary, json_object *array) { 2221 REQUIRE(VALID_CONTEXT(ctx)); 2222 REQUIRE(summary != NULL); 2223 REQUIRE(array != NULL); 2224 2225 json_object *ctxobj, *obj; 2226 char buf[1024]; 2227 2228 MCTXLOCK(ctx); 2229 2230 summary->contextsize += sizeof(*ctx) + 2231 (ctx->max_size + 1) * sizeof(struct stats) + 2232 ctx->max_size * sizeof(element *) + 2233 ctx->basic_table_count * sizeof(char *); 2234 summary->total += ctx->total; 2235 summary->inuse += ctx->inuse; 2236 summary->malloced += ctx->malloced; 2237 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { 2238 summary->blocksize += ctx->basic_table_count * 2239 NUM_BASIC_BLOCKS * ctx->mem_target; 2240 } 2241 #if ISC_MEM_TRACKLINES 2242 if (ctx->debuglist != NULL) { 2243 summary->contextsize += DEBUG_TABLE_COUNT * 2244 sizeof(debuglist_t) + 2245 ctx->debuglistcnt * sizeof(debuglink_t); 2246 } 2247 #endif /* if ISC_MEM_TRACKLINES */ 2248 2249 ctxobj = json_object_new_object(); 2250 CHECKMEM(ctxobj); 2251 2252 snprintf(buf, sizeof(buf), "%p", ctx); 2253 obj = json_object_new_string(buf); 2254 CHECKMEM(obj); 2255 json_object_object_add(ctxobj, "id", obj); 2256 2257 if (ctx->name[0] != 0) { 2258 obj = json_object_new_string(ctx->name); 2259 CHECKMEM(obj); 2260 json_object_object_add(ctxobj, "name", obj); 2261 } 2262 2263 obj = json_object_new_int64(isc_refcount_current(&ctx->references)); 2264 CHECKMEM(obj); 2265 json_object_object_add(ctxobj, "references", obj); 2266 2267 obj = json_object_new_int64(ctx->total); 2268 CHECKMEM(obj); 2269 json_object_object_add(ctxobj, "total", obj); 2270 2271 obj = json_object_new_int64(ctx->inuse); 2272 CHECKMEM(obj); 2273 json_object_object_add(ctxobj, "inuse", obj); 2274 2275 obj = json_object_new_int64(ctx->maxinuse); 2276 CHECKMEM(obj); 2277 json_object_object_add(ctxobj, "maxinuse", obj); 2278 2279 obj = json_object_new_int64(ctx->malloced); 2280 CHECKMEM(obj); 2281 json_object_object_add(ctxobj, "malloced", obj); 2282 2283 obj = json_object_new_int64(ctx->maxmalloced); 2284 CHECKMEM(obj); 2285 json_object_object_add(ctxobj, "maxmalloced", obj); 2286 2287 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { 2288 uint64_t blocksize; 2289 blocksize = ctx->basic_table_count * NUM_BASIC_BLOCKS * 2290 ctx->mem_target; 2291 obj = json_object_new_int64(blocksize); 2292 CHECKMEM(obj); 2293 json_object_object_add(ctxobj, "blocksize", obj); 2294 } 2295 2296 obj = json_object_new_int64(ctx->poolcnt); 2297 CHECKMEM(obj); 2298 json_object_object_add(ctxobj, "pools", obj); 2299 2300 summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t); 2301 2302 obj = json_object_new_int64(ctx->hi_water); 2303 CHECKMEM(obj); 2304 json_object_object_add(ctxobj, "hiwater", obj); 2305 2306 obj = json_object_new_int64(ctx->lo_water); 2307 CHECKMEM(obj); 2308 json_object_object_add(ctxobj, "lowater", obj); 2309 2310 MCTXUNLOCK(ctx); 2311 json_object_array_add(array, ctxobj); 2312 return (ISC_R_SUCCESS); 2313 } 2314 2315 isc_result_t 2316 isc_mem_renderjson(void *memobj0) { 2317 isc_result_t result = ISC_R_SUCCESS; 2318 isc__mem_t *ctx; 2319 summarystat_t summary; 2320 uint64_t lost; 2321 json_object *ctxarray, *obj; 2322 json_object *memobj = (json_object *)memobj0; 2323 2324 memset(&summary, 0, sizeof(summary)); 2325 2326 ctxarray = json_object_new_array(); 2327 CHECKMEM(ctxarray); 2328 2329 LOCK(&contextslock); 2330 lost = totallost; 2331 for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL; 2332 ctx = ISC_LIST_NEXT(ctx, link)) 2333 { 2334 result = json_renderctx(ctx, &summary, ctxarray); 2335 if (result != ISC_R_SUCCESS) { 2336 UNLOCK(&contextslock); 2337 goto error; 2338 } 2339 } 2340 UNLOCK(&contextslock); 2341 2342 obj = json_object_new_int64(summary.total); 2343 CHECKMEM(obj); 2344 json_object_object_add(memobj, "TotalUse", obj); 2345 2346 obj = json_object_new_int64(summary.inuse); 2347 CHECKMEM(obj); 2348 json_object_object_add(memobj, "InUse", obj); 2349 2350 obj = json_object_new_int64(summary.malloced); 2351 CHECKMEM(obj); 2352 json_object_object_add(memobj, "Malloced", obj); 2353 2354 obj = json_object_new_int64(summary.blocksize); 2355 CHECKMEM(obj); 2356 json_object_object_add(memobj, "BlockSize", obj); 2357 2358 obj = json_object_new_int64(summary.contextsize); 2359 CHECKMEM(obj); 2360 json_object_object_add(memobj, "ContextSize", obj); 2361 2362 obj = json_object_new_int64(lost); 2363 CHECKMEM(obj); 2364 json_object_object_add(memobj, "Lost", obj); 2365 2366 json_object_object_add(memobj, "contexts", ctxarray); 2367 return (ISC_R_SUCCESS); 2368 2369 error: 2370 if (ctxarray != NULL) { 2371 json_object_put(ctxarray); 2372 } 2373 return (result); 2374 } 2375 #endif /* HAVE_JSON_C */ 2376 2377 void 2378 isc_mem_create(isc_mem_t **mctxp) { 2379 mem_create(mctxp, isc_mem_defaultflags); 2380 } 2381 2382 void * 2383 isc__mem_get(isc_mem_t *mctx, size_t size FLARG) { 2384 REQUIRE(ISCAPI_MCTX_VALID(mctx)); 2385 2386 return (mctx->methods->memget(mctx, size FLARG_PASS)); 2387 } 2388 2389 void 2390 isc__mem_put(isc_mem_t *mctx, void *ptr, size_t size FLARG) { 2391 REQUIRE(ISCAPI_MCTX_VALID(mctx)); 2392 2393 mctx->methods->memput(mctx, ptr, size FLARG_PASS); 2394 } 2395 2396 void 2397 isc__mem_putanddetach(isc_mem_t **mctxp, void *ptr, size_t size FLARG) { 2398 REQUIRE(mctxp != NULL && ISCAPI_MCTX_VALID(*mctxp)); 2399 2400 (*mctxp)->methods->memputanddetach(mctxp, ptr, size FLARG_PASS); 2401 } 2402 2403 void * 2404 isc__mem_allocate(isc_mem_t *mctx, size_t size FLARG) { 2405 REQUIRE(ISCAPI_MCTX_VALID(mctx)); 2406 2407 return (mctx->methods->memallocate(mctx, size FLARG_PASS)); 2408 } 2409 2410 void * 2411 isc__mem_reallocate(isc_mem_t *mctx, void *ptr, size_t size FLARG) { 2412 REQUIRE(ISCAPI_MCTX_VALID(mctx)); 2413 2414 return (mctx->methods->memreallocate(mctx, ptr, size FLARG_PASS)); 2415 } 2416 2417 char * 2418 isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) { 2419 REQUIRE(ISCAPI_MCTX_VALID(mctx)); 2420 2421 return (mctx->methods->memstrdup(mctx, s FLARG_PASS)); 2422 } 2423 2424 char * 2425 isc__mem_strndup(isc_mem_t *mctx, const char *s, size_t size FLARG) { 2426 REQUIRE(ISCAPI_MCTX_VALID(mctx)); 2427 2428 return (mctx->methods->memstrndup(mctx, s, size FLARG_PASS)); 2429 } 2430 2431 void 2432 isc__mem_free(isc_mem_t *mctx, void *ptr FLARG) { 2433 REQUIRE(ISCAPI_MCTX_VALID(mctx)); 2434 2435 mctx->methods->memfree(mctx, ptr FLARG_PASS); 2436 } 2437 2438 void 2439 isc__mem_printactive(isc_mem_t *ctx0, FILE *file) { 2440 #if ISC_MEM_TRACKLINES 2441 REQUIRE(VALID_CONTEXT(ctx0)); 2442 REQUIRE(file != NULL); 2443 2444 isc__mem_t *ctx = (isc__mem_t *)ctx0; 2445 2446 print_active(ctx, file); 2447 #else /* if ISC_MEM_TRACKLINES */ 2448 UNUSED(ctx0); 2449 UNUSED(file); 2450 #endif /* if ISC_MEM_TRACKLINES */ 2451 } 2452