diff --git a/src/box/box.cc b/src/box/box.cc index 679f6dcfcd2d8e1f515cf8bbb5f5e07c0002a0c8..8629e4b0c1b00e88cbe4c45675128499380bfcf6 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> @@ -88,6 +91,7 @@ #include "version.h" #include "mp_uuid.h" #include "flightrec.h" +#include "small/static.h" static char status[64] = "unknown"; @@ -164,6 +168,15 @@ static struct fiber_pool tx_fiber_pool; */ static struct cbus_endpoint tx_prio_endpoint; +/** 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 void builtin_events_init(void); @@ -2252,20 +2265,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; } @@ -3818,6 +4034,7 @@ box_init(void) * being not supported from box.cfg not being called yet. */ builtin_events_init(); + crash_callback = box_crash_callback; } bool diff --git a/src/box/box.h b/src/box/box.h index 31dc715b3f761a9b376c65a0de0dde76c7be56a3..258fa6157ff661af3d429bfc1dcb1bf7eb42cd17 100644 --- a/src/box/box.h +++ b/src/box/box.h @@ -270,7 +270,7 @@ void box_set_replication_sync_timeout(void); void box_set_replication_skip_conflict(void); void box_set_replication_anon(void); void box_set_net_msg_max(void); -int box_set_crash(void); +int box_set_feedback(void); int box_set_txn_timeout(void); /** * Set default isolation level from cfg option txn_isolation. diff --git a/src/box/lua/cfg.cc b/src/box/lua/cfg.cc index f0b2c930af7edf059f3997fb1f30b18c025114a0..487a3beade3323c902733dfd22fdd9cb8b864c03 100644 --- a/src/box/lua/cfg.cc +++ b/src/box/lua/cfg.cc @@ -397,9 +397,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; } @@ -459,7 +459,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}, {NULL, NULL} diff --git a/src/box/lua/feedback_daemon.lua b/src/box/lua/feedback_daemon.lua index 652d63cf561bab2c6476116949e4095175da3c76..193ed790a453850b658b62f3853f2ac8abcac9ae 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 2abb08a1e068d6b14bf61eb2f67348a5d566b3dd..42cdca48b200b3665d2d59913d29df67e255c9fd 100644 --- a/src/lib/core/crash.c +++ b/src/lib/core/crash.c @@ -7,164 +7,18 @@ #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 "fiber.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 +66,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 +154,8 @@ crash_report_stderr(struct crash_info *cinfo) #endif } +crash_callback_f crash_callback = crash_report_stderr; + /** * Handle fatal (crashing) signal. * @@ -507,11 +182,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 +213,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 +236,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 195aef10b850ef143b7e195f0014107041d201c4..58e14d5f09eece56bb5269348e09f35e03e6c8c2 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 32f16bade6f33d3327f2d6ad90ddca6ebdd316a4..650b7af753c5289ba517e63c7394fc2b75efefa5 100644 --- a/src/main.cc +++ b/src/main.cc @@ -105,6 +105,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) { @@ -720,8 +729,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 af02ac475a073575d0a4bda5c39504e2ca2fe80a..e23a487e8e33a1b669902572831c8839e7c3ba6e 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 b44a7278d394c5af8288a52bc1f5c831a8025b23..55fbb3d970d6d8df6caea767df4b130432e9cef0 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;