aboutsummaryrefslogtreecommitdiff
path: root/sys/contrib/openzfs/cmd/zdb/zdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/openzfs/cmd/zdb/zdb.c')
-rw-r--r--sys/contrib/openzfs/cmd/zdb/zdb.c132
1 files changed, 101 insertions, 31 deletions
diff --git a/sys/contrib/openzfs/cmd/zdb/zdb.c b/sys/contrib/openzfs/cmd/zdb/zdb.c
index 449b6bf2ccb3..704fcf4422d4 100644
--- a/sys/contrib/openzfs/cmd/zdb/zdb.c
+++ b/sys/contrib/openzfs/cmd/zdb/zdb.c
@@ -85,6 +85,7 @@
#include <sys/brt_impl.h>
#include <zfs_comutil.h>
#include <sys/zstd/zstd.h>
+#include <sys/backtrace.h>
#include <libnvpair.h>
#include <libzutil.h>
@@ -120,6 +121,9 @@ static int flagbits[256];
static uint64_t max_inflight_bytes = 256 * 1024 * 1024; /* 256MB */
static int leaked_objects = 0;
static range_tree_t *mos_refd_objs;
+static spa_t *spa;
+static objset_t *os;
+static boolean_t kernel_init_done;
static void snprintf_blkptr_compact(char *, size_t, const blkptr_t *,
boolean_t);
@@ -131,6 +135,7 @@ static int dump_bpobj_cb(void *arg, const blkptr_t *bp, boolean_t free,
static void zdb_print_blkptr(const blkptr_t *bp, int flags);
+static void zdb_exit(int reason);
typedef struct sublivelist_verify_block_refcnt {
/* block pointer entry in livelist being verified */
@@ -818,17 +823,40 @@ usage(void)
(void) fprintf(stderr, "Specify an option more than once (e.g. -bb) "
"to make only that option verbose\n");
(void) fprintf(stderr, "Default is to dump everything non-verbosely\n");
- exit(1);
+ zdb_exit(1);
}
static void
dump_debug_buffer(void)
{
- if (dump_opt['G']) {
- (void) printf("\n");
- (void) fflush(stdout);
- zfs_dbgmsg_print("zdb");
- }
+ ssize_t ret __attribute__((unused));
+
+ 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(STDERR_FILENO, "\n", 1);
+ zfs_dbgmsg_print(STDERR_FILENO, "zdb");
+}
+
+static void sig_handler(int signo)
+{
+ struct sigaction action;
+
+ libspl_backtrace(STDERR_FILENO);
+ 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);
}
/*
@@ -849,7 +877,7 @@ fatal(const char *fmt, ...)
dump_debug_buffer();
- exit(1);
+ zdb_exit(1);
}
static void
@@ -2276,7 +2304,7 @@ snprintf_zstd_header(spa_t *spa, char *blkbuf, size_t buflen,
buf = malloc(SPA_MAXBLOCKSIZE);
if (buf == NULL) {
(void) fprintf(stderr, "out of memory\n");
- exit(1);
+ zdb_exit(1);
}
decode_embedded_bp_compressed(bp, buf);
memcpy(&zstd_hdr, buf, sizeof (zstd_hdr));
@@ -3231,6 +3259,23 @@ fuid_table_destroy(void)
}
}
+static void
+zdb_exit(int reason)
+{
+ if (os != NULL) {
+ close_objset(os, FTAG);
+ } else if (spa != NULL) {
+ spa_close(spa, FTAG);
+ }
+
+ fuid_table_destroy();
+
+ if (kernel_init_done)
+ kernel_fini();
+
+ exit(reason);
+}
+
/*
* print uid or gid information.
* For normal POSIX id just the id is printed in decimal format.
@@ -4161,32 +4206,32 @@ dump_cachefile(const char *cachefile)
if ((fd = open64(cachefile, O_RDONLY)) < 0) {
(void) printf("cannot open '%s': %s\n", cachefile,
strerror(errno));
- exit(1);
+ zdb_exit(1);
}
if (fstat64(fd, &statbuf) != 0) {
(void) printf("failed to stat '%s': %s\n", cachefile,
strerror(errno));
- exit(1);
+ zdb_exit(1);
}
if ((buf = malloc(statbuf.st_size)) == NULL) {
(void) fprintf(stderr, "failed to allocate %llu bytes\n",
(u_longlong_t)statbuf.st_size);
- exit(1);
+ zdb_exit(1);
}
if (read(fd, buf, statbuf.st_size) != statbuf.st_size) {
(void) fprintf(stderr, "failed to read %llu bytes\n",
(u_longlong_t)statbuf.st_size);
- exit(1);
+ zdb_exit(1);
}
(void) close(fd);
if (nvlist_unpack(buf, statbuf.st_size, &config, 0) != 0) {
(void) fprintf(stderr, "failed to unpack nvlist\n");
- exit(1);
+ zdb_exit(1);
}
free(buf);
@@ -5102,14 +5147,14 @@ dump_label(const char *dev)
if ((fd = open64(path, O_RDONLY)) < 0) {
(void) printf("cannot open '%s': %s\n", path, strerror(errno));
- exit(1);
+ zdb_exit(1);
}
if (fstat64_blk(fd, &statbuf) != 0) {
(void) printf("failed to stat '%s': %s\n", path,
strerror(errno));
(void) close(fd);
- exit(1);
+ zdb_exit(1);
}
if (S_ISBLK(statbuf.st_mode) && zfs_dev_flush(fd) != 0)
@@ -5122,7 +5167,7 @@ dump_label(const char *dev)
sizeof (cksum_record_t), offsetof(cksum_record_t, link));
psize = statbuf.st_size;
- psize = P2ALIGN(psize, (uint64_t)sizeof (vdev_label_t));
+ psize = P2ALIGN_TYPED(psize, sizeof (vdev_label_t), uint64_t);
ashift = SPA_MINBLOCKSHIFT;
/*
@@ -8221,7 +8266,7 @@ dump_zpool(spa_t *spa)
if (rc != 0) {
dump_debug_buffer();
- exit(rc);
+ zdb_exit(rc);
}
}
@@ -8825,18 +8870,18 @@ zdb_embedded_block(char *thing)
words + 12, words + 13, words + 14, words + 15);
if (err != 16) {
(void) fprintf(stderr, "invalid input format\n");
- exit(1);
+ zdb_exit(1);
}
ASSERT3U(BPE_GET_LSIZE(&bp), <=, SPA_MAXBLOCKSIZE);
buf = malloc(SPA_MAXBLOCKSIZE);
if (buf == NULL) {
(void) fprintf(stderr, "out of memory\n");
- exit(1);
+ zdb_exit(1);
}
err = decode_embedded_bp(&bp, buf, BPE_GET_LSIZE(&bp));
if (err != 0) {
(void) fprintf(stderr, "decode failed: %u\n", err);
- exit(1);
+ zdb_exit(1);
}
zdb_dump_block_raw(buf, BPE_GET_LSIZE(&bp), 0);
free(buf);
@@ -8863,8 +8908,6 @@ int
main(int argc, char **argv)
{
int c;
- spa_t *spa = NULL;
- objset_t *os = NULL;
int dump_all = 1;
int verbose = 0;
int error = 0;
@@ -8880,10 +8923,28 @@ main(int argc, char **argv)
char *spa_config_path_env, *objset_str;
boolean_t target_is_spa = B_TRUE, dataset_lookup = B_FALSE;
nvlist_t *cfg = NULL;
+ struct sigaction action;
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
* default spa_config_path setting. If -U flag is specified it will
* override this environment variable settings once again.
@@ -9093,6 +9154,7 @@ main(int argc, char **argv)
spa_mode_readable_spacemaps = B_TRUE;
kernel_init(SPA_MODE_READ);
+ kernel_init_done = B_TRUE;
if (dump_all)
verbose = MAX(verbose, 1);
@@ -9116,19 +9178,23 @@ main(int argc, char **argv)
if (argc != 1)
usage();
zdb_embedded_block(argv[0]);
- return (0);
+ error = 0;
+ goto fini;
}
if (argc < 1) {
if (!dump_opt['e'] && dump_opt['C']) {
dump_cachefile(spa_config_path);
- return (0);
+ error = 0;
+ goto fini;
}
usage();
}
- if (dump_opt['l'])
- return (dump_label(argv[0]));
+ if (dump_opt['l']) {
+ error = dump_label(argv[0]);
+ goto fini;
+ }
if (dump_opt['X'] || dump_opt['F'])
rewind = ZPOOL_DO_REWIND |
@@ -9183,7 +9249,8 @@ main(int argc, char **argv)
} else if (objset_str && !zdb_numeric(objset_str + 1) &&
dump_opt['N']) {
printf("Supply a numeric objset ID with -N\n");
- exit(1);
+ error = 1;
+ goto fini;
}
} else {
target_pool = target;
@@ -9240,7 +9307,8 @@ main(int argc, char **argv)
if (argc != 2)
usage();
dump_opt['v'] = verbose + 3;
- return (dump_path(argv[0], argv[1], NULL));
+ error = dump_path(argv[0], argv[1], NULL);
+ goto fini;
}
if (dump_opt['r']) {
@@ -9328,7 +9396,7 @@ main(int argc, char **argv)
fatal("can't dump '%s': %s", target,
strerror(error));
}
- return (error);
+ goto fini;
} else {
target_pool = strdup(target);
if (strpbrk(target, "/@") != NULL)
@@ -9458,9 +9526,10 @@ retry_lookup:
free(checkpoint_target);
}
+fini:
if (os != NULL) {
close_objset(os, FTAG);
- } else {
+ } else if (spa != NULL) {
spa_close(spa, FTAG);
}
@@ -9468,7 +9537,8 @@ retry_lookup:
dump_debug_buffer();
- kernel_fini();
+ if (kernel_init_done)
+ kernel_fini();
return (error);
}