mem.c revision 1.16 1 1.14 christos /* $NetBSD: mem.c,v 1.16 2024/09/22 00:14:08 christos Exp $ */
2 1.1 christos
3 1.1 christos /*
4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 1.1 christos *
6 1.12 christos * SPDX-License-Identifier: MPL-2.0
7 1.12 christos *
8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public
9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this
10 1.8 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 1.1 christos *
12 1.1 christos * See the COPYRIGHT file distributed with this work for additional
13 1.1 christos * information regarding copyright ownership.
14 1.1 christos */
15 1.1 christos
16 1.1 christos /*! \file */
17 1.1 christos
18 1.6 christos #include <errno.h>
19 1.3 christos #include <inttypes.h>
20 1.6 christos #include <limits.h>
21 1.3 christos #include <stdbool.h>
22 1.6 christos #include <stddef.h>
23 1.1 christos #include <stdio.h>
24 1.1 christos #include <stdlib.h>
25 1.1 christos
26 1.15 christos #include <isc/align.h>
27 1.3 christos #include <isc/hash.h>
28 1.1 christos #include <isc/magic.h>
29 1.1 christos #include <isc/mem.h>
30 1.3 christos #include <isc/mutex.h>
31 1.1 christos #include <isc/once.h>
32 1.15 christos #include <isc/os.h>
33 1.3 christos #include <isc/print.h>
34 1.3 christos #include <isc/refcount.h>
35 1.1 christos #include <isc/string.h>
36 1.15 christos #include <isc/types.h>
37 1.1 christos #include <isc/util.h>
38 1.6 christos
39 1.6 christos #ifdef HAVE_LIBXML2
40 1.6 christos #include <libxml/xmlwriter.h>
41 1.6 christos #define ISC_XMLCHAR (const xmlChar *)
42 1.6 christos #endif /* HAVE_LIBXML2 */
43 1.6 christos
44 1.6 christos #ifdef HAVE_JSON_C
45 1.6 christos #include <json_object.h>
46 1.6 christos #endif /* HAVE_JSON_C */
47 1.1 christos
48 1.15 christos /* On DragonFly BSD the header does not provide jemalloc API */
49 1.15 christos #if defined(HAVE_MALLOC_NP_H) && !defined(__DragonFly__)
50 1.15 christos #include <malloc_np.h>
51 1.15 christos #define JEMALLOC_API_SUPPORTED 1
52 1.15 christos #elif defined(HAVE_JEMALLOC)
53 1.15 christos #include <jemalloc/jemalloc.h>
54 1.15 christos #define JEMALLOC_API_SUPPORTED 1
55 1.15 christos
56 1.15 christos #if JEMALLOC_VERSION_MAJOR < 4
57 1.15 christos #define sdallocx(ptr, size, flags) dallocx(ptr, flags)
58 1.15 christos #define MALLOCX_TCACHE_NONE (0)
59 1.15 christos #endif /* JEMALLOC_VERSION_MAJOR < 4 */
60 1.15 christos
61 1.15 christos #else
62 1.15 christos #include "jemalloc_shim.h"
63 1.15 christos #endif
64 1.15 christos
65 1.3 christos #include "mem_p.h"
66 1.3 christos
67 1.6 christos #define MCTXLOCK(m) LOCK(&m->lock)
68 1.6 christos #define MCTXUNLOCK(m) UNLOCK(&m->lock)
69 1.1 christos
70 1.1 christos #ifndef ISC_MEM_DEBUGGING
71 1.1 christos #define ISC_MEM_DEBUGGING 0
72 1.6 christos #endif /* ifndef ISC_MEM_DEBUGGING */
73 1.15 christos unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
74 1.15 christos unsigned int isc_mem_defaultflags = ISC_MEMFLAG_DEFAULT;
75 1.15 christos
76 1.15 christos #define ISC_MEM_ILLEGAL_ARENA (UINT_MAX)
77 1.1 christos
78 1.16 christos volatile void *isc__mem_malloc = mallocx;
79 1.16 christos
80 1.1 christos /*
81 1.1 christos * Constants.
82 1.1 christos */
83 1.1 christos
84 1.15 christos #define ZERO_ALLOCATION_SIZE sizeof(void *)
85 1.15 christos #define ALIGNMENT 8U /*%< must be a power of 2 */
86 1.15 christos #define ALIGNMENT_SIZE sizeof(size_info)
87 1.15 christos #define DEBUG_TABLE_COUNT 512U
88 1.15 christos #define STATS_BUCKETS 512U
89 1.15 christos #define STATS_BUCKET_SIZE 32U
90 1.1 christos
91 1.1 christos /*
92 1.1 christos * Types.
93 1.1 christos */
94 1.1 christos #if ISC_MEM_TRACKLINES
95 1.1 christos typedef struct debuglink debuglink_t;
96 1.1 christos struct debuglink {
97 1.6 christos ISC_LINK(debuglink_t) link;
98 1.6 christos const void *ptr;
99 1.6 christos size_t size;
100 1.6 christos const char *file;
101 1.6 christos unsigned int line;
102 1.1 christos };
103 1.1 christos
104 1.6 christos typedef ISC_LIST(debuglink_t) debuglist_t;
105 1.1 christos
106 1.6 christos #define FLARG_PASS , file, line
107 1.6 christos #define FLARG , const char *file, unsigned int line
108 1.6 christos #else /* if ISC_MEM_TRACKLINES */
109 1.1 christos #define FLARG_PASS
110 1.1 christos #define FLARG
111 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
112 1.1 christos
113 1.1 christos typedef struct element element;
114 1.1 christos struct element {
115 1.6 christos element *next;
116 1.1 christos };
117 1.1 christos
118 1.1 christos struct stats {
119 1.15 christos atomic_size_t gets;
120 1.15 christos atomic_size_t totalgets;
121 1.1 christos };
122 1.1 christos
123 1.6 christos #define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C')
124 1.6 christos #define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC)
125 1.1 christos
126 1.1 christos /* List of all active memory contexts. */
127 1.1 christos
128 1.15 christos static ISC_LIST(isc_mem_t) contexts;
129 1.1 christos
130 1.10 christos static isc_once_t init_once = ISC_ONCE_INIT;
131 1.10 christos static isc_once_t shut_once = ISC_ONCE_INIT;
132 1.6 christos static isc_mutex_t contextslock;
133 1.1 christos
134 1.1 christos /*%
135 1.1 christos * Total size of lost memory due to a bug of external library.
136 1.1 christos * Locked by the global lock.
137 1.1 christos */
138 1.6 christos static uint64_t totallost;
139 1.6 christos
140 1.15 christos struct isc_mem {
141 1.15 christos unsigned int magic;
142 1.6 christos unsigned int flags;
143 1.15 christos unsigned int jemalloc_flags;
144 1.15 christos unsigned int jemalloc_arena;
145 1.6 christos isc_mutex_t lock;
146 1.6 christos bool checkfree;
147 1.15 christos struct stats stats[STATS_BUCKETS + 1];
148 1.6 christos isc_refcount_t references;
149 1.6 christos char name[16];
150 1.15 christos atomic_size_t total;
151 1.15 christos atomic_size_t inuse;
152 1.15 christos atomic_size_t maxinuse;
153 1.15 christos atomic_size_t malloced;
154 1.15 christos atomic_size_t maxmalloced;
155 1.15 christos atomic_bool hi_called;
156 1.15 christos atomic_bool is_overmem;
157 1.6 christos isc_mem_water_t water;
158 1.6 christos void *water_arg;
159 1.15 christos atomic_size_t hi_water;
160 1.15 christos atomic_size_t lo_water;
161 1.15 christos ISC_LIST(isc_mempool_t) pools;
162 1.6 christos unsigned int poolcnt;
163 1.1 christos
164 1.1 christos #if ISC_MEM_TRACKLINES
165 1.6 christos debuglist_t *debuglist;
166 1.6 christos size_t debuglistcnt;
167 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
168 1.1 christos
169 1.15 christos ISC_LINK(isc_mem_t) link;
170 1.1 christos };
171 1.1 christos
172 1.6 christos #define MEMPOOL_MAGIC ISC_MAGIC('M', 'E', 'M', 'p')
173 1.6 christos #define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
174 1.1 christos
175 1.15 christos struct isc_mempool {
176 1.1 christos /* always unlocked */
177 1.15 christos unsigned int magic;
178 1.15 christos isc_mem_t *mctx; /*%< our memory context */
179 1.15 christos ISC_LINK(isc_mempool_t) link; /*%< next pool in this mem context */
180 1.15 christos element *items; /*%< low water item list */
181 1.15 christos size_t size; /*%< size of each item on this pool */
182 1.15 christos size_t allocated; /*%< # of items currently given out */
183 1.15 christos size_t freecount; /*%< # of items on reserved list */
184 1.15 christos size_t freemax; /*%< # of items allowed on free list */
185 1.15 christos size_t fillcount; /*%< # of items to fetch on each fill */
186 1.1 christos /*%< Stats only. */
187 1.15 christos size_t gets; /*%< # of requests to this pool */
188 1.15 christos /*%< Debugging only. */
189 1.6 christos char name[16]; /*%< printed name in stats reports */
190 1.1 christos };
191 1.1 christos
192 1.1 christos /*
193 1.1 christos * Private Inline-able.
194 1.1 christos */
195 1.1 christos
196 1.6 christos #if !ISC_MEM_TRACKLINES
197 1.1 christos #define ADD_TRACE(a, b, c, d, e)
198 1.1 christos #define DELETE_TRACE(a, b, c, d, e)
199 1.1 christos #define ISC_MEMFUNC_SCOPE
200 1.6 christos #else /* if !ISC_MEM_TRACKLINES */
201 1.6 christos #define TRACE_OR_RECORD (ISC_MEM_DEBUGTRACE | ISC_MEM_DEBUGRECORD)
202 1.15 christos
203 1.15 christos #define SHOULD_TRACE_OR_RECORD(ptr) \
204 1.15 christos ((isc_mem_debugging & TRACE_OR_RECORD) != 0 && ptr != NULL)
205 1.15 christos
206 1.15 christos #define ADD_TRACE(a, b, c, d, e) \
207 1.15 christos if (SHOULD_TRACE_OR_RECORD(b)) { \
208 1.15 christos add_trace_entry(a, b, c, d, e); \
209 1.15 christos }
210 1.15 christos
211 1.15 christos #define DELETE_TRACE(a, b, c, d, e) \
212 1.15 christos if (SHOULD_TRACE_OR_RECORD(b)) { \
213 1.15 christos delete_trace_entry(a, b, c, d, e); \
214 1.15 christos }
215 1.1 christos
216 1.1 christos static void
217 1.15 christos print_active(isc_mem_t *ctx, FILE *out);
218 1.15 christos #endif /* ISC_MEM_TRACKLINES */
219 1.15 christos
220 1.15 christos static size_t
221 1.15 christos increment_malloced(isc_mem_t *ctx, size_t size) {
222 1.15 christos size_t malloced = atomic_fetch_add_relaxed(&ctx->malloced, size) + size;
223 1.15 christos size_t maxmalloced = atomic_load_relaxed(&ctx->maxmalloced);
224 1.1 christos
225 1.15 christos if (malloced > maxmalloced) {
226 1.15 christos atomic_compare_exchange_strong(&ctx->maxmalloced, &maxmalloced,
227 1.15 christos malloced);
228 1.15 christos }
229 1.15 christos
230 1.15 christos return (malloced);
231 1.15 christos }
232 1.1 christos
233 1.15 christos static size_t
234 1.15 christos decrement_malloced(isc_mem_t *ctx, size_t size) {
235 1.15 christos size_t malloced = atomic_fetch_sub_relaxed(&ctx->malloced, size) - size;
236 1.1 christos
237 1.15 christos return (malloced);
238 1.15 christos }
239 1.1 christos
240 1.1 christos #if ISC_MEM_TRACKLINES
241 1.1 christos /*!
242 1.15 christos * mctx must not be locked.
243 1.1 christos */
244 1.1 christos static void
245 1.15 christos add_trace_entry(isc_mem_t *mctx, const void *ptr, size_t size FLARG) {
246 1.15 christos debuglink_t *dl = NULL;
247 1.3 christos uint32_t hash;
248 1.3 christos uint32_t idx;
249 1.1 christos
250 1.15 christos MCTXLOCK(mctx);
251 1.15 christos
252 1.4 christos if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
253 1.4 christos fprintf(stderr, "add %p size %zu file %s line %u mctx %p\n",
254 1.1 christos ptr, size, file, line, mctx);
255 1.4 christos }
256 1.1 christos
257 1.6 christos if (mctx->debuglist == NULL) {
258 1.15 christos goto unlock;
259 1.6 christos }
260 1.1 christos
261 1.6 christos #ifdef __COVERITY__
262 1.6 christos /*
263 1.6 christos * Use simple conversion from pointer to hash to avoid
264 1.6 christos * tainting 'ptr' due to byte swap in isc_hash_function.
265 1.6 christos */
266 1.6 christos hash = (uintptr_t)ptr >> 3;
267 1.6 christos #else
268 1.5 christos hash = isc_hash_function(&ptr, sizeof(ptr), true);
269 1.6 christos #endif
270 1.1 christos idx = hash % DEBUG_TABLE_COUNT;
271 1.1 christos
272 1.15 christos dl = mallocx(sizeof(*dl), mctx->jemalloc_flags);
273 1.1 christos INSIST(dl != NULL);
274 1.15 christos increment_malloced(mctx, sizeof(*dl));
275 1.1 christos
276 1.1 christos ISC_LINK_INIT(dl, link);
277 1.1 christos dl->ptr = ptr;
278 1.1 christos dl->size = size;
279 1.1 christos dl->file = file;
280 1.1 christos dl->line = line;
281 1.1 christos
282 1.1 christos ISC_LIST_PREPEND(mctx->debuglist[idx], dl, link);
283 1.1 christos mctx->debuglistcnt++;
284 1.15 christos unlock:
285 1.15 christos MCTXUNLOCK(mctx);
286 1.1 christos }
287 1.1 christos
288 1.1 christos static void
289 1.15 christos delete_trace_entry(isc_mem_t *mctx, const void *ptr, size_t size,
290 1.6 christos const char *file, unsigned int line) {
291 1.15 christos debuglink_t *dl = NULL;
292 1.3 christos uint32_t hash;
293 1.3 christos uint32_t idx;
294 1.1 christos
295 1.15 christos MCTXLOCK(mctx);
296 1.15 christos
297 1.4 christos if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
298 1.4 christos fprintf(stderr, "del %p size %zu file %s line %u mctx %p\n",
299 1.1 christos ptr, size, file, line, mctx);
300 1.4 christos }
301 1.1 christos
302 1.6 christos if (mctx->debuglist == NULL) {
303 1.15 christos goto unlock;
304 1.6 christos }
305 1.1 christos
306 1.6 christos #ifdef __COVERITY__
307 1.6 christos /*
308 1.6 christos * Use simple conversion from pointer to hash to avoid
309 1.6 christos * tainting 'ptr' due to byte swap in isc_hash_function.
310 1.6 christos */
311 1.6 christos hash = (uintptr_t)ptr >> 3;
312 1.6 christos #else
313 1.5 christos hash = isc_hash_function(&ptr, sizeof(ptr), true);
314 1.6 christos #endif
315 1.1 christos idx = hash % DEBUG_TABLE_COUNT;
316 1.1 christos
317 1.1 christos dl = ISC_LIST_HEAD(mctx->debuglist[idx]);
318 1.15 christos while (dl != NULL) {
319 1.15 christos if (dl->ptr == ptr) {
320 1.1 christos ISC_LIST_UNLINK(mctx->debuglist[idx], dl, link);
321 1.15 christos decrement_malloced(mctx, sizeof(*dl));
322 1.15 christos sdallocx(dl, sizeof(*dl), mctx->jemalloc_flags);
323 1.15 christos goto unlock;
324 1.1 christos }
325 1.1 christos dl = ISC_LIST_NEXT(dl, link);
326 1.1 christos }
327 1.1 christos
328 1.1 christos /*
329 1.1 christos * If we get here, we didn't find the item on the list. We're
330 1.1 christos * screwed.
331 1.1 christos */
332 1.12 christos UNREACHABLE();
333 1.15 christos unlock:
334 1.15 christos MCTXUNLOCK(mctx);
335 1.1 christos }
336 1.1 christos #endif /* ISC_MEM_TRACKLINES */
337 1.1 christos
338 1.15 christos #define ADJUST_ZERO_ALLOCATION_SIZE(s) \
339 1.15 christos if (s == 0) { \
340 1.15 christos s = ZERO_ALLOCATION_SIZE; \
341 1.6 christos }
342 1.1 christos
343 1.15 christos #define MEM_ALIGN(a) ((a) ? MALLOCX_ALIGN(a) : 0)
344 1.1 christos
345 1.15 christos /*!
346 1.15 christos * Perform a malloc, doing memory filling and overrun detection as necessary.
347 1.15 christos */
348 1.12 christos static void *
349 1.15 christos mem_get(isc_mem_t *ctx, size_t size, int flags) {
350 1.15 christos char *ret = NULL;
351 1.1 christos
352 1.15 christos ADJUST_ZERO_ALLOCATION_SIZE(size);
353 1.1 christos
354 1.15 christos ret = mallocx(size, flags | ctx->jemalloc_flags);
355 1.15 christos INSIST(ret != NULL);
356 1.1 christos
357 1.15 christos if ((ctx->flags & ISC_MEMFLAG_FILL) != 0) {
358 1.15 christos memset(ret, 0xbe, size); /* Mnemonic for "beef". */
359 1.6 christos }
360 1.1 christos
361 1.1 christos return (ret);
362 1.1 christos }
363 1.1 christos
364 1.15 christos /*!
365 1.15 christos * Perform a free, doing memory filling and overrun detection as necessary.
366 1.15 christos */
367 1.15 christos /* coverity[+free : arg-1] */
368 1.12 christos static void
369 1.15 christos mem_put(isc_mem_t *ctx, void *mem, size_t size, int flags) {
370 1.15 christos ADJUST_ZERO_ALLOCATION_SIZE(size);
371 1.1 christos
372 1.15 christos if ((ctx->flags & ISC_MEMFLAG_FILL) != 0) {
373 1.15 christos memset(mem, 0xde, size); /* Mnemonic for "dead". */
374 1.1 christos }
375 1.15 christos sdallocx(mem, size, flags | ctx->jemalloc_flags);
376 1.1 christos }
377 1.1 christos
378 1.15 christos static void *
379 1.15 christos mem_realloc(isc_mem_t *ctx, void *old_ptr, size_t old_size, size_t new_size,
380 1.15 christos int flags) {
381 1.15 christos void *new_ptr = NULL;
382 1.1 christos
383 1.15 christos ADJUST_ZERO_ALLOCATION_SIZE(new_size);
384 1.1 christos
385 1.15 christos new_ptr = rallocx(old_ptr, new_size, flags | ctx->jemalloc_flags);
386 1.15 christos INSIST(new_ptr != NULL);
387 1.1 christos
388 1.15 christos if ((ctx->flags & ISC_MEMFLAG_FILL) != 0) {
389 1.15 christos ssize_t diff_size = new_size - old_size;
390 1.15 christos void *diff_ptr = (uint8_t *)new_ptr + old_size;
391 1.15 christos if (diff_size > 0) {
392 1.15 christos /* Mnemonic for "beef". */
393 1.15 christos memset(diff_ptr, 0xbe, diff_size);
394 1.6 christos }
395 1.1 christos }
396 1.1 christos
397 1.15 christos return (new_ptr);
398 1.1 christos }
399 1.1 christos
400 1.15 christos #define stats_bucket(ctx, size) \
401 1.15 christos ((size / STATS_BUCKET_SIZE) >= STATS_BUCKETS \
402 1.15 christos ? &ctx->stats[STATS_BUCKETS] \
403 1.15 christos : &ctx->stats[size / STATS_BUCKET_SIZE])
404 1.1 christos
405 1.1 christos /*!
406 1.1 christos * Update internal counters after a memory get.
407 1.1 christos */
408 1.12 christos static void
409 1.15 christos mem_getstats(isc_mem_t *ctx, size_t size) {
410 1.15 christos struct stats *stats = stats_bucket(ctx, size);
411 1.15 christos
412 1.15 christos atomic_fetch_add_relaxed(&ctx->total, size);
413 1.15 christos atomic_fetch_add_release(&ctx->inuse, size);
414 1.15 christos
415 1.15 christos atomic_fetch_add_relaxed(&stats->gets, 1);
416 1.15 christos atomic_fetch_add_relaxed(&stats->totalgets, 1);
417 1.1 christos
418 1.15 christos increment_malloced(ctx, size);
419 1.1 christos }
420 1.1 christos
421 1.1 christos /*!
422 1.1 christos * Update internal counters after a memory put.
423 1.1 christos */
424 1.12 christos static void
425 1.15 christos mem_putstats(isc_mem_t *ctx, void *ptr, size_t size) {
426 1.15 christos struct stats *stats = stats_bucket(ctx, size);
427 1.15 christos atomic_size_t s, g;
428 1.15 christos
429 1.1 christos UNUSED(ptr);
430 1.1 christos
431 1.15 christos s = atomic_fetch_sub_release(&ctx->inuse, size);
432 1.15 christos INSIST(s >= size);
433 1.15 christos
434 1.15 christos g = atomic_fetch_sub_release(&stats->gets, 1);
435 1.15 christos INSIST(g >= 1);
436 1.1 christos
437 1.15 christos decrement_malloced(ctx, size);
438 1.1 christos }
439 1.1 christos
440 1.1 christos /*
441 1.1 christos * Private.
442 1.1 christos */
443 1.1 christos
444 1.15 christos static bool
445 1.15 christos mem_jemalloc_arena_create(unsigned int *pnew_arenano) {
446 1.15 christos REQUIRE(pnew_arenano != NULL);
447 1.3 christos
448 1.15 christos #if defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4
449 1.15 christos unsigned int arenano = 0;
450 1.15 christos size_t len = sizeof(arenano);
451 1.15 christos int res = 0;
452 1.3 christos
453 1.15 christos res = mallctl("arenas.create", &arenano, &len, NULL, 0);
454 1.15 christos if (res != 0) {
455 1.15 christos return (false);
456 1.15 christos }
457 1.3 christos
458 1.15 christos *pnew_arenano = arenano;
459 1.3 christos
460 1.15 christos return (true);
461 1.15 christos #else
462 1.15 christos *pnew_arenano = ISC_MEM_ILLEGAL_ARENA;
463 1.15 christos return (true);
464 1.15 christos #endif /* defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4 */
465 1.1 christos }
466 1.1 christos
467 1.15 christos static bool
468 1.15 christos mem_jemalloc_arena_destroy(unsigned int arenano) {
469 1.15 christos #if defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4
470 1.15 christos int res = 0;
471 1.15 christos char buf[256] = { 0 };
472 1.15 christos
473 1.15 christos (void)snprintf(buf, sizeof(buf), "arena.%u.destroy", arenano);
474 1.15 christos res = mallctl(buf, NULL, NULL, NULL, 0);
475 1.15 christos if (res != 0) {
476 1.15 christos return (false);
477 1.15 christos }
478 1.15 christos
479 1.15 christos return (true);
480 1.15 christos #else
481 1.15 christos UNUSED(arenano);
482 1.15 christos return (true);
483 1.15 christos #endif /* defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4 */
484 1.1 christos }
485 1.1 christos
486 1.1 christos static void
487 1.10 christos mem_initialize(void) {
488 1.3 christos isc_mutex_init(&contextslock);
489 1.1 christos ISC_LIST_INIT(contexts);
490 1.1 christos totallost = 0;
491 1.1 christos }
492 1.1 christos
493 1.10 christos void
494 1.10 christos isc__mem_initialize(void) {
495 1.10 christos RUNTIME_CHECK(isc_once_do(&init_once, mem_initialize) == ISC_R_SUCCESS);
496 1.10 christos }
497 1.10 christos
498 1.10 christos static void
499 1.10 christos mem_shutdown(void) {
500 1.10 christos isc__mem_checkdestroyed();
501 1.10 christos
502 1.10 christos isc_mutex_destroy(&contextslock);
503 1.10 christos }
504 1.10 christos
505 1.10 christos void
506 1.10 christos isc__mem_shutdown(void) {
507 1.10 christos RUNTIME_CHECK(isc_once_do(&shut_once, mem_shutdown) == ISC_R_SUCCESS);
508 1.10 christos }
509 1.10 christos
510 1.6 christos static void
511 1.15 christos mem_create(isc_mem_t **ctxp, unsigned int flags, unsigned int jemalloc_flags) {
512 1.15 christos isc_mem_t *ctx = NULL;
513 1.15 christos
514 1.6 christos REQUIRE(ctxp != NULL && *ctxp == NULL);
515 1.10 christos
516 1.15 christos ctx = mallocx(sizeof(*ctx),
517 1.15 christos MALLOCX_ALIGN(isc_os_cacheline()) | jemalloc_flags);
518 1.15 christos INSIST(ctx != NULL);
519 1.15 christos
520 1.15 christos *ctx = (isc_mem_t){
521 1.15 christos .magic = MEM_MAGIC,
522 1.15 christos .flags = flags,
523 1.15 christos .jemalloc_flags = jemalloc_flags,
524 1.15 christos .jemalloc_arena = ISC_MEM_ILLEGAL_ARENA,
525 1.15 christos .checkfree = true,
526 1.15 christos };
527 1.1 christos
528 1.6 christos isc_mutex_init(&ctx->lock);
529 1.15 christos isc_refcount_init(&ctx->references, 1);
530 1.1 christos
531 1.15 christos atomic_init(&ctx->total, 0);
532 1.15 christos atomic_init(&ctx->inuse, 0);
533 1.15 christos atomic_init(&ctx->maxinuse, 0);
534 1.15 christos atomic_init(&ctx->malloced, sizeof(*ctx));
535 1.15 christos atomic_init(&ctx->maxmalloced, sizeof(*ctx));
536 1.15 christos atomic_init(&ctx->hi_water, 0);
537 1.15 christos atomic_init(&ctx->lo_water, 0);
538 1.15 christos atomic_init(&ctx->hi_called, false);
539 1.15 christos atomic_init(&ctx->is_overmem, false);
540 1.15 christos
541 1.15 christos for (size_t i = 0; i < STATS_BUCKETS + 1; i++) {
542 1.15 christos atomic_init(&ctx->stats[i].gets, 0);
543 1.15 christos atomic_init(&ctx->stats[i].totalgets, 0);
544 1.15 christos }
545 1.1 christos ISC_LIST_INIT(ctx->pools);
546 1.1 christos
547 1.1 christos #if ISC_MEM_TRACKLINES
548 1.15 christos if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
549 1.1 christos unsigned int i;
550 1.1 christos
551 1.15 christos ctx->debuglist =
552 1.15 christos mallocx((DEBUG_TABLE_COUNT * sizeof(debuglist_t)),
553 1.15 christos ctx->jemalloc_flags);
554 1.15 christos INSIST(ctx->debuglist != NULL);
555 1.15 christos
556 1.6 christos for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
557 1.1 christos ISC_LIST_INIT(ctx->debuglist[i]);
558 1.6 christos }
559 1.15 christos increment_malloced(ctx,
560 1.15 christos DEBUG_TABLE_COUNT * sizeof(debuglist_t));
561 1.1 christos }
562 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
563 1.1 christos
564 1.1 christos LOCK(&contextslock);
565 1.1 christos ISC_LIST_INITANDAPPEND(contexts, ctx, link);
566 1.1 christos UNLOCK(&contextslock);
567 1.1 christos
568 1.15 christos *ctxp = ctx;
569 1.6 christos }
570 1.3 christos
571 1.6 christos /*
572 1.6 christos * Public.
573 1.6 christos */
574 1.1 christos
575 1.1 christos static void
576 1.15 christos destroy(isc_mem_t *ctx) {
577 1.1 christos unsigned int i;
578 1.15 christos size_t malloced;
579 1.15 christos unsigned int arena_no;
580 1.1 christos
581 1.1 christos LOCK(&contextslock);
582 1.1 christos ISC_LIST_UNLINK(contexts, ctx, link);
583 1.15 christos totallost += isc_mem_inuse(ctx);
584 1.1 christos UNLOCK(&contextslock);
585 1.1 christos
586 1.15 christos ctx->magic = 0;
587 1.15 christos
588 1.15 christos arena_no = ctx->jemalloc_arena;
589 1.1 christos
590 1.1 christos INSIST(ISC_LIST_EMPTY(ctx->pools));
591 1.1 christos
592 1.1 christos #if ISC_MEM_TRACKLINES
593 1.15 christos if (ctx->debuglist != NULL) {
594 1.1 christos debuglink_t *dl;
595 1.6 christos for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
596 1.6 christos for (dl = ISC_LIST_HEAD(ctx->debuglist[i]); dl != NULL;
597 1.6 christos dl = ISC_LIST_HEAD(ctx->debuglist[i]))
598 1.6 christos {
599 1.6 christos if (ctx->checkfree && dl->ptr != NULL) {
600 1.1 christos print_active(ctx, stderr);
601 1.6 christos }
602 1.6 christos INSIST(!ctx->checkfree || dl->ptr == NULL);
603 1.1 christos
604 1.6 christos ISC_LIST_UNLINK(ctx->debuglist[i], dl, link);
605 1.15 christos sdallocx(dl, sizeof(*dl), ctx->jemalloc_flags);
606 1.15 christos decrement_malloced(ctx, sizeof(*dl));
607 1.1 christos }
608 1.6 christos }
609 1.1 christos
610 1.15 christos sdallocx(ctx->debuglist,
611 1.15 christos (DEBUG_TABLE_COUNT * sizeof(debuglist_t)),
612 1.15 christos ctx->jemalloc_flags);
613 1.15 christos decrement_malloced(ctx,
614 1.15 christos DEBUG_TABLE_COUNT * sizeof(debuglist_t));
615 1.1 christos }
616 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
617 1.1 christos
618 1.1 christos if (ctx->checkfree) {
619 1.15 christos for (i = 0; i <= STATS_BUCKETS; i++) {
620 1.15 christos struct stats *stats = &ctx->stats[i];
621 1.15 christos size_t gets = atomic_load_acquire(&stats->gets);
622 1.15 christos if (gets != 0U) {
623 1.1 christos fprintf(stderr,
624 1.1 christos "Failing assertion due to probable "
625 1.1 christos "leaked memory in context %p (\"%s\") "
626 1.15 christos "(stats[%u].gets == %zu).\n",
627 1.15 christos ctx, ctx->name, i, gets);
628 1.1 christos #if ISC_MEM_TRACKLINES
629 1.1 christos print_active(ctx, stderr);
630 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
631 1.15 christos INSIST(gets == 0U);
632 1.1 christos }
633 1.1 christos }
634 1.1 christos }
635 1.1 christos
636 1.15 christos isc_mutex_destroy(&ctx->lock);
637 1.15 christos
638 1.15 christos malloced = decrement_malloced(ctx, sizeof(*ctx));
639 1.1 christos
640 1.15 christos if (ctx->checkfree) {
641 1.15 christos INSIST(malloced == 0);
642 1.1 christos }
643 1.15 christos sdallocx(ctx, sizeof(*ctx),
644 1.15 christos MALLOCX_ALIGN(isc_os_cacheline()) | ctx->jemalloc_flags);
645 1.1 christos
646 1.15 christos if (arena_no != ISC_MEM_ILLEGAL_ARENA) {
647 1.15 christos RUNTIME_CHECK(mem_jemalloc_arena_destroy(arena_no) == true);
648 1.6 christos }
649 1.1 christos }
650 1.1 christos
651 1.1 christos void
652 1.15 christos isc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) {
653 1.15 christos REQUIRE(VALID_CONTEXT(source));
654 1.6 christos REQUIRE(targetp != NULL && *targetp == NULL);
655 1.6 christos
656 1.3 christos isc_refcount_increment(&source->references);
657 1.1 christos
658 1.15 christos *targetp = source;
659 1.1 christos }
660 1.1 christos
661 1.1 christos void
662 1.15 christos isc__mem_detach(isc_mem_t **ctxp FLARG) {
663 1.15 christos isc_mem_t *ctx = NULL;
664 1.15 christos
665 1.3 christos REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
666 1.6 christos
667 1.15 christos ctx = *ctxp;
668 1.3 christos *ctxp = NULL;
669 1.1 christos
670 1.3 christos if (isc_refcount_decrement(&ctx->references) == 1) {
671 1.3 christos isc_refcount_destroy(&ctx->references);
672 1.15 christos #if ISC_MEM_TRACKLINES
673 1.15 christos if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
674 1.15 christos fprintf(stderr, "destroy mctx %p file %s line %u\n",
675 1.15 christos ctx, file, line);
676 1.15 christos }
677 1.15 christos #endif
678 1.1 christos destroy(ctx);
679 1.3 christos }
680 1.1 christos }
681 1.1 christos
682 1.1 christos /*
683 1.1 christos * isc_mem_putanddetach() is the equivalent of:
684 1.1 christos *
685 1.1 christos * mctx = NULL;
686 1.1 christos * isc_mem_attach(ptr->mctx, &mctx);
687 1.1 christos * isc_mem_detach(&ptr->mctx);
688 1.1 christos * isc_mem_put(mctx, ptr, sizeof(*ptr);
689 1.1 christos * isc_mem_detach(&mctx);
690 1.1 christos */
691 1.1 christos
692 1.1 christos void
693 1.15 christos isc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size,
694 1.15 christos size_t alignment FLARG) {
695 1.15 christos isc_mem_t *ctx = NULL;
696 1.15 christos
697 1.3 christos REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
698 1.1 christos REQUIRE(ptr != NULL);
699 1.15 christos REQUIRE(size != 0);
700 1.6 christos
701 1.15 christos ctx = *ctxp;
702 1.1 christos *ctxp = NULL;
703 1.1 christos
704 1.1 christos DELETE_TRACE(ctx, ptr, size, file, line);
705 1.1 christos
706 1.15 christos mem_putstats(ctx, ptr, size);
707 1.15 christos mem_put(ctx, ptr, size, MEM_ALIGN(alignment));
708 1.1 christos
709 1.3 christos if (isc_refcount_decrement(&ctx->references) == 1) {
710 1.3 christos isc_refcount_destroy(&ctx->references);
711 1.1 christos destroy(ctx);
712 1.3 christos }
713 1.1 christos }
714 1.1 christos
715 1.1 christos void
716 1.15 christos isc__mem_destroy(isc_mem_t **ctxp FLARG) {
717 1.15 christos isc_mem_t *ctx = NULL;
718 1.15 christos
719 1.1 christos /*
720 1.1 christos * This routine provides legacy support for callers who use mctxs
721 1.1 christos * without attaching/detaching.
722 1.1 christos */
723 1.1 christos
724 1.6 christos REQUIRE(ctxp != NULL && VALID_CONTEXT(*ctxp));
725 1.6 christos
726 1.15 christos ctx = *ctxp;
727 1.15 christos *ctxp = NULL;
728 1.1 christos
729 1.1 christos #if ISC_MEM_TRACKLINES
730 1.15 christos if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
731 1.15 christos fprintf(stderr, "destroy mctx %p file %s line %u\n", ctx, file,
732 1.15 christos line);
733 1.15 christos }
734 1.15 christos
735 1.6 christos if (isc_refcount_decrement(&ctx->references) > 1) {
736 1.1 christos print_active(ctx, stderr);
737 1.3 christos }
738 1.6 christos #else /* if ISC_MEM_TRACKLINES */
739 1.8 christos isc_refcount_decrementz(&ctx->references);
740 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
741 1.3 christos isc_refcount_destroy(&ctx->references);
742 1.1 christos destroy(ctx);
743 1.1 christos
744 1.1 christos *ctxp = NULL;
745 1.1 christos }
746 1.1 christos
747 1.15 christos #define CALL_HI_WATER(ctx) \
748 1.15 christos { \
749 1.15 christos if (ctx->water != NULL && hi_water(ctx)) { \
750 1.15 christos (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER); \
751 1.15 christos } \
752 1.15 christos }
753 1.15 christos
754 1.15 christos #define CALL_LO_WATER(ctx) \
755 1.15 christos { \
756 1.15 christos if ((ctx->water != NULL) && lo_water(ctx)) { \
757 1.15 christos (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER); \
758 1.15 christos } \
759 1.15 christos }
760 1.6 christos
761 1.15 christos static bool
762 1.15 christos hi_water(isc_mem_t *ctx) {
763 1.15 christos size_t inuse;
764 1.15 christos size_t maxinuse;
765 1.15 christos size_t hiwater = atomic_load_relaxed(&ctx->hi_water);
766 1.1 christos
767 1.15 christos if (hiwater == 0) {
768 1.15 christos return (false);
769 1.6 christos }
770 1.1 christos
771 1.15 christos inuse = atomic_load_acquire(&ctx->inuse);
772 1.15 christos if (inuse <= hiwater) {
773 1.15 christos return (false);
774 1.1 christos }
775 1.1 christos
776 1.15 christos maxinuse = atomic_load_acquire(&ctx->maxinuse);
777 1.15 christos if (inuse > maxinuse) {
778 1.15 christos (void)atomic_compare_exchange_strong(&ctx->maxinuse, &maxinuse,
779 1.15 christos inuse);
780 1.1 christos
781 1.15 christos if ((isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) {
782 1.1 christos fprintf(stderr, "maxinuse = %lu\n",
783 1.15 christos (unsigned long)inuse);
784 1.6 christos }
785 1.1 christos }
786 1.1 christos
787 1.15 christos if (atomic_load_acquire(&ctx->hi_called)) {
788 1.15 christos return (false);
789 1.6 christos }
790 1.1 christos
791 1.15 christos /* We are over water (for the first time) */
792 1.15 christos atomic_store_release(&ctx->is_overmem, true);
793 1.15 christos
794 1.15 christos return (true);
795 1.1 christos }
796 1.1 christos
797 1.15 christos static bool
798 1.15 christos lo_water(isc_mem_t *ctx) {
799 1.15 christos size_t inuse;
800 1.15 christos size_t lowater = atomic_load_relaxed(&ctx->lo_water);
801 1.15 christos
802 1.15 christos if (lowater == 0) {
803 1.15 christos return (false);
804 1.15 christos }
805 1.6 christos
806 1.15 christos inuse = atomic_load_acquire(&ctx->inuse);
807 1.15 christos if (inuse >= lowater) {
808 1.15 christos return (false);
809 1.15 christos }
810 1.1 christos
811 1.15 christos if (!atomic_load_acquire(&ctx->hi_called)) {
812 1.15 christos return (false);
813 1.1 christos }
814 1.1 christos
815 1.15 christos /* We are no longer overmem */
816 1.15 christos atomic_store_release(&ctx->is_overmem, false);
817 1.15 christos
818 1.15 christos return (true);
819 1.15 christos }
820 1.15 christos
821 1.15 christos void *
822 1.15 christos isc__mem_get(isc_mem_t *ctx, size_t size, size_t alignment FLARG) {
823 1.15 christos void *ptr = NULL;
824 1.1 christos
825 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
826 1.1 christos
827 1.15 christos ptr = mem_get(ctx, size, MEM_ALIGN(alignment));
828 1.1 christos
829 1.15 christos mem_getstats(ctx, size);
830 1.15 christos ADD_TRACE(ctx, ptr, size, file, line);
831 1.1 christos
832 1.15 christos CALL_HI_WATER(ctx);
833 1.1 christos
834 1.15 christos return (ptr);
835 1.1 christos }
836 1.1 christos
837 1.1 christos void
838 1.15 christos isc__mem_put(isc_mem_t *ctx, void *ptr, size_t size, size_t alignment FLARG) {
839 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
840 1.15 christos
841 1.15 christos DELETE_TRACE(ctx, ptr, size, file, line);
842 1.6 christos
843 1.15 christos mem_putstats(ctx, ptr, size);
844 1.15 christos mem_put(ctx, ptr, size, MEM_ALIGN(alignment));
845 1.15 christos
846 1.15 christos CALL_LO_WATER(ctx);
847 1.15 christos }
848 1.15 christos
849 1.15 christos void
850 1.15 christos isc_mem_waterack(isc_mem_t *ctx, int flag) {
851 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
852 1.1 christos
853 1.6 christos if (flag == ISC_MEM_LOWATER) {
854 1.15 christos atomic_store(&ctx->hi_called, false);
855 1.6 christos } else if (flag == ISC_MEM_HIWATER) {
856 1.15 christos atomic_store(&ctx->hi_called, true);
857 1.6 christos }
858 1.1 christos }
859 1.1 christos
860 1.1 christos #if ISC_MEM_TRACKLINES
861 1.1 christos static void
862 1.15 christos print_active(isc_mem_t *mctx, FILE *out) {
863 1.1 christos if (mctx->debuglist != NULL) {
864 1.1 christos debuglink_t *dl;
865 1.1 christos unsigned int i;
866 1.3 christos bool found;
867 1.1 christos
868 1.15 christos fprintf(out, "Dump of all outstanding memory "
869 1.15 christos "allocations:\n");
870 1.3 christos found = false;
871 1.1 christos for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
872 1.1 christos dl = ISC_LIST_HEAD(mctx->debuglist[i]);
873 1.1 christos
874 1.4 christos if (dl != NULL) {
875 1.3 christos found = true;
876 1.4 christos }
877 1.1 christos
878 1.1 christos while (dl != NULL) {
879 1.4 christos if (dl->ptr != NULL) {
880 1.4 christos fprintf(out,
881 1.15 christos "\tptr %p size %zu "
882 1.15 christos "file %s "
883 1.6 christos "line %u\n",
884 1.6 christos dl->ptr, dl->size, dl->file,
885 1.6 christos dl->line);
886 1.4 christos }
887 1.1 christos dl = ISC_LIST_NEXT(dl, link);
888 1.1 christos }
889 1.1 christos }
890 1.1 christos
891 1.4 christos if (!found) {
892 1.15 christos fprintf(out, "\tNone.\n");
893 1.4 christos }
894 1.1 christos }
895 1.1 christos }
896 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
897 1.1 christos
898 1.1 christos /*
899 1.1 christos * Print the stats[] on the stream "out" with suitable formatting.
900 1.1 christos */
901 1.1 christos void
902 1.15 christos isc_mem_stats(isc_mem_t *ctx, FILE *out) {
903 1.15 christos isc_mempool_t *pool = NULL;
904 1.6 christos
905 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
906 1.1 christos
907 1.6 christos MCTXLOCK(ctx);
908 1.1 christos
909 1.15 christos for (size_t i = 0; i <= STATS_BUCKETS; i++) {
910 1.15 christos size_t totalgets;
911 1.15 christos size_t gets;
912 1.15 christos struct stats *stats = &ctx->stats[i];
913 1.1 christos
914 1.15 christos totalgets = atomic_load_acquire(&stats->totalgets);
915 1.15 christos gets = atomic_load_acquire(&stats->gets);
916 1.15 christos
917 1.15 christos if (totalgets != 0U && gets != 0U) {
918 1.15 christos fprintf(out, "%s%5zu: %11zu gets, %11zu rem",
919 1.15 christos (i == STATS_BUCKETS) ? ">=" : " ", i,
920 1.15 christos totalgets, gets);
921 1.15 christos fputc('\n', out);
922 1.6 christos }
923 1.1 christos }
924 1.1 christos
925 1.1 christos /*
926 1.15 christos * Note that since a pool can be locked now, these stats might
927 1.15 christos * be somewhat off if the pool is in active use at the time the
928 1.15 christos * stats are dumped. The link fields are protected by the
929 1.15 christos * isc_mem_t's lock, however, so walking this list and
930 1.15 christos * extracting integers from stats fields is always safe.
931 1.1 christos */
932 1.1 christos pool = ISC_LIST_HEAD(ctx->pools);
933 1.1 christos if (pool != NULL) {
934 1.15 christos fprintf(out, "[Pool statistics]\n");
935 1.15 christos fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %1s\n", "name",
936 1.15 christos "size", "allocated", "freecount", "freemax",
937 1.15 christos "fillcount", "gets", "L");
938 1.1 christos }
939 1.1 christos while (pool != NULL) {
940 1.15 christos fprintf(out,
941 1.15 christos "%15s %10zu %10zu %10zu %10zu %10zu %10zu %10zu %s\n",
942 1.15 christos pool->name, pool->size, (size_t)0, pool->allocated,
943 1.15 christos pool->freecount, pool->freemax, pool->fillcount,
944 1.15 christos pool->gets, "N");
945 1.1 christos pool = ISC_LIST_NEXT(pool, link);
946 1.1 christos }
947 1.1 christos
948 1.1 christos #if ISC_MEM_TRACKLINES
949 1.1 christos print_active(ctx, out);
950 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
951 1.1 christos
952 1.6 christos MCTXUNLOCK(ctx);
953 1.1 christos }
954 1.1 christos
955 1.15 christos void *
956 1.15 christos isc__mem_allocate(isc_mem_t *ctx, size_t size FLARG) {
957 1.15 christos void *ptr = NULL;
958 1.15 christos
959 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
960 1.15 christos
961 1.15 christos ptr = mem_get(ctx, size, 0);
962 1.1 christos
963 1.15 christos /* Recalculate the real allocated size */
964 1.15 christos size = sallocx(ptr, ctx->jemalloc_flags);
965 1.1 christos
966 1.15 christos mem_getstats(ctx, size);
967 1.15 christos ADD_TRACE(ctx, ptr, size, file, line);
968 1.1 christos
969 1.15 christos CALL_HI_WATER(ctx);
970 1.1 christos
971 1.15 christos return (ptr);
972 1.1 christos }
973 1.1 christos
974 1.1 christos void *
975 1.15 christos isc__mem_reget(isc_mem_t *ctx, void *old_ptr, size_t old_size, size_t new_size,
976 1.15 christos size_t alignment FLARG) {
977 1.15 christos void *new_ptr = NULL;
978 1.6 christos
979 1.15 christos if (old_ptr == NULL) {
980 1.15 christos REQUIRE(old_size == 0);
981 1.15 christos new_ptr = isc__mem_get(ctx, new_size, alignment FLARG_PASS);
982 1.15 christos } else if (new_size == 0) {
983 1.15 christos isc__mem_put(ctx, old_ptr, old_size, alignment FLARG_PASS);
984 1.15 christos } else {
985 1.15 christos DELETE_TRACE(ctx, old_ptr, old_size, file, line);
986 1.15 christos mem_putstats(ctx, old_ptr, old_size);
987 1.1 christos
988 1.15 christos new_ptr = mem_realloc(ctx, old_ptr, old_size, new_size,
989 1.15 christos MEM_ALIGN(alignment));
990 1.1 christos
991 1.15 christos mem_getstats(ctx, new_size);
992 1.15 christos ADD_TRACE(ctx, new_ptr, new_size, file, line);
993 1.1 christos
994 1.15 christos /*
995 1.15 christos * We want to postpone the call to water in edge case
996 1.15 christos * where the realloc will exactly hit on the boundary of
997 1.15 christos * the water and we would call water twice.
998 1.15 christos */
999 1.15 christos CALL_LO_WATER(ctx);
1000 1.15 christos CALL_HI_WATER(ctx);
1001 1.1 christos }
1002 1.1 christos
1003 1.15 christos return (new_ptr);
1004 1.1 christos }
1005 1.1 christos
1006 1.1 christos void *
1007 1.15 christos isc__mem_reallocate(isc_mem_t *ctx, void *old_ptr, size_t new_size FLARG) {
1008 1.15 christos void *new_ptr = NULL;
1009 1.6 christos
1010 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1011 1.15 christos
1012 1.15 christos if (old_ptr == NULL) {
1013 1.15 christos new_ptr = isc__mem_allocate(ctx, new_size FLARG_PASS);
1014 1.15 christos } else if (new_size == 0) {
1015 1.15 christos isc__mem_free(ctx, old_ptr FLARG_PASS);
1016 1.15 christos } else {
1017 1.15 christos size_t old_size = sallocx(old_ptr, ctx->jemalloc_flags);
1018 1.1 christos
1019 1.15 christos DELETE_TRACE(ctx, old_ptr, old_size, file, line);
1020 1.15 christos mem_putstats(ctx, old_ptr, old_size);
1021 1.1 christos
1022 1.15 christos new_ptr = mem_realloc(ctx, old_ptr, old_size, new_size, 0);
1023 1.1 christos
1024 1.15 christos /* Recalculate the real allocated size */
1025 1.15 christos new_size = sallocx(new_ptr, ctx->jemalloc_flags);
1026 1.6 christos
1027 1.15 christos mem_getstats(ctx, new_size);
1028 1.15 christos ADD_TRACE(ctx, new_ptr, new_size, file, line);
1029 1.1 christos
1030 1.15 christos /*
1031 1.15 christos * We want to postpone the call to water in edge case
1032 1.15 christos * where the realloc will exactly hit on the boundary of
1033 1.15 christos * the water and we would call water twice.
1034 1.15 christos */
1035 1.15 christos CALL_LO_WATER(ctx);
1036 1.15 christos CALL_HI_WATER(ctx);
1037 1.1 christos }
1038 1.1 christos
1039 1.15 christos return (new_ptr);
1040 1.15 christos }
1041 1.1 christos
1042 1.15 christos void
1043 1.15 christos isc__mem_free(isc_mem_t *ctx, void *ptr FLARG) {
1044 1.15 christos size_t size = 0;
1045 1.1 christos
1046 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1047 1.1 christos
1048 1.15 christos size = sallocx(ptr, ctx->jemalloc_flags);
1049 1.1 christos
1050 1.15 christos DELETE_TRACE(ctx, ptr, size, file, line);
1051 1.1 christos
1052 1.15 christos mem_putstats(ctx, ptr, size);
1053 1.15 christos mem_put(ctx, ptr, size, 0);
1054 1.1 christos
1055 1.15 christos CALL_LO_WATER(ctx);
1056 1.1 christos }
1057 1.1 christos
1058 1.1 christos /*
1059 1.1 christos * Other useful things.
1060 1.1 christos */
1061 1.1 christos
1062 1.1 christos char *
1063 1.15 christos isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) {
1064 1.15 christos size_t len;
1065 1.15 christos char *ns = NULL;
1066 1.15 christos
1067 1.15 christos REQUIRE(VALID_CONTEXT(mctx));
1068 1.6 christos REQUIRE(s != NULL);
1069 1.6 christos
1070 1.1 christos len = strlen(s) + 1;
1071 1.1 christos
1072 1.15 christos ns = isc__mem_allocate(mctx, len FLARG_PASS);
1073 1.1 christos
1074 1.15 christos strlcpy(ns, s, len);
1075 1.1 christos
1076 1.1 christos return (ns);
1077 1.1 christos }
1078 1.1 christos
1079 1.11 christos char *
1080 1.15 christos isc__mem_strndup(isc_mem_t *mctx, const char *s, size_t size FLARG) {
1081 1.15 christos size_t len;
1082 1.15 christos char *ns = NULL;
1083 1.15 christos
1084 1.15 christos REQUIRE(VALID_CONTEXT(mctx));
1085 1.11 christos REQUIRE(s != NULL);
1086 1.15 christos REQUIRE(size != 0);
1087 1.11 christos
1088 1.11 christos len = strlen(s) + 1;
1089 1.11 christos if (len > size) {
1090 1.11 christos len = size;
1091 1.11 christos }
1092 1.11 christos
1093 1.15 christos ns = isc__mem_allocate(mctx, len FLARG_PASS);
1094 1.11 christos
1095 1.15 christos strlcpy(ns, s, len);
1096 1.11 christos
1097 1.11 christos return (ns);
1098 1.11 christos }
1099 1.11 christos
1100 1.1 christos void
1101 1.15 christos isc_mem_setdestroycheck(isc_mem_t *ctx, bool flag) {
1102 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1103 1.1 christos
1104 1.6 christos MCTXLOCK(ctx);
1105 1.1 christos
1106 1.1 christos ctx->checkfree = flag;
1107 1.1 christos
1108 1.6 christos MCTXUNLOCK(ctx);
1109 1.1 christos }
1110 1.1 christos
1111 1.1 christos size_t
1112 1.15 christos isc_mem_inuse(isc_mem_t *ctx) {
1113 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1114 1.6 christos
1115 1.15 christos return (atomic_load_acquire(&ctx->inuse));
1116 1.15 christos }
1117 1.1 christos
1118 1.15 christos size_t
1119 1.15 christos isc_mem_maxinuse(isc_mem_t *ctx) {
1120 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1121 1.1 christos
1122 1.15 christos return (atomic_load_acquire(&ctx->maxinuse));
1123 1.15 christos }
1124 1.1 christos
1125 1.15 christos size_t
1126 1.15 christos isc_mem_total(isc_mem_t *ctx) {
1127 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1128 1.1 christos
1129 1.15 christos return (atomic_load_acquire(&ctx->total));
1130 1.1 christos }
1131 1.1 christos
1132 1.1 christos size_t
1133 1.15 christos isc_mem_malloced(isc_mem_t *ctx) {
1134 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1135 1.1 christos
1136 1.15 christos return (atomic_load_acquire(&ctx->malloced));
1137 1.1 christos }
1138 1.1 christos
1139 1.1 christos size_t
1140 1.15 christos isc_mem_maxmalloced(isc_mem_t *ctx) {
1141 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1142 1.6 christos
1143 1.15 christos return (atomic_load_acquire(&ctx->maxmalloced));
1144 1.15 christos }
1145 1.1 christos
1146 1.15 christos void
1147 1.15 christos isc_mem_clearwater(isc_mem_t *mctx) {
1148 1.15 christos isc_mem_setwater(mctx, NULL, NULL, 0, 0);
1149 1.1 christos }
1150 1.1 christos
1151 1.1 christos void
1152 1.15 christos isc_mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg,
1153 1.6 christos size_t hiwater, size_t lowater) {
1154 1.1 christos isc_mem_water_t oldwater;
1155 1.1 christos void *oldwater_arg;
1156 1.1 christos
1157 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1158 1.15 christos REQUIRE(hiwater >= lowater);
1159 1.15 christos
1160 1.1 christos oldwater = ctx->water;
1161 1.1 christos oldwater_arg = ctx->water_arg;
1162 1.15 christos
1163 1.15 christos /* No water was set and new water is also NULL */
1164 1.15 christos if (oldwater == NULL && water == NULL) {
1165 1.15 christos return;
1166 1.15 christos }
1167 1.15 christos
1168 1.15 christos /* The water function is being set for the first time */
1169 1.15 christos if (oldwater == NULL) {
1170 1.15 christos REQUIRE(water != NULL && lowater > 0);
1171 1.15 christos
1172 1.15 christos INSIST(atomic_load(&ctx->hi_water) == 0);
1173 1.15 christos INSIST(atomic_load(&ctx->lo_water) == 0);
1174 1.15 christos
1175 1.1 christos ctx->water = water;
1176 1.1 christos ctx->water_arg = water_arg;
1177 1.15 christos atomic_store(&ctx->hi_water, hiwater);
1178 1.15 christos atomic_store(&ctx->lo_water, lowater);
1179 1.15 christos
1180 1.15 christos return;
1181 1.1 christos }
1182 1.1 christos
1183 1.15 christos REQUIRE((water == oldwater && water_arg == oldwater_arg) ||
1184 1.15 christos (water == NULL && water_arg == NULL && hiwater == 0));
1185 1.15 christos
1186 1.15 christos atomic_store(&ctx->hi_water, hiwater);
1187 1.15 christos atomic_store(&ctx->lo_water, lowater);
1188 1.15 christos
1189 1.15 christos if (atomic_load_acquire(&ctx->hi_called) &&
1190 1.15 christos (atomic_load_acquire(&ctx->inuse) < lowater || lowater == 0U))
1191 1.15 christos {
1192 1.1 christos (oldwater)(oldwater_arg, ISC_MEM_LOWATER);
1193 1.6 christos }
1194 1.1 christos }
1195 1.1 christos
1196 1.15 christos bool
1197 1.15 christos isc_mem_isovermem(isc_mem_t *ctx) {
1198 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1199 1.6 christos
1200 1.15 christos return (atomic_load_relaxed(&ctx->is_overmem));
1201 1.1 christos }
1202 1.1 christos
1203 1.1 christos void
1204 1.15 christos isc_mem_setname(isc_mem_t *ctx, const char *name) {
1205 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1206 1.1 christos
1207 1.1 christos LOCK(&ctx->lock);
1208 1.1 christos strlcpy(ctx->name, name, sizeof(ctx->name));
1209 1.1 christos UNLOCK(&ctx->lock);
1210 1.1 christos }
1211 1.1 christos
1212 1.1 christos const char *
1213 1.15 christos isc_mem_getname(isc_mem_t *ctx) {
1214 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1215 1.1 christos
1216 1.6 christos if (ctx->name[0] == 0) {
1217 1.1 christos return ("");
1218 1.6 christos }
1219 1.1 christos
1220 1.1 christos return (ctx->name);
1221 1.1 christos }
1222 1.1 christos
1223 1.1 christos /*
1224 1.1 christos * Memory pool stuff
1225 1.1 christos */
1226 1.1 christos
1227 1.6 christos void
1228 1.15 christos isc__mempool_create(isc_mem_t *restrict mctx, const size_t element_size,
1229 1.15 christos isc_mempool_t **restrict mpctxp FLARG) {
1230 1.15 christos isc_mempool_t *restrict mpctx = NULL;
1231 1.15 christos size_t size = element_size;
1232 1.15 christos
1233 1.15 christos REQUIRE(VALID_CONTEXT(mctx));
1234 1.6 christos REQUIRE(size > 0U);
1235 1.6 christos REQUIRE(mpctxp != NULL && *mpctxp == NULL);
1236 1.6 christos
1237 1.1 christos /*
1238 1.15 christos * Mempools are stored as a linked list of element.
1239 1.1 christos */
1240 1.15 christos if (size < sizeof(element)) {
1241 1.15 christos size = sizeof(element);
1242 1.15 christos }
1243 1.1 christos
1244 1.3 christos /*
1245 1.15 christos * Allocate space for this pool, initialize values, and if all
1246 1.15 christos * works well, attach to the memory context.
1247 1.3 christos */
1248 1.15 christos mpctx = isc_mem_get(mctx, sizeof(isc_mempool_t));
1249 1.15 christos
1250 1.15 christos *mpctx = (isc_mempool_t){
1251 1.15 christos .size = size,
1252 1.15 christos .freemax = 1,
1253 1.15 christos .fillcount = 1,
1254 1.15 christos };
1255 1.15 christos
1256 1.15 christos #if ISC_MEM_TRACKLINES
1257 1.15 christos if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
1258 1.15 christos fprintf(stderr, "create pool %p file %s line %u mctx %p\n",
1259 1.15 christos mpctx, file, line, mctx);
1260 1.3 christos }
1261 1.15 christos #endif /* ISC_MEM_TRACKLINES */
1262 1.15 christos
1263 1.15 christos isc_mem_attach(mctx, &mpctx->mctx);
1264 1.15 christos mpctx->magic = MEMPOOL_MAGIC;
1265 1.1 christos
1266 1.1 christos *mpctxp = (isc_mempool_t *)mpctx;
1267 1.1 christos
1268 1.6 christos MCTXLOCK(mctx);
1269 1.1 christos ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
1270 1.1 christos mctx->poolcnt++;
1271 1.6 christos MCTXUNLOCK(mctx);
1272 1.1 christos }
1273 1.1 christos
1274 1.1 christos void
1275 1.15 christos isc_mempool_setname(isc_mempool_t *restrict mpctx, const char *name) {
1276 1.15 christos REQUIRE(VALID_MEMPOOL(mpctx));
1277 1.6 christos REQUIRE(name != NULL);
1278 1.6 christos
1279 1.1 christos strlcpy(mpctx->name, name, sizeof(mpctx->name));
1280 1.1 christos }
1281 1.1 christos
1282 1.1 christos void
1283 1.15 christos isc__mempool_destroy(isc_mempool_t **restrict mpctxp FLARG) {
1284 1.15 christos isc_mempool_t *restrict mpctx = NULL;
1285 1.15 christos isc_mem_t *mctx = NULL;
1286 1.15 christos element *restrict item = NULL;
1287 1.15 christos
1288 1.6 christos REQUIRE(mpctxp != NULL);
1289 1.6 christos REQUIRE(VALID_MEMPOOL(*mpctxp));
1290 1.6 christos
1291 1.15 christos mpctx = *mpctxp;
1292 1.15 christos *mpctxp = NULL;
1293 1.15 christos
1294 1.15 christos mctx = mpctx->mctx;
1295 1.15 christos
1296 1.15 christos #if ISC_MEM_TRACKLINES
1297 1.15 christos if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
1298 1.15 christos fprintf(stderr, "destroy pool %p file %s line %u mctx %p\n",
1299 1.15 christos mpctx, file, line, mctx);
1300 1.15 christos }
1301 1.15 christos #endif
1302 1.1 christos
1303 1.6 christos if (mpctx->allocated > 0) {
1304 1.15 christos UNEXPECTED_ERROR("mempool %s leaked memory", mpctx->name);
1305 1.6 christos }
1306 1.1 christos REQUIRE(mpctx->allocated == 0);
1307 1.1 christos
1308 1.1 christos /*
1309 1.1 christos * Return any items on the free list
1310 1.1 christos */
1311 1.1 christos while (mpctx->items != NULL) {
1312 1.1 christos INSIST(mpctx->freecount > 0);
1313 1.1 christos mpctx->freecount--;
1314 1.15 christos
1315 1.1 christos item = mpctx->items;
1316 1.1 christos mpctx->items = item->next;
1317 1.15 christos
1318 1.12 christos mem_putstats(mctx, item, mpctx->size);
1319 1.15 christos mem_put(mctx, item, mpctx->size, 0);
1320 1.1 christos }
1321 1.1 christos
1322 1.1 christos /*
1323 1.1 christos * Remove our linked list entry from the memory context.
1324 1.1 christos */
1325 1.6 christos MCTXLOCK(mctx);
1326 1.1 christos ISC_LIST_UNLINK(mctx->pools, mpctx, link);
1327 1.1 christos mctx->poolcnt--;
1328 1.6 christos MCTXUNLOCK(mctx);
1329 1.1 christos
1330 1.15 christos mpctx->magic = 0;
1331 1.1 christos
1332 1.15 christos isc_mem_putanddetach(&mpctx->mctx, mpctx, sizeof(isc_mempool_t));
1333 1.1 christos }
1334 1.1 christos
1335 1.10 christos void *
1336 1.15 christos isc__mempool_get(isc_mempool_t *restrict mpctx FLARG) {
1337 1.15 christos element *restrict item = NULL;
1338 1.10 christos
1339 1.15 christos REQUIRE(VALID_MEMPOOL(mpctx));
1340 1.10 christos
1341 1.10 christos mpctx->allocated++;
1342 1.10 christos
1343 1.15 christos if (mpctx->items == NULL) {
1344 1.15 christos isc_mem_t *mctx = mpctx->mctx;
1345 1.15 christos #if !__SANITIZE_ADDRESS__
1346 1.15 christos const size_t fillcount = mpctx->fillcount;
1347 1.15 christos #else
1348 1.15 christos const size_t fillcount = 1;
1349 1.15 christos #endif
1350 1.1 christos /*
1351 1.15 christos * We need to dip into the well. Fill up our free list.
1352 1.1 christos */
1353 1.15 christos for (size_t i = 0; i < fillcount; i++) {
1354 1.15 christos item = mem_get(mctx, mpctx->size, 0);
1355 1.12 christos mem_getstats(mctx, mpctx->size);
1356 1.1 christos item->next = mpctx->items;
1357 1.1 christos mpctx->items = item;
1358 1.1 christos mpctx->freecount++;
1359 1.1 christos }
1360 1.1 christos }
1361 1.1 christos
1362 1.1 christos item = mpctx->items;
1363 1.15 christos INSIST(item != NULL);
1364 1.1 christos
1365 1.1 christos mpctx->items = item->next;
1366 1.15 christos
1367 1.1 christos INSIST(mpctx->freecount > 0);
1368 1.1 christos mpctx->freecount--;
1369 1.1 christos mpctx->gets++;
1370 1.1 christos
1371 1.15 christos ADD_TRACE(mpctx->mctx, item, mpctx->size, file, line);
1372 1.1 christos
1373 1.1 christos return (item);
1374 1.1 christos }
1375 1.1 christos
1376 1.1 christos /* coverity[+free : arg-1] */
1377 1.1 christos void
1378 1.15 christos isc__mempool_put(isc_mempool_t *restrict mpctx, void *mem FLARG) {
1379 1.15 christos element *restrict item = NULL;
1380 1.15 christos
1381 1.15 christos REQUIRE(VALID_MEMPOOL(mpctx));
1382 1.6 christos REQUIRE(mem != NULL);
1383 1.6 christos
1384 1.15 christos isc_mem_t *mctx = mpctx->mctx;
1385 1.15 christos const size_t freecount = mpctx->freecount;
1386 1.15 christos #if !__SANITIZE_ADDRESS__
1387 1.15 christos const size_t freemax = mpctx->freemax;
1388 1.15 christos #else
1389 1.15 christos const size_t freemax = 0;
1390 1.15 christos #endif
1391 1.1 christos
1392 1.1 christos INSIST(mpctx->allocated > 0);
1393 1.1 christos mpctx->allocated--;
1394 1.1 christos
1395 1.15 christos DELETE_TRACE(mctx, mem, mpctx->size, file, line);
1396 1.1 christos
1397 1.1 christos /*
1398 1.1 christos * If our free list is full, return this to the mctx directly.
1399 1.1 christos */
1400 1.15 christos if (freecount >= freemax) {
1401 1.12 christos mem_putstats(mctx, mem, mpctx->size);
1402 1.15 christos mem_put(mctx, mem, mpctx->size, 0);
1403 1.1 christos return;
1404 1.1 christos }
1405 1.1 christos
1406 1.1 christos /*
1407 1.1 christos * Otherwise, attach it to our free list and bump the counter.
1408 1.1 christos */
1409 1.1 christos item = (element *)mem;
1410 1.1 christos item->next = mpctx->items;
1411 1.1 christos mpctx->items = item;
1412 1.15 christos mpctx->freecount++;
1413 1.1 christos }
1414 1.1 christos
1415 1.1 christos /*
1416 1.1 christos * Quotas
1417 1.1 christos */
1418 1.1 christos
1419 1.1 christos void
1420 1.15 christos isc_mempool_setfreemax(isc_mempool_t *restrict mpctx,
1421 1.15 christos const unsigned int limit) {
1422 1.15 christos REQUIRE(VALID_MEMPOOL(mpctx));
1423 1.1 christos mpctx->freemax = limit;
1424 1.1 christos }
1425 1.1 christos
1426 1.1 christos unsigned int
1427 1.15 christos isc_mempool_getfreemax(isc_mempool_t *restrict mpctx) {
1428 1.15 christos REQUIRE(VALID_MEMPOOL(mpctx));
1429 1.1 christos
1430 1.12 christos return (mpctx->freemax);
1431 1.1 christos }
1432 1.1 christos
1433 1.1 christos unsigned int
1434 1.15 christos isc_mempool_getfreecount(isc_mempool_t *restrict mpctx) {
1435 1.15 christos REQUIRE(VALID_MEMPOOL(mpctx));
1436 1.1 christos
1437 1.12 christos return (mpctx->freecount);
1438 1.1 christos }
1439 1.1 christos
1440 1.1 christos unsigned int
1441 1.15 christos isc_mempool_getallocated(isc_mempool_t *restrict mpctx) {
1442 1.15 christos REQUIRE(VALID_MEMPOOL(mpctx));
1443 1.1 christos
1444 1.12 christos return (mpctx->allocated);
1445 1.1 christos }
1446 1.1 christos
1447 1.1 christos void
1448 1.15 christos isc_mempool_setfillcount(isc_mempool_t *restrict mpctx,
1449 1.15 christos unsigned int const limit) {
1450 1.15 christos REQUIRE(VALID_MEMPOOL(mpctx));
1451 1.6 christos REQUIRE(limit > 0);
1452 1.6 christos
1453 1.1 christos mpctx->fillcount = limit;
1454 1.1 christos }
1455 1.1 christos
1456 1.1 christos unsigned int
1457 1.15 christos isc_mempool_getfillcount(isc_mempool_t *restrict mpctx) {
1458 1.15 christos REQUIRE(VALID_MEMPOOL(mpctx));
1459 1.1 christos
1460 1.12 christos return (mpctx->fillcount);
1461 1.1 christos }
1462 1.1 christos
1463 1.1 christos /*
1464 1.1 christos * Requires contextslock to be held by caller.
1465 1.1 christos */
1466 1.15 christos #if ISC_MEM_TRACKLINES
1467 1.1 christos static void
1468 1.1 christos print_contexts(FILE *file) {
1469 1.15 christos isc_mem_t *ctx;
1470 1.1 christos
1471 1.6 christos for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
1472 1.13 christos ctx = ISC_LIST_NEXT(ctx, link))
1473 1.13 christos {
1474 1.3 christos fprintf(file, "context: %p (%s): %" PRIuFAST32 " references\n",
1475 1.6 christos ctx, ctx->name[0] == 0 ? "<unknown>" : ctx->name,
1476 1.3 christos isc_refcount_current(&ctx->references));
1477 1.1 christos print_active(ctx, file);
1478 1.1 christos }
1479 1.1 christos fflush(file);
1480 1.1 christos }
1481 1.15 christos #endif
1482 1.1 christos
1483 1.12 christos static atomic_uintptr_t checkdestroyed = 0;
1484 1.10 christos
1485 1.1 christos void
1486 1.1 christos isc_mem_checkdestroyed(FILE *file) {
1487 1.10 christos atomic_store_release(&checkdestroyed, (uintptr_t)file);
1488 1.10 christos }
1489 1.10 christos
1490 1.10 christos void
1491 1.10 christos isc__mem_checkdestroyed(void) {
1492 1.10 christos FILE *file = (FILE *)atomic_load_acquire(&checkdestroyed);
1493 1.1 christos
1494 1.10 christos if (file == NULL) {
1495 1.10 christos return;
1496 1.10 christos }
1497 1.1 christos
1498 1.1 christos LOCK(&contextslock);
1499 1.1 christos if (!ISC_LIST_EMPTY(contexts)) {
1500 1.1 christos #if ISC_MEM_TRACKLINES
1501 1.15 christos if ((isc_mem_debugging & TRACE_OR_RECORD) != 0) {
1502 1.1 christos print_contexts(file);
1503 1.1 christos }
1504 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
1505 1.12 christos UNREACHABLE();
1506 1.1 christos }
1507 1.1 christos UNLOCK(&contextslock);
1508 1.1 christos }
1509 1.1 christos
1510 1.1 christos unsigned int
1511 1.15 christos isc_mem_references(isc_mem_t *ctx) {
1512 1.3 christos return (isc_refcount_current(&ctx->references));
1513 1.1 christos }
1514 1.1 christos
1515 1.1 christos typedef struct summarystat {
1516 1.6 christos uint64_t total;
1517 1.6 christos uint64_t inuse;
1518 1.6 christos uint64_t malloced;
1519 1.6 christos uint64_t contextsize;
1520 1.1 christos } summarystat_t;
1521 1.1 christos
1522 1.1 christos #ifdef HAVE_LIBXML2
1523 1.6 christos #define TRY0(a) \
1524 1.6 christos do { \
1525 1.6 christos xmlrc = (a); \
1526 1.6 christos if (xmlrc < 0) \
1527 1.6 christos goto error; \
1528 1.9 rillig } while (0)
1529 1.1 christos static int
1530 1.15 christos xml_renderctx(isc_mem_t *ctx, summarystat_t *summary, xmlTextWriterPtr writer) {
1531 1.6 christos REQUIRE(VALID_CONTEXT(ctx));
1532 1.6 christos
1533 1.1 christos int xmlrc;
1534 1.1 christos
1535 1.6 christos MCTXLOCK(ctx);
1536 1.1 christos
1537 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "context"));
1538 1.1 christos
1539 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id"));
1540 1.1 christos TRY0(xmlTextWriterWriteFormatString(writer, "%p", ctx));
1541 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* id */
1542 1.1 christos
1543 1.1 christos if (ctx->name[0] != 0) {
1544 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
1545 1.1 christos TRY0(xmlTextWriterWriteFormatString(writer, "%s", ctx->name));
1546 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* name */
1547 1.1 christos }
1548 1.1 christos
1549 1.15 christos summary->contextsize += sizeof(*ctx);
1550 1.1 christos #if ISC_MEM_TRACKLINES
1551 1.1 christos if (ctx->debuglist != NULL) {
1552 1.6 christos summary->contextsize += DEBUG_TABLE_COUNT *
1553 1.6 christos sizeof(debuglist_t) +
1554 1.6 christos ctx->debuglistcnt * sizeof(debuglink_t);
1555 1.1 christos }
1556 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
1557 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references"));
1558 1.6 christos TRY0(xmlTextWriterWriteFormatString(
1559 1.6 christos writer, "%" PRIuFAST32,
1560 1.6 christos isc_refcount_current(&ctx->references)));
1561 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* references */
1562 1.1 christos
1563 1.15 christos summary->total += isc_mem_total(ctx);
1564 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "total"));
1565 1.6 christos TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
1566 1.15 christos (uint64_t)isc_mem_total(ctx)));
1567 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* total */
1568 1.1 christos
1569 1.15 christos summary->inuse += isc_mem_inuse(ctx);
1570 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse"));
1571 1.6 christos TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
1572 1.15 christos (uint64_t)isc_mem_inuse(ctx)));
1573 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* inuse */
1574 1.1 christos
1575 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse"));
1576 1.6 christos TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
1577 1.15 christos (uint64_t)isc_mem_maxinuse(ctx)));
1578 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* maxinuse */
1579 1.1 christos
1580 1.15 christos summary->malloced += isc_mem_malloced(ctx);
1581 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "malloced"));
1582 1.6 christos TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
1583 1.15 christos (uint64_t)isc_mem_malloced(ctx)));
1584 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* malloced */
1585 1.1 christos
1586 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxmalloced"));
1587 1.15 christos TRY0(xmlTextWriterWriteFormatString(
1588 1.15 christos writer, "%" PRIu64 "", (uint64_t)isc_mem_maxmalloced(ctx)));
1589 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* maxmalloced */
1590 1.1 christos
1591 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools"));
1592 1.1 christos TRY0(xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt));
1593 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* pools */
1594 1.1 christos summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
1595 1.1 christos
1596 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater"));
1597 1.15 christos TRY0(xmlTextWriterWriteFormatString(
1598 1.15 christos writer, "%" PRIu64 "",
1599 1.15 christos (uint64_t)atomic_load_relaxed(&ctx->hi_water)));
1600 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* hiwater */
1601 1.1 christos
1602 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater"));
1603 1.15 christos TRY0(xmlTextWriterWriteFormatString(
1604 1.15 christos writer, "%" PRIu64 "",
1605 1.15 christos (uint64_t)atomic_load_relaxed(&ctx->lo_water)));
1606 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* lowater */
1607 1.1 christos
1608 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* context */
1609 1.1 christos
1610 1.6 christos error:
1611 1.6 christos MCTXUNLOCK(ctx);
1612 1.1 christos
1613 1.1 christos return (xmlrc);
1614 1.1 christos }
1615 1.1 christos
1616 1.1 christos int
1617 1.6 christos isc_mem_renderxml(void *writer0) {
1618 1.15 christos isc_mem_t *ctx;
1619 1.15 christos summarystat_t summary = { 0 };
1620 1.3 christos uint64_t lost;
1621 1.1 christos int xmlrc;
1622 1.6 christos xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
1623 1.1 christos
1624 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts"));
1625 1.1 christos
1626 1.1 christos LOCK(&contextslock);
1627 1.1 christos lost = totallost;
1628 1.6 christos for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
1629 1.13 christos ctx = ISC_LIST_NEXT(ctx, link))
1630 1.13 christos {
1631 1.1 christos xmlrc = xml_renderctx(ctx, &summary, writer);
1632 1.1 christos if (xmlrc < 0) {
1633 1.1 christos UNLOCK(&contextslock);
1634 1.1 christos goto error;
1635 1.1 christos }
1636 1.1 christos }
1637 1.1 christos UNLOCK(&contextslock);
1638 1.1 christos
1639 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* contexts */
1640 1.1 christos
1641 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary"));
1642 1.1 christos
1643 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse"));
1644 1.6 christos TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
1645 1.1 christos summary.total));
1646 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* TotalUse */
1647 1.1 christos
1648 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse"));
1649 1.6 christos TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
1650 1.1 christos summary.inuse));
1651 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* InUse */
1652 1.1 christos
1653 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Malloced"));
1654 1.6 christos TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
1655 1.1 christos summary.malloced));
1656 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* InUse */
1657 1.1 christos
1658 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize"));
1659 1.6 christos TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "",
1660 1.1 christos summary.contextsize));
1661 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* ContextSize */
1662 1.1 christos
1663 1.1 christos TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost"));
1664 1.6 christos TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", lost));
1665 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* Lost */
1666 1.1 christos
1667 1.1 christos TRY0(xmlTextWriterEndElement(writer)); /* summary */
1668 1.6 christos error:
1669 1.1 christos return (xmlrc);
1670 1.1 christos }
1671 1.1 christos
1672 1.1 christos #endif /* HAVE_LIBXML2 */
1673 1.1 christos
1674 1.6 christos #ifdef HAVE_JSON_C
1675 1.3 christos #define CHECKMEM(m) RUNTIME_CHECK(m != NULL)
1676 1.1 christos
1677 1.1 christos static isc_result_t
1678 1.15 christos json_renderctx(isc_mem_t *ctx, summarystat_t *summary, json_object *array) {
1679 1.1 christos REQUIRE(VALID_CONTEXT(ctx));
1680 1.1 christos REQUIRE(summary != NULL);
1681 1.1 christos REQUIRE(array != NULL);
1682 1.1 christos
1683 1.6 christos json_object *ctxobj, *obj;
1684 1.6 christos char buf[1024];
1685 1.6 christos
1686 1.6 christos MCTXLOCK(ctx);
1687 1.1 christos
1688 1.15 christos summary->contextsize += sizeof(*ctx);
1689 1.15 christos summary->total += isc_mem_total(ctx);
1690 1.15 christos summary->inuse += isc_mem_inuse(ctx);
1691 1.15 christos summary->malloced += isc_mem_malloced(ctx);
1692 1.1 christos #if ISC_MEM_TRACKLINES
1693 1.1 christos if (ctx->debuglist != NULL) {
1694 1.6 christos summary->contextsize += DEBUG_TABLE_COUNT *
1695 1.6 christos sizeof(debuglist_t) +
1696 1.6 christos ctx->debuglistcnt * sizeof(debuglink_t);
1697 1.1 christos }
1698 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
1699 1.1 christos
1700 1.1 christos ctxobj = json_object_new_object();
1701 1.1 christos CHECKMEM(ctxobj);
1702 1.1 christos
1703 1.1 christos snprintf(buf, sizeof(buf), "%p", ctx);
1704 1.1 christos obj = json_object_new_string(buf);
1705 1.1 christos CHECKMEM(obj);
1706 1.1 christos json_object_object_add(ctxobj, "id", obj);
1707 1.1 christos
1708 1.1 christos if (ctx->name[0] != 0) {
1709 1.1 christos obj = json_object_new_string(ctx->name);
1710 1.1 christos CHECKMEM(obj);
1711 1.1 christos json_object_object_add(ctxobj, "name", obj);
1712 1.1 christos }
1713 1.1 christos
1714 1.3 christos obj = json_object_new_int64(isc_refcount_current(&ctx->references));
1715 1.1 christos CHECKMEM(obj);
1716 1.1 christos json_object_object_add(ctxobj, "references", obj);
1717 1.1 christos
1718 1.15 christos obj = json_object_new_int64(isc_mem_total(ctx));
1719 1.1 christos CHECKMEM(obj);
1720 1.1 christos json_object_object_add(ctxobj, "total", obj);
1721 1.1 christos
1722 1.15 christos obj = json_object_new_int64(isc_mem_inuse(ctx));
1723 1.1 christos CHECKMEM(obj);
1724 1.1 christos json_object_object_add(ctxobj, "inuse", obj);
1725 1.1 christos
1726 1.15 christos obj = json_object_new_int64(isc_mem_maxinuse(ctx));
1727 1.1 christos CHECKMEM(obj);
1728 1.1 christos json_object_object_add(ctxobj, "maxinuse", obj);
1729 1.1 christos
1730 1.15 christos obj = json_object_new_int64(isc_mem_malloced(ctx));
1731 1.1 christos CHECKMEM(obj);
1732 1.1 christos json_object_object_add(ctxobj, "malloced", obj);
1733 1.1 christos
1734 1.15 christos obj = json_object_new_int64(isc_mem_maxmalloced(ctx));
1735 1.1 christos CHECKMEM(obj);
1736 1.1 christos json_object_object_add(ctxobj, "maxmalloced", obj);
1737 1.1 christos
1738 1.1 christos obj = json_object_new_int64(ctx->poolcnt);
1739 1.1 christos CHECKMEM(obj);
1740 1.1 christos json_object_object_add(ctxobj, "pools", obj);
1741 1.1 christos
1742 1.1 christos summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t);
1743 1.1 christos
1744 1.15 christos obj = json_object_new_int64(atomic_load_relaxed(&ctx->hi_water));
1745 1.1 christos CHECKMEM(obj);
1746 1.1 christos json_object_object_add(ctxobj, "hiwater", obj);
1747 1.1 christos
1748 1.15 christos obj = json_object_new_int64(atomic_load_relaxed(&ctx->lo_water));
1749 1.1 christos CHECKMEM(obj);
1750 1.1 christos json_object_object_add(ctxobj, "lowater", obj);
1751 1.1 christos
1752 1.6 christos MCTXUNLOCK(ctx);
1753 1.1 christos json_object_array_add(array, ctxobj);
1754 1.1 christos return (ISC_R_SUCCESS);
1755 1.1 christos }
1756 1.1 christos
1757 1.1 christos isc_result_t
1758 1.6 christos isc_mem_renderjson(void *memobj0) {
1759 1.1 christos isc_result_t result = ISC_R_SUCCESS;
1760 1.15 christos isc_mem_t *ctx;
1761 1.15 christos summarystat_t summary = { 0 };
1762 1.3 christos uint64_t lost;
1763 1.1 christos json_object *ctxarray, *obj;
1764 1.6 christos json_object *memobj = (json_object *)memobj0;
1765 1.1 christos
1766 1.1 christos ctxarray = json_object_new_array();
1767 1.1 christos CHECKMEM(ctxarray);
1768 1.1 christos
1769 1.1 christos LOCK(&contextslock);
1770 1.1 christos lost = totallost;
1771 1.6 christos for (ctx = ISC_LIST_HEAD(contexts); ctx != NULL;
1772 1.13 christos ctx = ISC_LIST_NEXT(ctx, link))
1773 1.13 christos {
1774 1.1 christos result = json_renderctx(ctx, &summary, ctxarray);
1775 1.1 christos if (result != ISC_R_SUCCESS) {
1776 1.1 christos UNLOCK(&contextslock);
1777 1.1 christos goto error;
1778 1.1 christos }
1779 1.1 christos }
1780 1.1 christos UNLOCK(&contextslock);
1781 1.1 christos
1782 1.1 christos obj = json_object_new_int64(summary.total);
1783 1.1 christos CHECKMEM(obj);
1784 1.1 christos json_object_object_add(memobj, "TotalUse", obj);
1785 1.1 christos
1786 1.1 christos obj = json_object_new_int64(summary.inuse);
1787 1.1 christos CHECKMEM(obj);
1788 1.1 christos json_object_object_add(memobj, "InUse", obj);
1789 1.1 christos
1790 1.1 christos obj = json_object_new_int64(summary.malloced);
1791 1.1 christos CHECKMEM(obj);
1792 1.1 christos json_object_object_add(memobj, "Malloced", obj);
1793 1.1 christos
1794 1.1 christos obj = json_object_new_int64(summary.contextsize);
1795 1.1 christos CHECKMEM(obj);
1796 1.1 christos json_object_object_add(memobj, "ContextSize", obj);
1797 1.1 christos
1798 1.1 christos obj = json_object_new_int64(lost);
1799 1.1 christos CHECKMEM(obj);
1800 1.1 christos json_object_object_add(memobj, "Lost", obj);
1801 1.1 christos
1802 1.1 christos json_object_object_add(memobj, "contexts", ctxarray);
1803 1.1 christos return (ISC_R_SUCCESS);
1804 1.1 christos
1805 1.6 christos error:
1806 1.6 christos if (ctxarray != NULL) {
1807 1.1 christos json_object_put(ctxarray);
1808 1.6 christos }
1809 1.1 christos return (result);
1810 1.1 christos }
1811 1.6 christos #endif /* HAVE_JSON_C */
1812 1.1 christos
1813 1.6 christos void
1814 1.15 christos isc__mem_create(isc_mem_t **mctxp FLARG) {
1815 1.15 christos mem_create(mctxp, isc_mem_defaultflags, 0);
1816 1.15 christos #if ISC_MEM_TRACKLINES
1817 1.15 christos if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
1818 1.15 christos fprintf(stderr, "create mctx %p file %s line %u\n", *mctxp,
1819 1.15 christos file, line);
1820 1.15 christos }
1821 1.15 christos #endif /* ISC_MEM_TRACKLINES */
1822 1.1 christos }
1823 1.1 christos
1824 1.15 christos void
1825 1.15 christos isc__mem_create_arena(isc_mem_t **mctxp FLARG) {
1826 1.15 christos unsigned int arena_no = ISC_MEM_ILLEGAL_ARENA;
1827 1.1 christos
1828 1.15 christos RUNTIME_CHECK(mem_jemalloc_arena_create(&arena_no));
1829 1.1 christos
1830 1.15 christos /*
1831 1.15 christos * We use MALLOCX_TCACHE_NONE to bypass the tcache and route
1832 1.15 christos * allocations directly to the arena. That is a recommendation
1833 1.15 christos * from jemalloc developers:
1834 1.15 christos *
1835 1.15 christos * https://github.com/jemalloc/jemalloc/issues/2483#issuecomment-1698173849
1836 1.15 christos */
1837 1.15 christos mem_create(mctxp, isc_mem_defaultflags,
1838 1.15 christos arena_no == ISC_MEM_ILLEGAL_ARENA
1839 1.15 christos ? 0
1840 1.15 christos : MALLOCX_ARENA(arena_no) | MALLOCX_TCACHE_NONE);
1841 1.15 christos (*mctxp)->jemalloc_arena = arena_no;
1842 1.15 christos #if ISC_MEM_TRACKLINES
1843 1.15 christos if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) {
1844 1.15 christos fprintf(stderr,
1845 1.15 christos "create mctx %p file %s line %u for jemalloc arena "
1846 1.15 christos "%u\n",
1847 1.15 christos *mctxp, file, line, arena_no);
1848 1.15 christos }
1849 1.15 christos #endif /* ISC_MEM_TRACKLINES */
1850 1.1 christos }
1851 1.1 christos
1852 1.15 christos #if defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4
1853 1.15 christos static bool
1854 1.15 christos jemalloc_set_ssize_value(const char *valname, ssize_t newval) {
1855 1.15 christos int ret;
1856 1.1 christos
1857 1.15 christos ret = mallctl(valname, NULL, NULL, &newval, sizeof(newval));
1858 1.15 christos return (ret == 0);
1859 1.1 christos }
1860 1.15 christos #endif /* defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4 */
1861 1.1 christos
1862 1.15 christos static isc_result_t
1863 1.15 christos mem_set_arena_ssize_value(isc_mem_t *mctx, const char *arena_valname,
1864 1.15 christos const ssize_t newval) {
1865 1.15 christos REQUIRE(VALID_CONTEXT(mctx));
1866 1.15 christos #if defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4
1867 1.15 christos bool ret;
1868 1.15 christos char buf[256] = { 0 };
1869 1.1 christos
1870 1.15 christos if (mctx->jemalloc_arena == ISC_MEM_ILLEGAL_ARENA) {
1871 1.15 christos return (ISC_R_UNEXPECTED);
1872 1.15 christos }
1873 1.1 christos
1874 1.15 christos (void)snprintf(buf, sizeof(buf), "arena.%u.%s", mctx->jemalloc_arena,
1875 1.15 christos arena_valname);
1876 1.1 christos
1877 1.15 christos ret = jemalloc_set_ssize_value(buf, newval);
1878 1.1 christos
1879 1.15 christos if (!ret) {
1880 1.15 christos return (ISC_R_FAILURE);
1881 1.15 christos }
1882 1.1 christos
1883 1.15 christos return (ISC_R_SUCCESS);
1884 1.15 christos #else
1885 1.15 christos UNUSED(arena_valname);
1886 1.15 christos UNUSED(newval);
1887 1.15 christos return (ISC_R_NOTIMPLEMENTED);
1888 1.15 christos #endif /* defined(JEMALLOC_API_SUPPORTED) && JEMALLOC_VERSION_MAJOR >= 4 */
1889 1.1 christos }
1890 1.1 christos
1891 1.15 christos isc_result_t
1892 1.15 christos isc_mem_arena_set_muzzy_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms) {
1893 1.15 christos return (mem_set_arena_ssize_value(mctx, "muzzy_decay_ms", decay_ms));
1894 1.11 christos }
1895 1.11 christos
1896 1.15 christos isc_result_t
1897 1.15 christos isc_mem_arena_set_dirty_decay_ms(isc_mem_t *mctx, const ssize_t decay_ms) {
1898 1.15 christos return (mem_set_arena_ssize_value(mctx, "dirty_decay_ms", decay_ms));
1899 1.1 christos }
1900 1.1 christos
1901 1.3 christos void
1902 1.15 christos isc__mem_printactive(isc_mem_t *ctx, FILE *file) {
1903 1.3 christos #if ISC_MEM_TRACKLINES
1904 1.15 christos REQUIRE(VALID_CONTEXT(ctx));
1905 1.6 christos REQUIRE(file != NULL);
1906 1.6 christos
1907 1.3 christos print_active(ctx, file);
1908 1.6 christos #else /* if ISC_MEM_TRACKLINES */
1909 1.15 christos UNUSED(ctx);
1910 1.3 christos UNUSED(file);
1911 1.6 christos #endif /* if ISC_MEM_TRACKLINES */
1912 1.1 christos }
1913 1.15 christos
1914 1.15 christos void *
1915 1.15 christos isc__mem_alloc_noctx(size_t size) {
1916 1.15 christos return mallocx(size, 0);
1917 1.15 christos }
1918 1.15 christos
1919 1.15 christos void
1920 1.15 christos isc__mem_free_noctx(void *ptr, size_t size) {
1921 1.15 christos ADJUST_ZERO_ALLOCATION_SIZE(size);
1922 1.15 christos sdallocx(ptr, size, 0);
1923 1.15 christos }
1924