common/daemon.c: remove #ifdef DEVELOPER in favor of runtime flag.

Also requires us to expose memleak when !DEVELOPER, however we only
ever used the memleak tracking when the LIGHTNINGD_DEV_MEMLEAK
environment variable was set, so keep that.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2023-09-21 15:06:26 +09:30
parent 67a391f8d0
commit a9f26b7d07
21 changed files with 109 additions and 130 deletions

View File

@ -18,13 +18,11 @@
/* Needs to be at end, since it doesn't include its own hdrs */
#include "full_channel_error_names_gen.h"
#if DEVELOPER
static void memleak_help_htlcmap(struct htable *memtable,
struct htlc_map *htlcs)
{
memleak_scan_htable(memtable, &htlcs->raw);
}
#endif /* DEVELOPER */
/* This is a dangerous thing! Because we apply HTLCs in many places
* in bulk, we can temporarily go negative. You must check balance_ok()

View File

@ -96,34 +96,7 @@ static void crashlog_activate(void)
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
}
#else
void send_backtrace(const char *why)
{
}
const char *backtrace_symname(const tal_t *ctx, const void *addr)
{
return "unknown (backtrace unsupported)";
}
#endif
int daemon_poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
const char *t;
t = taken_any();
if (t)
errx(1, "Outstanding taken pointers: %s", t);
if (wally_tal_ctx)
errx(1, "Outstanding tal_wally_start!");
clean_tmpctx();
return poll(fds, nfds, timeout);
}
#if DEVELOPER && BACKTRACE_SUPPORTED
static void steal_notify(tal_t *child, enum tal_notify_type n, tal_t *newparent)
{
tal_t *p = newparent;
@ -154,8 +127,33 @@ static void remove_steal_notifiers(void)
/* We remove this from root, assuming everything else freed. */
tal_del_notifier(NULL, add_steal_notifier);
}
#else
void send_backtrace(const char *why)
{
}
const char *backtrace_symname(const tal_t *ctx, const void *addr)
{
return "unknown (backtrace unsupported)";
}
#endif
int daemon_poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
const char *t;
t = taken_any();
if (t)
errx(1, "Outstanding taken pointers: %s", t);
if (wally_tal_ctx)
errx(1, "Outstanding tal_wally_start!");
clean_tmpctx();
return poll(fds, nfds, timeout);
}
void daemon_setup(const char *argv0,
void (*backtrace_print)(const char *fmt, ...),
void (*backtrace_exit)(void))
@ -164,22 +162,14 @@ void daemon_setup(const char *argv0,
#if BACKTRACE_SUPPORTED
bt_print = backtrace_print;
bt_exit = backtrace_exit;
#if DEVELOPER
/* Suppresses backtrace (breaks valgrind) */
if (!getenv("LIGHTNINGD_DEV_NO_BACKTRACE"))
backtrace_state = backtrace_create_state(argv0, 0, NULL, NULL);
add_steal_notifiers(NULL);
#else
backtrace_state = backtrace_create_state(argv0, 0, NULL, NULL);
#endif
crashlog_activate();
#endif
#if DEVELOPER
/* This has significant overhead, so we only enable it if told */
if (getenv("LIGHTNINGD_DEV_MEMLEAK"))
memleak_init();
#endif
memleak_init();
/* We handle write returning errors! */
signal(SIGPIPE, SIG_IGN);
@ -191,18 +181,27 @@ void daemon_shutdown(void)
{
common_shutdown();
#if DEVELOPER && BACKTRACE_SUPPORTED
#if BACKTRACE_SUPPORTED
/* Harmless if it wasn't applied */
remove_steal_notifiers();
#endif
}
void daemon_maybe_debug(char *argv[])
bool daemon_developer_mode(char *argv[])
{
#if DEVELOPER
for (int i = 1; argv[i]; i++) {
if (!streq(argv[i], "--debugger"))
continue;
bool developer = false, debug = false;
for (int i = 1; argv[i]; i++) {
if (streq(argv[i], "--debugger"))
debug = true;
else if (streq(argv[i], "--developer"))
developer = true;
}
if (!developer)
return false;
if (debug) {
/* Don't let this mess up stdout, so redir to /dev/null */
char *cmd = tal_fmt(NULL, "${DEBUG_TERM:-gnome-terminal --} gdb -ex 'attach %u' %s >/dev/null &", getpid(), argv[0]);
fprintf(stderr, "Running %s\n", cmd);
@ -214,5 +213,9 @@ void daemon_maybe_debug(char *argv[])
/* Continue in the debugger. */
kill(getpid(), SIGSTOP);
}
#endif /* DEVELOPER */
/* This checks for any tal_steal loops! */
add_steal_notifiers(NULL);
return true;
}

View File

@ -21,7 +21,8 @@ const char *backtrace_symname(const tal_t *ctx, const void *addr);
/* Shutdown for a valgrind-clean exit (frees everything) */
void daemon_shutdown(void);
/* Kick in a debugger if they set --debugger */
void daemon_maybe_debug(char *argv[]);
/* If --developer is set, set up extra developer checks, kick in a
* debugger if they set --debugger, and return true. */
bool daemon_developer_mode(char *argv[]);
#endif /* LIGHTNING_COMMON_DAEMON_H */

View File

@ -7,7 +7,7 @@
* but tal hierarchies tends to get freed at exit, so we need something
* more sophisticated).
*
* Memleak detection is only active if DEVELOPER is set. It does several
* Memleak detection is only active if $LIGHTNINGD_DEV_MEMLEAK is set. It does several
* things:
* 1. attaches a backtrace list to every allocation, so we can tell
* where it came from.
@ -32,7 +32,6 @@
struct backtrace_state *backtrace_state;
#if DEVELOPER
static bool memleak_track;
struct memleak_helper {
@ -324,9 +323,11 @@ struct htable *memleak_start(const tal_t *ctx)
void memleak_init(void)
{
memleak_track = true;
if (backtrace_state)
add_backtrace_notifiers(NULL);
if (getenv("LIGHTNINGD_DEV_MEMLEAK")) {
memleak_track = true;
if (backtrace_state)
add_backtrace_notifiers(NULL);
}
}
static int dump_syminfo(void *data, uintptr_t pc UNUSED,
@ -381,9 +382,3 @@ bool dump_memleak(struct htable *memtable,
return found_leak;
}
#else /* !DEVELOPER */
void *notleak_(void *ptr, bool plus_children UNNEEDED)
{
return ptr;
}
#endif /* !DEVELOPER */

View File

@ -38,7 +38,6 @@ void memleak_init(void);
void *notleak_(void *ptr, bool plus_children);
#if DEVELOPER
/**
* memleak_add_helper: help memleak look inside this tal object
* @p: the tal object
@ -55,10 +54,6 @@ void *notleak_(void *ptr, bool plus_children);
typesafe_cb_preargs(void, const tal_t *, \
(cb), (p), \
struct htable *))
#else
/* Don't refer to cb at all if !DEVELOPER */
#define memleak_add_helper(p, cb)
#endif
/* For update-mock: memleak_add_helper_ mock empty */
void memleak_add_helper_(const tal_t *p, void (*cb)(struct htable *memtable,
@ -147,10 +142,7 @@ const void *memleak_get(struct htable *memtable, const uintptr_t **backtrace);
extern struct backtrace_state *backtrace_state;
#if DEVELOPER
/* Only defined if DEVELOPER */
bool dump_memleak(struct htable *memtable,
void PRINTF_FMT(1,2) (*print)(const char *fmt, ...));
#endif
#endif /* LIGHTNING_COMMON_MEMLEAK_H */

View File

@ -234,7 +234,6 @@ void master_badmsg(u32 type_expected, const u8 *msg)
type_expected, tal_hex(tmpctx, msg));
}
#if DEVELOPER
/* Print BROKEN status: callback for dump_memleak. */
void memleak_status_broken(const char *fmt, ...)
{
@ -243,4 +242,3 @@ void memleak_status_broken(const char *fmt, ...)
status_vfmt(LOG_BROKEN, NULL, fmt, ap);
va_end(ap);
}
#endif

View File

@ -70,9 +70,7 @@ void status_send_fatal(const u8 *msg TAKES) NORETURN;
/* Only for sync status! */
void status_send_fd(int fd);
#if DEVELOPER
/* Print BROKEN status: callback for dump_memleak. */
void memleak_status_broken(const char *fmt, ...);
#endif
#endif /* LIGHTNING_COMMON_STATUS_H */

View File

@ -18,8 +18,10 @@ static void status_backtrace_exit(void)
status_failed(STATUS_FAIL_INTERNAL_ERROR, "FATAL SIGNAL");
}
void subdaemon_setup(int argc, char *argv[])
bool subdaemon_setup(int argc, char *argv[])
{
bool developer;
if (argc == 2 && streq(argv[1], "--version")) {
printf("%s\n", version());
exit(0);
@ -30,7 +32,7 @@ void subdaemon_setup(int argc, char *argv[])
logging_io = true;
}
daemon_maybe_debug(argv);
developer = daemon_developer_mode(argv);
daemon_setup(argv[0], status_backtrace_print, status_backtrace_exit);
return developer;
}

View File

@ -5,7 +5,7 @@
struct htable;
/* daemon_setup, but for subdaemons */
void subdaemon_setup(int argc, char *argv[]);
/* daemon_setup, but for subdaemons: returns true if --developer */
bool subdaemon_setup(int argc, char *argv[]);
#endif /* LIGHTNING_COMMON_SUBDAEMON_H */

View File

@ -1862,7 +1862,6 @@ static void peer_final_msg(struct io_conn *conn,
multiplex_final_msg(peer, take(finalmsg));
}
#if DEVELOPER
static void dev_connect_memleak(struct daemon *daemon, const u8 *msg)
{
struct htable *memtable;
@ -1881,6 +1880,7 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg)
found_leak)));
}
#if DEVELOPER
static void dev_suppress_gossip(struct daemon *daemon, const u8 *msg)
{
daemon->dev_suppress_gossip = true;
@ -2082,10 +2082,11 @@ static struct io_plan *recv_req(struct io_conn *conn,
goto out;
case WIRE_CONNECTD_DEV_MEMLEAK:
#if DEVELOPER
dev_connect_memleak(daemon, msg);
goto out;
#endif
if (daemon->developer) {
dev_connect_memleak(daemon, msg);
goto out;
}
/* Fall thru */
case WIRE_CONNECTD_DEV_SUPPRESS_GOSSIP:
#if DEVELOPER
dev_suppress_gossip(daemon, msg);
@ -2152,14 +2153,12 @@ static struct io_plan *recv_gossip(struct io_conn *conn,
return daemon_conn_read_next(conn, daemon->gossipd);
}
/*~ This is a hook used by the memleak code (if DEVELOPER=1): it can't see
* pointers inside hash tables, so we give it a hint here. */
#if DEVELOPER
/*~ This is a hook used by the memleak code: it can't see pointers
* inside hash tables, so we give it a hint here. */
static void memleak_daemon_cb(struct htable *memtable, struct daemon *daemon)
{
memleak_scan_htable(memtable, &daemon->peers->raw);
}
#endif /* DEVELOPER */
static void gossipd_failed(struct daemon_conn *gossipd)
{
@ -2168,15 +2167,17 @@ static void gossipd_failed(struct daemon_conn *gossipd)
int main(int argc, char *argv[])
{
struct daemon *daemon;
bool developer;
setup_locale();
struct daemon *daemon;
/* Common subdaemon setup code. */
subdaemon_setup(argc, argv);
developer = subdaemon_setup(argc, argv);
/* Allocate and set up our simple top-level structure. */
daemon = tal(NULL, struct daemon);
daemon->developer = developer;
daemon->connection_counter = 1;
daemon->peers = tal(daemon, struct peer_htable);
daemon->listeners = tal_arr(daemon, struct io_listener *, 0);

View File

@ -126,6 +126,9 @@ struct daemon {
/* Who am I? */
struct node_id id;
/* --developer? */
bool developer;
/* pubkey equivalent. */
struct pubkey mykey;

View File

@ -227,7 +227,6 @@ static bool timestamp_reasonable(struct routing_state *rstate, u32 timestamp)
return true;
}
#if DEVELOPER
static void memleak_help_routing_tables(struct htable *memtable,
struct routing_state *rstate)
{
@ -246,7 +245,6 @@ static void memleak_help_routing_tables(struct htable *memtable,
memleak_scan_htable(memtable, &n->chan_map->raw);
}
}
#endif /* DEVELOPER */
/* Once an hour, or at 10000 entries, we expire old ones */
static void txout_failure_age(struct routing_state *rstate)

View File

@ -1239,13 +1239,11 @@ static void destroy_jsonrpc(struct jsonrpc *jsonrpc)
strmap_clear(&jsonrpc->usagemap);
}
#if DEVELOPER
static void memleak_help_jsonrpc(struct htable *memtable,
struct jsonrpc *jsonrpc)
{
memleak_scan_strmap(memtable, &jsonrpc->usagemap);
}
#endif /* DEVELOPER */
void jsonrpc_setup(struct lightningd *ld)
{

View File

@ -82,10 +82,8 @@
#include <wally_bip32.h>
static void destroy_alt_subdaemons(struct lightningd *ld);
#if DEVELOPER
static void memleak_help_alt_subdaemons(struct htable *memtable,
struct lightningd *ld);
#endif /* DEVELOPER */
/*~ The core lightning object: it's passed everywhere, and is basically a
* global variable. This new_xxx pattern is something we'll see often:
@ -377,13 +375,11 @@ static void destroy_alt_subdaemons(struct lightningd *ld)
strmap_clear(&ld->alt_subdaemons);
}
#if DEVELOPER
static void memleak_help_alt_subdaemons(struct htable *memtable,
struct lightningd *ld)
{
memleak_scan_strmap(memtable, &ld->alt_subdaemons);
}
#endif /* DEVELOPER */
const char *subdaemon_path(const tal_t *ctx, const struct lightningd *ld, const char *name)
{

View File

@ -45,13 +45,11 @@ struct plugin_rpccall {
struct jsonrpc_request *request;
};
#if DEVELOPER
static void memleak_help_pending_requests(struct htable *memtable,
struct plugins *plugins)
{
memleak_scan_strmap(memtable, &plugins->pending_requests);
}
#endif /* DEVELOPER */
static const char *state_desc(const struct plugin *plugin)
{

View File

@ -194,7 +194,7 @@ void status_failed(enum status_failreason code UNNEEDED,
void status_setup_sync(int fd UNNEEDED)
{ fprintf(stderr, "status_setup_sync called!\n"); abort(); }
/* Generated stub for subdaemon_setup */
void subdaemon_setup(int argc UNNEEDED, char *argv[])
bool subdaemon_setup(int argc UNNEEDED, char *argv[])
{ fprintf(stderr, "subdaemon_setup called!\n"); abort(); }
/* Generated stub for to_self_wscript */
u8 *to_self_wscript(const tal_t *ctx UNNEEDED,

View File

@ -256,7 +256,7 @@ void status_fmt(enum log_level level UNNEEDED,
void status_setup_sync(int fd UNNEEDED)
{ fprintf(stderr, "status_setup_sync called!\n"); abort(); }
/* Generated stub for subdaemon_setup */
void subdaemon_setup(int argc UNNEEDED, char *argv[])
bool subdaemon_setup(int argc UNNEEDED, char *argv[])
{ fprintf(stderr, "subdaemon_setup called!\n"); abort(); }
/* Generated stub for to_self_wscript */
u8 *to_self_wscript(const tal_t *ctx UNNEEDED,

View File

@ -24,9 +24,9 @@
/* Generated stub for account_entry_tag_str */
const char *account_entry_tag_str(enum account_entry_tag tag UNNEEDED)
{ fprintf(stderr, "account_entry_tag_str called!\n"); abort(); }
/* Generated stub for daemon_maybe_debug */
void daemon_maybe_debug(char *argv[])
{ fprintf(stderr, "daemon_maybe_debug called!\n"); abort(); }
/* Generated stub for daemon_developer_mode */
bool daemon_developer_mode(char *argv[])
{ fprintf(stderr, "daemon_developer_mode called!\n"); abort(); }
/* Generated stub for daemon_setup */
void daemon_setup(const char *argv0 UNNEEDED,
void (*backtrace_print)(const char *fmt UNNEEDED, ...) UNNEEDED,

View File

@ -30,9 +30,9 @@
/* Generated stub for account_entry_tag_str */
const char *account_entry_tag_str(enum account_entry_tag tag UNNEEDED)
{ fprintf(stderr, "account_entry_tag_str called!\n"); abort(); }
/* Generated stub for daemon_maybe_debug */
void daemon_maybe_debug(char *argv[])
{ fprintf(stderr, "daemon_maybe_debug called!\n"); abort(); }
/* Generated stub for daemon_developer_mode */
bool daemon_developer_mode(char *argv[])
{ fprintf(stderr, "daemon_developer_mode called!\n"); abort(); }
/* Generated stub for daemon_setup */
void daemon_setup(const char *argv0 UNNEEDED,
void (*backtrace_print)(const char *fmt UNNEEDED, ...) UNNEEDED,

View File

@ -44,6 +44,8 @@ struct plugin {
struct io_conn *stdin_conn;
struct io_conn *stdout_conn;
bool developer;
/* to append to all our command ids */
const char *id;
@ -109,10 +111,8 @@ struct plugin {
const char **notif_topics;
size_t num_notif_topics;
#if DEVELOPER
/* Lets them remove ptrs from leak detection. */
void (*mark_mem)(struct plugin *plugin, struct htable *memtable);
#endif
};
/* command_result is mainly used as a compile-time check to encourage you
@ -947,14 +947,9 @@ handle_getmanifest(struct command *getmanifest_cmd,
if (streq(p->notif_subs[i].name, "shutdown"))
has_shutdown_notif = true;
}
#if DEVELOPER
/* For memleak detection, always get notified of shutdown. */
if (!has_shutdown_notif)
if (!has_shutdown_notif && p->developer)
json_add_string(params, NULL, "shutdown");
#else
/* Don't care this is unused: compiler don't complain! */
(void)has_shutdown_notif;
#endif
json_array_end(params);
json_array_start(params, "hooks");
@ -1509,7 +1504,6 @@ void plugin_log(struct plugin *p, enum log_level l, const char *fmt, ...)
va_end(ap);
}
#if DEVELOPER
/* 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, ...)
@ -1551,9 +1545,9 @@ void plugin_set_memleak_handler(struct plugin *plugin,
void (*mark_mem)(struct plugin *plugin,
struct htable *memtable))
{
plugin->mark_mem = mark_mem;
if (plugin->developer)
plugin->mark_mem = mark_mem;
}
#endif /* DEVELOPER */
static void ld_command_handle(struct plugin *plugin,
const jsmntok_t *toks)
@ -1600,11 +1594,10 @@ static void ld_command_handle(struct plugin *plugin,
/* If that's a notification. */
if (!cmd->id) {
#if DEVELOPER
bool is_shutdown = streq(cmd->methodname, "shutdown");
if (is_shutdown)
if (is_shutdown && plugin->developer)
memleak_check(plugin, cmd);
#endif
for (size_t i = 0; i < plugin->num_notif_subs; i++) {
if (streq(cmd->methodname,
plugin->notif_subs[i].name)
@ -1616,11 +1609,9 @@ static void ld_command_handle(struct plugin *plugin,
}
}
#if DEVELOPER
/* We subscribe them to this always */
if (is_shutdown)
if (is_shutdown && plugin->developer)
plugin_exit(plugin, 0);
#endif
plugin_err(plugin, "Unregistered notification %.*s",
json_tok_full_len(methtok),
@ -1810,6 +1801,7 @@ static struct io_plan *stdout_conn_init(struct io_conn *conn,
static struct plugin *new_plugin(const tal_t *ctx,
const char *argv0,
bool developer,
const char *(*init)(struct plugin *p,
const char *buf,
const jsmntok_t *),
@ -1834,6 +1826,7 @@ static struct plugin *new_plugin(const tal_t *ctx,
name = path_basename(p, argv0);
name[path_ext_off(name)] = '\0';
p->id = name;
p->developer = developer;
p->buffer = tal_arr(p, char, 64);
list_head_init(&p->js_list);
p->used = 0;
@ -1895,9 +1888,7 @@ static struct plugin *new_plugin(const tal_t *ctx,
tal_arr_expand(&p->opts, o);
}
#if DEVELOPER
p->mark_mem = NULL;
#endif
return p;
}
@ -1919,16 +1910,17 @@ void plugin_main(char *argv[],
{
struct plugin *plugin;
va_list ap;
bool developer;
setup_locale();
daemon_maybe_debug(argv);
developer = daemon_developer_mode(argv);
/* Note this already prints to stderr, which is enough for now */
daemon_setup(argv[0], NULL, NULL);
va_start(ap, num_notif_topics);
plugin = new_plugin(NULL, argv[0],
plugin = new_plugin(NULL, argv[0], developer,
init, restartability, init_rpc, features, commands,
num_commands, notif_subs, num_notif_subs, hook_subs,
num_hook_subs, notif_topics, num_notif_topics, ap);
@ -2132,3 +2124,8 @@ notification_handled(struct command *cmd)
tal_free(cmd);
return &complete;
}
bool plugin_developer_mode(const struct plugin *plugin)
{
return plugin->developer;
}

View File

@ -20,6 +20,7 @@
#include <stdarg.h>
struct json_out;
struct htable;
struct plugin;
struct rpc_conn;
@ -404,6 +405,9 @@ static inline void *plugin_option_cb_check(char *(*set)(struct plugin *plugin,
return set;
}
/* Is --developer enabled? */
bool plugin_developer_mode(const struct plugin *plugin);
/* Macro to define arguments */
#define plugin_option_(name, type, description, set, arg, deprecated, dynamic) \
(name), \
@ -489,12 +493,9 @@ struct route_hop *json_to_route(const tal_t *ctx, const char *buffer,
/* Create a prefix (ending in /) for this cmd_id, if any. */
const char *json_id_prefix(const tal_t *ctx, const struct command *cmd);
#if DEVELOPER
struct htable;
void plugin_set_memleak_handler(struct plugin *plugin,
void (*mark_mem)(struct plugin *plugin,
struct htable *memtable));
#endif /* DEVELOPER */
/* Synchronously call a JSON-RPC method and return its contents and
* the parser token. */