| /* |
| * 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 http://www.opensolaris.org/os/licensing. |
| * 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 <sys/zfs_context.h> |
| |
| typedef struct zfs_dbgmsg { |
| procfs_list_node_t zdm_node; |
| uint64_t zdm_timestamp; |
| int zdm_size; |
| char zdm_msg[1]; /* variable length allocation */ |
| } zfs_dbgmsg_t; |
| |
| procfs_list_t zfs_dbgmsgs; |
| int zfs_dbgmsg_size = 0; |
| int 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 = 1; |
| |
| 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(int max_size) |
| { |
| while (zfs_dbgmsg_size > max_size) { |
| zfs_dbgmsg_t *zdm = list_remove_head(&zfs_dbgmsgs.pl_list); |
| if (zdm == NULL) |
| return; |
| |
| int size = zdm->zdm_size; |
| kmem_free(zdm, size); |
| zfs_dbgmsg_size -= size; |
| } |
| } |
| |
| static int |
| zfs_dbgmsg_clear(procfs_list_t *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", |
| "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", err); |
| } |
| |
| void |
| __zfs_dbgmsg(char *buf) |
| { |
| int size = sizeof (zfs_dbgmsg_t) + strlen(buf); |
| 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(MAX(zfs_dbgmsg_maxsize, 0)); |
| 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, "%s%s:%d:%s(): ", 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)); |
| |
| /* |
| * 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"); |
| |
| module_param(zfs_dbgmsg_maxsize, int, 0644); |
| MODULE_PARM_DESC(zfs_dbgmsg_maxsize, "Maximum ZFS debug log size"); |
| #endif |