From b75d187250019615761f4d9a2419cea94a7153b4 Mon Sep 17 00:00:00 2001 From: Nikolay Shirokovskiy <nshyrokovskiy@gmail.com> Date: Tue, 24 Jan 2023 17:46:49 +0300 Subject: [PATCH] crash: factor out part not belonging to core Currently crash has code to report crashes to feedback URL. It does not belong to core. Also it brings dependencies from box to core with INSTANCE_UUID and REPLICASET_UUID variables. So let's move this part back to box. NO_DOC=refactoring NO_TEST=refactoring NO_CHANGELOG=refactoring --- src/box/box.cc | 229 ++++++++++++++++++++- src/box/box.h | 2 +- src/box/lua/cfg.cc | 6 +- src/box/lua/feedback_daemon.lua | 2 +- src/lib/core/crash.c | 348 +------------------------------- src/lib/core/crash.h | 134 +++++++++++- src/main.cc | 20 +- src/main.h | 10 + test/unit/box_test_utils.c | 5 + 9 files changed, 394 insertions(+), 362 deletions(-) diff --git a/src/box/box.cc b/src/box/box.cc index 545c3db4b2..766f01ef2c 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -32,6 +32,9 @@ #include "trivia/config.h" +#include <sys/utsname.h> +#include <spawn.h> + #include "lua/utils.h" /* lua_hash() */ #include "fiber_pool.h" #include <say.h> @@ -91,6 +94,7 @@ #include "flightrec.h" #include "wal_ext.h" #include "mp_util.h" +#include "small/static.h" static char status[64] = "unconfigured"; @@ -227,6 +231,15 @@ static const char *box_recovery_state_strs[box_recovery_state_MAX] = { /** Whether the triggers for "synced" recovery stage have already run. */ static bool recovery_state_synced_is_reached; +/** PATH_MAX is too big and 2K is recommended limit for web address. */ +#define BOX_FEEDBACK_HOST_MAX 2048 + +/** Feedback host URL to send crash info to. */ +static char box_feedback_host[BOX_FEEDBACK_HOST_MAX]; + +/** Whether sending crash info to feedback URL is enabled. */ +static bool box_feedback_crash_enabled; + static int box_run_on_recovery_state(enum box_recovery_state state) { @@ -2698,20 +2711,223 @@ box_set_prepared_stmt_cache_size(void) return 0; } +/** + * Report crash information to the feedback daemon + * (ie send it to feedback daemon). + */ +static int +box_feedback_report_crash(struct crash_info *cinfo) +{ + /* + * Update to a new number if the format get changed. + */ + const int crashinfo_version = 1; + + char *p = (char *)static_alloc(SMALL_STATIC_SIZE); + char *tail = &p[SMALL_STATIC_SIZE]; + char *e = &p[SMALL_STATIC_SIZE]; + char *head = p; + + int total = 0; + (void)total; + int size = 0; + +#define snprintf_safe(...) SNPRINT(total, snprintf, p, size, __VA_ARGS__) +#define jnprintf_safe(str) SNPRINT(total, json_escape, p, size, str) + + /* + * Lets reuse tail of the buffer as a temp space. + */ + struct utsname *uname_ptr = + (struct utsname *)&tail[-sizeof(struct utsname)]; + if (p >= (char *)uname_ptr) + return -1; + + if (uname(uname_ptr) != 0) { + say_syserror("uname call failed, ignore"); + memset(uname_ptr, 0, sizeof(struct utsname)); + } + + /* + * Start filling the script. The "data" key value is + * filled as a separate code block for easier + * modifications in future. + */ + size = (char *)uname_ptr - p; + snprintf_safe("{"); + snprintf_safe("\"crashdump\":{"); + snprintf_safe("\"version\":\"%d\",", crashinfo_version); + snprintf_safe("\"data\":"); + + /* The "data" key value */ + snprintf_safe("{"); + snprintf_safe("\"uname\":{"); + snprintf_safe("\"sysname\":\""); + jnprintf_safe(uname_ptr->sysname); + snprintf_safe("\","); + /* + * nodename might contain a sensitive information, skip. + */ + snprintf_safe("\"release\":\""); + jnprintf_safe(uname_ptr->release); + snprintf_safe("\","); + + snprintf_safe("\"version\":\""); + jnprintf_safe(uname_ptr->version); + snprintf_safe("\","); + + snprintf_safe("\"machine\":\""); + jnprintf_safe(uname_ptr->machine); + snprintf_safe("\""); + snprintf_safe("},"); + + /* Extend size, because now uname_ptr is not needed. */ + size = e - p; + + /* + * Instance block requires uuid encoding so take it + * from the tail of the buffer. + */ + snprintf_safe("\"instance\":{"); + char *uuid_buf = &tail[-(UUID_STR_LEN + 1)]; + if (p >= uuid_buf) + return -1; + size = uuid_buf - p; + + tt_uuid_to_string(&INSTANCE_UUID, uuid_buf); + snprintf_safe("\"server_id\":\"%s\",", uuid_buf); + tt_uuid_to_string(&REPLICASET_UUID, uuid_buf); + snprintf_safe("\"cluster_id\":\"%s\",", uuid_buf); + + /* No need for uuid_buf anymore. */ + size = e - p; + + struct timespec ts; + time_t uptime = 0; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + uptime = ts.tv_sec - tarantool_start_time; + snprintf_safe("\"uptime\":\"%llu\"", (unsigned long long)uptime); + snprintf_safe("},"); + + snprintf_safe("\"build\":{"); + snprintf_safe("\"version\":\"%s\",", PACKAGE_VERSION); + snprintf_safe("\"cmake_type\":\"%s\"", BUILD_INFO); + snprintf_safe("},"); + + snprintf_safe("\"signal\":{"); + snprintf_safe("\"signo\":%d,", cinfo->signo); + snprintf_safe("\"si_code\":%d,", cinfo->sicode); + if (cinfo->signo == SIGSEGV) { + if (cinfo->sicode == SEGV_MAPERR) { + snprintf_safe("\"si_code_str\":\"%s\",", + "SEGV_MAPERR"); + } else if (cinfo->sicode == SEGV_ACCERR) { + snprintf_safe("\"si_code_str\":\"%s\",", + "SEGV_ACCERR"); + } + } + snprintf_safe("\"si_addr\":\"0x%llx\",", + (long long)cinfo->siaddr); + +#ifdef ENABLE_BACKTRACE + snprintf_safe("\"backtrace\":\""); + jnprintf_safe(cinfo->backtrace_buf); + snprintf_safe("\","); +#endif + + /* 64 bytes should be enough for longest localtime */ + const int ts_size = 64; + char *timestamp_rt_str = &tail[-ts_size]; + if (p >= timestamp_rt_str) + return -1; + + struct tm tm; + localtime_r(&cinfo->timestamp_rt, &tm); + strftime(timestamp_rt_str, ts_size, "%F %T %Z", &tm); + timestamp_rt_str[ts_size - 1] = '\0'; + + size = timestamp_rt_str - p; + snprintf_safe("\"timestamp\":\""); + jnprintf_safe(timestamp_rt_str); + snprintf_safe("\""); + snprintf_safe("}"); + snprintf_safe("}"); + + /* Finalize the "data" key and the whole dump. */ + size = e - p; + snprintf_safe("}"); + snprintf_safe("}"); + +#undef snprintf_safe +#undef jnprintf_safe + + say_debug("crash dump: %s", head); + + /* Timeout 1 sec is taken from the feedback daemon. */ + const char *expr = + "require('http.client').post(arg[1],arg[2],{timeout=1});" + "os.exit(1);"; + const char *exec_argv[7] = { + tarantool_path, + "-e", expr, + "-", + box_feedback_host, + head, + NULL, + }; + + extern char **environ; + int rc = posix_spawn(NULL, exec_argv[0], NULL, NULL, + (char **)exec_argv, environ); + if (rc != 0) { + fprintf(stderr, + "posix_spawn with " + "exec(%s,[%s,%s,%s,%s,%s,%s,%s]) failed: %s\n", + exec_argv[0], exec_argv[0], exec_argv[1], exec_argv[2], + exec_argv[3], exec_argv[4], exec_argv[5], exec_argv[6], + tt_strerror(rc)); + return -1; + } + + return 0; +} + +/** + * Box callback to handle crashes. + */ +static void +box_crash_callback(struct crash_info *cinfo) +{ + crash_report_stderr(cinfo); + + if (box_feedback_crash_enabled && + box_feedback_report_crash(cinfo) != 0) + fprintf(stderr, "unable to send a crash report\n"); +} + int -box_set_crash(void) +box_set_feedback(void) { const char *host = cfg_gets("feedback_host"); - bool is_enabled_1 = cfg_getb("feedback_enabled"); - bool is_enabled_2 = cfg_getb("feedback_crashinfo"); - if (host != NULL && strlen(host) >= CRASH_FEEDBACK_HOST_MAX) { + if (host != NULL && strlen(host) >= BOX_FEEDBACK_HOST_MAX) { diag_set(ClientError, ER_CFG, "feedback_host", - "the address is too long"); + "the address is too long"); return -1; } - crash_cfg(host, is_enabled_1 && is_enabled_2); + if (cfg_getb("feedback_enabled") && + cfg_getb("feedback_crashinfo") && + host != NULL) { + box_feedback_crash_enabled = true; + strlcpy(box_feedback_host, host, sizeof(box_feedback_host)); + say_debug("enable sending crashinfo feedback"); + } else { + box_feedback_crash_enabled = false; + box_feedback_host[0] = '\0'; + say_debug("disable sending crashinfo feedback"); + } + return 0; } @@ -4900,6 +5116,7 @@ box_init(void) * supported from box.cfg not being called yet. */ builtin_events_init(); + crash_callback = box_crash_callback; } void diff --git a/src/box/box.h b/src/box/box.h index 29c5755617..29abceb8f3 100644 --- a/src/box/box.h +++ b/src/box/box.h @@ -306,7 +306,7 @@ void box_set_replication_skip_conflict(void); void box_set_replication_anon(void); void box_set_net_msg_max(void); int box_set_prepared_stmt_cache_size(void); -int box_set_crash(void); +int box_set_feedback(void); int box_set_txn_timeout(void); int box_set_txn_isolation(void); int box_set_auth_type(void); diff --git a/src/box/lua/cfg.cc b/src/box/lua/cfg.cc index f504384836..947a3a6a78 100644 --- a/src/box/lua/cfg.cc +++ b/src/box/lua/cfg.cc @@ -405,9 +405,9 @@ lbox_cfg_set_replication_skip_conflict(struct lua_State *L) } static int -lbox_cfg_set_crash(struct lua_State *L) +lbox_cfg_set_feedback(struct lua_State *L) { - if (box_set_crash() != 0) + if (box_set_feedback() != 0) luaT_error(L); return 0; } @@ -476,7 +476,7 @@ box_lua_cfg_init(struct lua_State *L) {"cfg_set_replication_anon", lbox_cfg_set_replication_anon}, {"cfg_set_net_msg_max", lbox_cfg_set_net_msg_max}, {"cfg_set_sql_cache_size", lbox_set_prepared_stmt_cache_size}, - {"cfg_set_crash", lbox_cfg_set_crash}, + {"cfg_set_feedback", lbox_cfg_set_feedback}, {"cfg_set_txn_timeout", lbox_cfg_set_txn_timeout}, {"cfg_set_txn_isolation", lbox_cfg_set_txn_isolation}, {"cfg_set_auth_type", lbox_cfg_set_auth_type}, diff --git a/src/box/lua/feedback_daemon.lua b/src/box/lua/feedback_daemon.lua index ff9296b992..6b1ab92391 100644 --- a/src/box/lua/feedback_daemon.lua +++ b/src/box/lua/feedback_daemon.lua @@ -417,7 +417,7 @@ end setmetatable(daemon, { __index = { set_feedback_params = function() - box.internal.cfg_set_crash() + box.internal.cfg_set_feedback() daemon.enabled = box.cfg.feedback_enabled daemon.host = box.cfg.feedback_host daemon.interval = box.cfg.feedback_interval diff --git a/src/lib/core/crash.c b/src/lib/core/crash.c index 602ed3e505..cd434b8eca 100644 --- a/src/lib/core/crash.c +++ b/src/lib/core/crash.c @@ -7,164 +7,17 @@ #include <string.h> #include <signal.h> #include <stdint.h> -#include <limits.h> #include <time.h> -#include <spawn.h> #include <sys/types.h> -#include <sys/utsname.h> -#include "small/static.h" #include "trivia/util.h" -#include "box/replication.h" #include "core/backtrace.h" #include "crash.h" #include "say.h" -#include "tt_strerror.h" -#include "tt_uuid.h" -#define pr_fmt(fmt) "crash: " fmt -#define pr_debug(fmt, ...) say_debug(pr_fmt(fmt), ##__VA_ARGS__) -#define pr_info(fmt, ...) say_info(pr_fmt(fmt), ##__VA_ARGS__) -#define pr_err(fmt, ...) say_error(pr_fmt(fmt), ##__VA_ARGS__) -#define pr_syserr(fmt, ...) say_syserror(pr_fmt(fmt), ##__VA_ARGS__) -#define pr_crit(fmt, ...) fprintf(stderr, pr_fmt(fmt) "\n", ##__VA_ARGS__) -#define pr_panic(fmt, ...) panic(pr_fmt(fmt), ##__VA_ARGS__) - -/** Use strlcpy with destination as an array */ -#define strlcpy_a(dst, src) strlcpy(dst, src, sizeof(dst)) - -#if defined(TARGET_OS_LINUX) && defined(__x86_64__) -# define HAS_GREG -#endif - -#ifdef HAS_GREG -struct crash_greg { - uint64_t r8; - uint64_t r9; - uint64_t r10; - uint64_t r11; - uint64_t r12; - uint64_t r13; - uint64_t r14; - uint64_t r15; - uint64_t di; - uint64_t si; - uint64_t bp; - uint64_t bx; - uint64_t dx; - uint64_t ax; - uint64_t cx; - uint64_t sp; - uint64_t ip; - uint64_t flags; - uint16_t cs; - uint16_t gs; - uint16_t fs; - uint16_t ss; - uint64_t err; - uint64_t trapno; - uint64_t oldmask; - uint64_t cr2; - uint64_t fpstate; - uint64_t reserved1[8]; -}; -#endif /* HAS_GREG */ - -static struct crash_info { - /** - * These two are mostly useless as being - * plain addresses and without real binary - * crash dump file we can't use them for - * anything suitable (in terms of analysis sake) - * but keep for backward compatibility. - */ - void *context_addr; - void *siginfo_addr; -#ifdef HAS_GREG - /** - * Registers contents. - */ - struct crash_greg greg; -#endif - /** - * Timestamp in seconds (realtime). - */ - long timestamp_rt; - /** - * Faulting address. - */ - void *siaddr; - /** - * Crash signal number. - */ - int signo; - /** - * Crash signal code. - */ - int sicode; -#ifdef ENABLE_BACKTRACE - /** - * 1K of memory should be enough to keep the backtrace. - * In worst case it gonna be simply trimmed. - */ - char backtrace_buf[1024]; -#endif -} crash_info; - -static char tarantool_path[PATH_MAX]; -static char feedback_host[CRASH_FEEDBACK_HOST_MAX]; -static bool send_crashinfo = false; -static time_t timestamp_mono = 0; - -void -crash_init(const char *tarantool_bin) -{ - strlcpy_a(tarantool_path, tarantool_bin); - if (strlen(tarantool_path) < strlen(tarantool_bin)) - pr_panic("executable path is trimmed"); - - /* - * We need to keep clock data locally to - * report uptime without binding to libev - * and etc. Because we're reporting information - * at the moment when crash happens and we are to - * be independent as much as we can. - */ - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) - timestamp_mono = ts.tv_sec; - else - pr_syserr("Can't fetch monotonic clock, ignore"); -} - -void -crash_cfg(const char *host, bool is_enabled) -{ - if (host == NULL || !is_enabled) { - if (send_crashinfo) { - pr_debug("disable sending crashinfo feedback"); - send_crashinfo = false; - feedback_host[0] = '\0'; - } - return; - } - - if (strcmp(feedback_host, host) != 0) { - strlcpy_a(feedback_host, host); - /* - * The caller should have tested already - * that there is enough space to keep - * the host address. - */ - assert(strlen(feedback_host) == strlen(host)); - } - - if (!send_crashinfo) { - pr_debug("enable sending crashinfo feedback"); - send_crashinfo = true; - } -} +/** Storage for crash_collect function return value. */ +static struct crash_info crash_info; /** * The routine is called inside crash signal handler so @@ -212,188 +65,7 @@ crash_collect(int signo, siginfo_t *siginfo, void *ucontext) return cinfo; } -/** - * Report crash information to the feedback daemon - * (ie send it to feedback daemon). - */ -static int -crash_report_feedback_daemon(struct crash_info *cinfo) -{ - /* - * Update to a new number if the format get changed. - */ - const int crashinfo_version = 1; - - char *p = static_alloc(SMALL_STATIC_SIZE); - char *tail = &p[SMALL_STATIC_SIZE]; - char *e = &p[SMALL_STATIC_SIZE]; - char *head = p; - - int total = 0; - (void)total; - int size = 0; - -#define snprintf_safe(...) SNPRINT(total, snprintf, p, size, __VA_ARGS__) -#define jnprintf_safe(str) SNPRINT(total, json_escape, p, size, str) - - /* - * Lets reuse tail of the buffer as a temp space. - */ - struct utsname *uname_ptr = (void *)&tail[-sizeof(struct utsname)]; - if (p >= (char *)uname_ptr) - return -1; - - if (uname(uname_ptr) != 0) { - pr_syserr("uname call failed, ignore"); - memset(uname_ptr, 0, sizeof(struct utsname)); - } - - /* - * Start filling the script. The "data" key value is - * filled as a separate code block for easier - * modifications in future. - */ - size = (char *)uname_ptr - p; - snprintf_safe("{"); - snprintf_safe("\"crashdump\":{"); - snprintf_safe("\"version\":\"%d\",", crashinfo_version); - snprintf_safe("\"data\":"); - - /* The "data" key value */ - snprintf_safe("{"); - snprintf_safe("\"uname\":{"); - snprintf_safe("\"sysname\":\""); - jnprintf_safe(uname_ptr->sysname); - snprintf_safe("\","); - /* - * nodename might contain a sensitive information, skip. - */ - snprintf_safe("\"release\":\""); - jnprintf_safe(uname_ptr->release); - snprintf_safe("\","); - - snprintf_safe("\"version\":\""); - jnprintf_safe(uname_ptr->version); - snprintf_safe("\","); - - snprintf_safe("\"machine\":\""); - jnprintf_safe(uname_ptr->machine); - snprintf_safe("\""); - snprintf_safe("},"); - - /* Extend size, because now uname_ptr is not needed. */ - size = e - p; - - /* - * Instance block requires uuid encoding so take it - * from the tail of the buffer. - */ - snprintf_safe("\"instance\":{"); - char *uuid_buf = &tail[-(UUID_STR_LEN+1)]; - if (p >= uuid_buf) - return -1; - size = uuid_buf - p; - - tt_uuid_to_string(&INSTANCE_UUID, uuid_buf); - snprintf_safe("\"server_id\":\"%s\",", uuid_buf); - tt_uuid_to_string(&REPLICASET_UUID, uuid_buf); - snprintf_safe("\"cluster_id\":\"%s\",", uuid_buf); - - /* No need for uuid_buf anymore. */ - size = e - p; - - struct timespec ts; - time_t uptime; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) - uptime = ts.tv_sec - timestamp_mono; - else - uptime = 0; - snprintf_safe("\"uptime\":\"%llu\"", - (unsigned long long)uptime); - snprintf_safe("},"); - - snprintf_safe("\"build\":{"); - snprintf_safe("\"version\":\"%s\",", PACKAGE_VERSION); - snprintf_safe("\"cmake_type\":\"%s\"", BUILD_INFO); - snprintf_safe("},"); - - snprintf_safe("\"signal\":{"); - snprintf_safe("\"signo\":%d,", cinfo->signo); - snprintf_safe("\"si_code\":%d,", cinfo->sicode); - if (cinfo->signo == SIGSEGV) { - if (cinfo->sicode == SEGV_MAPERR) { - snprintf_safe("\"si_code_str\":\"%s\",", - "SEGV_MAPERR"); - } else if (cinfo->sicode == SEGV_ACCERR) { - snprintf_safe("\"si_code_str\":\"%s\",", - "SEGV_ACCERR"); - } - } - snprintf_safe("\"si_addr\":\"0x%llx\",", - (long long)cinfo->siaddr); - -#ifdef ENABLE_BACKTRACE - snprintf_safe("\"backtrace\":\""); - jnprintf_safe(cinfo->backtrace_buf); - snprintf_safe("\","); -#endif - - /* 64 bytes should be enough for longest localtime */ - const int ts_size = 64; - char *timestamp_rt_str = &tail[-ts_size]; - if (p >= timestamp_rt_str) - return -1; - - struct tm tm; - localtime_r(&cinfo->timestamp_rt, &tm); - strftime(timestamp_rt_str, ts_size, "%F %T %Z", &tm); - timestamp_rt_str[ts_size - 1] = '\0'; - - size = timestamp_rt_str - p; - snprintf_safe("\"timestamp\":\""); - jnprintf_safe(timestamp_rt_str); - snprintf_safe("\""); - snprintf_safe("}"); - snprintf_safe("}"); - - /* Finalize the "data" key and the whole dump. */ - size = e - p; - snprintf_safe("}"); - snprintf_safe("}"); - - pr_debug("crash dump: %s", head); - - char *exec_argv[7] = { - [0] = tarantool_path, - [1] = "-e", - /* Timeout 1 sec is taken from the feedback daemon. */ - [2] = "require('http.client').post(arg[1],arg[2],{timeout=1});" - "os.exit(1);", - [3] = "-", - [4] = feedback_host, - [5] = head, - [6] = NULL, - }; - - extern char **environ; - int rc = posix_spawn(NULL, exec_argv[0], NULL, NULL, exec_argv, environ); - if (rc != 0) { - pr_crit("posix_spawn with " - "exec(%s,[%s,%s,%s,%s,%s,%s,%s]) failed: %s", - exec_argv[0], exec_argv[0], exec_argv[1], exec_argv[2], - exec_argv[3], exec_argv[4], exec_argv[5], exec_argv[6], - tt_strerror(rc)); - return -1; - } - - return 0; -} - -/** - * Report crash information to the stderr - * (usually a current console). - */ -static void +void crash_report_stderr(struct crash_info *cinfo) { const char *signal_code_repr = NULL; @@ -481,6 +153,8 @@ crash_report_stderr(struct crash_info *cinfo) #endif } +crash_callback_f crash_callback = crash_report_stderr; + /** * Handle fatal (crashing) signal. * @@ -507,11 +181,7 @@ crash_signal_cb(int signo, siginfo_t *siginfo, void *context) if (in_cb == 0) { in_cb = 1; cinfo = crash_collect(signo, siginfo, context); - crash_report_stderr(cinfo); - if (send_crashinfo && - crash_report_feedback_daemon(cinfo) != 0) { - pr_crit("unable to send a crash report"); - } + crash_callback(cinfo); } else { /* Got a signal while running the handler. */ fprintf(stderr, "Fatal %d while backtracing", signo); @@ -542,7 +212,7 @@ crash_signal_reset(void) for (size_t i = 0; i < lengthof(crash_signals); i++) { if (sigaction(crash_signals[i], &sa, NULL) == 0) continue; - pr_syserr("reset sigaction %d", crash_signals[i]); + say_syserror("reset sigaction %d", crash_signals[i]); } } @@ -565,7 +235,7 @@ crash_signal_init(void) for (size_t i = 0; i < lengthof(crash_signals); i++) { if (sigaction(crash_signals[i], &sa, NULL) == 0) continue; - pr_panic("sigaction %d (%s)", crash_signals[i], - tt_strerror(errno)); + panic("sigaction %d (%s)", crash_signals[i], + tt_strerror(errno)); } } diff --git a/src/lib/core/crash.h b/src/lib/core/crash.h index 195aef10b8..58e14d5f09 100644 --- a/src/lib/core/crash.h +++ b/src/lib/core/crash.h @@ -5,27 +5,135 @@ */ #pragma once +#include <stdint.h> + +#include "trivia/config.h" + #if defined(__cplusplus) extern "C" { #endif /* defined(__cplusplus) */ +#if defined(TARGET_OS_LINUX) && defined(__x86_64__) +# define HAS_GREG +#endif + +#ifdef HAS_GREG /** - * PATH_MAX is too big and 2K is recommended - * limit for web address. + * Values of x86 registers. */ -#define CRASH_FEEDBACK_HOST_MAX 2048 +struct crash_greg { + /** r8 register value */ + uint64_t r8; + /** r9 register value */ + uint64_t r9; + /** r10 register value */ + uint64_t r10; + /** r11 register value */ + uint64_t r11; + /** r12 register value */ + uint64_t r12; + /** r13 register value */ + uint64_t r13; + /** r14 register value */ + uint64_t r14; + /** r15 register value */ + uint64_t r15; + /** di register value */ + uint64_t di; + /** si register value */ + uint64_t si; + /** bp register value */ + uint64_t bp; + /** bx register value */ + uint64_t bx; + /** dx register value */ + uint64_t dx; + /** ax register value */ + uint64_t ax; + /** cx register value */ + uint64_t cx; + /** sp register value */ + uint64_t sp; + /** ip register value */ + uint64_t ip; + /** flags register value */ + uint64_t flags; + /** cs register value */ + uint16_t cs; + /** gs register value */ + uint16_t gs; + /** fs register value */ + uint16_t fs; + /** ss register value */ + uint16_t ss; + /** err register value */ + uint64_t err; + /** trapno register value */ + uint64_t trapno; + /** oldmask register value */ + uint64_t oldmask; + /** cr2 register value */ + uint64_t cr2; + /** fpstate register value */ + uint64_t fpstate; + /** Reserved. */ + uint64_t reserved1[8]; +}; +#endif /* HAS_GREG */ -/** - * Initialize crash subsystem. +/* + * Crash information. */ -void -crash_init(const char *tarantool_bin); +struct crash_info { + /** + * These two are mostly useless as being plain addresses and without + * real binary crash dump file we can't use them for anything suitable + * (in terms of analysis sake) but keep for backward compatibility. + */ + + /** Nearly useless. */ + void *context_addr; + /** Nearly useless. */ + void *siginfo_addr; +#ifdef HAS_GREG + /** + * Registers contents. + */ + struct crash_greg greg; +#endif + /** + * Timestamp in seconds (realtime). + */ + long timestamp_rt; + /** + * Faulting address. + */ + void *siaddr; + /** + * Crash signal number. + */ + int signo; + /** + * Crash signal code. + */ + int sicode; +#ifdef ENABLE_BACKTRACE + /** + * 1K of memory should be enough to keep the backtrace. + * In worst case it gonna be simply trimmed. + */ + char backtrace_buf[1024]; +#endif +}; + +typedef void +(*crash_callback_f)(struct crash_info *cinfo); /** - * Configure crash parameters. + * Callback to call on crash. Default value is crash_report_stderr. NULL value + * is not allowed. */ -void -crash_cfg(const char *host, bool is_enabled); +extern crash_callback_f crash_callback; /** * Initialize crash signal handlers. @@ -33,6 +141,12 @@ crash_cfg(const char *host, bool is_enabled); void crash_signal_init(void); +/** + * Report crash information to the stderr (usually a current console). + */ +void +crash_report_stderr(struct crash_info *cinfo); + /** * Reset crash signal handlers. */ diff --git a/src/main.cc b/src/main.cc index 9fad469caa..b76752e281 100644 --- a/src/main.cc +++ b/src/main.cc @@ -107,6 +107,15 @@ static struct fiber *on_shutdown_fiber = NULL; static bool is_shutting_down = false; static int exit_code = 0; +char tarantool_path[PATH_MAX]; + +/** + * We need to keep clock data locally to report uptime without binding + * to libev and etc. Because we're reporting information at the moment + * when crash happens and we are to be independent as much as we can. + */ +long tarantool_start_time; + double tarantool_uptime(void) { @@ -719,8 +728,15 @@ main(int argc, char **argv) script = argv[0]; title_set_script_name(argv[0]); } - - crash_init(tarantool_bin); + strlcpy(tarantool_path, tarantool_bin, sizeof(tarantool_path)); + if (strlen(tarantool_path) < strlen(tarantool_bin)) + panic("executable path is trimmed"); + + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + tarantool_start_time = ts.tv_sec; + else + say_syserror("failed to get start time, ignore"); random_init(); diff --git a/src/main.h b/src/main.h index af02ac475a..e23a487e8e 100644 --- a/src/main.h +++ b/src/main.h @@ -39,6 +39,16 @@ extern "C" { double tarantool_uptime(void); +/** + * Path to Tarantool binary of the process. + */ +extern char tarantool_path[]; + +/** + * Application start time on monotonic clocks. + */ +extern long tarantool_start_time; + void tarantool_exit(int); diff --git a/test/unit/box_test_utils.c b/test/unit/box_test_utils.c index b44a7278d3..55fbb3d970 100644 --- a/test/unit/box_test_utils.c +++ b/test/unit/box_test_utils.c @@ -1,3 +1,5 @@ +#include <limits.h> + /* * The definition of function set_sigint_cb is needed to avoid the error * while linking. This occurs because libbox.a contains the unresolved @@ -15,3 +17,6 @@ set_sigint_cb(sigint_cb_t new_sigint_cb) (void)new_sigint_cb; return new_sigint_cb; } + +char tarantool_path[PATH_MAX]; +long tarantool_start_time; -- GitLab