#include <isc/bind9.h>
#include <isc/json.h>
#include <isc/magic.h>
+#include <isc/hash.h>
#include <isc/mem.h>
#include <isc/msgs.h>
#include <isc/once.h>
#define ALIGNMENT_SIZE 8U /*%< must be a power of 2 */
#define NUM_BASIC_BLOCKS 64 /*%< must be > 1 */
#define TABLE_INCREMENT 1024
-#define DEBUGLIST_COUNT 1024
+#define DEBUG_TABLE_COUNT 65536
/*
* Types.
typedef struct debuglink debuglink_t;
struct debuglink {
ISC_LINK(debuglink_t) link;
- const void *ptr[DEBUGLIST_COUNT];
- size_t size[DEBUGLIST_COUNT];
- const char *file[DEBUGLIST_COUNT];
- unsigned int line[DEBUGLIST_COUNT];
- unsigned int count;
+ const void *ptr;
+ size_t size;
+ const char *file;
+ unsigned int line;
};
+typedef ISC_LIST(debuglink_t) debuglist_t;
+
#define FLARG_PASS , file, line
#define FLARG , const char *file, unsigned int line
#else
#define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C')
#define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC)
-#if ISC_MEM_TRACKLINES
-typedef ISC_LIST(debuglink_t) debuglist_t;
-#endif
-
/* List of all active memory contexts. */
static ISC_LIST(isc__mem_t) contexts;
#if ISC_MEM_TRACKLINES
debuglist_t * debuglist;
- unsigned int debuglistcnt;
+ size_t debuglistcnt;
#endif
unsigned int memalloc_failures;
#define TRACE_OR_RECORD (ISC_MEM_DEBUGTRACE|ISC_MEM_DEBUGRECORD)
#define ADD_TRACE(a, b, c, d, e) \
do { \
- if ((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \
- b != NULL) \
- add_trace_entry(a, b, c, d, e); \
+ if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \
+ b != NULL)) \
+ add_trace_entry(a, b, c, d, e); \
} while (0)
#define DELETE_TRACE(a, b, c, d, e) \
do { \
- if ((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \
- b != NULL) \
+ if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \
+ b != NULL)) \
delete_trace_entry(a, b, c, d, e); \
} while(0)
/*!
* mctx must be locked.
*/
-static inline void
+static void
add_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size FLARG) {
debuglink_t *dl;
- unsigned int i;
- size_t mysize = size;
+ isc_uint32_t hash;
+ isc_uint32_t idx;
if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
if (mctx->debuglist == NULL)
return;
- if (mysize > mctx->max_size)
- mysize = mctx->max_size;
-
- dl = ISC_LIST_HEAD(mctx->debuglist[mysize]);
- while (dl != NULL) {
- if (dl->count == DEBUGLIST_COUNT)
- goto next;
- for (i = 0; i < DEBUGLIST_COUNT; i++) {
- if (dl->ptr[i] == NULL) {
- dl->ptr[i] = ptr;
- dl->size[i] = size;
- dl->file[i] = file;
- dl->line[i] = line;
- dl->count++;
- return;
- }
- }
- next:
- dl = ISC_LIST_NEXT(dl, link);
+ hash = isc_hash_function(&ptr, sizeof(ptr), ISC_TRUE, NULL);
+ idx = hash % DEBUG_TABLE_COUNT;
+
+ dl = ISC_LIST_TAIL(mctx->debuglist[idx]);
+ if (ISC_LIKELY(dl != NULL && dl->ptr == NULL)) {
+ ISC_LIST_UNLINK(mctx->debuglist[idx], dl, link);
+ dl->ptr = ptr;
+ dl->size = size;
+ dl->file = file;
+ dl->line = line;
+ ISC_LIST_PREPEND(mctx->debuglist[idx], dl, link);
+ return;
}
dl = malloc(sizeof(debuglink_t));
mctx->maxmalloced = mctx->malloced;
ISC_LINK_INIT(dl, link);
- for (i = 1; i < DEBUGLIST_COUNT; i++) {
- dl->ptr[i] = NULL;
- dl->size[i] = 0;
- dl->file[i] = NULL;
- dl->line[i] = 0;
- }
-
- dl->ptr[0] = ptr;
- dl->size[0] = size;
- dl->file[0] = file;
- dl->line[0] = line;
- dl->count = 1;
+ dl->ptr = ptr;
+ dl->size = size;
+ dl->file = file;
+ dl->line = line;
- ISC_LIST_PREPEND(mctx->debuglist[mysize], dl, link);
+ ISC_LIST_PREPEND(mctx->debuglist[idx], dl, link);
mctx->debuglistcnt++;
}
-static inline void
+static void
delete_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size,
const char *file, unsigned int line)
{
debuglink_t *dl;
- unsigned int i;
+ isc_uint32_t hash;
+ isc_uint32_t idx;
if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
if (mctx->debuglist == NULL)
return;
- if (size > mctx->max_size)
- size = mctx->max_size;
-
- dl = ISC_LIST_HEAD(mctx->debuglist[size]);
- while (dl != NULL) {
- for (i = 0; i < DEBUGLIST_COUNT; i++) {
- if (dl->ptr[i] == ptr) {
- dl->ptr[i] = NULL;
- dl->size[i] = 0;
- dl->file[i] = NULL;
- dl->line[i] = 0;
-
- INSIST(dl->count > 0);
- dl->count--;
- if (dl->count == 0) {
- ISC_LIST_UNLINK(mctx->debuglist[size],
- dl, link);
- free(dl);
- mctx->malloced -= sizeof(*dl);
- }
- return;
- }
+ hash = isc_hash_function(&ptr, sizeof(ptr), ISC_TRUE, NULL);
+ idx = hash % DEBUG_TABLE_COUNT;
+
+ dl = ISC_LIST_HEAD(mctx->debuglist[idx]);
+ while (ISC_LIKELY(dl != NULL && dl->ptr != NULL)) {
+ if (ISC_UNLIKELY(dl->ptr == ptr)) {
+ ISC_LIST_UNLINK(mctx->debuglist[idx], dl, link);
+ dl->ptr = NULL;
+ dl->size = 0;
+ dl->file = NULL;
+ dl->line = 0;
+ ISC_LIST_APPEND(mctx->debuglist[idx], dl, link);
+ return;
}
dl = ISC_LIST_NEXT(dl, link);
}
* If we get here, we didn't find the item on the list. We're
* screwed.
*/
- INSIST(dl != NULL);
+ INSIST(0);
}
#endif /* ISC_MEM_TRACKLINES */
}
#if ISC_MEM_TRACKLINES
- if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
+ if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0)) {
unsigned int i;
- ctx->debuglist = (memalloc)(arg,
- (ctx->max_size+1) * sizeof(debuglist_t));
+ ctx->debuglist = (memalloc)(arg, (DEBUG_TABLE_COUNT *
+ sizeof(debuglist_t)));
if (ctx->debuglist == NULL) {
result = ISC_R_NOMEMORY;
goto error;
}
- for (i = 0; i <= ctx->max_size; i++)
+ for (i = 0; i < DEBUG_TABLE_COUNT; i++)
ISC_LIST_INIT(ctx->debuglist[i]);
- ctx->malloced += (ctx->max_size+1) * sizeof(debuglist_t);
- ctx->maxmalloced += (ctx->max_size+1) * sizeof(debuglist_t);
+ ctx->malloced += DEBUG_TABLE_COUNT * sizeof(debuglist_t);
+ ctx->maxmalloced += DEBUG_TABLE_COUNT * sizeof(debuglist_t);
}
#endif
INSIST(ISC_LIST_EMPTY(ctx->pools));
#if ISC_MEM_TRACKLINES
- if (ctx->debuglist != NULL) {
- if (ctx->checkfree) {
- for (i = 0; i <= ctx->max_size; i++) {
- if (!ISC_LIST_EMPTY(ctx->debuglist[i]))
+ if (ISC_UNLIKELY(ctx->debuglist != NULL)) {
+ debuglink_t *dl;
+ for (i = 0; i < DEBUG_TABLE_COUNT; i++)
+ for (dl = ISC_LIST_HEAD(ctx->debuglist[i]);
+ dl != NULL;
+ dl = ISC_LIST_HEAD(ctx->debuglist[i])) {
+ if (ctx->checkfree && dl->ptr != NULL)
print_active(ctx, stderr);
- INSIST(ISC_LIST_EMPTY(ctx->debuglist[i]));
+ INSIST (!ctx->checkfree || dl->ptr == NULL);
+
+ ISC_LIST_UNLINK(ctx->debuglist[i],
+ dl, link);
+ free(dl);
+ ctx->malloced -= sizeof(*dl);
}
- } else {
- debuglink_t *dl;
-
- for (i = 0; i <= ctx->max_size; i++)
- for (dl = ISC_LIST_HEAD(ctx->debuglist[i]);
- dl != NULL;
- dl = ISC_LIST_HEAD(ctx->debuglist[i])) {
- ISC_LIST_UNLINK(ctx->debuglist[i],
- dl, link);
- free(dl);
- ctx->malloced -= sizeof(*dl);
- }
- }
+
(ctx->memfree)(ctx->arg, ctx->debuglist);
- ctx->malloced -= (ctx->max_size+1) * sizeof(debuglist_t);
+ ctx->malloced -= DEBUG_TABLE_COUNT * sizeof(debuglist_t);
}
#endif
INSIST(ctx->references == 0);
*/
*ctxp = NULL;
- if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
+ if (ISC_UNLIKELY((isc_mem_debugging &
+ (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0))
+ {
if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
si = &(((size_info *)ptr)[-1]);
oldsize = si->u.size - ALIGNMENT_SIZE;
REQUIRE(VALID_CONTEXT(ctx));
- if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0)
+ if (ISC_UNLIKELY((isc_mem_debugging &
+ (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0))
return (isc__mem_allocate(ctx0, size FLARG_PASS));
if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
}
ADD_TRACE(ctx, ptr, size, file, line);
+
if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water) {
ctx->is_overmem = ISC_TRUE;
if (!ctx->hi_called)
REQUIRE(VALID_CONTEXT(ctx));
REQUIRE(ptr != NULL);
- if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
+ if (ISC_UNLIKELY((isc_mem_debugging &
+ (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0))
+ {
if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
si = &(((size_info *)ptr)[-1]);
oldsize = si->u.size - ALIGNMENT_SIZE;
print_active(isc__mem_t *mctx, FILE *out) {
if (mctx->debuglist != NULL) {
debuglink_t *dl;
- unsigned int i, j;
+ unsigned int i;
const char *format;
isc_boolean_t found;
format = isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
ISC_MSG_PTRFILELINE,
"\tptr %p size %u file %s line %u\n");
- for (i = 0; i <= mctx->max_size; i++) {
+ for (i = 0; i < DEBUG_TABLE_COUNT; i++) {
dl = ISC_LIST_HEAD(mctx->debuglist[i]);
if (dl != NULL)
found = ISC_TRUE;
while (dl != NULL) {
- for (j = 0; j < DEBUGLIST_COUNT; j++)
- if (dl->ptr[j] != NULL)
- fprintf(out, format,
- dl->ptr[j],
- dl->size[j],
- dl->file[j],
- dl->line[j]);
+ if (dl->ptr != NULL)
+ fprintf(out, format,
+ dl->ptr, dl->size,
+ dl->file, dl->line);
dl = ISC_LIST_NEXT(dl, link);
}
}
+
if (!found)
fputs(isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
ISC_MSG_NONE, "\tNone.\n"), out);
size_info *si;
size += ALIGNMENT_SIZE;
- if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
+ if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0))
size += ALIGNMENT_SIZE;
if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0)
if (si == NULL)
return (NULL);
- if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
+ if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)) {
si->u.ctx = ctx;
si++;
}
}
if (ctx->inuse > ctx->maxinuse) {
ctx->maxinuse = ctx->inuse;
- if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
- (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
+ if (ISC_UNLIKELY(ctx->hi_water != 0U &&
+ ctx->inuse > ctx->hi_water &&
+ (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0))
fprintf(stderr, "maxinuse = %lu\n",
(unsigned long)ctx->inuse);
}
oldsize = (((size_info *)ptr)[-1]).u.size;
INSIST(oldsize >= ALIGNMENT_SIZE);
oldsize -= ALIGNMENT_SIZE;
- if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
+ if (ISC_UNLIKELY((isc_mem_debugging &
+ ISC_MEM_DEBUGCTX) != 0))
+ {
INSIST(oldsize >= ALIGNMENT_SIZE);
oldsize -= ALIGNMENT_SIZE;
}
REQUIRE(VALID_CONTEXT(ctx));
REQUIRE(ptr != NULL);
- if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
+ if (ISC_UNLIKELY((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)) {
si = &(((size_info *)ptr)[-2]);
REQUIRE(si->u.ctx == ctx);
size = si[1].u.size;
UNLOCK(mpctx->lock);
#if ISC_MEM_TRACKLINES
- if (((isc_mem_debugging & TRACE_OR_RECORD) != 0) && item != NULL) {
+ if (ISC_UNLIKELY(((isc_mem_debugging & TRACE_OR_RECORD) != 0) &&
+ item != NULL))
+ {
MCTXLOCK(mctx, &mctx->lock);
ADD_TRACE(mctx, item, mpctx->size, file, line);
MCTXUNLOCK(mctx, &mctx->lock);
mpctx->allocated--;
#if ISC_MEM_TRACKLINES
- if ((isc_mem_debugging & TRACE_OR_RECORD) != 0) {
+ if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0)) {
MCTXLOCK(mctx, &mctx->lock);
DELETE_TRACE(mctx, mem, mpctx->size, file, line);
MCTXUNLOCK(mctx, &mctx->lock);
LOCK(&contextslock);
if (!ISC_LIST_EMPTY(contexts)) {
#if ISC_MEM_TRACKLINES
- if ((isc_mem_debugging & TRACE_OR_RECORD) != 0) {
+ if (ISC_UNLIKELY((isc_mem_debugging & TRACE_OR_RECORD) != 0)) {
isc__mem_t *ctx;
for (ctx = ISC_LIST_HEAD(contexts);
#if ISC_MEM_TRACKLINES
if (ctx->debuglist != NULL) {
summary->contextsize +=
- (ctx->max_size + 1) * sizeof(debuglist_t) +
+ DEBUG_TABLE_COUNT * sizeof(debuglist_t) +
ctx->debuglistcnt * sizeof(debuglink_t);
}
#endif
#if ISC_MEM_TRACKLINES
if (ctx->debuglist != NULL) {
summary->contextsize +=
- (ctx->max_size + 1) * sizeof(debuglist_t) +
+ DEBUG_TABLE_COUNT * sizeof(debuglist_t) +
ctx->debuglistcnt * sizeof(debuglink_t);
}
#endif
#include "isctest.h"
+#include <isc/file.h>
#include <isc/mem.h>
#include <isc/print.h>
#include <isc/result.h>
+#include <isc/stdio.h>
static void *
default_memalloc(void *arg, size_t size) {
isc_test_end();
}
+#if ISC_MEM_TRACKLINES
+ATF_TC(isc_mem_noflags);
+ATF_TC_HEAD(isc_mem_noflags, tc) {
+ atf_tc_set_md_var(tc, "descr", "test mem with no flags");
+}
+
+ATF_TC_BODY(isc_mem_noflags, tc) {
+ isc_result_t result;
+ isc_mem_t *mctx2 = NULL;
+ char buf[4096], *p, *q;
+ FILE *f;
+ void *ptr;
+ size_t size;
+
+ result = isc_stdio_open("mem.output", "w", &f);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_test_begin(NULL, ISC_TRUE);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
+ NULL, &mctx2, 0);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ isc_mem_debugging = 0;
+ ptr = isc_mem_get(mctx2, 2048);
+ ATF_CHECK(ptr != NULL);
+ isc__mem_printactive(mctx2, f);
+ isc_mem_put(mctx2, ptr, 2048);
+ isc_mem_destroy(&mctx2);
+ isc_stdio_close(f);
+
+ result = isc_stdio_open("mem.output", "r", &f);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = isc_stdio_read(buf, sizeof(buf), 1, f, &size);
+ isc_stdio_close(f);
+ isc_file_remove("mem.output");
+
+ p = strchr(buf, '\n');
+ p += 2;
+ q = strchr(p, '\n');
+ *q = '\0';
+ ATF_CHECK_STREQ(p, "None.");
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+ isc_test_end();
+
+}
+
+ATF_TC(isc_mem_recordflag);
+ATF_TC_HEAD(isc_mem_recordflag, tc) {
+ atf_tc_set_md_var(tc, "descr", "test mem with record flag");
+}
+
+ATF_TC_BODY(isc_mem_recordflag, tc) {
+ isc_result_t result;
+ isc_mem_t *mctx2 = NULL;
+ char buf[4096], *p;
+ FILE *f;
+ void *ptr;
+ size_t size;
+
+ result = isc_stdio_open("mem.output", "w", &f);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_test_begin(NULL, ISC_FALSE);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
+ NULL, &mctx2, 0);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ptr = isc_mem_get(mctx2, 2048);
+ ATF_CHECK(ptr != NULL);
+ isc__mem_printactive(mctx2, f);
+ isc_mem_put(mctx2, ptr, 2048);
+ isc_mem_destroy(&mctx2);
+ isc_stdio_close(f);
+
+ result = isc_stdio_open("mem.output", "r", &f);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = isc_stdio_read(buf, sizeof(buf), 1, f, &size);
+ isc_stdio_close(f);
+ isc_file_remove("mem.output");
+
+ p = strchr(buf, '\n');
+ ATF_CHECK(strncmp(p + 2, "ptr ", 4) == 0);
+ p = strchr(p + 1, '\n');
+ ATF_CHECK(strlen(p) == 1);
+
+ isc_test_end();
+}
+
+ATF_TC(isc_mem_traceflag);
+ATF_TC_HEAD(isc_mem_traceflag, tc) {
+ atf_tc_set_md_var(tc, "descr", "test mem with trace flag");
+}
+
+ATF_TC_BODY(isc_mem_traceflag, tc) {
+ isc_result_t result;
+ isc_mem_t *mctx2 = NULL;
+ char buf[4096], *p;
+ FILE *f;
+ void *ptr;
+ size_t size;
+
+ /* redirect stderr so we can check trace output */
+ f = freopen("mem.output", "w", stderr);
+ ATF_REQUIRE(f != NULL);
+
+ result = isc_test_begin(NULL, ISC_TRUE);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+ result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
+ NULL, &mctx2, 0);
+ isc_mem_debugging = ISC_MEM_DEBUGTRACE;
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ ptr = isc_mem_get(mctx2, 2048);
+ ATF_CHECK(ptr != NULL);
+ isc__mem_printactive(mctx2, f);
+ isc_mem_put(mctx2, ptr, 2048);
+ isc_mem_destroy(&mctx2);
+ isc_stdio_close(f);
+
+ result = isc_stdio_open("mem.output", "r", &f);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = isc_stdio_read(buf, sizeof(buf), 1, f, &size);
+ isc_stdio_close(f);
+ isc_file_remove("mem.output");
+
+ /* return stderr to TTY so we can see errors */
+ f = freopen("/dev/tty", "w", stderr);
+
+ ATF_CHECK(strncmp(buf, "add ", 4) == 0);
+ p = strchr(buf, '\n');
+ p = strchr(p + 1, '\n');
+ ATF_CHECK(strncmp(p + 2, "ptr ", 4) == 0);
+ p = strchr(p + 1, '\n');
+ ATF_CHECK(strncmp(p + 1, "del ", 4) == 0);
+
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+ isc_test_end();
+}
+#endif
+
/*
* Main
*/
ATF_TP_ADD_TCS(tp) {
ATF_TP_ADD_TC(tp, isc_mem_total);
ATF_TP_ADD_TC(tp, isc_mem_inuse);
+#if ISC_MEM_TRACKLINES
+ ATF_TP_ADD_TC(tp, isc_mem_noflags);
+ ATF_TP_ADD_TC(tp, isc_mem_recordflag);
+ ATF_TP_ADD_TC(tp, isc_mem_traceflag);
+#endif
return (atf_no_error());
}