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