diff --git a/module/os/freebsd/zfs/zfs_debug.c b/module/os/freebsd/zfs/zfs_debug.c index 78d50c6fd8b7..3e832a9104f2 100644 --- a/module/os/freebsd/zfs/zfs_debug.c +++ b/module/os/freebsd/zfs/zfs_debug.c @@ -1,252 +1,268 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2014 by Delphix. All rights reserved. */ #include #include typedef struct zfs_dbgmsg { list_node_t zdm_node; time_t zdm_timestamp; uint_t zdm_size; char zdm_msg[]; } zfs_dbgmsg_t; static list_t zfs_dbgmsgs; static uint_t zfs_dbgmsg_size = 0; static kmutex_t zfs_dbgmsgs_lock; uint_t zfs_dbgmsg_maxsize = 4<<20; /* 4MB */ static kstat_t *zfs_dbgmsg_kstat; /* * Internal ZFS debug messages are enabled by default. * * # Print debug messages as they're logged * dtrace -n 'zfs-dbgmsg { print(stringof(arg0)); }' * * # Print all logged dbgmsg entries * sysctl kstat.zfs.misc.dbgmsg * * # Disable the kernel debug message log. * sysctl vfs.zfs.dbgmsg_enable=0 */ int zfs_dbgmsg_enable = B_TRUE; static int zfs_dbgmsg_headers(char *buf, size_t size) { (void) snprintf(buf, size, "%-12s %-8s\n", "timestamp", "message"); return (0); } static int zfs_dbgmsg_data(char *buf, size_t size, void *data) { zfs_dbgmsg_t *zdm = (zfs_dbgmsg_t *)data; (void) snprintf(buf, size, "%-12llu %-s\n", (u_longlong_t)zdm->zdm_timestamp, zdm->zdm_msg); return (0); } static void * zfs_dbgmsg_addr(kstat_t *ksp, loff_t n) { zfs_dbgmsg_t *zdm = (zfs_dbgmsg_t *)ksp->ks_private; ASSERT(MUTEX_HELD(&zfs_dbgmsgs_lock)); if (n == 0) ksp->ks_private = list_head(&zfs_dbgmsgs); else if (zdm) ksp->ks_private = list_next(&zfs_dbgmsgs, zdm); return (ksp->ks_private); } static void zfs_dbgmsg_purge(uint_t max_size) { zfs_dbgmsg_t *zdm; uint_t size; ASSERT(MUTEX_HELD(&zfs_dbgmsgs_lock)); while (zfs_dbgmsg_size > max_size) { zdm = list_remove_head(&zfs_dbgmsgs); if (zdm == NULL) return; size = zdm->zdm_size; kmem_free(zdm, size); zfs_dbgmsg_size -= size; } } static int zfs_dbgmsg_update(kstat_t *ksp, int rw) { if (rw == KSTAT_WRITE) zfs_dbgmsg_purge(0); return (0); } void zfs_dbgmsg_init(void) { list_create(&zfs_dbgmsgs, sizeof (zfs_dbgmsg_t), offsetof(zfs_dbgmsg_t, zdm_node)); mutex_init(&zfs_dbgmsgs_lock, NULL, MUTEX_DEFAULT, NULL); zfs_dbgmsg_kstat = kstat_create("zfs", 0, "dbgmsg", "misc", KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL); if (zfs_dbgmsg_kstat) { zfs_dbgmsg_kstat->ks_lock = &zfs_dbgmsgs_lock; zfs_dbgmsg_kstat->ks_ndata = UINT32_MAX; zfs_dbgmsg_kstat->ks_private = NULL; zfs_dbgmsg_kstat->ks_update = zfs_dbgmsg_update; kstat_set_raw_ops(zfs_dbgmsg_kstat, zfs_dbgmsg_headers, zfs_dbgmsg_data, zfs_dbgmsg_addr); kstat_install(zfs_dbgmsg_kstat); } } void zfs_dbgmsg_fini(void) { if (zfs_dbgmsg_kstat) kstat_delete(zfs_dbgmsg_kstat); /* * TODO - decide how to make this permanent */ #ifdef _KERNEL mutex_enter(&zfs_dbgmsgs_lock); zfs_dbgmsg_purge(0); mutex_exit(&zfs_dbgmsgs_lock); mutex_destroy(&zfs_dbgmsgs_lock); #endif } void __zfs_dbgmsg(char *buf) { zfs_dbgmsg_t *zdm; uint_t size; DTRACE_PROBE1(zfs__dbgmsg, char *, buf); size = sizeof (zfs_dbgmsg_t) + strlen(buf) + 1; zdm = kmem_zalloc(size, KM_SLEEP); zdm->zdm_size = size; zdm->zdm_timestamp = gethrestime_sec(); strcpy(zdm->zdm_msg, buf); mutex_enter(&zfs_dbgmsgs_lock); list_insert_tail(&zfs_dbgmsgs, zdm); zfs_dbgmsg_size += size; zfs_dbgmsg_purge(zfs_dbgmsg_maxsize); mutex_exit(&zfs_dbgmsgs_lock); } void __set_error(const char *file, const char *func, int line, int err) { /* * To enable this: * * $ echo 512 >/sys/module/zfs/parameters/zfs_flags */ if (zfs_flags & ZFS_DEBUG_SET_ERROR) __dprintf(B_FALSE, file, func, line, "error %lu", (ulong_t)err); } #ifdef _KERNEL void __dprintf(boolean_t dprint, const char *file, const char *func, int line, const char *fmt, ...) { const char *newfile; va_list adx; size_t size; char *buf; char *nl; int i; size = 1024; buf = kmem_alloc(size, KM_SLEEP); /* * Get rid of annoying prefix to filename. */ newfile = strrchr(file, '/'); if (newfile != NULL) { newfile = newfile + 1; /* Get rid of leading / */ } else { newfile = file; } i = snprintf(buf, size, "%s:%d:%s(): ", newfile, line, func); if (i < size) { va_start(adx, fmt); (void) vsnprintf(buf + i, size - i, fmt, adx); va_end(adx); } /* * Get rid of trailing newline. */ nl = strrchr(buf, '\n'); if (nl != NULL) *nl = '\0'; __zfs_dbgmsg(buf); kmem_free(buf, size); } #else void zfs_dbgmsg_print(const char *tag) { - zfs_dbgmsg_t *zdm; + ssize_t ret __attribute__((unused)); - (void) printf("ZFS_DBGMSG(%s):\n", tag); mutex_enter(&zfs_dbgmsgs_lock); - for (zdm = list_head(&zfs_dbgmsgs); zdm; + + /* + * We use write() in this function instead of printf() + * so it is safe to call from a signal handler. + */ + ret = write(STDOUT_FILENO, "ZFS_DBGMSG(", 11); + ret = write(STDOUT_FILENO, tag, strlen(tag)); + ret = write(STDOUT_FILENO, ") START:\n", 9); + + for (zfs_dbgmsg_t zdm = list_head(&zfs_dbgmsgs); zdm != NULL; zdm = list_next(&zfs_dbgmsgs, zdm)) - (void) printf("%s\n", zdm->zdm_msg); + ret = write(STDOUT_FILENO, zdm->zdm_msg, + strlen(zdm->zdm_msg)); + ret = write(STDOUT_FILENO, "\n", 1); + } + + ret = write(STDOUT_FILENO, "ZFS_DBGMSG(", 11); + ret = write(STDOUT_FILENO, tag, strlen(tag)); + ret = write(STDOUT_FILENO, ") END\n", 6); + mutex_exit(&zfs_dbgmsgs_lock); } #endif /* _KERNEL */ ZFS_MODULE_PARAM(zfs, zfs_, dbgmsg_enable, INT, ZMOD_RW, "Enable ZFS debug message log"); ZFS_MODULE_PARAM(zfs, zfs_, dbgmsg_maxsize, UINT, ZMOD_RW, "Maximum ZFS debug log size"); diff --git a/module/os/linux/zfs/zfs_debug.c b/module/os/linux/zfs/zfs_debug.c index f707959c9445..bc5c028dca09 100644 --- a/module/os/linux/zfs/zfs_debug.c +++ b/module/os/linux/zfs/zfs_debug.c @@ -1,260 +1,261 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2014 by Delphix. All rights reserved. */ #include #include typedef struct zfs_dbgmsg { procfs_list_node_t zdm_node; uint64_t zdm_timestamp; uint_t zdm_size; char zdm_msg[]; /* variable length allocation */ } zfs_dbgmsg_t; static procfs_list_t zfs_dbgmsgs; static uint_t zfs_dbgmsg_size = 0; static uint_t zfs_dbgmsg_maxsize = 4<<20; /* 4MB */ /* * Internal ZFS debug messages are enabled by default. * * # Print debug messages * cat /proc/spl/kstat/zfs/dbgmsg * * # Disable the kernel debug message log. * echo 0 > /sys/module/zfs/parameters/zfs_dbgmsg_enable * * # Clear the kernel debug message log. * echo 0 >/proc/spl/kstat/zfs/dbgmsg */ int zfs_dbgmsg_enable = B_TRUE; static int zfs_dbgmsg_show_header(struct seq_file *f) { seq_printf(f, "%-12s %-8s\n", "timestamp", "message"); return (0); } static int zfs_dbgmsg_show(struct seq_file *f, void *p) { zfs_dbgmsg_t *zdm = (zfs_dbgmsg_t *)p; seq_printf(f, "%-12llu %-s\n", (u_longlong_t)zdm->zdm_timestamp, zdm->zdm_msg); return (0); } static void zfs_dbgmsg_purge(uint_t max_size) { while (zfs_dbgmsg_size > max_size) { zfs_dbgmsg_t *zdm = list_remove_head(&zfs_dbgmsgs.pl_list); if (zdm == NULL) return; uint_t size = zdm->zdm_size; kmem_free(zdm, size); zfs_dbgmsg_size -= size; } } static int zfs_dbgmsg_clear(procfs_list_t *procfs_list) { (void) procfs_list; mutex_enter(&zfs_dbgmsgs.pl_lock); zfs_dbgmsg_purge(0); mutex_exit(&zfs_dbgmsgs.pl_lock); return (0); } void zfs_dbgmsg_init(void) { procfs_list_install("zfs", NULL, "dbgmsg", 0600, &zfs_dbgmsgs, zfs_dbgmsg_show, zfs_dbgmsg_show_header, zfs_dbgmsg_clear, offsetof(zfs_dbgmsg_t, zdm_node)); } void zfs_dbgmsg_fini(void) { procfs_list_uninstall(&zfs_dbgmsgs); zfs_dbgmsg_purge(0); /* * TODO - decide how to make this permanent */ #ifdef _KERNEL procfs_list_destroy(&zfs_dbgmsgs); #endif } void __set_error(const char *file, const char *func, int line, int err) { /* * To enable this: * * $ echo 512 >/sys/module/zfs/parameters/zfs_flags */ if (zfs_flags & ZFS_DEBUG_SET_ERROR) __dprintf(B_FALSE, file, func, line, "error %lu", (ulong_t)err); } void __zfs_dbgmsg(char *buf) { uint_t size = sizeof (zfs_dbgmsg_t) + strlen(buf) + 1; zfs_dbgmsg_t *zdm = kmem_zalloc(size, KM_SLEEP); zdm->zdm_size = size; zdm->zdm_timestamp = gethrestime_sec(); strcpy(zdm->zdm_msg, buf); mutex_enter(&zfs_dbgmsgs.pl_lock); procfs_list_add(&zfs_dbgmsgs, zdm); zfs_dbgmsg_size += size; zfs_dbgmsg_purge(zfs_dbgmsg_maxsize); mutex_exit(&zfs_dbgmsgs.pl_lock); } #ifdef _KERNEL void __dprintf(boolean_t dprint, const char *file, const char *func, int line, const char *fmt, ...) { const char *newfile; va_list adx; size_t size; char *buf; char *nl; int i; char *prefix = (dprint) ? "dprintf: " : ""; size = 1024; buf = kmem_alloc(size, KM_SLEEP); /* * Get rid of annoying prefix to filename. */ newfile = strrchr(file, '/'); if (newfile != NULL) { newfile = newfile + 1; /* Get rid of leading / */ } else { newfile = file; } i = snprintf(buf, size, "%px %s%s:%d:%s(): ", curthread, prefix, newfile, line, func); if (i < size) { va_start(adx, fmt); (void) vsnprintf(buf + i, size - i, fmt, adx); va_end(adx); } /* * Get rid of trailing newline for dprintf logs. */ if (dprint && buf[0] != '\0') { nl = &buf[strlen(buf) - 1]; if (*nl == '\n') *nl = '\0'; } /* * To get this data enable the zfs__dprintf trace point as shown: * * # Enable zfs__dprintf tracepoint, clear the tracepoint ring buffer * $ echo 1 > /sys/kernel/debug/tracing/events/zfs/enable * $ echo 0 > /sys/kernel/debug/tracing/trace * * # Dump the ring buffer. * $ cat /sys/kernel/debug/tracing/trace */ DTRACE_PROBE1(zfs__dprintf, char *, buf); /* * To get this data: * * $ cat /proc/spl/kstat/zfs/dbgmsg * * To clear the buffer: * $ echo 0 > /proc/spl/kstat/zfs/dbgmsg */ __zfs_dbgmsg(buf); kmem_free(buf, size); } #else void zfs_dbgmsg_print(const char *tag) { ssize_t ret __attribute__((unused)); + mutex_enter(&zfs_dbgmsgs.pl_lock); + /* * We use write() in this function instead of printf() * so it is safe to call from a signal handler. */ ret = write(STDOUT_FILENO, "ZFS_DBGMSG(", 11); ret = write(STDOUT_FILENO, tag, strlen(tag)); ret = write(STDOUT_FILENO, ") START:\n", 9); - mutex_enter(&zfs_dbgmsgs.pl_lock); for (zfs_dbgmsg_t *zdm = list_head(&zfs_dbgmsgs.pl_list); zdm != NULL; zdm = list_next(&zfs_dbgmsgs.pl_list, zdm)) { ret = write(STDOUT_FILENO, zdm->zdm_msg, strlen(zdm->zdm_msg)); ret = write(STDOUT_FILENO, "\n", 1); } ret = write(STDOUT_FILENO, "ZFS_DBGMSG(", 11); ret = write(STDOUT_FILENO, tag, strlen(tag)); ret = write(STDOUT_FILENO, ") END\n", 6); mutex_exit(&zfs_dbgmsgs.pl_lock); } #endif /* _KERNEL */ #ifdef _KERNEL module_param(zfs_dbgmsg_enable, int, 0644); MODULE_PARM_DESC(zfs_dbgmsg_enable, "Enable ZFS debug message log"); /* BEGIN CSTYLED */ module_param(zfs_dbgmsg_maxsize, uint, 0644); /* END CSTYLED */ MODULE_PARM_DESC(zfs_dbgmsg_maxsize, "Maximum ZFS debug log size"); #endif