Browse Source
This is a primitive mark-and-sweep-style garbage detector. The core is in common/ for later use by subdaemons, but for now it's just lightningd. We initialize it before most other allocations. We walk the tal tree to get all the pointers, then search the `ld` object for those pointers, recursing down. Some specific helpers are required for hashtables (which stash bits in the unused pointer bits, so won't be found). There's `notleak()` for annotating things that aren't leaks: things like globals and timers, and other semi-transients. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>ppa-0.6.1
Rusty Russell
7 years ago
committed by
Christian Decker
14 changed files with 342 additions and 7 deletions
@ -0,0 +1,143 @@ |
|||
#include <ccan/cast/cast.h> |
|||
#include <ccan/crypto/siphash24/siphash24.h> |
|||
#include <ccan/htable/htable.h> |
|||
#include <ccan/tal/tal.h> |
|||
#include <common/memleak.h> |
|||
|
|||
#if DEVELOPER |
|||
static const void **notleaks; |
|||
|
|||
void *notleak_(const void *ptr) |
|||
{ |
|||
size_t nleaks; |
|||
|
|||
/* If we're not tracking, don't do anything. */ |
|||
if (!notleaks) |
|||
return cast_const(void *, ptr); |
|||
|
|||
/* FIXME: Doesn't work with reallocs, but tal_steal breaks lifetimes */ |
|||
nleaks = tal_count(notleaks); |
|||
tal_resize(¬leaks, nleaks+1); |
|||
notleaks[nleaks] = ptr; |
|||
return cast_const(void *, ptr); |
|||
} |
|||
|
|||
/* This only works if all objects have tal_len() */ |
|||
#ifndef CCAN_TAL_DEBUG |
|||
#error CCAN_TAL_DEBUG must be set |
|||
#endif |
|||
|
|||
static size_t hash_ptr(const void *elem, void *unused UNNEEDED) |
|||
{ |
|||
static struct siphash_seed seed; |
|||
return siphash24(&seed, &elem, sizeof(elem)); |
|||
} |
|||
|
|||
static bool pointer_referenced(struct htable *memtable, const void *p) |
|||
{ |
|||
return htable_del(memtable, hash_ptr(p, NULL), p); |
|||
} |
|||
|
|||
static void children_into_htable(const void *exclude, |
|||
struct htable *memtable, const tal_t *p) |
|||
{ |
|||
const tal_t *i; |
|||
|
|||
for (i = tal_first(p); i; i = tal_next(i)) { |
|||
if (p == exclude) |
|||
continue; |
|||
htable_add(memtable, hash_ptr(i, NULL), i); |
|||
children_into_htable(exclude, memtable, i); |
|||
} |
|||
} |
|||
|
|||
struct htable *memleak_enter_allocations(const tal_t *ctx, const void *exclude) |
|||
{ |
|||
struct htable *memtable = tal(ctx, struct htable); |
|||
htable_init(memtable, hash_ptr, NULL); |
|||
|
|||
/* First, add all pointers off NULL to table. */ |
|||
children_into_htable(exclude, memtable, NULL); |
|||
|
|||
tal_add_destructor(memtable, htable_clear); |
|||
return memtable; |
|||
} |
|||
|
|||
static void scan_for_pointers(struct htable *memtable, const tal_t *p) |
|||
{ |
|||
size_t i, n; |
|||
|
|||
/* Search for (aligned) pointers. */ |
|||
n = tal_len(p) / sizeof(void *); |
|||
for (i = 0; i < n; i++) { |
|||
void *ptr; |
|||
|
|||
memcpy(&ptr, (char *)p + i * sizeof(void *), sizeof(ptr)); |
|||
if (pointer_referenced(memtable, ptr)) |
|||
scan_for_pointers(memtable, ptr); |
|||
} |
|||
} |
|||
|
|||
void memleak_scan_region(struct htable *memtable, const void *ptr) |
|||
{ |
|||
pointer_referenced(memtable, ptr); |
|||
scan_for_pointers(memtable, ptr); |
|||
} |
|||
|
|||
void memleak_remove_referenced(struct htable *memtable, const void *root) |
|||
{ |
|||
/* Now delete the ones which are referenced. */ |
|||
memleak_scan_region(memtable, root); |
|||
memleak_scan_region(memtable, notleaks); |
|||
|
|||
/* Remove memtable itself */ |
|||
pointer_referenced(memtable, memtable); |
|||
} |
|||
|
|||
static void remove_with_children(struct htable *memtable, const tal_t *p) |
|||
{ |
|||
const tal_t *i; |
|||
|
|||
pointer_referenced(memtable, p); |
|||
for (i = tal_first(p); i; i = tal_next(i)) |
|||
remove_with_children(memtable, i); |
|||
} |
|||
|
|||
static bool ptr_match(const void *candidate, void *ptr) |
|||
{ |
|||
return candidate == ptr; |
|||
} |
|||
|
|||
const void *memleak_get(struct htable *memtable) |
|||
{ |
|||
struct htable_iter it; |
|||
const tal_t *i, *p; |
|||
|
|||
i = htable_first(memtable, &it); |
|||
if (!i) |
|||
return NULL; |
|||
|
|||
/* Delete from table (avoids parenting loops) */ |
|||
htable_delval(memtable, &it); |
|||
|
|||
/* Find ancestor, which is probably source of leak. */ |
|||
for (p = tal_parent(i); |
|||
htable_get(memtable, hash_ptr(p, NULL), ptr_match, p); |
|||
i = p, p = tal_parent(i)); |
|||
|
|||
/* Delete all children */ |
|||
remove_with_children(memtable, i); |
|||
|
|||
return i; |
|||
} |
|||
|
|||
void memleak_init(const tal_t *root) |
|||
{ |
|||
notleaks = tal_arr(NULL, const void *, 0); |
|||
} |
|||
|
|||
void memleak_cleanup(void) |
|||
{ |
|||
notleaks = tal_free(notleaks); |
|||
} |
|||
#endif /* DEVELOPER */ |
@ -0,0 +1,45 @@ |
|||
#ifndef LIGHTNING_COMMON_MEMLEAK_H |
|||
#define LIGHTNING_COMMON_MEMLEAK_H |
|||
#include "config.h" |
|||
#include <ccan/tal/tal.h> |
|||
|
|||
#if HAVE_TYPEOF |
|||
#define memleak_typeof(var) typeof(var) |
|||
#else |
|||
#define memleak_typeof(var) void * |
|||
#endif /* !HAVE_TYPEOF */ |
|||
|
|||
/* Mark a pointer as not being leaked. */ |
|||
#define notleak(p) ((memleak_typeof(p))notleak_(p)) |
|||
|
|||
#if DEVELOPER |
|||
void *notleak_(const void *ptr); |
|||
|
|||
struct htable; |
|||
|
|||
/* Initialize memleak detection, with this as the root */ |
|||
void memleak_init(const tal_t *root); |
|||
|
|||
/* Free memleak detection. */ |
|||
void memleak_cleanup(void); |
|||
|
|||
/* Allocate a htable with all the memory we've allocated. */ |
|||
struct htable *memleak_enter_allocations(const tal_t *ctx, const void *exclude); |
|||
|
|||
/* Remove any pointers to memory under root */ |
|||
void memleak_remove_referenced(struct htable *memtable, const void *root); |
|||
|
|||
/* Mark this pointer as being referenced, and search within for more. */ |
|||
void memleak_scan_region(struct htable *memtable, const void *p); |
|||
|
|||
/* Get (and remove) a leak from memtable, or NULL */ |
|||
const void *memleak_get(struct htable *memtable); |
|||
|
|||
#else /* ... !DEVELOPER */ |
|||
static inline void *notleak_(const void *ptr) |
|||
{ |
|||
return ptr; |
|||
} |
|||
#endif /* !DEVELOPER */ |
|||
|
|||
#endif /* LIGHTNING_COMMON_MEMLEAK_H */ |
Loading…
Reference in new issue