From ab735dcbe6ee214e4d2be1e17c79820d1f677156 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 22 Nov 2018 09:11:49 +1030 Subject: [PATCH] gossipd: wire up memleak detection. For simplicity we dump leaks to logs, and just return a bool to master. Signed-off-by: Rusty Russell --- gossipd/broadcast.c | 1 + gossipd/broadcast.h | 1 + gossipd/gossip_wire.csv | 6 +++ gossipd/gossipd.c | 24 +++++++++ gossipd/routing.c | 11 +++++ gossipd/routing.h | 4 ++ gossipd/test/run-bench-find_route.c | 9 ++++ gossipd/test/run-find_route-specific.c | 9 ++++ gossipd/test/run-find_route.c | 9 ++++ lightningd/gossip_control.c | 2 + lightningd/memdump.c | 67 +++++++++++++++++++++++--- 11 files changed, 135 insertions(+), 8 deletions(-) diff --git a/gossipd/broadcast.c b/gossipd/broadcast.c index 1b5a3d2d2..b84a20cfe 100644 --- a/gossipd/broadcast.c +++ b/gossipd/broadcast.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/gossipd/broadcast.h b/gossipd/broadcast.h index d68011e83..e157cacdb 100644 --- a/gossipd/broadcast.h +++ b/gossipd/broadcast.h @@ -35,4 +35,5 @@ const u8 *next_broadcast(struct broadcast_state *bstate, * NULL. */ struct broadcast_state *broadcast_state_check(struct broadcast_state *b, const char *abortstr); + #endif /* LIGHTNING_GOSSIPD_BROADCAST_H */ diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 32a422d4c..67fc54bc4 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -135,6 +135,12 @@ gossip_outpoint_spent,,short_channel_id,struct short_channel_id # master -> gossipd: stop gossip timers. gossip_dev_suppress,3032 +# master -> gossipd: do you have a memleak? +gossip_dev_memleak,3033 + +gossip_dev_memleak_reply,3133 +gossip_dev_memleak_reply,,leak,bool + #include # master -> gossipd: get route_info for our incoming channels diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 5fe32613c..8df0eeb4b 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -2342,6 +2342,26 @@ static struct io_plan *dev_gossip_suppress(struct io_conn *conn, suppress_gossip = true; return daemon_conn_read_next(conn, daemon->master); } + +static struct io_plan *dev_gossip_memleak(struct io_conn *conn, + struct daemon *daemon, + const u8 *msg) +{ + struct htable *memtable; + bool found_leak; + + memtable = memleak_enter_allocations(tmpctx, msg, msg); + + /* Now delete daemon and those which it has pointers to. */ + memleak_remove_referenced(memtable, daemon); + memleak_remove_routing_tables(memtable, daemon->rstate); + + found_leak = dump_memleak(memtable); + daemon_conn_send(daemon->master, + take(towire_gossip_dev_memleak_reply(NULL, + found_leak))); + return daemon_conn_read_next(conn, daemon->master); +} #endif /* DEVELOPER */ /*~ lightningd: so, tell me about this channel, so we can forward to it. */ @@ -2556,12 +2576,15 @@ static struct io_plan *recv_req(struct io_conn *conn, return dev_set_max_scids_encode_size(conn, daemon, msg); case WIRE_GOSSIP_DEV_SUPPRESS: return dev_gossip_suppress(conn, daemon, msg); + case WIRE_GOSSIP_DEV_MEMLEAK: + return dev_gossip_memleak(conn, daemon, msg); #else case WIRE_GOSSIP_QUERY_SCIDS: case WIRE_GOSSIP_SEND_TIMESTAMP_FILTER: case WIRE_GOSSIP_QUERY_CHANNEL_RANGE: case WIRE_GOSSIP_DEV_SET_MAX_SCIDS_ENCODE_SIZE: case WIRE_GOSSIP_DEV_SUPPRESS: + case WIRE_GOSSIP_DEV_MEMLEAK: break; #endif /* !DEVELOPER */ @@ -2575,6 +2598,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIP_GET_CHANNEL_PEER_REPLY: case WIRE_GOSSIP_GET_INCOMING_CHANNELS_REPLY: case WIRE_GOSSIP_GET_TXOUT: + case WIRE_GOSSIP_DEV_MEMLEAK_REPLY: break; } diff --git a/gossipd/routing.c b/gossipd/routing.c index f637ae505..0df4746c3 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -1715,6 +1716,16 @@ void route_prune(struct routing_state *rstate) tal_free(pruned); } +#if DEVELOPER +void memleak_remove_routing_tables(struct htable *memtable, + const struct routing_state *rstate) +{ + memleak_remove_htable(memtable, &rstate->nodes->raw); + memleak_remove_htable(memtable, &rstate->pending_node_map->raw); + memleak_remove_uintmap(memtable, &rstate->broadcasts->broadcasts); +} +#endif /* DEVELOPER */ + bool handle_local_add_channel(struct routing_state *rstate, const u8 *msg) { struct short_channel_id scid; diff --git a/gossipd/routing.h b/gossipd/routing.h index 82220ae29..3c2c16c92 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -327,4 +327,8 @@ bool routing_add_node_announcement(struct routing_state *rstate, */ bool handle_local_add_channel(struct routing_state *rstate, const u8 *msg); +#if DEVELOPER +void memleak_remove_routing_tables(struct htable *memtable, + const struct routing_state *rstate); +#endif #endif /* LIGHTNING_GOSSIPD_ROUTING_H */ diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c index 37872f385..a148b8442 100644 --- a/gossipd/test/run-bench-find_route.c +++ b/gossipd/test/run-bench-find_route.c @@ -140,6 +140,15 @@ const char *wire_type_name(int e UNNEEDED) { fprintf(stderr, "wire_type_name called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ +#if DEVELOPER +/* Generated stub for memleak_remove_htable */ +void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) +{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } +/* Generated stub for memleak_remove_intmap_ */ +void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) +{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } +#endif + /* Updates existing route if required. */ static void add_connection(struct routing_state *rstate, const struct pubkey *from, diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c index 27b365e7c..42e68e764 100644 --- a/gossipd/test/run-find_route-specific.c +++ b/gossipd/test/run-find_route-specific.c @@ -104,6 +104,15 @@ const char *wire_type_name(int e UNNEEDED) { fprintf(stderr, "wire_type_name called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ +#if DEVELOPER +/* Generated stub for memleak_remove_htable */ +void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) +{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } +/* Generated stub for memleak_remove_intmap_ */ +void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) +{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } +#endif + const void *trc; static struct half_chan * diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c index aa0c1ac8a..26b5efffa 100644 --- a/gossipd/test/run-find_route.c +++ b/gossipd/test/run-find_route.c @@ -102,6 +102,15 @@ const char *wire_type_name(int e UNNEEDED) { fprintf(stderr, "wire_type_name called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ +#if DEVELOPER +/* Generated stub for memleak_remove_htable */ +void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) +{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } +/* Generated stub for memleak_remove_intmap_ */ +void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) +{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } +#endif + /* Updates existing route if required. */ static void add_connection(struct routing_state *rstate, const struct pubkey *from, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 82c07e6d3..7ca3e5498 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -118,6 +118,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIP_DEV_SET_MAX_SCIDS_ENCODE_SIZE: case WIRE_GOSSIP_DEV_SUPPRESS: case WIRE_GOSSIP_LOCAL_CHANNEL_CLOSE: + case WIRE_GOSSIP_DEV_MEMLEAK: /* This is a reply, so never gets through to here. */ case WIRE_GOSSIP_GETNODES_REPLY: case WIRE_GOSSIP_GETROUTE_REPLY: @@ -126,6 +127,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIP_QUERY_CHANNEL_RANGE_REPLY: case WIRE_GOSSIP_GET_CHANNEL_PEER_REPLY: case WIRE_GOSSIP_GET_INCOMING_CHANNELS_REPLY: + case WIRE_GOSSIP_DEV_MEMLEAK_REPLY: break; case WIRE_GOSSIP_PING_REPLY: diff --git a/lightningd/memdump.c b/lightningd/memdump.c index d36a5300e..2a74b4797 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -5,12 +5,15 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include #include static void json_add_ptr(struct json_stream *response, const char *name, @@ -111,7 +114,8 @@ static void json_add_backtrace(struct json_stream *response, static void scan_mem(struct command *cmd, struct json_stream *response, - struct lightningd *ld) + struct lightningd *ld, + const struct subd *leaking_subd) { struct htable *memtable; const tal_t *i; @@ -147,14 +151,64 @@ static void scan_mem(struct command *cmd, json_array_end(response); json_object_end(response); } + + if (leaking_subd) { + json_object_start(response, NULL); + json_add_string(response, "subdaemon", leaking_subd->name); + json_object_end(response); + } json_array_end(response); } +struct leak_info { + struct command *cmd; + struct subd *leaker; +}; + +static void report_leak_info2(struct leak_info *leak_info) +{ + struct json_stream *response = json_stream_success(leak_info->cmd); + + json_object_start(response, NULL); + scan_mem(leak_info->cmd, response, leak_info->cmd->ld, leak_info->leaker); + json_object_end(response); + + command_success(leak_info->cmd, response); +} + +static void report_leak_info(struct command *cmd, struct subd *leaker) +{ + struct leak_info *leak_info = tal(cmd, struct leak_info); + + leak_info->cmd = cmd; + leak_info->leaker = leaker; + + /* Leak detection in a reply handler thinks we're leaking conn. */ + notleak(new_reltimer(&leak_info->cmd->ld->timers, leak_info->cmd, + time_from_sec(0), + report_leak_info2, leak_info)); +} + +static void gossip_dev_memleak_done(struct subd *gossipd, + const u8 *reply, + const int *fds UNUSED, + struct command *cmd) +{ + bool found_leak; + + if (!fromwire_gossip_dev_memleak_reply(reply, &found_leak)) { + command_fail(cmd, LIGHTNINGD, "Bad gossip_dev_memleak"); + return; + } + + report_leak_info(cmd, found_leak ? gossipd : NULL); +} + static void json_memleak(struct command *cmd, const char *buffer UNNEEDED, const jsmntok_t *params UNNEEDED) { - struct json_stream *response; + struct lightningd *ld = cmd->ld; if (!param(cmd, buffer, params, NULL)) return; @@ -165,12 +219,9 @@ static void json_memleak(struct command *cmd, return; } - response = json_stream_success(cmd); - json_object_start(response, NULL); - scan_mem(cmd, response, cmd->ld); - json_object_end(response); - - command_success(cmd, response); + subd_req(ld->gossip, ld->gossip, take(towire_gossip_dev_memleak(NULL)), + -1, 0, gossip_dev_memleak_done, cmd); + command_still_pending(cmd); } static const struct json_command dev_memleak_command = {