mirror of
https://github.com/isc-projects/bind9.git
synced 2026-03-01 21:10:55 -05:00
Record where memory requests and releases were made.
Double isc_mem_put()'s well now cause an INSIST failures. These are on by default during development but can be turned off by "#define ISC_MEM_RECORD 0" or equivalent.
This commit is contained in:
parent
94551f13c0
commit
feb72cfbe7
2 changed files with 178 additions and 29 deletions
|
|
@ -35,16 +35,18 @@ ISC_LANG_BEGINDECLS
|
|||
#define isc_mempool_put(c, p) __isc_mempool_putdebug(c, p, \
|
||||
__FILE__, __LINE__)
|
||||
#else
|
||||
#define isc_mem_get __isc_mem_get
|
||||
#define isc_mem_put __isc_mem_put
|
||||
#define isc_mem_get(c, s) __isc_mem_get(c, s, __FILE__, __LINE__)
|
||||
#define isc_mem_put(c, p, s) __isc_mem_put(c, p, s, __FILE__, __LINE__)
|
||||
#define isc_mempool_get __isc_mempool_get
|
||||
#define isc_mempool_put __isc_mempool_put
|
||||
#endif /* ISC_MEM_DEBUG */
|
||||
|
||||
isc_result_t isc_mem_create(size_t, size_t, isc_mem_t **);
|
||||
void isc_mem_destroy(isc_mem_t **);
|
||||
void * __isc_mem_get(isc_mem_t *, size_t);
|
||||
void __isc_mem_put(isc_mem_t *, void *, size_t);
|
||||
void * __isc_mem_get(isc_mem_t *, size_t,
|
||||
const char *, int);
|
||||
void __isc_mem_put(isc_mem_t *, void *, size_t,
|
||||
const char *, int);
|
||||
void * __isc_mem_getdebug(isc_mem_t *, size_t,
|
||||
const char *, int);
|
||||
void __isc_mem_putdebug(isc_mem_t *, void *,
|
||||
|
|
@ -56,6 +58,7 @@ void isc_mem_free(isc_mem_t *, void *);
|
|||
char * isc_mem_strdup(isc_mem_t *, const char *);
|
||||
void isc_mem_setquota(isc_mem_t *, size_t);
|
||||
size_t isc_mem_getquota(isc_mem_t *);
|
||||
void isc_mem_allocated(isc_mem_t *, FILE *);
|
||||
|
||||
#ifdef ISC_MEMCLUSTER_LEGACY
|
||||
|
||||
|
|
|
|||
196
lib/isc/mem.c
196
lib/isc/mem.c
|
|
@ -36,22 +36,32 @@
|
|||
#define UNLOCK(l)
|
||||
#endif
|
||||
|
||||
#ifndef ISC_MEM_FILL
|
||||
/*
|
||||
* XXXMPA
|
||||
* We want this on during development to catch:
|
||||
* We want ISC_MEM_FILL and ISC_MEM_RECORD on during development to
|
||||
* catch:
|
||||
* 1. some reference after free bugs.
|
||||
* 2. some failure to initalise bugs.
|
||||
* 3. some double free bugs.
|
||||
* 4. allow us to find where leaked memory was allocated.
|
||||
*/
|
||||
#ifndef ISC_MEM_FILL
|
||||
#define ISC_MEM_FILL 1
|
||||
#endif
|
||||
#ifndef ISC_MEM_RECORD
|
||||
#define ISC_MEM_RECORD 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Types.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
typedef struct element {
|
||||
void * next;
|
||||
#if ISC_MEM_RECORD
|
||||
const char * file;
|
||||
int line;
|
||||
#endif
|
||||
} element;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -78,6 +88,9 @@ struct isc_mem {
|
|||
size_t mem_target;
|
||||
element ** freelists;
|
||||
element * basic_blocks;
|
||||
#if ISC_MEM_RECORD
|
||||
element ** allocated;
|
||||
#endif
|
||||
unsigned char ** basic_table;
|
||||
unsigned int basic_table_count;
|
||||
unsigned int basic_table_size;
|
||||
|
|
@ -110,8 +123,10 @@ struct isc_mempool {
|
|||
/* Forward. */
|
||||
|
||||
static inline size_t quantize(size_t);
|
||||
static inline void mem_putunlocked(isc_mem_t *, void *, size_t);
|
||||
static inline void * mem_getunlocked(isc_mem_t *, size_t);
|
||||
static inline void mem_putunlocked(isc_mem_t *, void *, size_t,
|
||||
const char *, int);
|
||||
static inline void * mem_getunlocked(isc_mem_t *, size_t,
|
||||
const char *, int);
|
||||
|
||||
/* Constants. */
|
||||
|
||||
|
|
@ -133,7 +148,11 @@ quantize(size_t size) {
|
|||
* byte boundaries.
|
||||
*/
|
||||
|
||||
#if ISC_MEM_RECORD
|
||||
temp = size + sizeof(element) + (ALIGNMENT_SIZE - 1);
|
||||
#else
|
||||
temp = size + (ALIGNMENT_SIZE - 1);
|
||||
#endif
|
||||
return (temp - temp % ALIGNMENT_SIZE);
|
||||
}
|
||||
|
||||
|
|
@ -164,8 +183,7 @@ isc_mem_create(size_t init_max_size, size_t target_size,
|
|||
free(ctx);
|
||||
return (ISC_R_NOMEMORY);
|
||||
}
|
||||
memset(ctx->freelists, 0,
|
||||
ctx->max_size * sizeof (element *));
|
||||
memset(ctx->freelists, 0, ctx->max_size * sizeof (element *));
|
||||
ctx->stats = malloc((ctx->max_size+1) * sizeof (struct stats));
|
||||
if (ctx->stats == NULL) {
|
||||
free(ctx->freelists);
|
||||
|
|
@ -173,6 +191,16 @@ isc_mem_create(size_t init_max_size, size_t target_size,
|
|||
return (ISC_R_NOMEMORY);
|
||||
}
|
||||
memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof (struct stats));
|
||||
#if ISC_MEM_RECORD
|
||||
ctx->allocated = malloc((ctx->max_size + 1) * sizeof (element *));
|
||||
if (ctx->allocated == NULL) {
|
||||
free(ctx->freelists);
|
||||
free(ctx->stats);
|
||||
free(ctx);
|
||||
return (ISC_R_NOMEMORY);
|
||||
}
|
||||
memset(ctx->allocated, 0, (ctx->max_size + 1) * sizeof (element *));
|
||||
#endif
|
||||
ctx->basic_blocks = NULL;
|
||||
ctx->basic_table = NULL;
|
||||
ctx->basic_table_count = 0;
|
||||
|
|
@ -182,6 +210,9 @@ isc_mem_create(size_t init_max_size, size_t target_size,
|
|||
if (isc_mutex_init(&ctx->lock) != ISC_R_SUCCESS) {
|
||||
free(ctx->stats);
|
||||
free(ctx->freelists);
|
||||
#if ISC_MEM_RECORD
|
||||
free(ctx->allocated);
|
||||
#endif
|
||||
free(ctx);
|
||||
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
||||
"isc_mutex_init() failed");
|
||||
|
|
@ -196,6 +227,33 @@ isc_mem_create(size_t init_max_size, size_t target_size,
|
|||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
void
|
||||
isc_mem_allocated(isc_mem_t *ctx, FILE *file) {
|
||||
#if ISC_MEM_RECORD
|
||||
unsigned int i;
|
||||
element *el;
|
||||
|
||||
REQUIRE(VALID_CONTEXT(ctx));
|
||||
REQUIRE(file != NULL);
|
||||
|
||||
fprintf(file, "Allocated memory:\n");
|
||||
for (i = 0 ; i <= ctx->max_size; i++) {
|
||||
el = ctx->allocated[i];
|
||||
while (el != NULL) {
|
||||
fprintf(file,
|
||||
"bytes %d: address %p: file %s: line %d\n",
|
||||
i, el + 1,
|
||||
(el->file != NULL) ? el->file : "<UNKNOWN>",
|
||||
el->line);
|
||||
el = el->next;
|
||||
}
|
||||
}
|
||||
#else
|
||||
ctx = ctx; /* unused */
|
||||
file = file; /* unused */
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
isc_mem_destroy(isc_mem_t **ctxp) {
|
||||
unsigned int i;
|
||||
|
|
@ -205,6 +263,10 @@ isc_mem_destroy(isc_mem_t **ctxp) {
|
|||
ctx = *ctxp;
|
||||
REQUIRE(VALID_CONTEXT(ctx));
|
||||
|
||||
#if ISC_MEM_RECORD
|
||||
isc_mem_allocated(ctx, stderr);
|
||||
#endif
|
||||
|
||||
ctx->magic = 0;
|
||||
|
||||
INSIST(ISC_LIST_EMPTY(ctx->pools));
|
||||
|
|
@ -215,6 +277,9 @@ isc_mem_destroy(isc_mem_t **ctxp) {
|
|||
for (i = 0; i < ctx->basic_table_count; i++)
|
||||
free(ctx->basic_table[i]);
|
||||
free(ctx->freelists);
|
||||
#if ISC_MEM_RECORD
|
||||
free(ctx->allocated);
|
||||
#endif
|
||||
free(ctx->stats);
|
||||
free(ctx->basic_table);
|
||||
(void)isc_mutex_destroy(&ctx->lock);
|
||||
|
|
@ -287,7 +352,7 @@ more_basic_blocks(isc_mem_t *ctx) {
|
|||
}
|
||||
|
||||
void *
|
||||
__isc_mem_get(isc_mem_t *ctx, size_t size)
|
||||
__isc_mem_get(isc_mem_t *ctx, size_t size, const char *file, int line)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
|
|
@ -295,17 +360,23 @@ __isc_mem_get(isc_mem_t *ctx, size_t size)
|
|||
REQUIRE(VALID_CONTEXT(ctx));
|
||||
|
||||
LOCK(&ctx->lock);
|
||||
ret = mem_getunlocked(ctx, size);
|
||||
ret = mem_getunlocked(ctx, size, file, line);
|
||||
UNLOCK(&ctx->lock);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
mem_getunlocked(isc_mem_t *ctx, size_t size)
|
||||
mem_getunlocked(isc_mem_t *ctx, size_t size, const char *file, int line)
|
||||
{
|
||||
size_t new_size = quantize(size);
|
||||
void *ret;
|
||||
#if ISC_MEM_RECORD
|
||||
element *el;
|
||||
#else
|
||||
file = file;
|
||||
line = line;
|
||||
#endif
|
||||
|
||||
if (size >= ctx->max_size || new_size >= ctx->max_size) {
|
||||
/* memget() was called on something beyond our upper limit. */
|
||||
|
|
@ -318,7 +389,15 @@ mem_getunlocked(isc_mem_t *ctx, size_t size)
|
|||
ctx->total += size;
|
||||
ctx->stats[ctx->max_size].gets++;
|
||||
ctx->stats[ctx->max_size].totalgets++;
|
||||
#if ISC_MEM_RECORD
|
||||
el = ret;
|
||||
el->file = file;
|
||||
el->line = line;
|
||||
el->next = ctx->allocated[ctx->max_size];
|
||||
ctx->allocated[ctx->max_size] = el;
|
||||
#endif
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
@ -351,11 +430,19 @@ mem_getunlocked(isc_mem_t *ctx, size_t size)
|
|||
next = curr + new_size;
|
||||
for (i = 0; i < (frags - 1); i++) {
|
||||
((element *)curr)->next = next;
|
||||
#if ISC_MEM_RECORD
|
||||
((element *)curr)->file = NULL;
|
||||
((element *)curr)->line = -1;
|
||||
#endif
|
||||
curr = next;
|
||||
next += new_size;
|
||||
}
|
||||
/* curr is now pointing at the last block in the array. */
|
||||
((element *)curr)->next = NULL;
|
||||
#if ISC_MEM_RECORD
|
||||
((element *)curr)->file = NULL;
|
||||
((element *)curr)->line = -1;
|
||||
#endif
|
||||
ctx->freelists[new_size] = new;
|
||||
}
|
||||
|
||||
|
|
@ -371,10 +458,21 @@ mem_getunlocked(isc_mem_t *ctx, size_t size)
|
|||
*/
|
||||
ctx->stats[size].gets++;
|
||||
ctx->stats[size].totalgets++;
|
||||
#if ISC_MEM_RECORD
|
||||
el = ret;
|
||||
el->file = file;
|
||||
el->line = line;
|
||||
el->next = ctx->allocated[size];
|
||||
ctx->allocated[size] = el;
|
||||
#endif
|
||||
ctx->stats[new_size].freefrags--;
|
||||
|
||||
done:
|
||||
|
||||
#if ISC_MEM_RECORD
|
||||
ret = (element *)ret + 1;
|
||||
new_size -= sizeof(element);
|
||||
#endif
|
||||
#if ISC_MEM_FILL
|
||||
if (ret != NULL)
|
||||
memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
|
||||
|
|
@ -384,27 +482,54 @@ mem_getunlocked(isc_mem_t *ctx, size_t size)
|
|||
}
|
||||
|
||||
void
|
||||
__isc_mem_put(isc_mem_t *ctx, void *mem, size_t size)
|
||||
__isc_mem_put(isc_mem_t *ctx, void *mem, size_t size,
|
||||
const char *file, int line)
|
||||
{
|
||||
REQUIRE(size > 0);
|
||||
REQUIRE(VALID_CONTEXT(ctx));
|
||||
|
||||
LOCK(&ctx->lock);
|
||||
mem_putunlocked(ctx, mem, size);
|
||||
mem_putunlocked(ctx, mem, size, file, line);
|
||||
UNLOCK(&ctx->lock);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size)
|
||||
mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size,
|
||||
const char *file, int line)
|
||||
{
|
||||
size_t new_size = quantize(size);
|
||||
|
||||
#if ISC_MEM_FILL
|
||||
memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
|
||||
element *el;
|
||||
#if ISC_MEM_RECORD
|
||||
element *e, *p;
|
||||
#else
|
||||
file = file;
|
||||
line = line;
|
||||
#endif
|
||||
|
||||
#if ISC_MEM_FILL
|
||||
/* Mnemonic for "dead". */
|
||||
memset(mem, 0xde, new_size - sizeof(element));
|
||||
#endif
|
||||
|
||||
#if ISC_MEM_RECORD
|
||||
mem = (element *)mem - 1;
|
||||
#endif
|
||||
el = mem;
|
||||
if (size == ctx->max_size || new_size >= ctx->max_size) {
|
||||
/* memput() called on something beyond our upper limit */
|
||||
#if ISC_MEM_RECORD
|
||||
e = ctx->allocated[ctx->max_size];
|
||||
p = NULL;
|
||||
while (e != NULL && e != el) {
|
||||
p = e;
|
||||
e = e->next;
|
||||
}
|
||||
INSIST(e != NULL);
|
||||
if (p == NULL)
|
||||
ctx->allocated[ctx->max_size] = el->next;
|
||||
else
|
||||
p->next = el->next;
|
||||
#endif
|
||||
free(mem);
|
||||
INSIST(ctx->stats[ctx->max_size].gets != 0);
|
||||
ctx->stats[ctx->max_size].gets--;
|
||||
|
|
@ -413,9 +538,28 @@ mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size)
|
|||
return;
|
||||
}
|
||||
|
||||
#if ISC_MEM_RECORD
|
||||
/* Remove element from allocated list */
|
||||
e = ctx->allocated[size];
|
||||
p = NULL;
|
||||
while (e != NULL && e != el) {
|
||||
p = e;
|
||||
e = e->next;
|
||||
}
|
||||
INSIST(e != NULL);
|
||||
if (p == NULL)
|
||||
ctx->allocated[size] = el->next;
|
||||
else
|
||||
p->next = el->next;
|
||||
#endif
|
||||
|
||||
/* The free list uses the "rounded-up" size "new_size": */
|
||||
((element *)mem)->next = ctx->freelists[new_size];
|
||||
ctx->freelists[new_size] = (element *)mem;
|
||||
el->next = ctx->freelists[new_size];
|
||||
#if ISC_MEM_RECORD
|
||||
el->file = file;
|
||||
el->line = line;
|
||||
#endif
|
||||
ctx->freelists[new_size] = el;
|
||||
|
||||
/*
|
||||
* The stats[] uses the _actual_ "size" requested by the
|
||||
|
|
@ -432,7 +576,7 @@ void *
|
|||
__isc_mem_getdebug(isc_mem_t *ctx, size_t size, const char *file, int line) {
|
||||
void *ptr;
|
||||
|
||||
ptr = __isc_mem_get(ctx, size);
|
||||
ptr = __isc_mem_get(ctx, size, file, line);
|
||||
fprintf(stderr, "%s:%d: mem_get(%p, %lu) -> %p\n", file, line,
|
||||
ctx, (unsigned long)size, ptr);
|
||||
return (ptr);
|
||||
|
|
@ -444,7 +588,7 @@ __isc_mem_putdebug(isc_mem_t *ctx, void *ptr, size_t size, const char *file,
|
|||
{
|
||||
fprintf(stderr, "%s:%d: mem_put(%p, %p, %lu)\n", file, line,
|
||||
ctx, ptr, (unsigned long)size);
|
||||
__isc_mem_put(ctx, ptr, size);
|
||||
__isc_mem_put(ctx, ptr, size, file, line);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -719,7 +863,7 @@ mempool_releaseall(isc_mempool_t *mpctx)
|
|||
|
||||
do {
|
||||
next = item->next;
|
||||
mem_putunlocked(mctx, item, mpctx->size);
|
||||
mem_putunlocked(mctx, item, mpctx->size, __FILE__, __LINE__);
|
||||
INSIST(mpctx->freecount > 0);
|
||||
mpctx->freecount--;
|
||||
item = next;
|
||||
|
|
@ -742,7 +886,8 @@ isc_mempool_create(isc_mem_t *mctx, size_t size,
|
|||
*/
|
||||
LOCK(&mctx->lock);
|
||||
|
||||
mpctx = mem_getunlocked(mctx, sizeof(isc_mempool_t));
|
||||
mpctx = mem_getunlocked(mctx, sizeof(isc_mempool_t),
|
||||
__FILE__, __LINE__);
|
||||
if (mpctx == NULL) {
|
||||
UNLOCK(&mctx->lock);
|
||||
return (ISC_R_NOMEMORY);
|
||||
|
|
@ -795,7 +940,8 @@ isc_mempool_destroy(isc_mempool_t **mpctxp)
|
|||
|
||||
mpctx->magic = 0;
|
||||
|
||||
mem_putunlocked(mpctx->mctx, mpctx, sizeof(isc_mempool_t));
|
||||
mem_putunlocked(mpctx->mctx, mpctx, sizeof(isc_mempool_t),
|
||||
__FILE__, __LINE__);
|
||||
|
||||
UNLOCK(&mctx->lock);
|
||||
|
||||
|
|
@ -838,7 +984,7 @@ __isc_mempool_get(isc_mempool_t *mpctx)
|
|||
*/
|
||||
LOCK(&mctx->lock);
|
||||
for (i = 0 ; i < mpctx->fillcount ; i++) {
|
||||
item = mem_getunlocked(mctx, mpctx->size);
|
||||
item = mem_getunlocked(mctx, mpctx->size, __FILE__, __LINE__);
|
||||
if (item == NULL)
|
||||
break;
|
||||
item->next = mpctx->items;
|
||||
|
|
@ -879,7 +1025,7 @@ __isc_mempool_put(isc_mempool_t *mpctx, void *mem)
|
|||
* If our free list is full, return this to the mctx directly.
|
||||
*/
|
||||
if (mpctx->freecount >= mpctx->freemax) {
|
||||
__isc_mem_put(mctx, mem, mpctx->size);
|
||||
__isc_mem_put(mctx, mem, mpctx->size, __FILE__, __LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue