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