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