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