From dfc132b2fe6d0b5e1ca802fd93dcffc00e32993f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 15 Dec 2017 20:48:54 +1030 Subject: [PATCH] memleak: add backtrace to allocations. We use the tal notifiers to attach a `backtrace` object on every allocation. This also means moving backtrace_state from log.c into lightningd.c, so we can hand it to memleak_init(). Signed-off-by: Rusty Russell --- common/memleak.c | 49 ++++++++++++++++++++++++++++-- common/memleak.h | 6 ++-- lightningd/lightningd.c | 14 ++++++++- lightningd/lightningd.h | 7 +++++ lightningd/log.c | 11 +------ lightningd/log.h | 3 -- lightningd/memdump.c | 39 +++++++++++++++++++++++- lightningd/test/run-find_my_path.c | 2 +- 8 files changed, 111 insertions(+), 20 deletions(-) diff --git a/common/memleak.c b/common/memleak.c index 280be92cf..ffc333aea 100644 --- a/common/memleak.c +++ b/common/memleak.c @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -5,6 +7,7 @@ #include #if DEVELOPER +static struct backtrace_state *backtrace_state; static const void **notleaks; void *notleak_(const void *ptr) @@ -46,6 +49,11 @@ static void children_into_htable(const void *exclude, for (i = tal_first(p); i; i = tal_next(i)) { if (p == exclude) continue; + + /* Don't add backtrace objects. */ + if (tal_name(i) && streq(tal_name(i), "backtrace")) + continue; + htable_add(memtable, hash_ptr(i, NULL), i); children_into_htable(exclude, memtable, i); } @@ -108,7 +116,7 @@ static bool ptr_match(const void *candidate, void *ptr) return candidate == ptr; } -const void *memleak_get(struct htable *memtable) +const void *memleak_get(struct htable *memtable, const uintptr_t **backtrace) { struct htable_iter it; const tal_t *i, *p; @@ -128,12 +136,49 @@ const void *memleak_get(struct htable *memtable) /* Delete all children */ remove_with_children(memtable, i); + /* Does it have a child called "backtrace"? */ + for (*backtrace = tal_first(i); + *backtrace; + *backtrace = tal_next(*backtrace)) { + if (tal_name(*backtrace) + && streq(tal_name(*backtrace), "backtrace")) + break; + } + return i; } -void memleak_init(const tal_t *root) +static int append_bt(void *data, uintptr_t pc) { + uintptr_t *bt = data; + + if (bt[0] == 32) + return 1; + + bt[bt[0]++] = pc; + return 0; +} + +static void add_backtrace(tal_t *parent, enum tal_notify_type type UNNEEDED, + void *child) +{ + uintptr_t *bt = tal_alloc_arr_(child, sizeof(uintptr_t), 32, true, true, + "backtrace"); + + /* First serves as counter. */ + bt[0] = 1; + backtrace_simple(backtrace_state, 2, append_bt, NULL, bt); + tal_add_notifier(child, TAL_NOTIFY_ADD_CHILD, add_backtrace); +} + +void memleak_init(const tal_t *root, struct backtrace_state *bstate) +{ + assert(!notleaks); + backtrace_state = bstate; notleaks = tal_arr(NULL, const void *, 0); + + if (backtrace_state) + tal_add_notifier(root, TAL_NOTIFY_ADD_CHILD, add_backtrace); } void memleak_cleanup(void) diff --git a/common/memleak.h b/common/memleak.h index aa3a4ffbb..0858f40ed 100644 --- a/common/memleak.h +++ b/common/memleak.h @@ -2,6 +2,7 @@ #define LIGHTNING_COMMON_MEMLEAK_H #include "config.h" #include +#include #if HAVE_TYPEOF #define memleak_typeof(var) typeof(var) @@ -16,9 +17,10 @@ void *notleak_(const void *ptr); struct htable; +struct backtrace_state; /* Initialize memleak detection, with this as the root */ -void memleak_init(const tal_t *root); +void memleak_init(const tal_t *root, struct backtrace_state *bstate); /* Free memleak detection. */ void memleak_cleanup(void); @@ -33,7 +35,7 @@ void memleak_remove_referenced(struct htable *memtable, const void *root); 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); +const void *memleak_get(struct htable *memtable, const uintptr_t **backtrace); #else /* ... !DEVELOPER */ static inline void *notleak_(const void *ptr) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index d4c40672c..bd9b365fe 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -3,6 +3,7 @@ #include "lightningd.h" #include "peer_control.h" #include "subd.h" +#include #include #include #include @@ -33,6 +34,12 @@ char *bitcoin_datadir; +#if DEVELOPER +bool dev_no_backtrace; +#endif + +struct backtrace_state *backtrace_state; + void db_resolve_invoice(struct lightningd *ld, const char *label); void db_resolve_invoice(struct lightningd *ld, @@ -71,7 +78,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx, ld->dev_disconnect_fd = -1; ld->dev_hsm_seed = NULL; ld->dev_subdaemon_fail = false; - memleak_init(ld); + memleak_init(ld, backtrace_state); #endif list_head_init(&ld->peers); @@ -237,6 +244,11 @@ int main(int argc, char *argv[]) err_set_progname(argv[0]); +#if DEVELOPER + if (!dev_no_backtrace) +#endif + backtrace_state = backtrace_create_state(argv[0], 0, NULL, NULL); + /* Things log on shutdown, so we need this to outlive lightningd */ log_book = new_log_book(NULL, 20*1024*1024, LOG_INFORM); ld = new_lightningd(NULL, log_book); diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 597cac4c4..949eb221f 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -171,4 +171,11 @@ void derive_peer_seed(struct lightningd *ld, struct privkey *peer_seed, const struct pubkey *peer_id, const u64 channel_id); struct chainparams *get_chainparams(const struct lightningd *ld); + +/* State for performing backtraces. */ +struct backtrace_state *backtrace_state; + +#if DEVELOPER +extern bool dev_no_backtrace; +#endif #endif /* LIGHTNING_LIGHTNINGD_LIGHTNINGD_H */ diff --git a/lightningd/log.c b/lightningd/log.c index e0659271b..d11b78834 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -12,18 +12,13 @@ #include #include #include +#include #include #include #include #include #include -#if DEVELOPER -bool dev_no_backtrace; -#endif - -static struct backtrace_state *backtrace_state; - struct log_entry { struct list_node list; struct timeabs time; @@ -475,10 +470,6 @@ void crashlog_activate(const char *argv0, struct log *log) struct sigaction sa; crashlog = log; -#if DEVELOPER - if (!dev_no_backtrace) -#endif - backtrace_state = backtrace_create_state(argv0, 0, NULL, NULL); sa.sa_handler = log_crash; sigemptyset(&sa.sa_mask); /* We want to fall through to default handler */ diff --git a/lightningd/log.h b/lightningd/log.h index 46aa53734..a31ffa6ea 100644 --- a/lightningd/log.h +++ b/lightningd/log.h @@ -95,7 +95,4 @@ const tal_t *ltmp; /* Before the crashlog is activated, just prints to stderr. */ void NORETURN PRINTF_FMT(1,2) fatal(const char *fmt, ...); -#if DEVELOPER -extern bool dev_no_backtrace; -#endif #endif /* LIGHTNING_LIGHTNINGD_LOG_H */ diff --git a/lightningd/memdump.c b/lightningd/memdump.c index c8e2cca5b..2c138c9fd 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -1,5 +1,7 @@ /* Only possible if we're in developer mode. */ #ifdef DEVELOPER +#include +#include #include #include #include @@ -65,12 +67,44 @@ static const struct json_command dev_memdump_command = { }; AUTODATA(json_command, &dev_memdump_command); +static int json_add_syminfo(void *data, uintptr_t pc, + const char *filename, int lineno, + const char *function) +{ + struct json_result *response = data; + char *str; + + str = tal_fmt(response, "%s:%u (%s)", filename, lineno, function); + json_add_string(response, NULL, str); + tal_free(str); + return 0; +} + +static void json_add_backtrace(struct json_result *response, + const uintptr_t *bt) +{ + size_t i; + + if (!bt) + return; + + json_array_start(response, "backtrace"); + /* First one serves as counter. */ + for (i = 1; i < bt[0]; i++) { + backtrace_pcinfo(backtrace_state, + bt[i], json_add_syminfo, + NULL, response); + } + json_array_end(response); +} + static void scan_mem(struct command *cmd, struct json_result *response, struct lightningd *ld) { struct htable *memtable; const tal_t *i; + const uintptr_t *backtrace; /* Enter everything, except this cmd. */ memtable = memleak_enter_allocations(cmd, cmd); @@ -84,7 +118,7 @@ static void scan_mem(struct command *cmd, memleak_remove_referenced(memtable, ld); json_array_start(response, "leaks"); - while ((i = memleak_get(memtable)) != NULL) { + while ((i = memleak_get(memtable, &backtrace)) != NULL) { const tal_t *p; json_object_start(response, NULL); @@ -92,6 +126,7 @@ static void scan_mem(struct command *cmd, if (tal_name(i)) json_add_string(response, "label", tal_name(i)); + json_add_backtrace(response, backtrace); json_array_start(response, "parents"); for (p = tal_parent(i); p; p = tal_parent(p)) { json_add_string(response, NULL, tal_name(p)); @@ -109,7 +144,9 @@ static void json_memleak(struct command *cmd, { struct json_result *response = new_json_result(cmd); + json_object_start(response, NULL); scan_mem(cmd, response, cmd->ld); + json_object_end(response); command_success(cmd, response); } diff --git a/lightningd/test/run-find_my_path.c b/lightningd/test/run-find_my_path.c index 3645585f0..c6b83e842 100644 --- a/lightningd/test/run-find_my_path.c +++ b/lightningd/test/run-find_my_path.c @@ -48,7 +48,7 @@ void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const char *f void memleak_cleanup(void) { fprintf(stderr, "memleak_cleanup called!\n"); abort(); } /* Generated stub for memleak_init */ -void memleak_init(const tal_t *root UNNEEDED) +void memleak_init(const tal_t *root UNNEEDED, struct backtrace_state *bstate UNNEEDED) { fprintf(stderr, "memleak_init called!\n"); abort(); } /* Generated stub for new_log */ struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...)