diff --git a/common/subdaemon.c b/common/subdaemon.c index a9e29cefb..ed3b8bd0b 100644 --- a/common/subdaemon.c +++ b/common/subdaemon.c @@ -60,3 +60,57 @@ void subdaemon_setup(int argc, char *argv[]) daemon_setup(argv[0], status_backtrace_print, status_backtrace_exit); } +#if DEVELOPER + // Indented to avoid header-order check + #include + #include + +static int dump_syminfo(void *data UNUSED, uintptr_t pc UNUSED, + const char *filename, int lineno, + const char *function) +{ + /* This can happen in backtraces. */ + if (!filename || !function) + return 0; + + status_trace(" %s:%u (%s)", filename, lineno, function); + return 0; +} + +static void dump_leak_backtrace(const uintptr_t *bt) +{ + if (!bt) + return; + + /* First one serves as counter. */ + status_trace(" backtrace:"); + for (size_t i = 1; i < bt[0]; i++) { + backtrace_pcinfo(backtrace_state, + bt[i], dump_syminfo, + NULL, NULL); + } +} + +bool dump_memleak(struct htable *memtable) +{ + const tal_t *i; + const uintptr_t *backtrace; + bool found_leak = false; + + while ((i = memleak_get(memtable, &backtrace)) != NULL) { + status_broken("MEMLEAK: %p", i); + if (tal_name(i)) + status_broken(" label=%s", tal_name(i)); + + dump_leak_backtrace(backtrace); + status_broken(" parents:"); + for (tal_t *p = tal_parent(i); p; p = tal_parent(p)) { + status_broken(" %s", tal_name(p)); + p = tal_parent(p); + } + found_leak = true; + } + + return found_leak; +} +#endif /* DEVELOPER */ diff --git a/common/subdaemon.h b/common/subdaemon.h index cbdac8ce8..7e18bc3fa 100644 --- a/common/subdaemon.h +++ b/common/subdaemon.h @@ -6,4 +6,10 @@ /* daemon_setup, but for subdaemons */ void subdaemon_setup(int argc, char *argv[]); +#if DEVELOPER +struct htable; + +bool dump_memleak(struct htable *memtable); +#endif + #endif /* LIGHTNING_COMMON_SUBDAEMON_H */