Skip to content

Commit 485888c

Browse files
committed
zdb: bring crash handling over from ztest
ztest has a very nice ability to show a backtrace when there's an unexpected crash. zdb is used often enough on corrupted data and can blow up too, so nice output is useful there too. Sponsored-by: Klara, Inc. Sponsored-by: Wasabi Technology, Inc. Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Rob Norris <[email protected]> Closes openzfs#16181 (cherry picked from commit 91c46d4)
1 parent b76c9b3 commit 485888c

File tree

1 file changed

+56
-5
lines changed

1 file changed

+56
-5
lines changed

cmd/zdb/zdb.c

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@
8484
#include <sys/brt_impl.h>
8585
#include <zfs_comutil.h>
8686
#include <sys/zstd/zstd.h>
87+
#if (__GLIBC__ && !__UCLIBC__)
88+
#include <execinfo.h> /* for backtrace() */
89+
#endif
8790

8891
#include <libnvpair.h>
8992
#include <libzutil.h>
@@ -926,11 +929,41 @@ usage(void)
926929
static void
927930
dump_debug_buffer(void)
928931
{
929-
if (dump_opt['G']) {
930-
(void) printf("\n");
931-
(void) fflush(stdout);
932-
zfs_dbgmsg_print("zdb");
933-
}
932+
ssize_t ret __attribute__((unused));
933+
934+
if (!dump_opt['G'])
935+
return;
936+
/*
937+
* We use write() instead of printf() so that this function
938+
* is safe to call from a signal handler.
939+
*/
940+
ret = write(STDOUT_FILENO, "\n", 1);
941+
zfs_dbgmsg_print("zdb");
942+
}
943+
944+
#define BACKTRACE_SZ 100
945+
946+
static void sig_handler(int signo)
947+
{
948+
struct sigaction action;
949+
#if (__GLIBC__ && !__UCLIBC__) /* backtrace() is a GNU extension */
950+
int nptrs;
951+
void *buffer[BACKTRACE_SZ];
952+
953+
nptrs = backtrace(buffer, BACKTRACE_SZ);
954+
backtrace_symbols_fd(buffer, nptrs, STDERR_FILENO);
955+
#endif
956+
dump_debug_buffer();
957+
958+
/*
959+
* Restore default action and re-raise signal so SIGSEGV and
960+
* SIGABRT can trigger a core dump.
961+
*/
962+
action.sa_handler = SIG_DFL;
963+
sigemptyset(&action.sa_mask);
964+
action.sa_flags = 0;
965+
(void) sigaction(signo, &action, NULL);
966+
raise(signo);
934967
}
935968

936969
/*
@@ -8934,9 +8967,27 @@ main(int argc, char **argv)
89348967
char *spa_config_path_env, *objset_str;
89358968
boolean_t target_is_spa = B_TRUE, dataset_lookup = B_FALSE;
89368969
nvlist_t *cfg = NULL;
8970+
struct sigaction action;
89378971

89388972
dprintf_setup(&argc, argv);
89398973

8974+
/*
8975+
* Set up signal handlers, so if we crash due to bad on-disk data we
8976+
* can get more info. Unlike ztest, we don't bail out if we can't set
8977+
* up signal handlers, because zdb is very useful without them.
8978+
*/
8979+
action.sa_handler = sig_handler;
8980+
sigemptyset(&action.sa_mask);
8981+
action.sa_flags = 0;
8982+
if (sigaction(SIGSEGV, &action, NULL) < 0) {
8983+
(void) fprintf(stderr, "zdb: cannot catch SIGSEGV: %s\n",
8984+
strerror(errno));
8985+
}
8986+
if (sigaction(SIGABRT, &action, NULL) < 0) {
8987+
(void) fprintf(stderr, "zdb: cannot catch SIGABRT: %s\n",
8988+
strerror(errno));
8989+
}
8990+
89408991
/*
89418992
* If there is an environment variable SPA_CONFIG_PATH it overrides
89428993
* default spa_config_path setting. If -U flag is specified it will

0 commit comments

Comments
 (0)