BIND - Update BIND to 9.5.2
[dragonfly.git] / contrib / bind-9.5.2 / lib / isc / mem.c
blob5bcd56172762947e3e4e78c4f75ede6620a74527
1 /*
2 * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1997-2003 Internet Software Consortium.
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.
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.
18 /* $Id: mem.c,v 1.137.16.8 2009/02/16 03:17:29 marka Exp $ */
20 /*! \file */
22 #include <config.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stddef.h>
28 #include <limits.h>
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>
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)
44 #ifndef ISC_MEM_DEBUGGING
45 #define ISC_MEM_DEBUGGING 0
46 #endif
47 LIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
50 * Constants.
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
61 * Types.
63 #if ISC_MEM_TRACKLINES
64 typedef struct debuglink debuglink_t;
65 struct 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;
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
81 typedef struct element element;
82 struct element {
83 element * next;
86 typedef struct {
87 /*!
88 * This structure must be ALIGNMENT_SIZE bytes.
90 union {
91 size_t size;
92 isc_mem_t *ctx;
93 char bytes[ALIGNMENT_SIZE];
94 } u;
95 } size_info;
97 struct stats {
98 unsigned long gets;
99 unsigned long totalgets;
100 unsigned long blocks;
101 unsigned long freefrags;
104 #define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C')
105 #define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC)
107 #if ISC_MEM_TRACKLINES
108 typedef ISC_LIST(debuglink_t) debuglist_t;
109 #endif
111 /* List of all active memory contexts. */
113 static ISC_LIST(isc_mem_t) contexts;
114 static isc_once_t once = ISC_ONCE_INIT;
115 static isc_mutex_t lock;
118 * Total size of lost memory due to a bug of external library.
119 * Locked by the global lock.
121 static isc_uint64_t totallost;
123 struct 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;
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;
159 #if ISC_MEM_TRACKLINES
160 debuglist_t * debuglist;
161 unsigned int debuglistcnt;
162 #endif
164 unsigned int memalloc_failures;
165 ISC_LINK(isc_mem_t) link;
168 #define MEMPOOL_MAGIC ISC_MAGIC('M', 'E', 'M', 'p')
169 #define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
171 struct 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
195 * Private Inline-able.
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)
211 static void
212 print_active(isc_mem_t *ctx, FILE *out);
215 * mctx must be locked.
217 static inline void
218 add_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size
219 FLARG)
221 debuglink_t *dl;
222 unsigned int i;
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);
231 if (mctx->debuglist == NULL)
232 return;
234 if (size > mctx->max_size)
235 size = mctx->max_size;
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;
251 next:
252 dl = ISC_LIST_NEXT(dl, link);
255 dl = malloc(sizeof(debuglink_t));
256 INSIST(dl != NULL);
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;
266 dl->ptr[0] = ptr;
267 dl->size[0] = size;
268 dl->file[0] = file;
269 dl->line[0] = line;
270 dl->count = 1;
272 ISC_LIST_PREPEND(mctx->debuglist[size], dl, link);
273 mctx->debuglistcnt++;
276 static inline void
277 delete_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size,
278 const char *file, unsigned int line)
280 debuglink_t *dl;
281 unsigned int i;
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);
290 if (mctx->debuglist == NULL)
291 return;
293 if (size > mctx->max_size)
294 size = mctx->max_size;
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;
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);
312 return;
315 dl = ISC_LIST_NEXT(dl, link);
319 * If we get here, we didn't find the item on the list. We're
320 * screwed.
322 INSIST(dl != NULL);
324 #endif /* ISC_MEM_TRACKLINES */
326 static inline size_t
327 rmsize(size_t size) {
329 * round down to ALIGNMENT_SIZE
331 return (size & (~(ALIGNMENT_SIZE - 1)));
334 static inline size_t
335 quantize(size_t size) {
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.
342 if (size == 0U)
343 return (ALIGNMENT_SIZE);
344 return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));
347 static inline isc_boolean_t
348 more_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;
357 /* Require: we hold the context lock. */
360 * Did we hit the quota for this context?
362 increment = NUM_BASIC_BLOCKS * ctx->mem_target;
363 if (ctx->quota != 0U && ctx->total + increment > ctx->quota)
364 return (ISC_FALSE);
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);
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);
381 ctx->basic_table = table;
382 ctx->basic_table_size = table_size;
385 new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target);
386 if (new == NULL) {
387 ctx->memalloc_failures++;
388 return (ISC_FALSE);
390 ctx->total += increment;
391 ctx->basic_table[ctx->basic_table_count] = new;
392 ctx->basic_table_count++;
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;
402 * curr is now pointing at the last block in the
403 * array.
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;
414 return (ISC_TRUE);
417 static inline isc_boolean_t
418 more_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;
425 * Try to get more fragments by chopping up a basic block.
428 if (ctx->basic_blocks == NULL) {
429 if (!more_basic_blocks(ctx)) {
431 * We can't get more memory from the OS, or we've
432 * hit the quota for this context.
435 * XXXRTH "At quota" notification here.
437 return (ISC_FALSE);
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;
448 * Set up a linked-list of blocks of size
449 * "new_size".
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;
461 * Add the remaining fragment of the basic block to a free list.
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++;
470 * curr is now pointing at the last block in the
471 * array.
473 ((element *)curr)->next = NULL;
474 ctx->freelists[new_size] = new;
476 return (ISC_TRUE);
479 static inline void *
480 mem_getunlocked(isc_mem_t *ctx, size_t size) {
481 size_t new_size = quantize(size);
482 void *ret;
484 if (size >= ctx->max_size || new_size >= ctx->max_size) {
486 * memget() was called on something beyond our upper limit.
488 if (ctx->quota != 0U && ctx->total + size > ctx->quota) {
489 ret = NULL;
490 goto done;
492 ret = (ctx->memalloc)(ctx->arg, size);
493 if (ret == NULL) {
494 ctx->memalloc_failures++;
495 goto done;
497 ctx->total += size;
498 ctx->inuse += size;
499 ctx->stats[ctx->max_size].gets++;
500 ctx->stats[ctx->max_size].totalgets++;
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.
506 new_size = size;
507 goto done;
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.
515 if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size))
516 return (NULL);
519 * The free list uses the "rounded-up" size "new_size".
521 ret = ctx->freelists[new_size];
522 ctx->freelists[new_size] = ctx->freelists[new_size]->next;
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.
530 ctx->stats[size].gets++;
531 ctx->stats[size].totalgets++;
532 ctx->stats[new_size].freefrags--;
533 ctx->inuse += new_size;
535 done:
537 #if ISC_MEM_FILL
538 if (ret != NULL)
539 memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
540 #endif
542 return (ret);
545 #if ISC_MEM_FILL && ISC_MEM_CHECKOVERRUN
546 static inline void
547 check_overrun(void *mem, size_t size, size_t new_size) {
548 unsigned char *cp;
550 cp = (unsigned char *)mem;
551 cp += size;
552 while (size < new_size) {
553 INSIST(*cp == 0xbe);
554 cp++;
555 size++;
558 #endif
560 static inline void
561 mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size) {
562 size_t new_size = quantize(size);
564 if (size == ctx->max_size || new_size >= ctx->max_size) {
566 * memput() called on something beyond our upper limit.
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;
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
588 * The free list uses the "rounded-up" size "new_size".
590 ((element *)mem)->next = ctx->freelists[new_size];
591 ctx->freelists[new_size] = (element *)mem;
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.
599 INSIST(ctx->stats[size].gets != 0U);
600 ctx->stats[size].gets--;
601 ctx->stats[new_size].freefrags++;
602 ctx->inuse -= new_size;
606 * Perform a malloc, doing memory filling and overrun detection as necessary.
608 static inline void *
609 mem_get(isc_mem_t *ctx, size_t size) {
610 char *ret;
612 #if ISC_MEM_CHECKOVERRUN
613 size += 1;
614 #endif
616 ret = (ctx->memalloc)(ctx->arg, size);
617 if (ret == NULL)
618 ctx->memalloc_failures++;
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
630 return (ret);
634 * Perform a free, doing memory filling and overrun detection as necessary.
636 static inline void
637 mem_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);
650 * Update internal counters after a memory get.
652 static inline void
653 mem_getstats(isc_mem_t *ctx, size_t size) {
654 ctx->total += size;
655 ctx->inuse += size;
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++;
667 * Update internal counters after a memory put.
669 static inline void
670 mem_putstats(isc_mem_t *ctx, void *ptr, size_t size) {
671 UNUSED(ptr);
673 INSIST(ctx->inuse >= size);
674 ctx->inuse -= size;
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--;
686 * Private.
689 static void *
690 default_memalloc(void *arg, size_t size) {
691 UNUSED(arg);
692 if (size == 0U)
693 size = 1;
694 return (malloc(size));
697 static void
698 default_memfree(void *arg, void *ptr) {
699 UNUSED(arg);
700 free(ptr);
703 static void
704 initialize_action(void) {
705 RUNTIME_CHECK(isc_mutex_init(&lock) == ISC_R_SUCCESS);
706 ISC_LIST_INIT(contexts);
707 totallost = 0;
711 * Public.
714 isc_result_t
715 isc_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)
719 return (isc_mem_createx2(init_max_size, target_size, memalloc, memfree,
720 arg, ctxp, ISC_MEMFLAG_DEFAULT));
724 isc_result_t
725 isc_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)
729 isc_mem_t *ctx;
730 isc_result_t result;
732 REQUIRE(ctxp != NULL && *ctxp == NULL);
733 REQUIRE(memalloc != NULL);
734 REQUIRE(memfree != NULL);
736 INSIST((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0);
738 RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
740 ctx = (memalloc)(arg, sizeof(*ctx));
741 if (ctx == NULL)
742 return (ISC_R_NOMEMORY);
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);
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;
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;
796 memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats));
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;
809 memset(ctx->freelists, 0,
810 ctx->max_size * sizeof(element *));
813 #if ISC_MEM_TRACKLINES
814 if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
815 unsigned int i;
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;
823 for (i = 0; i <= ctx->max_size; i++)
824 ISC_LIST_INIT(ctx->debuglist[i]);
826 #endif
828 ctx->memalloc_failures = 0;
830 LOCK(&lock);
831 ISC_LIST_INITANDAPPEND(contexts, ctx, link);
832 UNLOCK(&lock);
834 *ctxp = ctx;
835 return (ISC_R_SUCCESS);
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);
852 return (result);
855 isc_result_t
856 isc_mem_create(size_t init_max_size, size_t target_size,
857 isc_mem_t **ctxp)
859 return (isc_mem_createx2(init_max_size, target_size,
860 default_memalloc, default_memfree, NULL,
861 ctxp, ISC_MEMFLAG_DEFAULT));
864 isc_result_t
865 isc_mem_create2(size_t init_max_size, size_t target_size,
866 isc_mem_t **ctxp, unsigned int flags)
868 return (isc_mem_createx2(init_max_size, target_size,
869 default_memalloc, default_memfree, NULL,
870 ctxp, flags));
873 static void
874 destroy(isc_mem_t *ctx) {
875 unsigned int i;
876 isc_ondestroy_t ondest;
878 ctx->magic = 0;
880 LOCK(&lock);
881 ISC_LIST_UNLINK(contexts, ctx, link);
882 totallost += ctx->inuse;
883 UNLOCK(&lock);
885 INSIST(ISC_LIST_EMPTY(ctx->pools));
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]));
895 } else {
896 debuglink_t *dl;
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);
907 (ctx->memfree)(ctx->arg, ctx->debuglist);
909 #endif
910 INSIST(ctx->references == 0);
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);
922 (ctx->memfree)(ctx->arg, ctx->stats);
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);
932 ondest = ctx->ondestroy;
934 if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
935 DESTROYLOCK(&ctx->lock);
936 (ctx->memfree)(ctx->arg, ctx);
938 isc_ondestroy_notify(&ondest, ctx);
941 void
942 isc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) {
943 REQUIRE(VALID_CONTEXT(source));
944 REQUIRE(targetp != NULL && *targetp == NULL);
946 MCTXLOCK(source, &source->lock);
947 source->references++;
948 MCTXUNLOCK(source, &source->lock);
950 *targetp = source;
953 void
954 isc_mem_detach(isc_mem_t **ctxp) {
955 isc_mem_t *ctx;
956 isc_boolean_t want_destroy = ISC_FALSE;
958 REQUIRE(ctxp != NULL);
959 ctx = *ctxp;
960 REQUIRE(VALID_CONTEXT(ctx));
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);
969 if (want_destroy)
970 destroy(ctx);
972 *ctxp = NULL;
976 * isc_mem_putanddetach() is the equivalent of:
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);
985 void
986 isc__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;
992 REQUIRE(ctxp != NULL);
993 ctx = *ctxp;
994 REQUIRE(VALID_CONTEXT(ctx));
995 REQUIRE(ptr != NULL);
998 * Must be before mem_putunlocked() as ctxp is usually within
999 * [ptr..ptr+size).
1001 *ctxp = NULL;
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);
1011 isc__mem_free(ctx, ptr FLARG_PASS);
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);
1021 return;
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);
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;
1039 MCTXUNLOCK(ctx, &ctx->lock);
1041 if (want_destroy)
1042 destroy(ctx);
1045 void
1046 isc_mem_destroy(isc_mem_t **ctxp) {
1047 isc_mem_t *ctx;
1050 * This routine provides legacy support for callers who use mctxs
1051 * without attaching/detaching.
1054 REQUIRE(ctxp != NULL);
1055 ctx = *ctxp;
1056 REQUIRE(VALID_CONTEXT(ctx));
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);
1067 destroy(ctx);
1069 *ctxp = NULL;
1072 isc_result_t
1073 isc_mem_ondestroy(isc_mem_t *ctx, isc_task_t *task, isc_event_t **event) {
1074 isc_result_t res;
1076 MCTXLOCK(ctx, &ctx->lock);
1077 res = isc_ondestroy_register(&ctx->ondestroy, task, event);
1078 MCTXUNLOCK(ctx, &ctx->lock);
1080 return (res);
1084 void *
1085 isc__mem_get(isc_mem_t *ctx, size_t size FLARG) {
1086 void *ptr;
1087 isc_boolean_t call_water = ISC_FALSE;
1089 REQUIRE(VALID_CONTEXT(ctx));
1091 if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0)
1092 return (isc__mem_allocate(ctx, size FLARG_PASS));
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);
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;
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);
1116 MCTXUNLOCK(ctx, &ctx->lock);
1118 if (call_water)
1119 (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1121 return (ptr);
1124 void
1125 isc__mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG)
1127 isc_boolean_t call_water = ISC_FALSE;
1128 size_info *si;
1129 size_t oldsize;
1131 REQUIRE(VALID_CONTEXT(ctx));
1132 REQUIRE(ptr != NULL);
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);
1142 isc__mem_free(ctx, ptr FLARG_PASS);
1143 return;
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);
1155 DELETE_TRACE(ctx, ptr, size, file, line);
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.
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;
1167 MCTXUNLOCK(ctx, &ctx->lock);
1169 if (call_water)
1170 (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1173 void
1174 isc_mem_waterack(isc_mem_t *ctx, int flag) {
1175 REQUIRE(VALID_CONTEXT(ctx));
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);
1185 #if ISC_MEM_TRACKLINES
1186 static void
1187 print_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;
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]);
1205 if (dl != NULL)
1206 found = ISC_TRUE;
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);
1219 if (!found)
1220 fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1221 ISC_MSG_NONE, "\tNone.\n"));
1224 #endif
1227 * Print the stats[] on the stream "out" with suitable formatting.
1229 void
1230 isc_mem_stats(isc_mem_t *ctx, FILE *out) {
1231 size_t i;
1232 const struct stats *s;
1233 const isc_mempool_t *pool;
1235 REQUIRE(VALID_CONTEXT(ctx));
1236 MCTXLOCK(ctx, &ctx->lock);
1238 for (i = 0; i <= ctx->max_size; i++) {
1239 s = &ctx->stats[i];
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);
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.
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");
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);
1293 #if ISC_MEM_TRACKLINES
1294 print_active(ctx, out);
1295 #endif
1297 MCTXUNLOCK(ctx, &ctx->lock);
1301 * Replacements for malloc() and free() -- they implicitly remember the
1302 * size of the object allocated (with some additional overhead).
1305 static void *
1306 isc__mem_allocateunlocked(isc_mem_t *ctx, size_t size) {
1307 size_info *si;
1309 size += ALIGNMENT_SIZE;
1310 if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1311 size += ALIGNMENT_SIZE;
1313 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0)
1314 si = mem_getunlocked(ctx, size);
1315 else
1316 si = mem_get(ctx, size);
1318 if (si == NULL)
1319 return (NULL);
1320 if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1321 si->u.ctx = ctx;
1322 si++;
1324 si->u.size = size;
1325 return (&si[1]);
1328 void *
1329 isc__mem_allocate(isc_mem_t *ctx, size_t size FLARG) {
1330 size_info *si;
1331 isc_boolean_t call_water = ISC_FALSE;
1333 REQUIRE(VALID_CONTEXT(ctx));
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);
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;
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);
1360 MCTXUNLOCK(ctx, &ctx->lock);
1362 if (call_water)
1363 (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1365 return (si);
1368 void *
1369 isc__mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG) {
1370 void *new_ptr = NULL;
1371 size_t oldsize, copysize;
1373 REQUIRE(VALID_CONTEXT(ctx));
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.
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);
1396 } else if (ptr != NULL)
1397 isc__mem_free(ctx, ptr FLARG_PASS);
1399 return (new_ptr);
1402 void
1403 isc__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;
1408 REQUIRE(VALID_CONTEXT(ctx));
1409 REQUIRE(ptr != NULL);
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;
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);
1429 DELETE_TRACE(ctx, ptr, size, file, line);
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.
1436 if (ctx->hi_called &&
1437 (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1438 ctx->hi_called = ISC_FALSE;
1440 if (ctx->water != NULL)
1441 call_water = ISC_TRUE;
1443 MCTXUNLOCK(ctx, &ctx->lock);
1445 if (call_water)
1446 (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1451 * Other useful things.
1454 char *
1455 isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) {
1456 size_t len;
1457 char *ns;
1459 REQUIRE(VALID_CONTEXT(mctx));
1460 REQUIRE(s != NULL);
1462 len = strlen(s);
1464 ns = isc__mem_allocate(mctx, len + 1 FLARG_PASS);
1466 if (ns != NULL)
1467 strncpy(ns, s, len + 1);
1469 return (ns);
1472 void
1473 isc_mem_setdestroycheck(isc_mem_t *ctx, isc_boolean_t flag) {
1474 REQUIRE(VALID_CONTEXT(ctx));
1475 MCTXLOCK(ctx, &ctx->lock);
1477 ctx->checkfree = flag;
1479 MCTXUNLOCK(ctx, &ctx->lock);
1483 * Quotas
1486 void
1487 isc_mem_setquota(isc_mem_t *ctx, size_t quota) {
1488 REQUIRE(VALID_CONTEXT(ctx));
1489 MCTXLOCK(ctx, &ctx->lock);
1491 ctx->quota = quota;
1493 MCTXUNLOCK(ctx, &ctx->lock);
1496 size_t
1497 isc_mem_getquota(isc_mem_t *ctx) {
1498 size_t quota;
1500 REQUIRE(VALID_CONTEXT(ctx));
1501 MCTXLOCK(ctx, &ctx->lock);
1503 quota = ctx->quota;
1505 MCTXUNLOCK(ctx, &ctx->lock);
1507 return (quota);
1510 size_t
1511 isc_mem_inuse(isc_mem_t *ctx) {
1512 size_t inuse;
1514 REQUIRE(VALID_CONTEXT(ctx));
1515 MCTXLOCK(ctx, &ctx->lock);
1517 inuse = ctx->inuse;
1519 MCTXUNLOCK(ctx, &ctx->lock);
1521 return (inuse);
1524 void
1525 isc_mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg,
1526 size_t hiwater, size_t lowater)
1528 isc_boolean_t callwater = ISC_FALSE;
1529 isc_mem_water_t oldwater;
1530 void *oldwater_arg;
1532 REQUIRE(VALID_CONTEXT(ctx));
1533 REQUIRE(hiwater >= lowater);
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;
1556 MCTXUNLOCK(ctx, &ctx->lock);
1558 if (callwater && oldwater != NULL)
1559 (oldwater)(oldwater_arg, ISC_MEM_LOWATER);
1562 void
1563 isc_mem_setname(isc_mem_t *ctx, const char *name, void *tag) {
1564 REQUIRE(VALID_CONTEXT(ctx));
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);
1573 const char *
1574 isc_mem_getname(isc_mem_t *ctx) {
1575 REQUIRE(VALID_CONTEXT(ctx));
1577 return (ctx->name);
1580 void *
1581 isc_mem_gettag(isc_mem_t *ctx) {
1582 REQUIRE(VALID_CONTEXT(ctx));
1584 return (ctx->tag);
1588 * Memory pool stuff
1591 isc_result_t
1592 isc_mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp) {
1593 isc_mempool_t *mpctx;
1595 REQUIRE(VALID_CONTEXT(mctx));
1596 REQUIRE(size > 0U);
1597 REQUIRE(mpctxp != NULL && *mpctxp == NULL);
1600 * Allocate space for this pool, initialize values, and if all works
1601 * well, attach to the memory context.
1603 mpctx = isc_mem_get(mctx, sizeof(isc_mempool_t));
1604 if (mpctx == NULL)
1605 return (ISC_R_NOMEMORY);
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;
1622 *mpctxp = mpctx;
1624 MCTXLOCK(mctx, &mctx->lock);
1625 ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
1626 mctx->poolcnt++;
1627 MCTXUNLOCK(mctx, &mctx->lock);
1629 return (ISC_R_SUCCESS);
1632 void
1633 isc_mempool_setname(isc_mempool_t *mpctx, const char *name) {
1634 REQUIRE(name != NULL);
1636 #if ISC_MEMPOOL_NAMES
1637 if (mpctx->lock != NULL)
1638 LOCK(mpctx->lock);
1640 strncpy(mpctx->name, name, sizeof(mpctx->name) - 1);
1641 mpctx->name[sizeof(mpctx->name) - 1] = '\0';
1643 if (mpctx->lock != NULL)
1644 UNLOCK(mpctx->lock);
1645 #else
1646 UNUSED(mpctx);
1647 UNUSED(name);
1648 #endif
1651 void
1652 isc_mempool_destroy(isc_mempool_t **mpctxp) {
1653 isc_mempool_t *mpctx;
1654 isc_mem_t *mctx;
1655 isc_mutex_t *lock;
1656 element *item;
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);
1670 mctx = mpctx->mctx;
1672 lock = mpctx->lock;
1674 if (lock != NULL)
1675 LOCK(lock);
1678 * Return any items on the free list
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;
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);
1694 MCTXUNLOCK(mctx, &mctx->lock);
1697 * Remove our linked list entry from the memory context.
1699 MCTXLOCK(mctx, &mctx->lock);
1700 ISC_LIST_UNLINK(mctx->pools, mpctx, link);
1701 mctx->poolcnt--;
1702 MCTXUNLOCK(mctx, &mctx->lock);
1704 mpctx->magic = 0;
1706 isc_mem_put(mpctx->mctx, mpctx, sizeof(isc_mempool_t));
1708 if (lock != NULL)
1709 UNLOCK(lock);
1711 *mpctxp = NULL;
1714 void
1715 isc_mempool_associatelock(isc_mempool_t *mpctx, isc_mutex_t *lock) {
1716 REQUIRE(VALID_MEMPOOL(mpctx));
1717 REQUIRE(mpctx->lock == NULL);
1718 REQUIRE(lock != NULL);
1720 mpctx->lock = lock;
1723 void *
1724 isc__mempool_get(isc_mempool_t *mpctx FLARG) {
1725 element *item;
1726 isc_mem_t *mctx;
1727 unsigned int i;
1729 REQUIRE(VALID_MEMPOOL(mpctx));
1731 mctx = mpctx->mctx;
1733 if (mpctx->lock != NULL)
1734 LOCK(mpctx->lock);
1737 * Don't let the caller go over quota
1739 if (mpctx->allocated >= mpctx->maxalloc) {
1740 item = NULL;
1741 goto out;
1745 * if we have a free list item, return the first here
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;
1758 * We need to dip into the well. Lock the memory context here and
1759 * fill up our free list.
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);
1770 if (item == NULL)
1771 break;
1772 item->next = mpctx->items;
1773 mpctx->items = item;
1774 mpctx->freecount++;
1776 MCTXUNLOCK(mctx, &mctx->lock);
1779 * If we didn't get any items, return NULL.
1781 item = mpctx->items;
1782 if (item == NULL)
1783 goto out;
1785 mpctx->items = item->next;
1786 mpctx->freecount--;
1787 mpctx->gets++;
1788 mpctx->allocated++;
1790 out:
1791 if (mpctx->lock != NULL)
1792 UNLOCK(mpctx->lock);
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);
1800 #endif /* ISC_MEM_TRACKLINES */
1802 return (item);
1805 void
1806 isc__mempool_put(isc_mempool_t *mpctx, void *mem FLARG) {
1807 isc_mem_t *mctx;
1808 element *item;
1810 REQUIRE(VALID_MEMPOOL(mpctx));
1811 REQUIRE(mem != NULL);
1813 mctx = mpctx->mctx;
1815 if (mpctx->lock != NULL)
1816 LOCK(mpctx->lock);
1818 INSIST(mpctx->allocated > 0);
1819 mpctx->allocated--;
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 */
1828 * If our free list is full, return this to the mctx directly.
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);
1841 if (mpctx->lock != NULL)
1842 UNLOCK(mpctx->lock);
1843 return;
1847 * Otherwise, attach it to our free list and bump the counter.
1849 mpctx->freecount++;
1850 item = (element *)mem;
1851 item->next = mpctx->items;
1852 mpctx->items = item;
1854 if (mpctx->lock != NULL)
1855 UNLOCK(mpctx->lock);
1859 * Quotas
1862 void
1863 isc_mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit) {
1864 REQUIRE(VALID_MEMPOOL(mpctx));
1866 if (mpctx->lock != NULL)
1867 LOCK(mpctx->lock);
1869 mpctx->freemax = limit;
1871 if (mpctx->lock != NULL)
1872 UNLOCK(mpctx->lock);
1875 unsigned int
1876 isc_mempool_getfreemax(isc_mempool_t *mpctx) {
1877 unsigned int freemax;
1879 REQUIRE(VALID_MEMPOOL(mpctx));
1881 if (mpctx->lock != NULL)
1882 LOCK(mpctx->lock);
1884 freemax = mpctx->freemax;
1886 if (mpctx->lock != NULL)
1887 UNLOCK(mpctx->lock);
1889 return (freemax);
1892 unsigned int
1893 isc_mempool_getfreecount(isc_mempool_t *mpctx) {
1894 unsigned int freecount;
1896 REQUIRE(VALID_MEMPOOL(mpctx));
1898 if (mpctx->lock != NULL)
1899 LOCK(mpctx->lock);
1901 freecount = mpctx->freecount;
1903 if (mpctx->lock != NULL)
1904 UNLOCK(mpctx->lock);
1906 return (freecount);
1909 void
1910 isc_mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit) {
1911 REQUIRE(limit > 0);
1913 REQUIRE(VALID_MEMPOOL(mpctx));
1915 if (mpctx->lock != NULL)
1916 LOCK(mpctx->lock);
1918 mpctx->maxalloc = limit;
1920 if (mpctx->lock != NULL)
1921 UNLOCK(mpctx->lock);
1924 unsigned int
1925 isc_mempool_getmaxalloc(isc_mempool_t *mpctx) {
1926 unsigned int maxalloc;
1928 REQUIRE(VALID_MEMPOOL(mpctx));
1930 if (mpctx->lock != NULL)
1931 LOCK(mpctx->lock);
1933 maxalloc = mpctx->maxalloc;
1935 if (mpctx->lock != NULL)
1936 UNLOCK(mpctx->lock);
1938 return (maxalloc);
1941 unsigned int
1942 isc_mempool_getallocated(isc_mempool_t *mpctx) {
1943 unsigned int allocated;
1945 REQUIRE(VALID_MEMPOOL(mpctx));
1947 if (mpctx->lock != NULL)
1948 LOCK(mpctx->lock);
1950 allocated = mpctx->allocated;
1952 if (mpctx->lock != NULL)
1953 UNLOCK(mpctx->lock);
1955 return (allocated);
1958 void
1959 isc_mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit) {
1960 REQUIRE(limit > 0);
1961 REQUIRE(VALID_MEMPOOL(mpctx));
1963 if (mpctx->lock != NULL)
1964 LOCK(mpctx->lock);
1966 mpctx->fillcount = limit;
1968 if (mpctx->lock != NULL)
1969 UNLOCK(mpctx->lock);
1972 unsigned int
1973 isc_mempool_getfillcount(isc_mempool_t *mpctx) {
1974 unsigned int fillcount;
1976 REQUIRE(VALID_MEMPOOL(mpctx));
1978 if (mpctx->lock != NULL)
1979 LOCK(mpctx->lock);
1981 fillcount = mpctx->fillcount;
1983 if (mpctx->lock != NULL)
1984 UNLOCK(mpctx->lock);
1986 return (fillcount);
1989 void
1990 isc_mem_printactive(isc_mem_t *ctx, FILE *file) {
1992 REQUIRE(VALID_CONTEXT(ctx));
1993 REQUIRE(file != NULL);
1995 #if !ISC_MEM_TRACKLINES
1996 UNUSED(ctx);
1997 UNUSED(file);
1998 #else
1999 print_active(ctx, file);
2000 #endif
2003 void
2004 isc_mem_printallactive(FILE *file) {
2005 #if !ISC_MEM_TRACKLINES
2006 UNUSED(file);
2007 #else
2008 isc_mem_t *ctx;
2010 RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
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);
2019 UNLOCK(&lock);
2020 #endif
2023 void
2024 isc_mem_checkdestroyed(FILE *file) {
2026 RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2028 LOCK(&lock);
2029 if (!ISC_LIST_EMPTY(contexts)) {
2030 #if ISC_MEM_TRACKLINES
2031 isc_mem_t *ctx;
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);
2039 fflush(file);
2040 #endif
2041 INSIST(0);
2043 UNLOCK(&lock);
2046 #ifdef HAVE_LIBXML2
2048 typedef struct summarystat {
2049 isc_uint64_t total;
2050 isc_uint64_t inuse;
2051 isc_uint64_t blocksize;
2052 isc_uint64_t contextsize;
2053 } summarystat_t;
2055 static void
2056 renderctx(isc_mem_t *ctx, summarystat_t *summary, xmlTextWriterPtr writer) {
2057 REQUIRE(VALID_CONTEXT(ctx));
2059 xmlTextWriterStartElement(writer, ISC_XMLCHAR "context");
2061 xmlTextWriterStartElement(writer, ISC_XMLCHAR "id");
2062 xmlTextWriterWriteFormatString(writer, "%p", ctx);
2063 xmlTextWriterEndElement(writer); /* id */
2065 if (ctx->name[0] != 0) {
2066 xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
2067 xmlTextWriterWriteFormatString(writer, "%s", ctx->name);
2068 xmlTextWriterEndElement(writer); /* name */
2071 REQUIRE(VALID_CONTEXT(ctx));
2072 MCTXLOCK(ctx, &ctx->lock);
2074 summary->contextsize += sizeof(*ctx) +
2075 (ctx->max_size + 1) * sizeof(struct stats) +
2076 ctx->max_size * sizeof(element *) +
2077 ctx->basic_table_count * sizeof(char *);
2078 #if ISC_MEM_TRACKLINES
2079 if (ctx->debuglist != NULL) {
2080 summary->contextsize +=
2081 (ctx->max_size + 1) * sizeof(debuglist_t) +
2082 ctx->debuglistcnt * sizeof(debuglink_t);
2084 #endif
2085 xmlTextWriterStartElement(writer, ISC_XMLCHAR "references");
2086 xmlTextWriterWriteFormatString(writer, "%d", ctx->references);
2087 xmlTextWriterEndElement(writer); /* references */
2089 summary->total += ctx->total;
2090 xmlTextWriterStartElement(writer, ISC_XMLCHAR "total");
2091 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2092 (isc_uint64_t)ctx->total);
2093 xmlTextWriterEndElement(writer); /* total */
2095 summary->inuse += ctx->inuse;
2096 xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse");
2097 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2098 (isc_uint64_t)ctx->inuse);
2099 xmlTextWriterEndElement(writer); /* inuse */
2101 xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse");
2102 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2103 (isc_uint64_t)ctx->maxinuse);
2104 xmlTextWriterEndElement(writer); /* maxinuse */
2106 xmlTextWriterStartElement(writer, ISC_XMLCHAR "blocksize");
2107 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2108 summary->blocksize += ctx->basic_table_count *
2109 NUM_BASIC_BLOCKS * ctx->mem_target;
2110 xmlTextWriterWriteFormatString(writer,
2111 "%" ISC_PRINT_QUADFORMAT "u",
2112 (isc_uint64_t)
2113 ctx->basic_table_count *
2114 NUM_BASIC_BLOCKS *
2115 ctx->mem_target);
2116 } else
2117 xmlTextWriterWriteFormatString(writer, "%s", "-");
2118 xmlTextWriterEndElement(writer); /* blocksize */
2120 xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools");
2121 xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt);
2122 xmlTextWriterEndElement(writer); /* pools */
2123 summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
2125 xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater");
2126 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2127 (isc_uint64_t)ctx->hi_water);
2128 xmlTextWriterEndElement(writer); /* hiwater */
2130 xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater");
2131 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2132 (isc_uint64_t)ctx->lo_water);
2133 xmlTextWriterEndElement(writer); /* lowater */
2135 MCTXUNLOCK(ctx, &ctx->lock);
2137 xmlTextWriterEndElement(writer); /* context */
2140 void
2141 isc_mem_renderxml(xmlTextWriterPtr writer) {
2142 isc_mem_t *ctx;
2143 summarystat_t summary;
2144 isc_uint64_t lost;
2146 memset(&summary, 0, sizeof(summary));
2148 xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts");
2150 RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2152 LOCK(&lock);
2153 lost = totallost;
2154 for (ctx = ISC_LIST_HEAD(contexts);
2155 ctx != NULL;
2156 ctx = ISC_LIST_NEXT(ctx, link)) {
2157 renderctx(ctx, &summary, writer);
2159 UNLOCK(&lock);
2161 xmlTextWriterEndElement(writer); /* contexts */
2163 xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary");
2165 xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse");
2166 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2167 summary.total);
2168 xmlTextWriterEndElement(writer); /* TotalUse */
2170 xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse");
2171 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2172 summary.inuse);
2173 xmlTextWriterEndElement(writer); /* InUse */
2175 xmlTextWriterStartElement(writer, ISC_XMLCHAR "BlockSize");
2176 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2177 summary.blocksize);
2178 xmlTextWriterEndElement(writer); /* BlockSize */
2180 xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize");
2181 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2182 summary.contextsize);
2183 xmlTextWriterEndElement(writer); /* ContextSize */
2185 xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost");
2186 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2187 lost);
2188 xmlTextWriterEndElement(writer); /* Lost */
2190 xmlTextWriterEndElement(writer); /* summary */
2193 #endif /* HAVE_LIBXML2 */