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