mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
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>
This commit is contained in:
parent
c956d9f5eb
commit
dfc132b2fe
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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…
Reference in New Issue
Block a user