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