Skip to content

Commit

Permalink
common/memleak: implement callback arg for dump_memleak.
Browse files Browse the repository at this point in the history
This makes it easier to use outside simple subds, and now lightningd can
simply dump to log rather than returning JSON.

JSON formatting was a lot of work, and we only did it for lightningd, not for
subdaemons.  Easier to use the logs in all cases.

Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell authored and vincenzopalazzo committed Oct 3, 2023
1 parent 0b680c5 commit e11b35c
Show file tree
Hide file tree
Showing 16 changed files with 70 additions and 98 deletions.
2 changes: 1 addition & 1 deletion channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -5585,7 +5585,7 @@ static void handle_dev_memleak(struct peer *peer, const u8 *msg)
/* Now delete peer and things it has pointers to. */
memleak_scan_obj(memtable, peer);

found_leak = dump_memleak(memtable, memleak_status_broken);
found_leak = dump_memleak(memtable, memleak_status_broken, NULL);
wire_sync_write(MASTER_FD,
take(towire_channeld_dev_memleak_reply(NULL,
found_leak)));
Expand Down
2 changes: 1 addition & 1 deletion closingd/closingd.c
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ static void closing_dev_memleak(const tal_t *ctx,
memleak_ptr(memtable, scriptpubkey[REMOTE]);
memleak_ptr(memtable, funding_wscript);

dump_memleak(memtable, memleak_status_broken);
dump_memleak(memtable, memleak_status_broken, NULL);
}

/* Figure out what weight we actually expect for this closing tx (using zero fees
Expand Down
38 changes: 24 additions & 14 deletions common/memleak.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ static bool ptr_match(const void *candidate, void *ptr)
return candidate == ptr;
}

const void *memleak_get(struct htable *memtable, const uintptr_t **backtrace)
static const void *memleak_get(struct htable *memtable, const uintptr_t **backtrace)
{
struct htable_iter it;
const tal_t *i, *p;
Expand Down Expand Up @@ -330,51 +330,61 @@ void memleak_init(void)
}
}

struct print_and_arg {
void PRINTF_FMT(2,3) (*print)(void *arg, const char *fmt, ...);
void *arg;
};

static int dump_syminfo(void *data, uintptr_t pc UNUSED,
const char *filename, int lineno,
const char *function)
{
void PRINTF_FMT(1,2) (*print)(const char *fmt, ...) = data;
struct print_and_arg *pag = data;
/* This can happen in backtraces. */
if (!filename || !function)
return 0;

print(" %s:%u (%s)", filename, lineno, function);
pag->print(pag->arg, " %s:%u (%s)", filename, lineno, function);
return 0;
}

static void dump_leak_backtrace(const uintptr_t *bt,
void PRINTF_FMT(1,2)
(*print)(const char *fmt, ...))
void (*print)(void *arg, const char *fmt, ...),
void *arg)
{
struct print_and_arg pag;

if (!bt)
return;

pag.print = print;
pag.arg = arg;
/* First one serves as counter. */
print(" backtrace:");
print(arg, " backtrace:");
for (size_t i = 1; i < bt[0]; i++) {
backtrace_pcinfo(backtrace_state,
bt[i], dump_syminfo,
NULL, print);
NULL, &pag);
}
}

bool dump_memleak(struct htable *memtable,
void PRINTF_FMT(1,2) (*print)(const char *fmt, ...))
bool dump_memleak_(struct htable *memtable,
void PRINTF_FMT(2,3) (*print)(void *arg, const char *fmt, ...),
void *arg)
{
const tal_t *i;
const uintptr_t *backtrace;
bool found_leak = false;

while ((i = memleak_get(memtable, &backtrace)) != NULL) {
print("MEMLEAK: %p", i);
print(arg, "MEMLEAK: %p", i);
if (tal_name(i))
print(" label=%s", tal_name(i));
print(arg, " label=%s", tal_name(i));

dump_leak_backtrace(backtrace, print);
print(" parents:");
dump_leak_backtrace(backtrace, print, arg);
print(arg, " parents:");
for (tal_t *p = tal_parent(i); p; p = tal_parent(p)) {
print(" %s", tal_name(p));
print(arg, " %s", tal_name(p));
p = tal_parent(p);
}
found_leak = true;
Expand Down
21 changes: 13 additions & 8 deletions common/memleak.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "config.h"
#include <ccan/strmap/strmap.h>
#include <ccan/tal/tal.h>
#include <ccan/typesafe_cb/typesafe_cb.h>
#include <inttypes.h>

struct htable;
Expand Down Expand Up @@ -131,18 +132,22 @@ void memleak_scan_strmap_(struct htable *memtable, const struct strmap *m);
void memleak_ignore_children(struct htable *memtable, const void *p);

/**
* memleak_get: get (and remove) a leak from memtable, or NULL
* dump_memleak: use print function to dump memleak details
* @memtable: the memtable after all known allocations removed.
* @backtrace: the backtrace to set if there is one.
* @print: the printf-style function to use (takes @arg first)
* @arg: the arg for @print
*
* If this returns NULL, it means the @memtable was empty. Otherwise
* it return a pointer to a leak (and removes it from @memtable)
* Returns true if there was a leak.
*/
const void *memleak_get(struct htable *memtable, const uintptr_t **backtrace);
#define dump_memleak(memtable, print, arg) \
dump_memleak_((memtable), \
typesafe_cb_postargs(void, void *, (print), (arg), const char *, ...), \
(arg))

extern struct backtrace_state *backtrace_state;
bool dump_memleak_(struct htable *memtable,
void PRINTF_FMT(2,3) (*print)(void *arg, const char *fmt, ...),
void *arg);

bool dump_memleak(struct htable *memtable,
void PRINTF_FMT(1,2) (*print)(const char *fmt, ...));
extern struct backtrace_state *backtrace_state;

#endif /* LIGHTNING_COMMON_MEMLEAK_H */
2 changes: 1 addition & 1 deletion common/status.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ void master_badmsg(u32 type_expected, const u8 *msg)
}

/* Print BROKEN status: callback for dump_memleak. */
void memleak_status_broken(const char *fmt, ...)
void memleak_status_broken(void *unused, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
Expand Down
2 changes: 1 addition & 1 deletion common/status.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,6 @@ void status_send_fatal(const u8 *msg TAKES) NORETURN;
void status_send_fd(int fd);

/* Print BROKEN status: callback for dump_memleak. */
void memleak_status_broken(const char *fmt, ...);
void memleak_status_broken(void *unused, const char *fmt, ...);

#endif /* LIGHTNING_COMMON_STATUS_H */
2 changes: 1 addition & 1 deletion connectd/connectd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1861,7 +1861,7 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg)
memleak_scan_obj(memtable, daemon);
memleak_scan_htable(memtable, &daemon->peers->raw);

found_leak = dump_memleak(memtable, memleak_status_broken);
found_leak = dump_memleak(memtable, memleak_status_broken, NULL);
daemon_conn_send(daemon->master,
take(towire_connectd_dev_memleak_reply(NULL,
found_leak)));
Expand Down
2 changes: 1 addition & 1 deletion gossipd/gossipd.c
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ static void dev_gossip_memleak(struct daemon *daemon, const u8 *msg)
memleak_scan_obj(memtable, daemon);
memleak_scan_htable(memtable, &daemon->peers->raw);

found_leak = dump_memleak(memtable, memleak_status_broken);
found_leak = dump_memleak(memtable, memleak_status_broken, NULL);
daemon_conn_send(daemon->master,
take(towire_gossipd_dev_memleak_reply(NULL,
found_leak)));
Expand Down
2 changes: 1 addition & 1 deletion hsmd/hsmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ static struct io_plan *handle_memleak(struct io_conn *conn,
memleak_ptr(memtable, dev_force_privkey);
memleak_ptr(memtable, dev_force_bip32_seed);

found_leak = dump_memleak(memtable, memleak_status_broken);
found_leak = dump_memleak(memtable, memleak_status_broken, NULL);
reply = towire_hsmd_dev_memleak_reply(NULL, found_leak);
return req_reply(conn, c, take(reply));
}
Expand Down
58 changes: 8 additions & 50 deletions lightningd/memdump.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,46 +88,17 @@ static const struct json_command dev_memdump_command = {
};
AUTODATA(json_command, &dev_memdump_command);

static int json_add_syminfo(void *data, uintptr_t pc UNUSED,
const char *filename, int lineno,
const char *function)
static void memleak_log(struct logger *log, const char *fmt, ...)
{
struct json_stream *response = data;
char *str;

/* This can happen in backtraces. */
if (!filename || !function)
return 0;

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_stream *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);
va_list ap;
va_start(ap, fmt);
logv(log, LOG_BROKEN, NULL, true, fmt, ap);
va_end(ap);
}

static void finish_report(const struct leak_detect *leaks)
{
struct htable *memtable;
const tal_t *i;
const uintptr_t *backtrace;
struct command *cmd;
struct lightningd *ld;
struct json_stream *response;
Expand Down Expand Up @@ -162,24 +133,11 @@ static void finish_report(const struct leak_detect *leaks)
/* Now delete ld and those which it has pointers to. */
memleak_scan_obj(memtable, ld);

if (dump_memleak(memtable, memleak_log, ld->log))
tal_arr_expand(&leaks->leakers, "lightningd");

response = json_stream_success(cmd);
json_array_start(response, "leaks");
while ((i = memleak_get(memtable, &backtrace)) != NULL) {
const tal_t *p;

json_object_start(response, NULL);
json_add_ptr(response, "value", i);
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));
json_array_end(response);
json_object_end(response);
}

for (size_t num_leakers = 0;
num_leakers < tal_count(leaks->leakers);
num_leakers++) {
Expand Down
2 changes: 1 addition & 1 deletion onchaind/onchaind.c
Original file line number Diff line number Diff line change
Expand Up @@ -1606,7 +1606,7 @@ static void handle_dev_memleak(struct tracked_output ***outs, const u8 *msg)
memleak_remove_globals(memtable, tal_parent(*outs));
memleak_scan_obj(memtable, *outs);

found_leak = dump_memleak(memtable, memleak_status_broken);
found_leak = dump_memleak(memtable, memleak_status_broken, NULL);
wire_sync_write(REQ_FD,
take(towire_onchaind_dev_memleak_reply(NULL,
found_leak)));
Expand Down
11 changes: 6 additions & 5 deletions onchaind/test/run-grind_feerate-bug.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ bool derive_keyset(const struct pubkey *per_commitment_point UNNEEDED,
bool option_static_remotekey UNNEEDED,
struct keyset *keyset UNNEEDED)
{ fprintf(stderr, "derive_keyset called!\n"); abort(); }
/* Generated stub for dump_memleak */
bool dump_memleak(struct htable *memtable UNNEEDED,
void (*print)(const char *fmt UNNEEDED, ...))
{ fprintf(stderr, "dump_memleak called!\n"); abort(); }
/* Generated stub for dump_memleak_ */
bool dump_memleak_(struct htable *memtable UNNEEDED,
void (*print)(void *arg UNNEEDED, const char *fmt UNNEEDED, ...) UNNEEDED,
void *arg UNNEEDED)
{ fprintf(stderr, "dump_memleak_ called!\n"); abort(); }
/* Generated stub for fromwire_basepoints */
void fromwire_basepoints(const u8 **ptr UNNEEDED, size_t *max UNNEEDED,
struct basepoints *b UNNEEDED)
Expand Down Expand Up @@ -101,7 +102,7 @@ void memleak_scan_obj(struct htable *memtable UNNEEDED, const void *obj UNNEEDED
struct htable *memleak_start(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "memleak_start called!\n"); abort(); }
/* Generated stub for memleak_status_broken */
void memleak_status_broken(const char *fmt UNNEEDED, ...)
void memleak_status_broken(void *unused UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "memleak_status_broken called!\n"); abort(); }
/* Generated stub for new_coin_channel_close */
struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED,
Expand Down
11 changes: 6 additions & 5 deletions onchaind/test/run-grind_feerate.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ bool derive_keyset(const struct pubkey *per_commitment_point UNNEEDED,
bool option_static_remotekey UNNEEDED,
struct keyset *keyset UNNEEDED)
{ fprintf(stderr, "derive_keyset called!\n"); abort(); }
/* Generated stub for dump_memleak */
bool dump_memleak(struct htable *memtable UNNEEDED,
void (*print)(const char *fmt UNNEEDED, ...))
{ fprintf(stderr, "dump_memleak called!\n"); abort(); }
/* Generated stub for dump_memleak_ */
bool dump_memleak_(struct htable *memtable UNNEEDED,
void (*print)(void *arg UNNEEDED, const char *fmt UNNEEDED, ...) UNNEEDED,
void *arg UNNEEDED)
{ fprintf(stderr, "dump_memleak_ called!\n"); abort(); }
/* Generated stub for fromwire */
const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED)
{ fprintf(stderr, "fromwire called!\n"); abort(); }
Expand Down Expand Up @@ -148,7 +149,7 @@ void memleak_scan_obj(struct htable *memtable UNNEEDED, const void *obj UNNEEDED
struct htable *memleak_start(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "memleak_start called!\n"); abort(); }
/* Generated stub for memleak_status_broken */
void memleak_status_broken(const char *fmt UNNEEDED, ...)
void memleak_status_broken(void *unused UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "memleak_status_broken called!\n"); abort(); }
/* Generated stub for new_coin_channel_close */
struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED,
Expand Down
2 changes: 1 addition & 1 deletion openingd/dualopend.c
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ static void handle_dev_memleak(struct state *state, const u8 *msg)
memleak_scan_obj(memtable, state);

/* If there's anything left, dump it to logs, and return true. */
found_leak = dump_memleak(memtable, memleak_status_broken);
found_leak = dump_memleak(memtable, memleak_status_broken, NULL);
wire_sync_write(REQ_FD,
take(towire_dualopend_dev_memleak_reply(NULL,
found_leak)));
Expand Down
2 changes: 1 addition & 1 deletion openingd/openingd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1419,7 +1419,7 @@ static void handle_dev_memleak(struct state *state, const u8 *msg)
memleak_scan_obj(memtable, state);

/* If there's anything left, dump it to logs, and return true. */
found_leak = dump_memleak(memtable, memleak_status_broken);
found_leak = dump_memleak(memtable, memleak_status_broken, NULL);
wire_sync_write(REQ_FD,
take(towire_openingd_dev_memleak_reply(NULL,
found_leak)));
Expand Down
9 changes: 3 additions & 6 deletions plugins/libplugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -1513,14 +1513,12 @@ void plugin_log(struct plugin *p, enum log_level l, const char *fmt, ...)
va_end(ap);
}

/* Hack since we have no extra ptr to log_memleak */
static struct plugin *memleak_plugin;
static void PRINTF_FMT(1,2) log_memleak(const char *fmt, ...)
static void PRINTF_FMT(2,3) log_memleak(struct plugin *plugin, const char *fmt, ...)
{
va_list ap;

va_start(ap, fmt);
plugin_logv(memleak_plugin, LOG_BROKEN, fmt, ap);
plugin_logv(plugin, LOG_BROKEN, fmt, ap);
va_end(ap);
}

Expand All @@ -1546,8 +1544,7 @@ static void memleak_check(struct plugin *plugin, struct command *cmd)
if (plugin->mark_mem)
plugin->mark_mem(plugin, memtable);

memleak_plugin = plugin;
dump_memleak(memtable, log_memleak);
dump_memleak(memtable, log_memleak, plugin);
}

void plugin_set_memleak_handler(struct plugin *plugin,
Expand Down

0 comments on commit e11b35c

Please sign in to comment.