1 /* $NetBSD: mem.c,v 1.5 2020/05/25 20:47:20 christos Exp $ */
2
3 /*
4 * Copyright (C) 2004-2010, 2012 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1997-2003 Internet Software Consortium.
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /* Id */
21
22 /*! \file */
23
24 #include <config.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29
30 #include <limits.h>
31
32 #include <isc/magic.h>
33 #include <isc/mem.h>
34 #include <isc/msgs.h>
35 #include <isc/once.h>
36 #include <isc/ondestroy.h>
37 #include <isc/string.h>
38 #include <isc/mutex.h>
39 #include <isc/print.h>
40 #include <isc/util.h>
41 #include <isc/xml.h>
42
43 #define MCTXLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) LOCK(l)
44 #define MCTXUNLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) UNLOCK(l)
45
46 #ifndef ISC_MEM_DEBUGGING
47 #define ISC_MEM_DEBUGGING 0
48 #endif
49 LIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
50
51 /*
52 * Constants.
53 */
54
55 #define DEF_MAX_SIZE 1100
56 #define DEF_MEM_TARGET 4096
57 #define ALIGNMENT_SIZE 8U /*%< must be a power of 2 */
58 #define NUM_BASIC_BLOCKS 64 /*%< must be > 1 */
59 #define TABLE_INCREMENT 1024
60 #define DEBUGLIST_COUNT 1024
61
62 /*
63 * Types.
64 */
65 typedef struct isc__mem isc__mem_t;
66 typedef struct isc__mempool isc__mempool_t;
67
68 #if ISC_MEM_TRACKLINES
69 typedef struct debuglink debuglink_t;
70 struct debuglink {
71 ISC_LINK(debuglink_t) link;
72 const void *ptr[DEBUGLIST_COUNT];
73 unsigned int size[DEBUGLIST_COUNT];
74 const char *file[DEBUGLIST_COUNT];
75 unsigned int line[DEBUGLIST_COUNT];
76 unsigned int count;
77 };
78
79 #define FLARG_PASS , file, line
80 #define FLARG , const char *file, unsigned int line
81 #else
82 #define FLARG_PASS
83 #define FLARG
84 #endif
85
86 typedef struct element element;
87 struct element {
88 element * next;
89 };
90
91 typedef struct {
92 /*!
93 * This structure must be ALIGNMENT_SIZE bytes.
94 */
95 union {
96 size_t size;
97 isc__mem_t *ctx;
98 char bytes[ALIGNMENT_SIZE];
99 } u;
100 } size_info;
101
102 struct stats {
103 unsigned long gets;
104 unsigned long totalgets;
105 unsigned long blocks;
106 unsigned long freefrags;
107 };
108
109 #define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C')
110 #define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC)
111
112 #if ISC_MEM_TRACKLINES
113 typedef ISC_LIST(debuglink_t) debuglist_t;
114 #endif
115
116 /* List of all active memory contexts. */
117
118 static ISC_LIST(isc__mem_t) contexts;
119 static isc_once_t once = ISC_ONCE_INIT;
120 static isc_mutex_t lock;
121
122 /*%
123 * Total size of lost memory due to a bug of external library.
124 * Locked by the global lock.
125 */
126 static isc_uint64_t totallost;
127
128 struct isc__mem {
129 isc_mem_t common;
130 isc_ondestroy_t ondestroy;
131 unsigned int flags;
132 isc_mutex_t lock;
133 isc_memalloc_t memalloc;
134 isc_memfree_t memfree;
135 void * arg;
136 size_t max_size;
137 isc_boolean_t checkfree;
138 struct stats * stats;
139 unsigned int references;
140 char name[16];
141 void * tag;
142 size_t quota;
143 size_t total;
144 size_t inuse;
145 size_t maxinuse;
146 size_t hi_water;
147 size_t lo_water;
148 isc_boolean_t hi_called;
149 isc_boolean_t is_overmem;
150 isc_mem_water_t water;
151 void * water_arg;
152 ISC_LIST(isc__mempool_t) pools;
153 unsigned int poolcnt;
154
155 /* ISC_MEMFLAG_INTERNAL */
156 size_t mem_target;
157 element ** freelists;
158 element * basic_blocks;
159 unsigned char ** basic_table;
160 unsigned int basic_table_count;
161 unsigned int basic_table_size;
162 unsigned char * lowest;
163 unsigned char * highest;
164
165 #if ISC_MEM_TRACKLINES
166 debuglist_t * debuglist;
167 unsigned int debuglistcnt;
168 #endif
169
170 unsigned int memalloc_failures;
171 ISC_LINK(isc__mem_t) link;
172 };
173
174 #define MEMPOOL_MAGIC ISC_MAGIC('M', 'E', 'M', 'p')
175 #define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
176
177 struct isc__mempool {
178 /* always unlocked */
179 isc_mempool_t common; /*%< common header of mempool's */
180 isc_mutex_t *lock; /*%< optional lock */
181 isc__mem_t *mctx; /*%< our memory context */
182 /*%< locked via the memory context's lock */
183 ISC_LINK(isc__mempool_t) link; /*%< next pool in this mem context */
184 /*%< optionally locked from here down */
185 element *items; /*%< low water item list */
186 size_t size; /*%< size of each item on this pool */
187 unsigned int maxalloc; /*%< max number of items allowed */
188 unsigned int allocated; /*%< # of items currently given out */
189 unsigned int freecount; /*%< # of items on reserved list */
190 unsigned int freemax; /*%< # of items allowed on free list */
191 unsigned int fillcount; /*%< # of items to fetch on each fill */
192 /*%< Stats only. */
193 unsigned int gets; /*%< # of requests to this pool */
194 /*%< Debugging only. */
195 #if ISC_MEMPOOL_NAMES
196 char name[16]; /*%< printed name in stats reports */
197 #endif
198 };
199
200 /*
201 * Private Inline-able.
202 */
203
204 #if ! ISC_MEM_TRACKLINES
205 #define ADD_TRACE(a, b, c, d, e)
206 #define DELETE_TRACE(a, b, c, d, e)
207 #else
208 #define ADD_TRACE(a, b, c, d, e) \
209 do { \
210 if ((isc_mem_debugging & (ISC_MEM_DEBUGTRACE | \
211 ISC_MEM_DEBUGRECORD)) != 0 && \
212 b != NULL) \
213 add_trace_entry(a, b, c, d, e); \
214 } while (0)
215 #define DELETE_TRACE(a, b, c, d, e) delete_trace_entry(a, b, c, d, e)
216
217 static void
218 print_active(isc__mem_t *ctx, FILE *out);
219
220 /*%
221 * The following can be either static or public, depending on build environment.
222 */
223
224 #ifdef BIND9
225 #define ISC_MEMFUNC_SCOPE
226 #else
227 #define ISC_MEMFUNC_SCOPE static
228 #endif
229
230 ISC_MEMFUNC_SCOPE isc_result_t
231 isc__mem_createx(size_t init_max_size, size_t target_size,
232 isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
233 isc_mem_t **ctxp);
234 ISC_MEMFUNC_SCOPE isc_result_t
235 isc__mem_createx2(size_t init_max_size, size_t target_size,
236 isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
237 isc_mem_t **ctxp, unsigned int flags);
238 ISC_MEMFUNC_SCOPE isc_result_t
239 isc__mem_create(size_t init_max_size, size_t target_size, isc_mem_t **ctxp);
240 ISC_MEMFUNC_SCOPE isc_result_t
241 isc__mem_create2(size_t init_max_size, size_t target_size,
242 isc_mem_t **ctxp, unsigned int flags);
243 ISC_MEMFUNC_SCOPE void
244 isc__mem_attach(isc_mem_t *source, isc_mem_t **targetp);
245 ISC_MEMFUNC_SCOPE void
246 isc__mem_detach(isc_mem_t **ctxp);
247 ISC_MEMFUNC_SCOPE void
248 isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG);
249 ISC_MEMFUNC_SCOPE void
250 isc__mem_destroy(isc_mem_t **ctxp);
251 ISC_MEMFUNC_SCOPE isc_result_t
252 isc__mem_ondestroy(isc_mem_t *ctx, isc_task_t *task, isc_event_t **event);
253 ISC_MEMFUNC_SCOPE void *
254 isc___mem_get(isc_mem_t *ctx, size_t size FLARG);
255 ISC_MEMFUNC_SCOPE void
256 isc___mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG);
257 ISC_MEMFUNC_SCOPE void
258 isc__mem_stats(isc_mem_t *ctx, FILE *out);
259 ISC_MEMFUNC_SCOPE void *
260 isc___mem_allocate(isc_mem_t *ctx, size_t size FLARG);
261 ISC_MEMFUNC_SCOPE void *
262 isc___mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG);
263 ISC_MEMFUNC_SCOPE void
264 isc___mem_free(isc_mem_t *ctx, void *ptr FLARG);
265 ISC_MEMFUNC_SCOPE char *
266 isc___mem_strdup(isc_mem_t *mctx, const char *s FLARG);
267 ISC_MEMFUNC_SCOPE void
268 isc__mem_setdestroycheck(isc_mem_t *ctx, isc_boolean_t flag);
269 ISC_MEMFUNC_SCOPE void
270 isc__mem_setquota(isc_mem_t *ctx, size_t quota);
271 ISC_MEMFUNC_SCOPE size_t
272 isc__mem_getquota(isc_mem_t *ctx);
273 ISC_MEMFUNC_SCOPE size_t
274 isc__mem_inuse(isc_mem_t *ctx);
275 ISC_MEMFUNC_SCOPE isc_boolean_t
276 isc__mem_isovermem(isc_mem_t *ctx);
277 ISC_MEMFUNC_SCOPE void
278 isc__mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg,
279 size_t hiwater, size_t lowater);
280 ISC_MEMFUNC_SCOPE void
281 isc__mem_waterack(isc_mem_t *ctx0, int flag);
282 ISC_MEMFUNC_SCOPE void
283 isc__mem_setname(isc_mem_t *ctx, const char *name, void *tag);
284 ISC_MEMFUNC_SCOPE const char *
285 isc__mem_getname(isc_mem_t *ctx);
286 ISC_MEMFUNC_SCOPE void *
287 isc__mem_gettag(isc_mem_t *ctx);
288 ISC_MEMFUNC_SCOPE isc_result_t
289 isc__mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp);
290 ISC_MEMFUNC_SCOPE void
291 isc__mempool_setname(isc_mempool_t *mpctx, const char *name);
292 ISC_MEMFUNC_SCOPE void
293 isc__mempool_destroy(isc_mempool_t **mpctxp);
294 ISC_MEMFUNC_SCOPE void
295 isc__mempool_associatelock(isc_mempool_t *mpctx, isc_mutex_t *lock);
296 ISC_MEMFUNC_SCOPE void *
297 isc___mempool_get(isc_mempool_t *mpctx FLARG);
298 ISC_MEMFUNC_SCOPE void
299 isc___mempool_put(isc_mempool_t *mpctx, void *mem FLARG);
300 ISC_MEMFUNC_SCOPE void
301 isc__mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit);
302 ISC_MEMFUNC_SCOPE unsigned int
303 isc__mempool_getfreemax(isc_mempool_t *mpctx);
304 ISC_MEMFUNC_SCOPE unsigned int
305 isc__mempool_getfreecount(isc_mempool_t *mpctx);
306 ISC_MEMFUNC_SCOPE void
307 isc__mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit);
308 ISC_MEMFUNC_SCOPE unsigned int
309 isc__mempool_getmaxalloc(isc_mempool_t *mpctx);
310 ISC_MEMFUNC_SCOPE unsigned int
311 isc__mempool_getallocated(isc_mempool_t *mpctx);
312 ISC_MEMFUNC_SCOPE void
313 isc__mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit);
314 ISC_MEMFUNC_SCOPE unsigned int
315 isc__mempool_getfillcount(isc_mempool_t *mpctx);
316 #ifdef BIND9
317 ISC_MEMFUNC_SCOPE void
318 isc__mem_printactive(isc_mem_t *ctx0, FILE *file);
319 ISC_MEMFUNC_SCOPE void
320 isc__mem_printallactive(FILE *file);
321 ISC_MEMFUNC_SCOPE void
322 isc__mem_checkdestroyed(FILE *file);
323 ISC_MEMFUNC_SCOPE unsigned int
324 isc__mem_references(isc_mem_t *ctx0);
325 #endif
326
327 static struct isc__memmethods {
328 isc_memmethods_t methods;
329
330 /*%
331 * The following are defined just for avoiding unused static functions.
332 */
333 #ifndef BIND9
334 void *createx, *create, *create2, *ondestroy, *stats,
335 *setquota, *getquota, *setname, *getname, *gettag;
336 #endif
337 } memmethods = {
338 {
339 isc__mem_attach,
340 isc__mem_detach,
341 isc__mem_destroy,
342 isc___mem_get,
343 isc___mem_put,
344 isc___mem_putanddetach,
345 isc___mem_allocate,
346 isc___mem_reallocate,
347 isc___mem_strdup,
348 isc___mem_free,
349 isc__mem_setdestroycheck,
350 isc__mem_setwater,
351 isc__mem_waterack,
352 isc__mem_inuse,
353 isc__mem_isovermem,
354 isc__mempool_create
355 }
356 #ifndef BIND9
357 ,
358 (void *)isc__mem_createx, (void *)isc__mem_create,
359 (void *)isc__mem_create2, (void *)isc__mem_ondestroy,
360 (void *)isc__mem_stats, (void *)isc__mem_setquota,
361 (void *)isc__mem_getquota, (void *)isc__mem_setname,
362 (void *)isc__mem_getname, (void *)isc__mem_gettag
363 #endif
364 };
365
366 static struct isc__mempoolmethods {
367 isc_mempoolmethods_t methods;
368
369 /*%
370 * The following are defined just for avoiding unused static functions.
371 */
372 #ifndef BIND9
373 void *getfreemax, *getfreecount, *getmaxalloc, *getfillcount;
374 #endif
375 } mempoolmethods = {
376 {
377 isc__mempool_destroy,
378 isc___mempool_get,
379 isc___mempool_put,
380 isc__mempool_getallocated,
381 isc__mempool_setmaxalloc,
382 isc__mempool_setfreemax,
383 isc__mempool_setname,
384 isc__mempool_associatelock,
385 isc__mempool_setfillcount
386 }
387 #ifndef BIND9
388 ,
389 (void *)isc__mempool_getfreemax, (void *)isc__mempool_getfreecount,
390 (void *)isc__mempool_getmaxalloc, (void *)isc__mempool_getfillcount
391 #endif
392 };
393
394 /*!
395 * mctx must be locked.
396 */
397 static inline void
add_trace_entry(isc__mem_t * mctx,const void * ptr,unsigned int size FLARG)398 add_trace_entry(isc__mem_t *mctx, const void *ptr, unsigned int size
399 FLARG)
400 {
401 debuglink_t *dl;
402 unsigned int i;
403 unsigned int 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,unsigned int size,const char * file,unsigned int line)458 delete_trace_entry(isc__mem_t *mctx, const void *ptr, unsigned int 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 memcpy(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 = 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 (size >= ctx->max_size || 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 (size == ctx->max_size || 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->total);
756 ctx->inuse -= size;
757 ctx->total -= size;
758 return;
759 }
760
761 #if ISC_MEM_FILL
762 #if ISC_MEM_CHECKOVERRUN
763 check_overrun(mem, size, new_size);
764 #endif
765 memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
766 #endif
767
768 /*
769 * The free list uses the "rounded-up" size "new_size".
770 */
771 ((element *)mem)->next = ctx->freelists[new_size];
772 ctx->freelists[new_size] = (element *)mem;
773
774 /*
775 * The stats[] uses the _actual_ "size" requested by the
776 * caller, with the caveat (in the code above) that "size" >= the
777 * max. size (max_size) ends up getting recorded as a call to
778 * max_size.
779 */
780 INSIST(ctx->stats[size].gets != 0U);
781 ctx->stats[size].gets--;
782 ctx->stats[new_size].freefrags++;
783 ctx->inuse -= new_size;
784 }
785
786 /*!
787 * Perform a malloc, doing memory filling and overrun detection as necessary.
788 */
789 static inline void *
mem_get(isc__mem_t * ctx,size_t size)790 mem_get(isc__mem_t *ctx, size_t size) {
791 char *ret;
792
793 #if ISC_MEM_CHECKOVERRUN
794 size += 1;
795 #endif
796
797 ret = (ctx->memalloc)(ctx->arg, size);
798 if (ret == NULL)
799 ctx->memalloc_failures++;
800
801 #if ISC_MEM_FILL
802 if (ret != NULL)
803 memset(ret, 0xbe, size); /* Mnemonic for "beef". */
804 #else
805 # if ISC_MEM_CHECKOVERRUN
806 if (ret != NULL)
807 ret[size-1] = 0xbe;
808 # endif
809 #endif
810
811 return (ret);
812 }
813
814 /*!
815 * Perform a free, doing memory filling and overrun detection as necessary.
816 */
817 static inline void
mem_put(isc__mem_t * ctx,void * mem,size_t size)818 mem_put(isc__mem_t *ctx, void *mem, size_t size) {
819 #if ISC_MEM_CHECKOVERRUN
820 INSIST(((unsigned char *)mem)[size] == 0xbe);
821 #endif
822 #if ISC_MEM_FILL
823 memset(mem, 0xde, size); /* Mnemonic for "dead". */
824 #else
825 UNUSED(size);
826 #endif
827 (ctx->memfree)(ctx->arg, mem);
828 }
829
830 /*!
831 * Update internal counters after a memory get.
832 */
833 static inline void
mem_getstats(isc__mem_t * ctx,size_t size)834 mem_getstats(isc__mem_t *ctx, size_t size) {
835 ctx->total += size;
836 ctx->inuse += size;
837
838 if (size > ctx->max_size) {
839 ctx->stats[ctx->max_size].gets++;
840 ctx->stats[ctx->max_size].totalgets++;
841 } else {
842 ctx->stats[size].gets++;
843 ctx->stats[size].totalgets++;
844 }
845 }
846
847 /*!
848 * Update internal counters after a memory put.
849 */
850 static inline void
mem_putstats(isc__mem_t * ctx,void * ptr,size_t size)851 mem_putstats(isc__mem_t *ctx, void *ptr, size_t size) {
852 UNUSED(ptr);
853
854 INSIST(ctx->inuse >= size);
855 ctx->inuse -= size;
856
857 if (size > ctx->max_size) {
858 INSIST(ctx->stats[ctx->max_size].gets > 0U);
859 ctx->stats[ctx->max_size].gets--;
860 } else {
861 INSIST(ctx->stats[size].gets > 0U);
862 ctx->stats[size].gets--;
863 }
864 }
865
866 /*
867 * Private.
868 */
869
870 static void *
default_memalloc(void * arg,size_t size)871 default_memalloc(void *arg, size_t size) {
872 UNUSED(arg);
873 if (size == 0U)
874 size = 1;
875 return (malloc(size));
876 }
877
878 static void
default_memfree(void * arg,void * ptr)879 default_memfree(void *arg, void *ptr) {
880 UNUSED(arg);
881 free(ptr);
882 }
883
884 static void
initialize_action(void)885 initialize_action(void) {
886 RUNTIME_CHECK(isc_mutex_init(&lock) == 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_MEMFLAG_DEFAULT));
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(&lock);
1015 ISC_LIST_INITANDAPPEND(contexts, ctx, link);
1016 UNLOCK(&lock);
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_MEMFLAG_DEFAULT));
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(&lock);
1061 ISC_LIST_UNLINK(contexts, ctx, link);
1062 totallost += ctx->inuse;
1063 UNLOCK(&lock);
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 ISC_MEM_TRACKLINES
1098 if (ctx->stats[i].gets != 0U)
1099 print_active(ctx, stderr);
1100 #endif
1101 INSIST(ctx->stats[i].gets == 0U);
1102 }
1103 }
1104
1105 (ctx->memfree)(ctx->arg, ctx->stats);
1106
1107 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1108 for (i = 0; i < ctx->basic_table_count; i++)
1109 (ctx->memfree)(ctx->arg, ctx->basic_table[i]);
1110 (ctx->memfree)(ctx->arg, ctx->freelists);
1111 if (ctx->basic_table != NULL)
1112 (ctx->memfree)(ctx->arg, ctx->basic_table);
1113 }
1114
1115 ondest = ctx->ondestroy;
1116
1117 if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
1118 DESTROYLOCK(&ctx->lock);
1119 (ctx->memfree)(ctx->arg, ctx);
1120
1121 isc_ondestroy_notify(&ondest, ctx);
1122 }
1123
1124 ISC_MEMFUNC_SCOPE void
isc__mem_attach(isc_mem_t * source0,isc_mem_t ** targetp)1125 isc__mem_attach(isc_mem_t *source0, isc_mem_t **targetp) {
1126 isc__mem_t *source = (isc__mem_t *)source0;
1127
1128 REQUIRE(VALID_CONTEXT(source));
1129 REQUIRE(targetp != NULL && *targetp == NULL);
1130
1131 MCTXLOCK(source, &source->lock);
1132 source->references++;
1133 MCTXUNLOCK(source, &source->lock);
1134
1135 *targetp = (isc_mem_t *)source;
1136 }
1137
1138 ISC_MEMFUNC_SCOPE void
isc__mem_detach(isc_mem_t ** ctxp)1139 isc__mem_detach(isc_mem_t **ctxp) {
1140 isc__mem_t *ctx;
1141 isc_boolean_t want_destroy = ISC_FALSE;
1142
1143 REQUIRE(ctxp != NULL);
1144 ctx = (isc__mem_t *)*ctxp;
1145 REQUIRE(VALID_CONTEXT(ctx));
1146
1147 MCTXLOCK(ctx, &ctx->lock);
1148 INSIST(ctx->references > 0);
1149 ctx->references--;
1150 if (ctx->references == 0)
1151 want_destroy = ISC_TRUE;
1152 MCTXUNLOCK(ctx, &ctx->lock);
1153
1154 if (want_destroy)
1155 destroy(ctx);
1156
1157 *ctxp = NULL;
1158 }
1159
1160 /*
1161 * isc_mem_putanddetach() is the equivalent of:
1162 *
1163 * mctx = NULL;
1164 * isc_mem_attach(ptr->mctx, &mctx);
1165 * isc_mem_detach(&ptr->mctx);
1166 * isc_mem_put(mctx, ptr, sizeof(*ptr);
1167 * isc_mem_detach(&mctx);
1168 */
1169
1170 ISC_MEMFUNC_SCOPE void
isc___mem_putanddetach(isc_mem_t ** ctxp,void * ptr,size_t size FLARG)1171 isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) {
1172 isc__mem_t *ctx;
1173 isc_boolean_t want_destroy = ISC_FALSE;
1174 size_info *si;
1175 size_t oldsize;
1176
1177 REQUIRE(ctxp != NULL);
1178 ctx = (isc__mem_t *)*ctxp;
1179 REQUIRE(VALID_CONTEXT(ctx));
1180 REQUIRE(ptr != NULL);
1181
1182 /*
1183 * Must be before mem_putunlocked() as ctxp is usually within
1184 * [ptr..ptr+size).
1185 */
1186 *ctxp = NULL;
1187
1188 if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
1189 if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
1190 si = &(((size_info *)ptr)[-1]);
1191 oldsize = si->u.size - ALIGNMENT_SIZE;
1192 if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1193 oldsize -= ALIGNMENT_SIZE;
1194 INSIST(oldsize == size);
1195 }
1196 isc_mem_free((isc_mem_t *)ctx, ptr);
1197
1198 MCTXLOCK(ctx, &ctx->lock);
1199 ctx->references--;
1200 if (ctx->references == 0)
1201 want_destroy = ISC_TRUE;
1202 MCTXUNLOCK(ctx, &ctx->lock);
1203 if (want_destroy)
1204 destroy(ctx);
1205
1206 return;
1207 }
1208
1209 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1210 MCTXLOCK(ctx, &ctx->lock);
1211 mem_putunlocked(ctx, ptr, size);
1212 } else {
1213 mem_put(ctx, ptr, size);
1214 MCTXLOCK(ctx, &ctx->lock);
1215 mem_putstats(ctx, ptr, size);
1216 }
1217
1218 DELETE_TRACE(ctx, ptr, size, file, line);
1219 INSIST(ctx->references > 0);
1220 ctx->references--;
1221 if (ctx->references == 0)
1222 want_destroy = ISC_TRUE;
1223
1224 MCTXUNLOCK(ctx, &ctx->lock);
1225
1226 if (want_destroy)
1227 destroy(ctx);
1228 }
1229
1230 ISC_MEMFUNC_SCOPE void
isc__mem_destroy(isc_mem_t ** ctxp)1231 isc__mem_destroy(isc_mem_t **ctxp) {
1232 isc__mem_t *ctx;
1233
1234 /*
1235 * This routine provides legacy support for callers who use mctxs
1236 * without attaching/detaching.
1237 */
1238
1239 REQUIRE(ctxp != NULL);
1240 ctx = (isc__mem_t *)*ctxp;
1241 REQUIRE(VALID_CONTEXT(ctx));
1242
1243 MCTXLOCK(ctx, &ctx->lock);
1244 #if ISC_MEM_TRACKLINES
1245 if (ctx->references != 1)
1246 print_active(ctx, stderr);
1247 #endif
1248 REQUIRE(ctx->references == 1);
1249 ctx->references--;
1250 MCTXUNLOCK(ctx, &ctx->lock);
1251
1252 destroy(ctx);
1253
1254 *ctxp = NULL;
1255 }
1256
1257 ISC_MEMFUNC_SCOPE isc_result_t
isc__mem_ondestroy(isc_mem_t * ctx0,isc_task_t * task,isc_event_t ** event)1258 isc__mem_ondestroy(isc_mem_t *ctx0, isc_task_t *task, isc_event_t **event) {
1259 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1260 isc_result_t res;
1261
1262 MCTXLOCK(ctx, &ctx->lock);
1263 res = isc_ondestroy_register(&ctx->ondestroy, task, event);
1264 MCTXUNLOCK(ctx, &ctx->lock);
1265
1266 return (res);
1267 }
1268
1269 ISC_MEMFUNC_SCOPE void *
isc___mem_get(isc_mem_t * ctx0,size_t size FLARG)1270 isc___mem_get(isc_mem_t *ctx0, size_t size FLARG) {
1271 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1272 void *ptr;
1273 isc_boolean_t call_water = ISC_FALSE;
1274
1275 REQUIRE(VALID_CONTEXT(ctx));
1276
1277 if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0)
1278 return (isc__mem_allocate(ctx0, size FLARG_PASS));
1279
1280 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1281 MCTXLOCK(ctx, &ctx->lock);
1282 ptr = mem_getunlocked(ctx, size);
1283 } else {
1284 ptr = mem_get(ctx, size);
1285 MCTXLOCK(ctx, &ctx->lock);
1286 if (ptr != NULL)
1287 mem_getstats(ctx, size);
1288 }
1289
1290 ADD_TRACE(ctx, ptr, size, file, line);
1291 if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1292 !ctx->is_overmem) {
1293 ctx->is_overmem = ISC_TRUE;
1294 }
1295 if (ctx->hi_water != 0U && !ctx->hi_called &&
1296 ctx->inuse > ctx->hi_water) {
1297 call_water = ISC_TRUE;
1298 }
1299 if (ctx->inuse > ctx->maxinuse) {
1300 ctx->maxinuse = ctx->inuse;
1301 if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
1302 (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
1303 fprintf(stderr, "maxinuse = %lu\n",
1304 (unsigned long)ctx->inuse);
1305 }
1306 MCTXUNLOCK(ctx, &ctx->lock);
1307
1308 if (call_water)
1309 (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
1310
1311 return (ptr);
1312 }
1313
1314 ISC_MEMFUNC_SCOPE void
isc___mem_put(isc_mem_t * ctx0,void * ptr,size_t size FLARG)1315 isc___mem_put(isc_mem_t *ctx0, void *ptr, size_t size FLARG) {
1316 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1317 isc_boolean_t call_water = ISC_FALSE;
1318 size_info *si;
1319 size_t oldsize;
1320
1321 REQUIRE(VALID_CONTEXT(ctx));
1322 REQUIRE(ptr != NULL);
1323
1324 if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
1325 if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
1326 si = &(((size_info *)ptr)[-1]);
1327 oldsize = si->u.size - ALIGNMENT_SIZE;
1328 if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1329 oldsize -= ALIGNMENT_SIZE;
1330 INSIST(oldsize == size);
1331 }
1332 isc_mem_free((isc_mem_t *)ctx, ptr);
1333 return;
1334 }
1335
1336 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1337 MCTXLOCK(ctx, &ctx->lock);
1338 mem_putunlocked(ctx, ptr, size);
1339 } else {
1340 mem_put(ctx, ptr, size);
1341 MCTXLOCK(ctx, &ctx->lock);
1342 mem_putstats(ctx, ptr, size);
1343 }
1344
1345 DELETE_TRACE(ctx, ptr, size, file, line);
1346
1347 /*
1348 * The check against ctx->lo_water == 0 is for the condition
1349 * when the context was pushed over hi_water but then had
1350 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1351 */
1352 if (ctx->is_overmem &&
1353 (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1354 ctx->is_overmem = ISC_FALSE;
1355 }
1356 if (ctx->hi_called &&
1357 (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1358 if (ctx->water != NULL)
1359 call_water = ISC_TRUE;
1360 }
1361 MCTXUNLOCK(ctx, &ctx->lock);
1362
1363 if (call_water)
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 fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
1417 ISC_MSG_NONE, "\tNone.\n"));
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 pool->name, (unsigned long) pool->size, pool->maxalloc,
1484 pool->allocated, pool->freecount, pool->freemax,
1485 pool->fillcount, pool->gets,
1486 (pool->lock == NULL ? "N" : "Y"));
1487 pool = ISC_LIST_NEXT(pool, link);
1488 }
1489
1490 #if ISC_MEM_TRACKLINES
1491 print_active(ctx, out);
1492 #endif
1493
1494 MCTXUNLOCK(ctx, &ctx->lock);
1495 }
1496
1497 /*
1498 * Replacements for malloc() and free() -- they implicitly remember the
1499 * size of the object allocated (with some additional overhead).
1500 */
1501
1502 static void *
isc__mem_allocateunlocked(isc_mem_t * ctx0,size_t size)1503 isc__mem_allocateunlocked(isc_mem_t *ctx0, size_t size) {
1504 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1505 size_info *si;
1506
1507 size += ALIGNMENT_SIZE;
1508 if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
1509 size += ALIGNMENT_SIZE;
1510
1511 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0)
1512 si = mem_getunlocked(ctx, size);
1513 else
1514 si = mem_get(ctx, size);
1515
1516 if (si == NULL)
1517 return (NULL);
1518 if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1519 si->u.ctx = ctx;
1520 si++;
1521 }
1522 si->u.size = size;
1523 return (&si[1]);
1524 }
1525
1526 ISC_MEMFUNC_SCOPE void *
isc___mem_allocate(isc_mem_t * ctx0,size_t size FLARG)1527 isc___mem_allocate(isc_mem_t *ctx0, size_t size FLARG) {
1528 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1529 size_info *si;
1530 isc_boolean_t call_water = ISC_FALSE;
1531
1532 REQUIRE(VALID_CONTEXT(ctx));
1533
1534 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1535 MCTXLOCK(ctx, &ctx->lock);
1536 si = isc__mem_allocateunlocked((isc_mem_t *)ctx, size);
1537 } else {
1538 si = isc__mem_allocateunlocked((isc_mem_t *)ctx, size);
1539 MCTXLOCK(ctx, &ctx->lock);
1540 if (si != NULL)
1541 mem_getstats(ctx, si[-1].u.size);
1542 }
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 copysize = oldsize > size ? size : oldsize;
1598 memcpy(new_ptr, ptr, copysize);
1599 isc__mem_free(ctx0, ptr FLARG_PASS);
1600 }
1601 } else if (ptr != NULL)
1602 isc__mem_free(ctx0, ptr FLARG_PASS);
1603
1604 return (new_ptr);
1605 }
1606
1607 ISC_MEMFUNC_SCOPE void
isc___mem_free(isc_mem_t * ctx0,void * ptr FLARG)1608 isc___mem_free(isc_mem_t *ctx0, void *ptr FLARG) {
1609 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1610 size_info *si;
1611 size_t size;
1612 isc_boolean_t call_water= ISC_FALSE;
1613
1614 REQUIRE(VALID_CONTEXT(ctx));
1615 REQUIRE(ptr != NULL);
1616
1617 if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
1618 si = &(((size_info *)ptr)[-2]);
1619 REQUIRE(si->u.ctx == ctx);
1620 size = si[1].u.size;
1621 } else {
1622 si = &(((size_info *)ptr)[-1]);
1623 size = si->u.size;
1624 }
1625
1626 if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1627 MCTXLOCK(ctx, &ctx->lock);
1628 mem_putunlocked(ctx, si, size);
1629 } else {
1630 mem_put(ctx, si, size);
1631 MCTXLOCK(ctx, &ctx->lock);
1632 mem_putstats(ctx, si, size);
1633 }
1634
1635 DELETE_TRACE(ctx, ptr, size, file, line);
1636
1637 /*
1638 * The check against ctx->lo_water == 0 is for the condition
1639 * when the context was pushed over hi_water but then had
1640 * isc_mem_setwater() called with 0 for hi_water and lo_water.
1641 */
1642 if (ctx->is_overmem &&
1643 (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1644 ctx->is_overmem = ISC_FALSE;
1645 }
1646
1647 if (ctx->hi_called &&
1648 (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
1649 ctx->hi_called = ISC_FALSE;
1650
1651 if (ctx->water != NULL)
1652 call_water = ISC_TRUE;
1653 }
1654 MCTXUNLOCK(ctx, &ctx->lock);
1655
1656 if (call_water)
1657 (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
1658 }
1659
1660
1661 /*
1662 * Other useful things.
1663 */
1664
1665 ISC_MEMFUNC_SCOPE char *
isc___mem_strdup(isc_mem_t * mctx0,const char * s FLARG)1666 isc___mem_strdup(isc_mem_t *mctx0, const char *s FLARG) {
1667 isc__mem_t *mctx = (isc__mem_t *)mctx0;
1668 size_t len;
1669 char *ns;
1670
1671 REQUIRE(VALID_CONTEXT(mctx));
1672 REQUIRE(s != NULL);
1673
1674 len = strlen(s);
1675
1676 ns = isc___mem_allocate((isc_mem_t *)mctx, len + 1 FLARG_PASS);
1677
1678 if (ns != NULL)
1679 strncpy(ns, s, len + 1);
1680
1681 return (ns);
1682 }
1683
1684 ISC_MEMFUNC_SCOPE void
isc__mem_setdestroycheck(isc_mem_t * ctx0,isc_boolean_t flag)1685 isc__mem_setdestroycheck(isc_mem_t *ctx0, isc_boolean_t flag) {
1686 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1687
1688 REQUIRE(VALID_CONTEXT(ctx));
1689 MCTXLOCK(ctx, &ctx->lock);
1690
1691 ctx->checkfree = flag;
1692
1693 MCTXUNLOCK(ctx, &ctx->lock);
1694 }
1695
1696 /*
1697 * Quotas
1698 */
1699
1700 ISC_MEMFUNC_SCOPE void
isc__mem_setquota(isc_mem_t * ctx0,size_t quota)1701 isc__mem_setquota(isc_mem_t *ctx0, size_t quota) {
1702 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1703
1704 REQUIRE(VALID_CONTEXT(ctx));
1705 MCTXLOCK(ctx, &ctx->lock);
1706
1707 ctx->quota = quota;
1708
1709 MCTXUNLOCK(ctx, &ctx->lock);
1710 }
1711
1712 ISC_MEMFUNC_SCOPE size_t
isc__mem_getquota(isc_mem_t * ctx0)1713 isc__mem_getquota(isc_mem_t *ctx0) {
1714 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1715 size_t quota;
1716
1717 REQUIRE(VALID_CONTEXT(ctx));
1718 MCTXLOCK(ctx, &ctx->lock);
1719
1720 quota = ctx->quota;
1721
1722 MCTXUNLOCK(ctx, &ctx->lock);
1723
1724 return (quota);
1725 }
1726
1727 ISC_MEMFUNC_SCOPE size_t
isc__mem_inuse(isc_mem_t * ctx0)1728 isc__mem_inuse(isc_mem_t *ctx0) {
1729 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1730 size_t inuse;
1731
1732 REQUIRE(VALID_CONTEXT(ctx));
1733 MCTXLOCK(ctx, &ctx->lock);
1734
1735 inuse = ctx->inuse;
1736
1737 MCTXUNLOCK(ctx, &ctx->lock);
1738
1739 return (inuse);
1740 }
1741
1742 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)1743 isc__mem_setwater(isc_mem_t *ctx0, isc_mem_water_t water, void *water_arg,
1744 size_t hiwater, size_t lowater)
1745 {
1746 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1747 isc_boolean_t callwater = ISC_FALSE;
1748 isc_mem_water_t oldwater;
1749 void *oldwater_arg;
1750
1751 REQUIRE(VALID_CONTEXT(ctx));
1752 REQUIRE(hiwater >= lowater);
1753
1754 MCTXLOCK(ctx, &ctx->lock);
1755 oldwater = ctx->water;
1756 oldwater_arg = ctx->water_arg;
1757 if (water == NULL) {
1758 callwater = ctx->hi_called;
1759 ctx->water = NULL;
1760 ctx->water_arg = NULL;
1761 ctx->hi_water = 0;
1762 ctx->lo_water = 0;
1763 ctx->hi_called = ISC_FALSE;
1764 } else {
1765 if (ctx->hi_called &&
1766 (ctx->water != water || ctx->water_arg != water_arg ||
1767 ctx->inuse < lowater || lowater == 0U))
1768 callwater = ISC_TRUE;
1769 ctx->water = water;
1770 ctx->water_arg = water_arg;
1771 ctx->hi_water = hiwater;
1772 ctx->lo_water = lowater;
1773 ctx->hi_called = ISC_FALSE;
1774 }
1775 MCTXUNLOCK(ctx, &ctx->lock);
1776
1777 if (callwater && oldwater != NULL)
1778 (oldwater)(oldwater_arg, ISC_MEM_LOWATER);
1779 }
1780
1781 ISC_MEMFUNC_SCOPE isc_boolean_t
isc__mem_isovermem(isc_mem_t * ctx0)1782 isc__mem_isovermem(isc_mem_t *ctx0) {
1783 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1784
1785 REQUIRE(VALID_CONTEXT(ctx));
1786
1787 /*
1788 * We don't bother to lock the context because 100% accuracy isn't
1789 * necessary (and even if we locked the context the returned value
1790 * could be different from the actual state when it's used anyway)
1791 */
1792 return (ctx->is_overmem);
1793 }
1794
1795 ISC_MEMFUNC_SCOPE void
isc__mem_setname(isc_mem_t * ctx0,const char * name,void * tag)1796 isc__mem_setname(isc_mem_t *ctx0, const char *name, void *tag) {
1797 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1798
1799 REQUIRE(VALID_CONTEXT(ctx));
1800
1801 LOCK(&ctx->lock);
1802 memset(ctx->name, 0, sizeof(ctx->name));
1803 strncpy(ctx->name, name, sizeof(ctx->name) - 1);
1804 ctx->tag = tag;
1805 UNLOCK(&ctx->lock);
1806 }
1807
1808 ISC_MEMFUNC_SCOPE const char *
isc__mem_getname(isc_mem_t * ctx0)1809 isc__mem_getname(isc_mem_t *ctx0) {
1810 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1811
1812 REQUIRE(VALID_CONTEXT(ctx));
1813
1814 return (ctx->name);
1815 }
1816
1817 ISC_MEMFUNC_SCOPE void *
isc__mem_gettag(isc_mem_t * ctx0)1818 isc__mem_gettag(isc_mem_t *ctx0) {
1819 isc__mem_t *ctx = (isc__mem_t *)ctx0;
1820
1821 REQUIRE(VALID_CONTEXT(ctx));
1822
1823 return (ctx->tag);
1824 }
1825
1826 /*
1827 * Memory pool stuff
1828 */
1829
1830 ISC_MEMFUNC_SCOPE isc_result_t
isc__mempool_create(isc_mem_t * mctx0,size_t size,isc_mempool_t ** mpctxp)1831 isc__mempool_create(isc_mem_t *mctx0, size_t size, isc_mempool_t **mpctxp) {
1832 isc__mem_t *mctx = (isc__mem_t *)mctx0;
1833 isc__mempool_t *mpctx;
1834
1835 REQUIRE(VALID_CONTEXT(mctx));
1836 REQUIRE(size > 0U);
1837 REQUIRE(mpctxp != NULL && *mpctxp == NULL);
1838
1839 /*
1840 * Allocate space for this pool, initialize values, and if all works
1841 * well, attach to the memory context.
1842 */
1843 mpctx = isc_mem_get((isc_mem_t *)mctx, sizeof(isc__mempool_t));
1844 if (mpctx == NULL)
1845 return (ISC_R_NOMEMORY);
1846
1847 mpctx->common.methods = (isc_mempoolmethods_t *)&mempoolmethods;
1848 mpctx->common.impmagic = MEMPOOL_MAGIC;
1849 mpctx->common.magic = ISCAPI_MPOOL_MAGIC;
1850 mpctx->lock = NULL;
1851 mpctx->mctx = mctx;
1852 mpctx->size = size;
1853 mpctx->maxalloc = UINT_MAX;
1854 mpctx->allocated = 0;
1855 mpctx->freecount = 0;
1856 mpctx->freemax = 1;
1857 mpctx->fillcount = 1;
1858 mpctx->gets = 0;
1859 #if ISC_MEMPOOL_NAMES
1860 mpctx->name[0] = 0;
1861 #endif
1862 mpctx->items = NULL;
1863
1864 *mpctxp = (isc_mempool_t *)mpctx;
1865
1866 MCTXLOCK(mctx, &mctx->lock);
1867 ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
1868 mctx->poolcnt++;
1869 MCTXUNLOCK(mctx, &mctx->lock);
1870
1871 return (ISC_R_SUCCESS);
1872 }
1873
1874 ISC_MEMFUNC_SCOPE void
isc__mempool_setname(isc_mempool_t * mpctx0,const char * name)1875 isc__mempool_setname(isc_mempool_t *mpctx0, const char *name) {
1876 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1877
1878 REQUIRE(name != NULL);
1879 REQUIRE(VALID_MEMPOOL(mpctx));
1880
1881 #if ISC_MEMPOOL_NAMES
1882 if (mpctx->lock != NULL)
1883 LOCK(mpctx->lock);
1884
1885 strncpy(mpctx->name, name, sizeof(mpctx->name) - 1);
1886 mpctx->name[sizeof(mpctx->name) - 1] = '\0';
1887
1888 if (mpctx->lock != NULL)
1889 UNLOCK(mpctx->lock);
1890 #else
1891 UNUSED(mpctx);
1892 UNUSED(name);
1893 #endif
1894 }
1895
1896 ISC_MEMFUNC_SCOPE void
isc__mempool_destroy(isc_mempool_t ** mpctxp)1897 isc__mempool_destroy(isc_mempool_t **mpctxp) {
1898 isc__mempool_t *mpctx;
1899 isc__mem_t *mctx;
1900 isc_mutex_t *lock;
1901 element *item;
1902
1903 REQUIRE(mpctxp != NULL);
1904 mpctx = (isc__mempool_t *)*mpctxp;
1905 REQUIRE(VALID_MEMPOOL(mpctx));
1906 #if ISC_MEMPOOL_NAMES
1907 if (mpctx->allocated > 0)
1908 UNEXPECTED_ERROR(__FILE__, __LINE__,
1909 "isc__mempool_destroy(): mempool %s "
1910 "leaked memory",
1911 mpctx->name);
1912 #endif
1913 REQUIRE(mpctx->allocated == 0);
1914
1915 mctx = mpctx->mctx;
1916
1917 lock = mpctx->lock;
1918
1919 if (lock != NULL)
1920 LOCK(lock);
1921
1922 /*
1923 * Return any items on the free list
1924 */
1925 MCTXLOCK(mctx, &mctx->lock);
1926 while (mpctx->items != NULL) {
1927 INSIST(mpctx->freecount > 0);
1928 mpctx->freecount--;
1929 item = mpctx->items;
1930 mpctx->items = item->next;
1931
1932 if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
1933 mem_putunlocked(mctx, item, mpctx->size);
1934 } else {
1935 mem_put(mctx, item, mpctx->size);
1936 mem_putstats(mctx, item, mpctx->size);
1937 }
1938 }
1939 MCTXUNLOCK(mctx, &mctx->lock);
1940
1941 /*
1942 * Remove our linked list entry from the memory context.
1943 */
1944 MCTXLOCK(mctx, &mctx->lock);
1945 ISC_LIST_UNLINK(mctx->pools, mpctx, link);
1946 mctx->poolcnt--;
1947 MCTXUNLOCK(mctx, &mctx->lock);
1948
1949 mpctx->common.impmagic = 0;
1950 mpctx->common.magic = 0;
1951
1952 isc_mem_put((isc_mem_t *)mpctx->mctx, mpctx, sizeof(isc__mempool_t));
1953
1954 if (lock != NULL)
1955 UNLOCK(lock);
1956
1957 *mpctxp = NULL;
1958 }
1959
1960 ISC_MEMFUNC_SCOPE void
isc__mempool_associatelock(isc_mempool_t * mpctx0,isc_mutex_t * lock)1961 isc__mempool_associatelock(isc_mempool_t *mpctx0, isc_mutex_t *lock) {
1962 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1963
1964 REQUIRE(VALID_MEMPOOL(mpctx));
1965 REQUIRE(mpctx->lock == NULL);
1966 REQUIRE(lock != NULL);
1967
1968 mpctx->lock = lock;
1969 }
1970
1971 ISC_MEMFUNC_SCOPE void *
isc___mempool_get(isc_mempool_t * mpctx0 FLARG)1972 isc___mempool_get(isc_mempool_t *mpctx0 FLARG) {
1973 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
1974 element *item;
1975 isc__mem_t *mctx;
1976 unsigned int i;
1977
1978 REQUIRE(VALID_MEMPOOL(mpctx));
1979
1980 mctx = mpctx->mctx;
1981
1982 if (mpctx->lock != NULL)
1983 LOCK(mpctx->lock);
1984
1985 /*
1986 * Don't let the caller go over quota
1987 */
1988 if (mpctx->allocated >= mpctx->maxalloc) {
1989 item = NULL;
1990 goto out;
1991 }
1992
1993 /*
1994 * if we have a free list item, return the first here
1995 */
1996 item = mpctx->items;
1997 if (item != NULL) {
1998 mpctx->items = item->next;
1999 INSIST(mpctx->freecount > 0);
2000 mpctx->freecount--;
2001 mpctx->gets++;
2002 mpctx->allocated++;
2003 goto out;
2004 }
2005
2006 /*
2007 * We need to dip into the well. Lock the memory context here and
2008 * fill up our free list.
2009 */
2010 MCTXLOCK(mctx, &mctx->lock);
2011 for (i = 0; i < mpctx->fillcount; i++) {
2012 if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2013 item = mem_getunlocked(mctx, mpctx->size);
2014 } else {
2015 item = mem_get(mctx, mpctx->size);
2016 if (item != NULL)
2017 mem_getstats(mctx, mpctx->size);
2018 }
2019 if (item == NULL)
2020 break;
2021 item->next = mpctx->items;
2022 mpctx->items = item;
2023 mpctx->freecount++;
2024 }
2025 MCTXUNLOCK(mctx, &mctx->lock);
2026
2027 /*
2028 * If we didn't get any items, return NULL.
2029 */
2030 item = mpctx->items;
2031 if (item == NULL)
2032 goto out;
2033
2034 mpctx->items = item->next;
2035 mpctx->freecount--;
2036 mpctx->gets++;
2037 mpctx->allocated++;
2038
2039 out:
2040 if (mpctx->lock != NULL)
2041 UNLOCK(mpctx->lock);
2042
2043 #if ISC_MEM_TRACKLINES
2044 if (item != NULL) {
2045 MCTXLOCK(mctx, &mctx->lock);
2046 ADD_TRACE(mctx, item, mpctx->size, file, line);
2047 MCTXUNLOCK(mctx, &mctx->lock);
2048 }
2049 #endif /* ISC_MEM_TRACKLINES */
2050
2051 return (item);
2052 }
2053
2054 ISC_MEMFUNC_SCOPE void
isc___mempool_put(isc_mempool_t * mpctx0,void * mem FLARG)2055 isc___mempool_put(isc_mempool_t *mpctx0, void *mem FLARG) {
2056 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2057 isc__mem_t *mctx;
2058 element *item;
2059
2060 REQUIRE(VALID_MEMPOOL(mpctx));
2061 REQUIRE(mem != NULL);
2062
2063 mctx = mpctx->mctx;
2064
2065 if (mpctx->lock != NULL)
2066 LOCK(mpctx->lock);
2067
2068 INSIST(mpctx->allocated > 0);
2069 mpctx->allocated--;
2070
2071 #if ISC_MEM_TRACKLINES
2072 MCTXLOCK(mctx, &mctx->lock);
2073 DELETE_TRACE(mctx, mem, mpctx->size, file, line);
2074 MCTXUNLOCK(mctx, &mctx->lock);
2075 #endif /* ISC_MEM_TRACKLINES */
2076
2077 /*
2078 * If our free list is full, return this to the mctx directly.
2079 */
2080 if (mpctx->freecount >= mpctx->freemax) {
2081 if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
2082 MCTXLOCK(mctx, &mctx->lock);
2083 mem_putunlocked(mctx, mem, mpctx->size);
2084 MCTXUNLOCK(mctx, &mctx->lock);
2085 } else {
2086 mem_put(mctx, mem, mpctx->size);
2087 MCTXLOCK(mctx, &mctx->lock);
2088 mem_putstats(mctx, mem, mpctx->size);
2089 MCTXUNLOCK(mctx, &mctx->lock);
2090 }
2091 if (mpctx->lock != NULL)
2092 UNLOCK(mpctx->lock);
2093 return;
2094 }
2095
2096 /*
2097 * Otherwise, attach it to our free list and bump the counter.
2098 */
2099 mpctx->freecount++;
2100 item = (element *)mem;
2101 item->next = mpctx->items;
2102 mpctx->items = item;
2103
2104 if (mpctx->lock != NULL)
2105 UNLOCK(mpctx->lock);
2106 }
2107
2108 /*
2109 * Quotas
2110 */
2111
2112 ISC_MEMFUNC_SCOPE void
isc__mempool_setfreemax(isc_mempool_t * mpctx0,unsigned int limit)2113 isc__mempool_setfreemax(isc_mempool_t *mpctx0, unsigned int limit) {
2114 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2115
2116 REQUIRE(VALID_MEMPOOL(mpctx));
2117
2118 if (mpctx->lock != NULL)
2119 LOCK(mpctx->lock);
2120
2121 mpctx->freemax = limit;
2122
2123 if (mpctx->lock != NULL)
2124 UNLOCK(mpctx->lock);
2125 }
2126
2127 ISC_MEMFUNC_SCOPE unsigned int
isc__mempool_getfreemax(isc_mempool_t * mpctx0)2128 isc__mempool_getfreemax(isc_mempool_t *mpctx0) {
2129 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2130 unsigned int freemax;
2131
2132 REQUIRE(VALID_MEMPOOL(mpctx));
2133
2134 if (mpctx->lock != NULL)
2135 LOCK(mpctx->lock);
2136
2137 freemax = mpctx->freemax;
2138
2139 if (mpctx->lock != NULL)
2140 UNLOCK(mpctx->lock);
2141
2142 return (freemax);
2143 }
2144
2145 ISC_MEMFUNC_SCOPE unsigned int
isc__mempool_getfreecount(isc_mempool_t * mpctx0)2146 isc__mempool_getfreecount(isc_mempool_t *mpctx0) {
2147 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2148 unsigned int freecount;
2149
2150 REQUIRE(VALID_MEMPOOL(mpctx));
2151
2152 if (mpctx->lock != NULL)
2153 LOCK(mpctx->lock);
2154
2155 freecount = mpctx->freecount;
2156
2157 if (mpctx->lock != NULL)
2158 UNLOCK(mpctx->lock);
2159
2160 return (freecount);
2161 }
2162
2163 ISC_MEMFUNC_SCOPE void
isc__mempool_setmaxalloc(isc_mempool_t * mpctx0,unsigned int limit)2164 isc__mempool_setmaxalloc(isc_mempool_t *mpctx0, unsigned int limit) {
2165 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2166
2167 REQUIRE(limit > 0);
2168
2169 REQUIRE(VALID_MEMPOOL(mpctx));
2170
2171 if (mpctx->lock != NULL)
2172 LOCK(mpctx->lock);
2173
2174 mpctx->maxalloc = limit;
2175
2176 if (mpctx->lock != NULL)
2177 UNLOCK(mpctx->lock);
2178 }
2179
2180 ISC_MEMFUNC_SCOPE unsigned int
isc__mempool_getmaxalloc(isc_mempool_t * mpctx0)2181 isc__mempool_getmaxalloc(isc_mempool_t *mpctx0) {
2182 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2183 unsigned int maxalloc;
2184
2185 REQUIRE(VALID_MEMPOOL(mpctx));
2186
2187 if (mpctx->lock != NULL)
2188 LOCK(mpctx->lock);
2189
2190 maxalloc = mpctx->maxalloc;
2191
2192 if (mpctx->lock != NULL)
2193 UNLOCK(mpctx->lock);
2194
2195 return (maxalloc);
2196 }
2197
2198 ISC_MEMFUNC_SCOPE unsigned int
isc__mempool_getallocated(isc_mempool_t * mpctx0)2199 isc__mempool_getallocated(isc_mempool_t *mpctx0) {
2200 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2201 unsigned int allocated;
2202
2203 REQUIRE(VALID_MEMPOOL(mpctx));
2204
2205 if (mpctx->lock != NULL)
2206 LOCK(mpctx->lock);
2207
2208 allocated = mpctx->allocated;
2209
2210 if (mpctx->lock != NULL)
2211 UNLOCK(mpctx->lock);
2212
2213 return (allocated);
2214 }
2215
2216 ISC_MEMFUNC_SCOPE void
isc__mempool_setfillcount(isc_mempool_t * mpctx0,unsigned int limit)2217 isc__mempool_setfillcount(isc_mempool_t *mpctx0, unsigned int limit) {
2218 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2219
2220 REQUIRE(limit > 0);
2221 REQUIRE(VALID_MEMPOOL(mpctx));
2222
2223 if (mpctx->lock != NULL)
2224 LOCK(mpctx->lock);
2225
2226 mpctx->fillcount = limit;
2227
2228 if (mpctx->lock != NULL)
2229 UNLOCK(mpctx->lock);
2230 }
2231
2232 ISC_MEMFUNC_SCOPE unsigned int
isc__mempool_getfillcount(isc_mempool_t * mpctx0)2233 isc__mempool_getfillcount(isc_mempool_t *mpctx0) {
2234 isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0;
2235
2236 unsigned int fillcount;
2237
2238 REQUIRE(VALID_MEMPOOL(mpctx));
2239
2240 if (mpctx->lock != NULL)
2241 LOCK(mpctx->lock);
2242
2243 fillcount = mpctx->fillcount;
2244
2245 if (mpctx->lock != NULL)
2246 UNLOCK(mpctx->lock);
2247
2248 return (fillcount);
2249 }
2250
2251 #ifdef USE_MEMIMPREGISTER
2252 isc_result_t
isc__mem_register()2253 isc__mem_register() {
2254 return (isc_mem_register(isc__mem_create2));
2255 }
2256 #endif
2257
2258 #ifdef BIND9
2259 ISC_MEMFUNC_SCOPE void
isc__mem_printactive(isc_mem_t * ctx0,FILE * file)2260 isc__mem_printactive(isc_mem_t *ctx0, FILE *file) {
2261 isc__mem_t *ctx = (isc__mem_t *)ctx0;
2262
2263 REQUIRE(VALID_CONTEXT(ctx));
2264 REQUIRE(file != NULL);
2265
2266 #if !ISC_MEM_TRACKLINES
2267 UNUSED(ctx);
2268 UNUSED(file);
2269 #else
2270 print_active(ctx, file);
2271 #endif
2272 }
2273
2274 ISC_MEMFUNC_SCOPE void
isc__mem_printallactive(FILE * file)2275 isc__mem_printallactive(FILE *file) {
2276 #if !ISC_MEM_TRACKLINES
2277 UNUSED(file);
2278 #else
2279 isc__mem_t *ctx;
2280
2281 RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2282
2283 LOCK(&lock);
2284 for (ctx = ISC_LIST_HEAD(contexts);
2285 ctx != NULL;
2286 ctx = ISC_LIST_NEXT(ctx, link)) {
2287 fprintf(file, "context: %p\n", ctx);
2288 print_active(ctx, file);
2289 }
2290 UNLOCK(&lock);
2291 #endif
2292 }
2293
2294 ISC_MEMFUNC_SCOPE void
isc__mem_checkdestroyed(FILE * file)2295 isc__mem_checkdestroyed(FILE *file) {
2296
2297 RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2298
2299 LOCK(&lock);
2300 if (!ISC_LIST_EMPTY(contexts)) {
2301 #if ISC_MEM_TRACKLINES
2302 isc__mem_t *ctx;
2303
2304 for (ctx = ISC_LIST_HEAD(contexts);
2305 ctx != NULL;
2306 ctx = ISC_LIST_NEXT(ctx, link)) {
2307 fprintf(file, "context: %p\n", ctx);
2308 print_active(ctx, file);
2309 }
2310 fflush(file);
2311 #endif
2312 INSIST(0);
2313 }
2314 UNLOCK(&lock);
2315 }
2316
2317 ISC_MEMFUNC_SCOPE unsigned int
isc_mem_references(isc_mem_t * ctx0)2318 isc_mem_references(isc_mem_t *ctx0) {
2319 isc__mem_t *ctx = (isc__mem_t *)ctx0;
2320 unsigned int references;
2321
2322 REQUIRE(VALID_CONTEXT(ctx));
2323
2324 MCTXLOCK(ctx, &ctx->lock);
2325 references = ctx->references;
2326 MCTXUNLOCK(ctx, &ctx->lock);
2327
2328 return (references);
2329 }
2330
2331 #ifdef HAVE_LIBXML2
2332
2333 typedef struct summarystat {
2334 isc_uint64_t total;
2335 isc_uint64_t inuse;
2336 isc_uint64_t blocksize;
2337 isc_uint64_t contextsize;
2338 } summarystat_t;
2339
2340 static void
renderctx(isc__mem_t * ctx,summarystat_t * summary,xmlTextWriterPtr writer)2341 renderctx(isc__mem_t *ctx, summarystat_t *summary, xmlTextWriterPtr writer) {
2342 REQUIRE(VALID_CONTEXT(ctx));
2343
2344 xmlTextWriterStartElement(writer, ISC_XMLCHAR "context");
2345
2346 xmlTextWriterStartElement(writer, ISC_XMLCHAR "id");
2347 xmlTextWriterWriteFormatString(writer, "%p", ctx);
2348 xmlTextWriterEndElement(writer); /* id */
2349
2350 if (ctx->name[0] != 0) {
2351 xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
2352 xmlTextWriterWriteFormatString(writer, "%s", ctx->name);
2353 xmlTextWriterEndElement(writer); /* name */
2354 }
2355
2356 REQUIRE(VALID_CONTEXT(ctx));
2357 MCTXLOCK(ctx, &ctx->lock);
2358
2359 summary->contextsize += sizeof(*ctx) +
2360 (ctx->max_size + 1) * sizeof(struct stats) +
2361 ctx->max_size * sizeof(element *) +
2362 ctx->basic_table_count * sizeof(char *);
2363 #if ISC_MEM_TRACKLINES
2364 if (ctx->debuglist != NULL) {
2365 summary->contextsize +=
2366 (ctx->max_size + 1) * sizeof(debuglist_t) +
2367 ctx->debuglistcnt * sizeof(debuglink_t);
2368 }
2369 #endif
2370 xmlTextWriterStartElement(writer, ISC_XMLCHAR "references");
2371 xmlTextWriterWriteFormatString(writer, "%d", ctx->references);
2372 xmlTextWriterEndElement(writer); /* references */
2373
2374 summary->total += ctx->total;
2375 xmlTextWriterStartElement(writer, ISC_XMLCHAR "total");
2376 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2377 (isc_uint64_t)ctx->total);
2378 xmlTextWriterEndElement(writer); /* total */
2379
2380 summary->inuse += ctx->inuse;
2381 xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse");
2382 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2383 (isc_uint64_t)ctx->inuse);
2384 xmlTextWriterEndElement(writer); /* inuse */
2385
2386 xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse");
2387 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2388 (isc_uint64_t)ctx->maxinuse);
2389 xmlTextWriterEndElement(writer); /* maxinuse */
2390
2391 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 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 xmlTextWriterWriteFormatString(writer, "%s", "-");
2403 xmlTextWriterEndElement(writer); /* blocksize */
2404
2405 xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools");
2406 xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt);
2407 xmlTextWriterEndElement(writer); /* pools */
2408 summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
2409
2410 xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater");
2411 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2412 (isc_uint64_t)ctx->hi_water);
2413 xmlTextWriterEndElement(writer); /* hiwater */
2414
2415 xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater");
2416 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2417 (isc_uint64_t)ctx->lo_water);
2418 xmlTextWriterEndElement(writer); /* lowater */
2419
2420 MCTXUNLOCK(ctx, &ctx->lock);
2421
2422 xmlTextWriterEndElement(writer); /* context */
2423 }
2424
2425 void
isc_mem_renderxml(xmlTextWriterPtr writer)2426 isc_mem_renderxml(xmlTextWriterPtr writer) {
2427 isc__mem_t *ctx;
2428 summarystat_t summary;
2429 isc_uint64_t lost;
2430
2431 memset(&summary, 0, sizeof(summary));
2432
2433 xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts");
2434
2435 RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
2436
2437 LOCK(&lock);
2438 lost = totallost;
2439 for (ctx = ISC_LIST_HEAD(contexts);
2440 ctx != NULL;
2441 ctx = ISC_LIST_NEXT(ctx, link)) {
2442 renderctx(ctx, &summary, writer);
2443 }
2444 UNLOCK(&lock);
2445
2446 xmlTextWriterEndElement(writer); /* contexts */
2447
2448 xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary");
2449
2450 xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse");
2451 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2452 summary.total);
2453 xmlTextWriterEndElement(writer); /* TotalUse */
2454
2455 xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse");
2456 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2457 summary.inuse);
2458 xmlTextWriterEndElement(writer); /* InUse */
2459
2460 xmlTextWriterStartElement(writer, ISC_XMLCHAR "BlockSize");
2461 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2462 summary.blocksize);
2463 xmlTextWriterEndElement(writer); /* BlockSize */
2464
2465 xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize");
2466 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2467 summary.contextsize);
2468 xmlTextWriterEndElement(writer); /* ContextSize */
2469
2470 xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost");
2471 xmlTextWriterWriteFormatString(writer, "%" ISC_PRINT_QUADFORMAT "u",
2472 lost);
2473 xmlTextWriterEndElement(writer); /* Lost */
2474
2475 xmlTextWriterEndElement(writer); /* summary */
2476 }
2477
2478 #endif /* HAVE_LIBXML2 */
2479 #endif /* BIND9 */
2480