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