]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Refactor tracklines code (#45126)
authorMukund Sivaraman <muks@isc.org>
Thu, 24 Aug 2017 05:28:20 +0000 (10:58 +0530)
committerMukund Sivaraman <muks@isc.org>
Thu, 24 Aug 2017 05:28:55 +0000 (10:58 +0530)
CHANGES
lib/isc/include/isc/mem.h
lib/isc/mem.c
lib/isc/tests/mem_test.c

diff --git a/CHANGES b/CHANGES
index c29c72afbaa3b6b1a9854dd3eaceabeb0bfa9581..8d89c6f8f3b384e8d14e252e4e1d8dc5d3b48a04 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,5 @@
+4687.  [func]          Refactor tracklines code. [RT #45126]
+
 4686.  [bug]           dnssec-settime -p could print a bogus warning about
                        key deletion scheduled before its inactivation when a
                        key had an inactivation date set but no deletion date
index 8e8f7f77433264f9a605e8ddc67fd5c8a8baa684..13f11e0e0c902e74266e26679eb6effd84922753 100644 (file)
@@ -745,6 +745,15 @@ isc__mem_register(void);
  * usually do not have to care about this function: it would call
  * isc_lib_register(), which internally calls this function.
  */
+void
+isc__mem_printactive(isc_mem_t *mctx, FILE *file);
+void
+isc__mem_printallactive(FILE *file);
+/*%<
+ * For internal use by the isc module and its unit tests, these functions
+ * print lists of active memory blocks for a single memory context or for
+ * all contexts.
+ */
 
 ISC_LANG_ENDDECLS
 
index 68bb3b2bd9f263754e387f57df4a43a03ae09870..e139429e619e614ef41ab40bd168b71ac234160e 100644 (file)
@@ -19,6 +19,7 @@
 #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>
@@ -47,7 +48,7 @@ LIBISC_EXTERNAL_DATA unsigned int isc_mem_defaultflags = ISC_MEMFLAG_DEFAULT;
 #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.
@@ -59,13 +60,14 @@ typedef struct isc__mempool isc__mempool_t;
 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
@@ -99,10 +101,6 @@ struct stats {
 #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;
@@ -158,7 +156,7 @@ struct isc__mem {
 
 #if ISC_MEM_TRACKLINES
        debuglist_t *           debuglist;
-       unsigned int            debuglistcnt;
+       size_t                  debuglistcnt;
 #endif
 
        unsigned int            memalloc_failures;
@@ -203,14 +201,14 @@ struct isc__mempool {
 #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)
 
@@ -382,11 +380,11 @@ static struct isc__mempoolmethods {
 /*!
  * 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,
@@ -398,25 +396,18 @@ add_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size FLARG) {
        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));
@@ -426,29 +417,22 @@ add_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size FLARG) {
                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,
@@ -460,28 +444,19 @@ delete_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size,
        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);
        }
@@ -490,7 +465,7 @@ delete_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size,
         * 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 */
 
@@ -1016,19 +991,19 @@ isc_mem_createx2(size_t init_max_size, size_t target_size,
        }
 
 #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
 
@@ -1075,28 +1050,24 @@ destroy(isc__mem_t *ctx) {
        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);
@@ -1210,7 +1181,9 @@ isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) {
         */
        *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;
@@ -1300,7 +1273,8 @@ isc___mem_get(isc_mem_t *ctx0, size_t size FLARG) {
 
        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) {
@@ -1314,6 +1288,7 @@ isc___mem_get(isc_mem_t *ctx0, size_t size FLARG) {
        }
 
        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)
@@ -1344,7 +1319,9 @@ isc___mem_put(isc_mem_t *ctx0, void *ptr, size_t size FLARG) {
        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;
@@ -1403,7 +1380,7 @@ static void
 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;
 
@@ -1415,23 +1392,21 @@ print_active(isc__mem_t *mctx, FILE *out) {
                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);
@@ -1530,7 +1505,7 @@ mem_allocateunlocked(isc_mem_t *ctx0, size_t size) {
        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)
@@ -1540,7 +1515,7 @@ mem_allocateunlocked(isc_mem_t *ctx0, size_t size) {
 
        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++;
        }
@@ -1574,8 +1549,9 @@ isc___mem_allocate(isc_mem_t *ctx0, size_t size FLARG) {
        }
        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);
        }
@@ -1612,7 +1588,9 @@ isc___mem_reallocate(isc_mem_t *ctx0, void *ptr, size_t size FLARG) {
                        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;
                        }
@@ -1636,7 +1614,7 @@ isc___mem_free(isc_mem_t *ctx0, void *ptr FLARG) {
        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;
@@ -2084,7 +2062,9 @@ isc___mempool_get(isc_mempool_t *mpctx0 FLARG) {
                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);
@@ -2113,7 +2093,7 @@ isc___mempool_put(isc_mempool_t *mpctx0, void *mem FLARG) {
        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);
@@ -2343,7 +2323,7 @@ isc_mem_checkdestroyed(FILE *file) {
        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);
@@ -2415,7 +2395,7 @@ xml_renderctx(isc__mem_t *ctx, summarystat_t *summary,
 #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
@@ -2601,7 +2581,7 @@ json_renderctx(isc__mem_t *ctx, summarystat_t *summary, json_object *array) {
 #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
index b2ae45934868717b923425fe9e45ea8edd554b87..f6c67b9055534d94c9ef12da853c70d5e2d72655 100644 (file)
 
 #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) {
@@ -141,12 +143,160 @@ ATF_TC_BODY(isc_mem_inuse, tc) {
        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());
 }