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/crypto/siphash24/siphash24.h>
#include <ccan/htable/htable.h>
@ -5,6 +7,7 @@
#include <common/memleak.h>
#if DEVELOPER
static struct backtrace_state *backtrace_state;
static const void **notleaks;
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)) {
if (p == exclude)
continue;
/* Don't add backtrace objects. */
if (tal_name(i) && streq(tal_name(i), "backtrace"))
continue;
htable_add(memtable, hash_ptr(i, NULL), i);
children_into_htable(exclude, memtable, i);
}
@ -108,7 +116,7 @@ static bool ptr_match(const void *candidate, void *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;
const tal_t *i, *p;
@ -128,12 +136,49 @@ const void *memleak_get(struct htable *memtable)
/* Delete all children */
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;
}
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);
if (backtrace_state)
tal_add_notifier(root, TAL_NOTIFY_ADD_CHILD, add_backtrace);
}
void memleak_cleanup(void)

6
common/memleak.h

@ -2,6 +2,7 @@
#define LIGHTNING_COMMON_MEMLEAK_H
#include "config.h"
#include <ccan/tal/tal.h>
#include <inttypes.h>
#if HAVE_TYPEOF
#define memleak_typeof(var) typeof(var)
@ -16,9 +17,10 @@
void *notleak_(const void *ptr);
struct htable;
struct backtrace_state;
/* 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. */
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);
/* 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 */
static inline void *notleak_(const void *ptr)

14
lightningd/lightningd.c

@ -3,6 +3,7 @@
#include "lightningd.h"
#include "peer_control.h"
#include "subd.h"
#include <backtrace.h>
#include <ccan/array_size/array_size.h>
#include <ccan/cast/cast.h>
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
@ -33,6 +34,12 @@
char *bitcoin_datadir;
#if DEVELOPER
bool dev_no_backtrace;
#endif
struct backtrace_state *backtrace_state;
void db_resolve_invoice(struct lightningd *ld,
const char *label);
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_hsm_seed = NULL;
ld->dev_subdaemon_fail = false;
memleak_init(ld);
memleak_init(ld, backtrace_state);
#endif
list_head_init(&ld->peers);
@ -237,6 +244,11 @@ int main(int argc, char *argv[])
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 */
log_book = new_log_book(NULL, 20*1024*1024, LOG_INFORM);
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);
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 */

11
lightningd/log.c

@ -12,18 +12,13 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <lightningd/lightningd.h>
#include <signal.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#if DEVELOPER
bool dev_no_backtrace;
#endif
static struct backtrace_state *backtrace_state;
struct log_entry {
struct list_node list;
struct timeabs time;
@ -475,10 +470,6 @@ void crashlog_activate(const char *argv0, struct log *log)
struct sigaction sa;
crashlog = log;
#if DEVELOPER
if (!dev_no_backtrace)
#endif
backtrace_state = backtrace_create_state(argv0, 0, NULL, NULL);
sa.sa_handler = log_crash;
sigemptyset(&sa.sa_mask);
/* 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. */
void NORETURN PRINTF_FMT(1,2) fatal(const char *fmt, ...);
#if DEVELOPER
extern bool dev_no_backtrace;
#endif
#endif /* LIGHTNING_LIGHTNINGD_LOG_H */

39
lightningd/memdump.c

@ -1,5 +1,7 @@
/* Only possible if we're in developer mode. */
#ifdef DEVELOPER
#include <backtrace.h>
#include <ccan/tal/str/str.h>
#include <common/memleak.h>
#include <lightningd/chaintopology.h>
#include <lightningd/jsonrpc.h>
@ -65,12 +67,44 @@ static const struct 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,
struct json_result *response,
struct lightningd *ld)
{
struct htable *memtable;
const tal_t *i;
const uintptr_t *backtrace;
/* Enter everything, except this cmd. */
memtable = memleak_enter_allocations(cmd, cmd);
@ -84,7 +118,7 @@ static void scan_mem(struct command *cmd,
memleak_remove_referenced(memtable, ld);
json_array_start(response, "leaks");
while ((i = memleak_get(memtable)) != NULL) {
while ((i = memleak_get(memtable, &backtrace)) != NULL) {
const tal_t *p;
json_object_start(response, NULL);
@ -92,6 +126,7 @@ static void scan_mem(struct command *cmd,
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));
@ -109,7 +144,9 @@ static void json_memleak(struct command *cmd,
{
struct json_result *response = new_json_result(cmd);
json_object_start(response, NULL);
scan_mem(cmd, response, cmd->ld);
json_object_end(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)
{ fprintf(stderr, "memleak_cleanup called!\n"); abort(); }
/* 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(); }
/* Generated stub for new_log */
struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...)

Loading…
Cancel
Save