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;