diff --git a/lightningd/Makefile b/lightningd/Makefile index 68b3191b5..62090aa4f 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -70,6 +70,7 @@ LIGHTNINGD_SRC := \ lightningd/lightningd.c \ lightningd/log.c \ lightningd/log_status.c \ + lightningd/memdump.c \ lightningd/onchain_control.c \ lightningd/opening_control.c \ lightningd/options.c \ @@ -83,11 +84,7 @@ LIGHTNINGD_SRC := \ lightningd/subd.c \ lightningd/watch.c -# Source files without corresponding headers -LIGHTNINGD_SRC_NOHDR := \ - lightningd/memdump.c - -LIGHTNINGD_OBJS := $(LIGHTNINGD_SRC:.c=.o) $(LIGHTNINGD_SRC_NOHDR:.c=.o) +LIGHTNINGD_OBJS := $(LIGHTNINGD_SRC:.c=.o) # Make sure these depend on everything. ALL_OBJS += $(LIGHTNINGD_OBJS) diff --git a/lightningd/memdump.c b/lightningd/memdump.c index 383123d7c..8b7cba511 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -1,5 +1,5 @@ /* Only possible if we're in developer mode. */ -#include "config.h" +#include "memdump.h" #if DEVELOPER #include #include @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -254,13 +255,26 @@ static void hsm_dev_memleak_done(struct subd *hsmd, -1, 0, connect_dev_memleak_done, cmd); } +void opening_memleak_done(struct command *cmd, struct subd *leaker) +{ + if (leaker) + report_leak_info(cmd, leaker); + else { + /* No leak there, try hsmd (we talk to hsm sync) */ + u8 *msg = towire_hsm_dev_memleak(NULL); + if (!wire_sync_write(cmd->ld->hsm_fd, take(msg))) + fatal("Could not write to HSM: %s", strerror(errno)); + + hsm_dev_memleak_done(cmd->ld->hsm, + wire_sync_read(tmpctx, cmd->ld->hsm_fd), + cmd); + } +} + static void json_memleak(struct command *cmd, const char *buffer UNNEEDED, const jsmntok_t *params UNNEEDED) { - struct lightningd *ld = cmd->ld; - u8 *msg; - if (!param(cmd, buffer, params, NULL)) return; @@ -274,12 +288,8 @@ static void json_memleak(struct command *cmd, * immediately. */ command_still_pending(cmd); - /* We talk to hsm sync. */ - msg = towire_hsm_dev_memleak(NULL); - if (!wire_sync_write(ld->hsm_fd, take(msg))) - fatal("Could not write to HSM: %s", strerror(errno)); - - hsm_dev_memleak_done(ld->hsm, wire_sync_read(tmpctx, ld->hsm_fd), cmd); + /* This calls opening_memleak_done() async when all done. */ + opening_dev_memleak(cmd); } static const struct json_command dev_memleak_command = { diff --git a/lightningd/memdump.h b/lightningd/memdump.h new file mode 100644 index 000000000..88741a364 --- /dev/null +++ b/lightningd/memdump.h @@ -0,0 +1,11 @@ +#ifndef LIGHTNING_LIGHTNINGD_MEMDUMP_H +#define LIGHTNING_LIGHTNINGD_MEMDUMP_H +#include "config.h" +#include +#include + +struct command; +struct subd; + +void opening_memleak_done(struct command *cmd, struct subd *leaker); +#endif /* LIGHTNING_LIGHTNINGD_MEMDUMP_H */ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 9e1ad1bdf..6a4c375ff 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -675,6 +675,9 @@ static unsigned int openingd_msg(struct subd *openingd, case WIRE_OPENING_INIT: case WIRE_OPENING_FUNDER: case WIRE_OPENING_CAN_ACCEPT_CHANNEL: + case WIRE_OPENING_DEV_MEMLEAK: + /* Replies never get here */ + case WIRE_OPENING_DEV_MEMLEAK_REPLY: break; } log_broken(openingd->log, "Unexpected msg %s: %s", @@ -855,3 +858,58 @@ static const struct json_command fund_channel_command = { "Fund channel with {id} using {satoshi} (or 'all') satoshis, at optional {feerate}" }; AUTODATA(json_command, &fund_channel_command); + +#if DEVELOPER + /* Indented to avoid include ordering check */ + #include + +/* Mutual recursion */ +static void opening_memleak_req_next(struct command *cmd, struct peer *prev); +static void opening_memleak_req_done(struct subd *openingd, + const u8 *msg, const int *fds UNUSED, + struct command *cmd) +{ + bool found_leak; + struct uncommitted_channel *uc = openingd->channel; + + if (!fromwire_opening_dev_memleak_reply(msg, &found_leak)) { + command_fail(cmd, LIGHTNINGD, "Bad opening_dev_memleak"); + return; + } + + if (found_leak) { + opening_memleak_done(cmd, openingd); + return; + } + opening_memleak_req_next(cmd, uc->peer); +} + +static void opening_memleak_req_next(struct command *cmd, struct peer *prev) +{ + struct peer *p; + + list_for_each(&cmd->ld->peers, p, list) { + if (!p->uncommitted_channel) + continue; + if (p == prev) { + prev = NULL; + continue; + } + if (prev != NULL) + continue; + + /* FIXME: If openingd dies, we'll get stuck here! */ + subd_req(p, + p->uncommitted_channel->openingd, + take(towire_opening_dev_memleak(NULL)), + -1, 0, opening_memleak_req_done, cmd); + return; + } + opening_memleak_done(cmd, NULL); +} + +void opening_dev_memleak(struct command *cmd) +{ + opening_memleak_req_next(cmd, NULL); +} +#endif /* DEVELOPER */ diff --git a/lightningd/opening_control.h b/lightningd/opening_control.h index fc0287c01..616ae1680 100644 --- a/lightningd/opening_control.h +++ b/lightningd/opening_control.h @@ -22,4 +22,10 @@ void opening_peer_no_active_channels(struct peer *peer); void kill_uncommitted_channel(struct uncommitted_channel *uc, const char *why); +#if DEVELOPER +struct command; +/* Calls report_leak_info() async. */ +void opening_dev_memleak(struct command *cmd); +#endif + #endif /* LIGHTNING_LIGHTNINGD_OPENING_CONTROL_H */ diff --git a/openingd/opening_wire.csv b/openingd/opening_wire.csv index 5a25577c5..72cb9fb70 100644 --- a/openingd/opening_wire.csv +++ b/openingd/opening_wire.csv @@ -85,3 +85,9 @@ opening_fundee,,feerate_per_kw,u32 opening_fundee,,msglen,u16 opening_fundee,,funding_signed_msg,msglen*u8 opening_fundee,,our_channel_reserve_satoshis,u64 + +# master -> openingd: do you have a memleak? +opening_dev_memleak,6033 + +opening_dev_memleak_reply,6133 +opening_dev_memleak_reply,,leak,bool diff --git a/openingd/openingd.c b/openingd/openingd.c index 72060c76c..d9813a278 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1043,6 +1044,24 @@ static void fail_if_all_error(const u8 *inner) exit(0); } +#if DEVELOPER +static void handle_dev_memleak(struct state *state, const u8 *msg) +{ + struct htable *memtable; + bool found_leak; + + memtable = memleak_enter_allocations(tmpctx, msg, msg); + + /* Now delete state and things it has pointers to. */ + memleak_remove_referenced(memtable, state); + + found_leak = dump_memleak(memtable); + wire_sync_write(REQ_FD, + take(towire_opening_dev_memleak_reply(NULL, + found_leak))); +} +#endif /* DEVELOPER */ + static u8 *handle_master_in(struct state *state) { u8 *msg = wire_sync_read(tmpctx, REQ_FD); @@ -1076,6 +1095,12 @@ static u8 *handle_master_in(struct state *state) state->can_accept_channel = true; return NULL; + case WIRE_OPENING_DEV_MEMLEAK: +#if DEVELOPER + handle_dev_memleak(state, msg); + return NULL; +#endif + case WIRE_OPENING_DEV_MEMLEAK_REPLY: case WIRE_OPENING_INIT: case WIRE_OPENING_FUNDER_REPLY: case WIRE_OPENING_FUNDEE: