Skip to content

Commit 75616f6

Browse files
committed
common: add new_htable() macro to allocate, initialize and setup memleak coverage for any typed hash table.
You can now simply add per-tal-object helpers for memleak, but our older pattern required calling memleak functions explicitly during memleak handling. Hash tables in particular need to be dynamically allocated (we override the allocators using htable_set_allocator and assume this), so it makes sense to have a helper macro that does all three. This eliminates a huge amount of code. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent 06f18b1 commit 75616f6

23 files changed

+46
-184
lines changed

channeld/full_channel.c

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,6 @@
1818
/* Needs to be at end, since it doesn't include its own hdrs */
1919
#include "full_channel_error_names_gen.h"
2020

21-
static void memleak_help_htlcmap(struct htable *memtable,
22-
struct htlc_map *htlcs)
23-
{
24-
memleak_scan_htable(memtable, &htlcs->raw);
25-
}
26-
2721
/* This is a dangerous thing! Because we apply HTLCs in many places
2822
* in bulk, we can temporarily go negative. You must check balance_ok()
2923
* at the end! */
@@ -114,11 +108,9 @@ struct channel *new_full_channel(const tal_t *ctx,
114108
option_wumbo,
115109
opener);
116110

117-
if (channel) {
118-
channel->htlcs = tal(channel, struct htlc_map);
119-
htlc_map_init(channel->htlcs);
120-
memleak_add_helper(channel->htlcs, memleak_help_htlcmap);
121-
}
111+
if (channel)
112+
channel->htlcs = new_htable(channel, htlc_map);
113+
122114
return channel;
123115
}
124116

common/memleak.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,18 @@ void memleak_scan_region(struct htable *memtable, const void *p, size_t len);
107107
/* Objects inside this htable (which is opaque to memleak) are not leaks. */
108108
void memleak_scan_htable(struct htable *memtable, const struct htable *ht);
109109

110+
/* Allocate a htable, set up memleak scan automatically (assumes &p->raw == p) */
111+
#define new_htable(ctx, type) \
112+
({ \
113+
const struct htable *raw; \
114+
struct type *p = tal(ctx, struct type); \
115+
type##_init(p); \
116+
raw = &p->raw; \
117+
assert((void *)raw == (void *)p); \
118+
memleak_add_helper(raw, memleak_scan_htable); \
119+
p; \
120+
})
121+
110122
/* Objects inside this uintmap (which is opaque to memleak) are not leaks. */
111123
#define memleak_scan_uintmap(memtable, umap) \
112124
memleak_scan_intmap_(memtable, uintmap_unwrap_(umap))

connectd/connectd.c

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2019,9 +2019,6 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg)
20192019

20202020
/* Now delete daemon and those which it has pointers to. */
20212021
memleak_scan_obj(memtable, daemon);
2022-
memleak_scan_htable(memtable, &daemon->peers->raw);
2023-
memleak_scan_htable(memtable, &daemon->scid_htable->raw);
2024-
memleak_scan_htable(memtable, &daemon->important_ids->raw);
20252022

20262023
found_leak = dump_memleak(memtable, memleak_status_broken, NULL);
20272024
daemon_conn_send(daemon->master,
@@ -2435,14 +2432,6 @@ static struct io_plan *recv_gossip(struct io_conn *conn,
24352432
return daemon_conn_read_next(conn, daemon->gossipd);
24362433
}
24372434

2438-
/*~ This is a hook used by the memleak code: it can't see pointers
2439-
* inside hash tables, so we give it a hint here. */
2440-
static void memleak_daemon_cb(struct htable *memtable, struct daemon *daemon)
2441-
{
2442-
memleak_scan_htable(memtable, &daemon->peers->raw);
2443-
memleak_scan_htable(memtable, &daemon->connecting->raw);
2444-
}
2445-
24462435
static void gossipd_failed(struct daemon_conn *gossipd)
24472436
{
24482437
status_failed(STATUS_FAIL_GOSSIP_IO, "gossipd exited?");
@@ -2462,14 +2451,13 @@ int main(int argc, char *argv[])
24622451
daemon = tal(NULL, struct daemon);
24632452
daemon->developer = developer;
24642453
daemon->connection_counter = 1;
2465-
daemon->peers = tal(daemon, struct peer_htable);
2454+
/* htable_new is our helper which allocates a htable, initializes it
2455+
* and set up the memleak callback so our memleak code can see objects
2456+
* inside it */
2457+
daemon->peers = new_htable(daemon, peer_htable);
24662458
daemon->listeners = tal_arr(daemon, struct io_listener *, 0);
2467-
peer_htable_init(daemon->peers);
2468-
memleak_add_helper(daemon, memleak_daemon_cb);
2469-
daemon->connecting = tal(daemon, struct connecting_htable);
2470-
connecting_htable_init(daemon->connecting);
2471-
daemon->important_ids = tal(daemon, struct important_id_htable);
2472-
important_id_htable_init(daemon->important_ids);
2459+
daemon->connecting = new_htable(daemon, connecting_htable);
2460+
daemon->important_ids = new_htable(daemon, important_id_htable);
24732461
timers_init(&daemon->timers, time_mono());
24742462
daemon->gossmap_raw = NULL;
24752463
daemon->shutting_down = false;
@@ -2479,8 +2467,7 @@ int main(int argc, char *argv[])
24792467
daemon->dev_exhausted_fds = false;
24802468
/* We generally allow 1MB per second per peer, except for dev testing */
24812469
daemon->gossip_stream_limit = 1000000;
2482-
daemon->scid_htable = tal(daemon, struct scid_htable);
2483-
scid_htable_init(daemon->scid_htable);
2470+
daemon->scid_htable = new_htable(daemon, scid_htable);
24842471

24852472
/* stdin == control */
24862473
daemon->master = daemon_conn_new(daemon, STDIN_FILENO, recv_req, NULL,

gossipd/gossipd.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,6 @@ static void dev_gossip_memleak(struct daemon *daemon, const u8 *msg)
472472
memleak_ptr(memtable, msg);
473473
/* Now delete daemon and those which it has pointers to. */
474474
memleak_scan_obj(memtable, daemon);
475-
memleak_scan_htable(memtable, &daemon->peers->raw);
476475
dev_seeker_memleak(memtable, daemon->seeker);
477476
gossmap_manage_memleak(memtable, daemon->gm);
478477

@@ -625,8 +624,7 @@ int main(int argc, char *argv[])
625624
daemon = tal(NULL, struct daemon);
626625
daemon->developer = developer;
627626
daemon->dev_gossip_time = NULL;
628-
daemon->peers = tal(daemon, struct peer_node_id_map);
629-
peer_node_id_map_init(daemon->peers);
627+
daemon->peers = new_htable(daemon, peer_node_id_map);
630628
daemon->deferred_txouts = tal_arr(daemon, struct short_channel_id, 0);
631629
daemon->current_blockheight = 0; /* i.e. unknown */
632630

lightningd/chaintopology.c

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,14 +1220,10 @@ struct chain_topology *new_topology(struct lightningd *ld, struct logger *log)
12201220
struct chain_topology *topo = tal(ld, struct chain_topology);
12211221

12221222
topo->ld = ld;
1223-
topo->block_map = tal(topo, struct block_map);
1224-
block_map_init(topo->block_map);
1225-
topo->outgoing_txs = tal(topo, struct outgoing_tx_map);
1226-
outgoing_tx_map_init(topo->outgoing_txs);
1227-
topo->txwatches = tal(topo, struct txwatch_hash);
1228-
txwatch_hash_init(topo->txwatches);
1229-
topo->txowatches = tal(topo, struct txowatch_hash);
1230-
txowatch_hash_init(topo->txowatches);
1223+
topo->block_map = new_htable(topo, block_map);
1224+
topo->outgoing_txs = new_htable(topo, outgoing_tx_map);
1225+
topo->txwatches = new_htable(topo, txwatch_hash);
1226+
topo->txowatches = new_htable(topo, txowatch_hash);
12311227
topo->log = log;
12321228
topo->bitcoind = new_bitcoind(topo, ld, log);
12331229
topo->poll_seconds = 30;

lightningd/lightningd.c

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -186,42 +186,34 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
186186
* list attached to the channel structure itself, or even left them in
187187
* the database rather than making an in-memory version. Obviously
188188
* I was in a premature optimization mood when I wrote this: */
189-
ld->htlcs_in = tal(ld, struct htlc_in_map);
190-
htlc_in_map_init(ld->htlcs_in);
189+
ld->htlcs_in = new_htable(ld, htlc_in_map);
191190

192191
/*~ Note also: we didn't need to use an allocation here! We could
193192
* have simply made the `struct htlc_out_map` a member. But we
194193
* override the htable allocation routines to use tal(), and they
195194
* want a tal parent, so we always make our hash table a tallocated
196195
* object. */
197-
ld->htlcs_out = tal(ld, struct htlc_out_map);
198-
htlc_out_map_init(ld->htlcs_out);
196+
ld->htlcs_out = new_htable(ld, htlc_out_map);
199197

200198
/*~ This is the hash table of peers: converted from a
201199
* linked-list as part of the 100k-peers project! */
202-
ld->peers = tal(ld, struct peer_node_id_map);
203-
peer_node_id_map_init(ld->peers);
200+
ld->peers = new_htable(ld, peer_node_id_map);
204201
/*~ And this was done at the same time, for db lookups at startup */
205-
ld->peers_by_dbid = tal(ld, struct peer_dbid_map);
206-
peer_dbid_map_init(ld->peers_by_dbid);
202+
ld->peers_by_dbid = new_htable(ld, peer_dbid_map);
207203

208204
/*~ This speeds lookups for short_channel_ids to their channels. */
209-
ld->channels_by_scid = tal(ld, struct channel_scid_map);
210-
channel_scid_map_init(ld->channels_by_scid);
205+
ld->channels_by_scid = new_htable(ld, channel_scid_map);
211206

212207
/*~ Coin movements in db are indexed by the channel dbid. */
213-
ld->channels_by_dbid = tal(ld, struct channel_dbid_map);
214-
channel_dbid_map_init(ld->channels_by_dbid);
208+
ld->channels_by_dbid = new_htable(ld, channel_dbid_map);
215209

216210
/*~ For multi-part payments, we need to keep some incoming payments
217211
* in limbo until we get all the parts, or we time them out. */
218-
ld->htlc_sets = tal(ld, struct htlc_set_map);
219-
htlc_set_map_init(ld->htlc_sets);
212+
ld->htlc_sets = new_htable(ld, htlc_set_map);
220213

221214
/*~ We keep a map of closed channels. Mainly so we can respond to peers
222215
* who talk to us about long-closed channels. */
223-
ld->closed_channels = tal(ld, struct closed_channel_map);
224-
closed_channel_map_init(ld->closed_channels);
216+
ld->closed_channels = new_htable(ld, closed_channel_map);
225217

226218
/*~ We have a multi-entry log-book infrastructure: we define a 10MB log
227219
* book to hold all the entries (and trims as necessary), and multiple

lightningd/memdump.c

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -189,19 +189,6 @@ static bool lightningd_check_leaks(struct command *cmd)
189189
memleak_ptr(memtable, cmd);
190190
memleak_ignore_children(memtable, cmd);
191191

192-
/* First delete known false positives. */
193-
memleak_scan_htable(memtable, &ld->topology->txwatches->raw);
194-
memleak_scan_htable(memtable, &ld->topology->txowatches->raw);
195-
memleak_scan_htable(memtable, &ld->topology->outgoing_txs->raw);
196-
memleak_scan_htable(memtable, &ld->htlcs_in->raw);
197-
memleak_scan_htable(memtable, &ld->htlcs_out->raw);
198-
memleak_scan_htable(memtable, &ld->htlc_sets->raw);
199-
memleak_scan_htable(memtable, &ld->peers->raw);
200-
memleak_scan_htable(memtable, &ld->peers_by_dbid->raw);
201-
memleak_scan_htable(memtable, &ld->channels_by_scid->raw);
202-
memleak_scan_htable(memtable, &ld->closed_channels->raw);
203-
wallet_memleak_scan(memtable, ld->wallet);
204-
205192
/* Now delete ld and those which it has pointers to. */
206193
memleak_scan_obj(memtable, ld);
207194

lightningd/onchain_control.c

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,6 @@ static bool replay_tx_eq_txid(const struct replay_tx *rtx,
4343
HTABLE_DEFINE_NODUPS_TYPE(struct replay_tx, replay_tx_keyof, txid_hash, replay_tx_eq_txid,
4444
replay_tx_hash);
4545

46-
/* Helper for memleak detection */
47-
static void memleak_replay_tx_hash(struct htable *memtable,
48-
struct replay_tx_hash *replay_tx_hash)
49-
{
50-
memleak_scan_htable(memtable, &replay_tx_hash->raw);
51-
}
52-
5346
/* We dump all the known preimages when onchaind starts up. */
5447
static void onchaind_tell_fulfill(struct channel *channel)
5548
{
@@ -1898,11 +1891,8 @@ void onchaind_replay_channels(struct lightningd *ld)
18981891
channel_state_name(channel), blockheight);
18991892

19001893
/* We're in replay mode */
1901-
channel->onchaind_replay_watches = tal(channel, struct replay_tx_hash);
1894+
channel->onchaind_replay_watches = new_htable(channel, replay_tx_hash);
19021895
channel->onchaind_replay_height = blockheight;
1903-
replay_tx_hash_init(channel->onchaind_replay_watches);
1904-
memleak_add_helper(channel->onchaind_replay_watches,
1905-
memleak_replay_tx_hash);
19061896

19071897
onchaind_funding_spent(channel, tx, blockheight);
19081898
onchaind_replay(channel);

plugins/askrene/askrene.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,13 +1291,6 @@ static const struct plugin_command commands[] = {
12911291
},
12921292
};
12931293

1294-
static void askrene_markmem(struct plugin *plugin, struct htable *memtable)
1295-
{
1296-
struct askrene *askrene = get_askrene(plugin);
1297-
layer_memleak_mark(askrene, memtable);
1298-
reserve_memleak_mark(askrene, memtable);
1299-
}
1300-
13011294
static const char *init(struct command *init_cmd,
13021295
const char *buf UNUSED, const jsmntok_t *config UNUSED)
13031296
{
@@ -1317,7 +1310,6 @@ static const char *init(struct command *init_cmd,
13171310
"{id:%}", JSON_SCAN(json_to_node_id, &askrene->my_id));
13181311

13191312
plugin_set_data(plugin, askrene);
1320-
plugin_set_memleak_handler(plugin, askrene_markmem);
13211313

13221314
load_layers(askrene, init_cmd);
13231315

plugins/askrene/layer.c

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,10 @@ struct layer *new_temp_layer(const tal_t *ctx, struct askrene *askrene, const ch
162162
l->askrene = askrene;
163163
l->name = tal_strdup(l, name);
164164
l->persistent = false;
165-
l->local_channels = tal(l, struct local_channel_hash);
166-
local_channel_hash_init(l->local_channels);
167-
l->local_updates = tal(l, struct local_update_hash);
168-
local_update_hash_init(l->local_updates);
169-
l->constraints = tal(l, struct constraint_hash);
170-
constraint_hash_init(l->constraints);
171-
l->biases = tal(l, struct bias_hash);
172-
bias_hash_init(l->biases);
165+
l->local_channels = new_htable(l, local_channel_hash);
166+
l->local_updates = new_htable(l, local_update_hash);
167+
l->constraints = new_htable(l, constraint_hash);
168+
l->biases = new_htable(l, bias_hash);
173169
l->disabled_nodes = tal_arr(l, struct node_id, 0);
174170

175171
return l;
@@ -1162,14 +1158,3 @@ bool layer_disables_node(const struct layer *layer,
11621158
}
11631159
return false;
11641160
}
1165-
1166-
void layer_memleak_mark(struct askrene *askrene, struct htable *memtable)
1167-
{
1168-
struct layer *l;
1169-
list_for_each(&askrene->layers, l, list) {
1170-
memleak_scan_htable(memtable, &l->constraints->raw);
1171-
memleak_scan_htable(memtable, &l->local_channels->raw);
1172-
memleak_scan_htable(memtable, &l->local_updates->raw);
1173-
memleak_scan_htable(memtable, &l->biases->raw);
1174-
}
1175-
}

0 commit comments

Comments
 (0)