Browse Source

memleak: add backtrace to allocations.

We use the tal notifiers to attach a `backtrace` object on every
allocation.

This also means moving backtrace_state from log.c into lightningd.c, so
we can hand it to memleak_init().

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 7 years ago
committed by Christian Decker
parent
commit
dfc132b2fe
  1. 49
      common/memleak.c
  2. 6
      common/memleak.h
  3. 14
      lightningd/lightningd.c
  4. 7
      lightningd/lightningd.h
  5. 11
      lightningd/log.c
  6. 3
      lightningd/log.h
  7. 39
      lightningd/memdump.c
  8. 2
      lightningd/test/run-find_my_path.c

49
common/memleak.c

@ -1,3 +1,5 @@
#include <assert.h>
#include <backtrace.h>
#include <ccan/cast/cast.h> #include <ccan/cast/cast.h>
#include <ccan/crypto/siphash24/siphash24.h> #include <ccan/crypto/siphash24/siphash24.h>
#include <ccan/htable/htable.h> #include <ccan/htable/htable.h>
@ -5,6 +7,7 @@
#include <common/memleak.h> #include <common/memleak.h>
#if DEVELOPER #if DEVELOPER
static struct backtrace_state *backtrace_state;
static const void **notleaks; static const void **notleaks;
void *notleak_(const void *ptr) void *notleak_(const void *ptr)
@ -46,6 +49,11 @@ static void children_into_htable(const void *exclude,
for (i = tal_first(p); i; i = tal_next(i)) { for (i = tal_first(p); i; i = tal_next(i)) {
if (p == exclude) if (p == exclude)
continue; continue;
/* Don't add backtrace objects. */
if (tal_name(i) && streq(tal_name(i), "backtrace"))
continue;
htable_add(memtable, hash_ptr(i, NULL), i); htable_add(memtable, hash_ptr(i, NULL), i);
children_into_htable(exclude, memtable, i); children_into_htable(exclude, memtable, i);
} }
@ -108,7 +116,7 @@ static bool ptr_match(const void *candidate, void *ptr)
return candidate == ptr; return candidate == ptr;
} }
const void *memleak_get(struct htable *memtable) const void *memleak_get(struct htable *memtable, const uintptr_t **backtrace)
{ {
struct htable_iter it; struct htable_iter it;
const tal_t *i, *p; const tal_t *i, *p;
@ -128,12 +136,49 @@ const void *memleak_get(struct htable *memtable)
/* Delete all children */ /* Delete all children */
remove_with_children(memtable, i); remove_with_children(memtable, i);
/* Does it have a child called "backtrace"? */
for (*backtrace = tal_first(i);
*backtrace;
*backtrace = tal_next(*backtrace)) {
if (tal_name(*backtrace)
&& streq(tal_name(*backtrace), "backtrace"))
break;
}
return i; return i;
} }
void memleak_init(const tal_t *root) static int append_bt(void *data, uintptr_t pc)
{ {
uintptr_t *bt = data;
if (bt[0] == 32)
return 1;
bt[bt[0]++] = pc;
return 0;
}
static void add_backtrace(tal_t *parent, enum tal_notify_type type UNNEEDED,
void *child)
{
uintptr_t *bt = tal_alloc_arr_(child, sizeof(uintptr_t), 32, true, true,
"backtrace");
/* First serves as counter. */
bt[0] = 1;
backtrace_simple(backtrace_state, 2, append_bt, NULL, bt);
tal_add_notifier(child, TAL_NOTIFY_ADD_CHILD, add_backtrace);
}
void memleak_init(const tal_t *root, struct backtrace_state *bstate)
{
assert(!notleaks);
backtrace_state = bstate;
notleaks = tal_arr(NULL, const void *, 0); notleaks = tal_arr(NULL, const void *, 0);
if (backtrace_state)
tal_add_notifier(root, TAL_NOTIFY_ADD_CHILD, add_backtrace);
} }
void memleak_cleanup(void) void memleak_cleanup(void)

6
common/memleak.h

@ -2,6 +2,7 @@
#define LIGHTNING_COMMON_MEMLEAK_H #define LIGHTNING_COMMON_MEMLEAK_H
#include "config.h" #include "config.h"
#include <ccan/tal/tal.h> #include <ccan/tal/tal.h>
#include <inttypes.h>
#if HAVE_TYPEOF #if HAVE_TYPEOF
#define memleak_typeof(var) typeof(var) #define memleak_typeof(var) typeof(var)
@ -16,9 +17,10 @@
void *notleak_(const void *ptr); void *notleak_(const void *ptr);
struct htable; struct htable;
struct backtrace_state;
/* Initialize memleak detection, with this as the root */ /* Initialize memleak detection, with this as the root */
void memleak_init(const tal_t *root); void memleak_init(const tal_t *root, struct backtrace_state *bstate);
/* Free memleak detection. */ /* Free memleak detection. */
void memleak_cleanup(void); void memleak_cleanup(void);
@ -33,7 +35,7 @@ void memleak_remove_referenced(struct htable *memtable, const void *root);
void memleak_scan_region(struct htable *memtable, const void *p); void memleak_scan_region(struct htable *memtable, const void *p);
/* Get (and remove) a leak from memtable, or NULL */ /* Get (and remove) a leak from memtable, or NULL */
const void *memleak_get(struct htable *memtable); const void *memleak_get(struct htable *memtable, const uintptr_t **backtrace);
#else /* ... !DEVELOPER */ #else /* ... !DEVELOPER */
static inline void *notleak_(const void *ptr) static inline void *notleak_(const void *ptr)

14
lightningd/lightningd.c

@ -3,6 +3,7 @@
#include "lightningd.h" #include "lightningd.h"
#include "peer_control.h" #include "peer_control.h"
#include "subd.h" #include "subd.h"
#include <backtrace.h>
#include <ccan/array_size/array_size.h> #include <ccan/array_size/array_size.h>
#include <ccan/cast/cast.h> #include <ccan/cast/cast.h>
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h> #include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
@ -33,6 +34,12 @@
char *bitcoin_datadir; char *bitcoin_datadir;
#if DEVELOPER
bool dev_no_backtrace;
#endif
struct backtrace_state *backtrace_state;
void db_resolve_invoice(struct lightningd *ld, void db_resolve_invoice(struct lightningd *ld,
const char *label); const char *label);
void db_resolve_invoice(struct lightningd *ld, void db_resolve_invoice(struct lightningd *ld,
@ -71,7 +78,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx,
ld->dev_disconnect_fd = -1; ld->dev_disconnect_fd = -1;
ld->dev_hsm_seed = NULL; ld->dev_hsm_seed = NULL;
ld->dev_subdaemon_fail = false; ld->dev_subdaemon_fail = false;
memleak_init(ld); memleak_init(ld, backtrace_state);
#endif #endif
list_head_init(&ld->peers); list_head_init(&ld->peers);
@ -237,6 +244,11 @@ int main(int argc, char *argv[])
err_set_progname(argv[0]); err_set_progname(argv[0]);
#if DEVELOPER
if (!dev_no_backtrace)
#endif
backtrace_state = backtrace_create_state(argv[0], 0, NULL, NULL);
/* Things log on shutdown, so we need this to outlive lightningd */ /* Things log on shutdown, so we need this to outlive lightningd */
log_book = new_log_book(NULL, 20*1024*1024, LOG_INFORM); log_book = new_log_book(NULL, 20*1024*1024, LOG_INFORM);
ld = new_lightningd(NULL, log_book); ld = new_lightningd(NULL, log_book);

7
lightningd/lightningd.h

@ -171,4 +171,11 @@ void derive_peer_seed(struct lightningd *ld, struct privkey *peer_seed,
const struct pubkey *peer_id, const u64 channel_id); const struct pubkey *peer_id, const u64 channel_id);
struct chainparams *get_chainparams(const struct lightningd *ld); struct chainparams *get_chainparams(const struct lightningd *ld);
/* State for performing backtraces. */
struct backtrace_state *backtrace_state;
#if DEVELOPER
extern bool dev_no_backtrace;
#endif
#endif /* LIGHTNING_LIGHTNINGD_LIGHTNINGD_H */ #endif /* LIGHTNING_LIGHTNINGD_LIGHTNINGD_H */

11
lightningd/log.c

@ -12,18 +12,13 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <inttypes.h> #include <inttypes.h>
#include <lightningd/lightningd.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#if DEVELOPER
bool dev_no_backtrace;
#endif
static struct backtrace_state *backtrace_state;
struct log_entry { struct log_entry {
struct list_node list; struct list_node list;
struct timeabs time; struct timeabs time;
@ -475,10 +470,6 @@ void crashlog_activate(const char *argv0, struct log *log)
struct sigaction sa; struct sigaction sa;
crashlog = log; crashlog = log;
#if DEVELOPER
if (!dev_no_backtrace)
#endif
backtrace_state = backtrace_create_state(argv0, 0, NULL, NULL);
sa.sa_handler = log_crash; sa.sa_handler = log_crash;
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
/* We want to fall through to default handler */ /* We want to fall through to default handler */

3
lightningd/log.h

@ -95,7 +95,4 @@ const tal_t *ltmp;
/* Before the crashlog is activated, just prints to stderr. */ /* Before the crashlog is activated, just prints to stderr. */
void NORETURN PRINTF_FMT(1,2) fatal(const char *fmt, ...); void NORETURN PRINTF_FMT(1,2) fatal(const char *fmt, ...);
#if DEVELOPER
extern bool dev_no_backtrace;
#endif
#endif /* LIGHTNING_LIGHTNINGD_LOG_H */ #endif /* LIGHTNING_LIGHTNINGD_LOG_H */

39
lightningd/memdump.c

@ -1,5 +1,7 @@
/* Only possible if we're in developer mode. */ /* Only possible if we're in developer mode. */
#ifdef DEVELOPER #ifdef DEVELOPER
#include <backtrace.h>
#include <ccan/tal/str/str.h>
#include <common/memleak.h> #include <common/memleak.h>
#include <lightningd/chaintopology.h> #include <lightningd/chaintopology.h>
#include <lightningd/jsonrpc.h> #include <lightningd/jsonrpc.h>
@ -65,12 +67,44 @@ static const struct json_command dev_memdump_command = {
}; };
AUTODATA(json_command, &dev_memdump_command); AUTODATA(json_command, &dev_memdump_command);
static int json_add_syminfo(void *data, uintptr_t pc,
const char *filename, int lineno,
const char *function)
{
struct json_result *response = data;
char *str;
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_result *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);
}
static void scan_mem(struct command *cmd, static void scan_mem(struct command *cmd,
struct json_result *response, struct json_result *response,
struct lightningd *ld) struct lightningd *ld)
{ {
struct htable *memtable; struct htable *memtable;
const tal_t *i; const tal_t *i;
const uintptr_t *backtrace;
/* Enter everything, except this cmd. */ /* Enter everything, except this cmd. */
memtable = memleak_enter_allocations(cmd, cmd); memtable = memleak_enter_allocations(cmd, cmd);
@ -84,7 +118,7 @@ static void scan_mem(struct command *cmd,
memleak_remove_referenced(memtable, ld); memleak_remove_referenced(memtable, ld);
json_array_start(response, "leaks"); json_array_start(response, "leaks");
while ((i = memleak_get(memtable)) != NULL) { while ((i = memleak_get(memtable, &backtrace)) != NULL) {
const tal_t *p; const tal_t *p;
json_object_start(response, NULL); json_object_start(response, NULL);
@ -92,6 +126,7 @@ static void scan_mem(struct command *cmd,
if (tal_name(i)) if (tal_name(i))
json_add_string(response, "label", tal_name(i)); json_add_string(response, "label", tal_name(i));
json_add_backtrace(response, backtrace);
json_array_start(response, "parents"); json_array_start(response, "parents");
for (p = tal_parent(i); p; p = tal_parent(p)) { for (p = tal_parent(i); p; p = tal_parent(p)) {
json_add_string(response, NULL, tal_name(p)); json_add_string(response, NULL, tal_name(p));
@ -109,7 +144,9 @@ static void json_memleak(struct command *cmd,
{ {
struct json_result *response = new_json_result(cmd); struct json_result *response = new_json_result(cmd);
json_object_start(response, NULL);
scan_mem(cmd, response, cmd->ld); scan_mem(cmd, response, cmd->ld);
json_object_end(response);
command_success(cmd, response); command_success(cmd, response);
} }

2
lightningd/test/run-find_my_path.c

@ -48,7 +48,7 @@ void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const char *f
void memleak_cleanup(void) void memleak_cleanup(void)
{ fprintf(stderr, "memleak_cleanup called!\n"); abort(); } { fprintf(stderr, "memleak_cleanup called!\n"); abort(); }
/* Generated stub for memleak_init */ /* Generated stub for memleak_init */
void memleak_init(const tal_t *root UNNEEDED) void memleak_init(const tal_t *root UNNEEDED, struct backtrace_state *bstate UNNEEDED)
{ fprintf(stderr, "memleak_init called!\n"); abort(); } { fprintf(stderr, "memleak_init called!\n"); abort(); }
/* Generated stub for new_log */ /* Generated stub for new_log */
struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...) struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...)

Loading…
Cancel
Save