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 <behlendorf1@llnl.gov>
Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
Closes #16181
This commit is contained in:
Rob Norris 2024-05-10 09:56:48 +10:00 committed by Tony Hutter
parent 2a2e358475
commit d06c8de748
1 changed files with 56 additions and 5 deletions

View File

@ -84,6 +84,9 @@
#include <sys/brt_impl.h> #include <sys/brt_impl.h>
#include <zfs_comutil.h> #include <zfs_comutil.h>
#include <sys/zstd/zstd.h> #include <sys/zstd/zstd.h>
#if (__GLIBC__ && !__UCLIBC__)
#include <execinfo.h> /* for backtrace() */
#endif
#include <libnvpair.h> #include <libnvpair.h>
#include <libzutil.h> #include <libzutil.h>
@ -926,11 +929,41 @@ usage(void)
static void static void
dump_debug_buffer(void) dump_debug_buffer(void)
{ {
if (dump_opt['G']) { ssize_t ret __attribute__((unused));
(void) printf("\n");
(void) fflush(stdout); if (!dump_opt['G'])
return;
/*
* We use write() instead of printf() so that this function
* is safe to call from a signal handler.
*/
ret = write(STDOUT_FILENO, "\n", 1);
zfs_dbgmsg_print("zdb"); zfs_dbgmsg_print("zdb");
} }
#define BACKTRACE_SZ 100
static void sig_handler(int signo)
{
struct sigaction action;
#if (__GLIBC__ && !__UCLIBC__) /* backtrace() is a GNU extension */
int nptrs;
void *buffer[BACKTRACE_SZ];
nptrs = backtrace(buffer, BACKTRACE_SZ);
backtrace_symbols_fd(buffer, nptrs, STDERR_FILENO);
#endif
dump_debug_buffer();
/*
* Restore default action and re-raise signal so SIGSEGV and
* SIGABRT can trigger a core dump.
*/
action.sa_handler = SIG_DFL;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
(void) sigaction(signo, &action, NULL);
raise(signo);
} }
/* /*
@ -8934,9 +8967,27 @@ main(int argc, char **argv)
char *spa_config_path_env, *objset_str; char *spa_config_path_env, *objset_str;
boolean_t target_is_spa = B_TRUE, dataset_lookup = B_FALSE; boolean_t target_is_spa = B_TRUE, dataset_lookup = B_FALSE;
nvlist_t *cfg = NULL; nvlist_t *cfg = NULL;
struct sigaction action;
dprintf_setup(&argc, argv); dprintf_setup(&argc, argv);
/*
* Set up signal handlers, so if we crash due to bad on-disk data we
* can get more info. Unlike ztest, we don't bail out if we can't set
* up signal handlers, because zdb is very useful without them.
*/
action.sa_handler = sig_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGSEGV, &action, NULL) < 0) {
(void) fprintf(stderr, "zdb: cannot catch SIGSEGV: %s\n",
strerror(errno));
}
if (sigaction(SIGABRT, &action, NULL) < 0) {
(void) fprintf(stderr, "zdb: cannot catch SIGABRT: %s\n",
strerror(errno));
}
/* /*
* If there is an environment variable SPA_CONFIG_PATH it overrides * If there is an environment variable SPA_CONFIG_PATH it overrides
* default spa_config_path setting. If -U flag is specified it will * default spa_config_path setting. If -U flag is specified it will