2017-12-15 11:16:54 +01:00
|
|
|
/* Only possible if we're in developer mode. */
|
2018-06-14 04:14:12 +02:00
|
|
|
#include "config.h"
|
2017-12-15 14:26:06 +01:00
|
|
|
#if DEVELOPER
|
2017-12-15 11:18:54 +01:00
|
|
|
#include <backtrace.h>
|
|
|
|
#include <ccan/tal/str/str.h>
|
2018-08-24 06:06:37 +02:00
|
|
|
#include <common/daemon.h>
|
2017-12-15 11:17:54 +01:00
|
|
|
#include <common/memleak.h>
|
|
|
|
#include <lightningd/chaintopology.h>
|
2017-12-15 11:16:54 +01:00
|
|
|
#include <lightningd/jsonrpc.h>
|
2018-05-24 23:40:18 +02:00
|
|
|
#include <lightningd/jsonrpc_errors.h>
|
2017-12-15 11:17:54 +01:00
|
|
|
#include <lightningd/lightningd.h>
|
|
|
|
#include <lightningd/log.h>
|
2018-09-14 16:51:04 +02:00
|
|
|
#include <lightningd/param.h>
|
2017-12-15 11:16:54 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
2018-10-19 03:17:49 +02:00
|
|
|
static void json_add_ptr(struct json_stream *response, const char *name,
|
2017-12-15 11:16:54 +01:00
|
|
|
const void *ptr)
|
|
|
|
{
|
|
|
|
char ptrstr[STR_MAX_CHARS(void *)];
|
2018-07-31 14:56:04 +02:00
|
|
|
snprintf(ptrstr, sizeof(ptrstr), "%p", ptr);
|
2017-12-15 11:16:54 +01:00
|
|
|
json_add_string(response, name, ptrstr);
|
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:49 +02:00
|
|
|
static void add_memdump(struct json_stream *response,
|
2017-12-15 11:16:54 +01:00
|
|
|
const char *name, const tal_t *root,
|
|
|
|
struct command *cmd)
|
|
|
|
{
|
|
|
|
const tal_t *i;
|
|
|
|
|
|
|
|
json_array_start(response, name);
|
|
|
|
for (i = tal_first(root); i; i = tal_next(i)) {
|
|
|
|
const char *name = tal_name(i);
|
|
|
|
|
|
|
|
/* Don't try to dump this command! */
|
|
|
|
if (i == cmd || i == cmd->jcon)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Don't dump logs, we know they grow. */
|
|
|
|
if (name && streq(name, "struct log_book"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
json_object_start(response, NULL);
|
|
|
|
json_add_ptr(response, "parent", tal_parent(i));
|
|
|
|
json_add_ptr(response, "value", i);
|
|
|
|
if (name)
|
|
|
|
json_add_string(response, "label", name);
|
|
|
|
|
|
|
|
if (tal_first(i))
|
|
|
|
add_memdump(response, "children", i, cmd);
|
|
|
|
json_object_end(response);
|
|
|
|
}
|
|
|
|
json_array_end(response);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void json_memdump(struct command *cmd,
|
|
|
|
const char *buffer UNNEEDED,
|
|
|
|
const jsmntok_t *params UNNEEDED)
|
|
|
|
{
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2017-12-15 11:16:54 +01:00
|
|
|
|
2018-09-14 16:51:04 +02:00
|
|
|
if (!param(cmd, buffer, params, NULL))
|
|
|
|
return;
|
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
2017-12-15 11:16:54 +01:00
|
|
|
add_memdump(response, NULL, NULL, cmd);
|
|
|
|
|
|
|
|
command_success(cmd, response);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command dev_memdump_command = {
|
|
|
|
"dev-memdump",
|
|
|
|
json_memdump,
|
2018-01-22 09:55:07 +01:00
|
|
|
"Show memory objects currently in use"
|
2017-12-15 11:16:54 +01:00
|
|
|
};
|
|
|
|
AUTODATA(json_command, &dev_memdump_command);
|
2017-12-15 11:17:54 +01:00
|
|
|
|
2018-02-21 16:06:07 +01:00
|
|
|
static int json_add_syminfo(void *data, uintptr_t pc UNUSED,
|
2017-12-15 11:18:54 +01:00
|
|
|
const char *filename, int lineno,
|
|
|
|
const char *function)
|
|
|
|
{
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response = data;
|
2017-12-15 11:18:54 +01:00
|
|
|
char *str;
|
|
|
|
|
2018-03-15 05:30:12 +01:00
|
|
|
/* This can happen in backtraces. */
|
|
|
|
if (!filename || !function)
|
|
|
|
return 0;
|
|
|
|
|
2017-12-15 11:18:54 +01:00
|
|
|
str = tal_fmt(response, "%s:%u (%s)", filename, lineno, function);
|
|
|
|
json_add_string(response, NULL, str);
|
|
|
|
tal_free(str);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-19 03:17:49 +02:00
|
|
|
static void json_add_backtrace(struct json_stream *response,
|
2017-12-15 11:18:54 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-12-15 11:17:54 +01:00
|
|
|
static void scan_mem(struct command *cmd,
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response,
|
2017-12-15 11:17:54 +01:00
|
|
|
struct lightningd *ld)
|
|
|
|
{
|
|
|
|
struct htable *memtable;
|
|
|
|
const tal_t *i;
|
2017-12-15 11:18:54 +01:00
|
|
|
const uintptr_t *backtrace;
|
2017-12-15 11:17:54 +01:00
|
|
|
|
2017-12-15 11:20:54 +01:00
|
|
|
/* Enter everything, except this cmd and its jcon */
|
|
|
|
memtable = memleak_enter_allocations(cmd, cmd, cmd->jcon);
|
2017-12-15 11:17:54 +01:00
|
|
|
|
|
|
|
/* First delete known false positives. */
|
2018-08-24 07:20:02 +02:00
|
|
|
memleak_remove_htable(memtable, &ld->topology->txwatches.raw);
|
|
|
|
memleak_remove_htable(memtable, &ld->topology->txowatches.raw);
|
|
|
|
memleak_remove_htable(memtable, &ld->htlcs_in.raw);
|
|
|
|
memleak_remove_htable(memtable, &ld->htlcs_out.raw);
|
2017-12-15 11:17:54 +01:00
|
|
|
|
|
|
|
/* Now delete ld and those which it has pointers to. */
|
|
|
|
memleak_remove_referenced(memtable, ld);
|
|
|
|
|
|
|
|
json_array_start(response, "leaks");
|
2017-12-15 11:18:54 +01:00
|
|
|
while ((i = memleak_get(memtable, &backtrace)) != NULL) {
|
2017-12-15 11:17:54 +01:00
|
|
|
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));
|
|
|
|
|
2017-12-15 11:18:54 +01:00
|
|
|
json_add_backtrace(response, backtrace);
|
2017-12-15 11:17:54 +01:00
|
|
|
json_array_start(response, "parents");
|
|
|
|
for (p = tal_parent(i); p; p = tal_parent(p)) {
|
|
|
|
json_add_string(response, NULL, tal_name(p));
|
|
|
|
p = tal_parent(p);
|
|
|
|
}
|
|
|
|
json_array_end(response);
|
|
|
|
json_object_end(response);
|
|
|
|
}
|
|
|
|
json_array_end(response);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void json_memleak(struct command *cmd,
|
|
|
|
const char *buffer UNNEEDED,
|
|
|
|
const jsmntok_t *params UNNEEDED)
|
|
|
|
{
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response;
|
2017-12-15 11:17:54 +01:00
|
|
|
|
2018-09-14 16:51:04 +02:00
|
|
|
if (!param(cmd, buffer, params, NULL))
|
|
|
|
return;
|
|
|
|
|
2018-01-14 22:58:22 +01:00
|
|
|
if (!getenv("LIGHTNINGD_DEV_MEMLEAK")) {
|
2018-05-24 23:40:18 +02:00
|
|
|
command_fail(cmd, LIGHTNINGD,
|
2017-12-17 04:17:12 +01:00
|
|
|
"Leak detection needs $LIGHTNINGD_DEV_MEMLEAK");
|
2018-01-14 22:58:22 +01:00
|
|
|
return;
|
|
|
|
}
|
2017-12-17 04:17:12 +01:00
|
|
|
|
2018-10-19 03:17:48 +02:00
|
|
|
response = json_stream_success(cmd);
|
2017-12-15 11:18:54 +01:00
|
|
|
json_object_start(response, NULL);
|
2017-12-15 11:17:54 +01:00
|
|
|
scan_mem(cmd, response, cmd->ld);
|
2017-12-15 11:18:54 +01:00
|
|
|
json_object_end(response);
|
2017-12-15 11:17:54 +01:00
|
|
|
|
|
|
|
command_success(cmd, response);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command dev_memleak_command = {
|
|
|
|
"dev-memleak",
|
|
|
|
json_memleak,
|
2018-01-22 09:55:07 +01:00
|
|
|
"Show unreferenced memory objects"
|
2017-12-15 11:17:54 +01:00
|
|
|
};
|
|
|
|
AUTODATA(json_command, &dev_memleak_command);
|
2017-12-15 11:16:54 +01:00
|
|
|
#endif /* DEVELOPER */
|