Coverage Report

Created: 2026-08-31 07:21

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