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