1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 2012, 2014 by Delphix. All rights reserved.
24  */
25 
26 #include <sys/zfs_context.h>
27 #include <sys/kstat.h>
28 
29 typedef struct zfs_dbgmsg {
30 	list_node_t zdm_node;
31 	time_t zdm_timestamp;
32 	int zdm_size;
33 	char zdm_msg[1]; /* variable length allocation */
34 } zfs_dbgmsg_t;
35 
36 list_t zfs_dbgmsgs;
37 int zfs_dbgmsg_size = 0;
38 kmutex_t zfs_dbgmsgs_lock;
39 int zfs_dbgmsg_maxsize = 4<<20; /* 4MB */
40 kstat_t *zfs_dbgmsg_kstat;
41 
42 /*
43  * Internal ZFS debug messages are enabled by default.
44  *
45  * # Print debug messages
46  * dtrace -n 'zfs-dbgmsg { print(stringof(arg0)); }'
47  *
48  * # Disable the kernel debug message log.
49  * sysctl vfs.zfs.dbgmsg_enable=0
50  */
51 int zfs_dbgmsg_enable = 1;
52 
53 static int
54 zfs_dbgmsg_headers(char *buf, size_t size)
55 {
56 	(void) snprintf(buf, size, "%-12s %-8s\n", "timestamp", "message");
57 
58 	return (0);
59 }
60 
61 static int
62 zfs_dbgmsg_data(char *buf, size_t size, void *data)
63 {
64 	zfs_dbgmsg_t *zdm = (zfs_dbgmsg_t *)data;
65 
66 	(void) snprintf(buf, size, "%-12llu %-s\n",
67 	    (u_longlong_t)zdm->zdm_timestamp, zdm->zdm_msg);
68 
69 	return (0);
70 }
71 
72 static void *
73 zfs_dbgmsg_addr(kstat_t *ksp, loff_t n)
74 {
75 	zfs_dbgmsg_t *zdm = (zfs_dbgmsg_t *)ksp->ks_private;
76 
77 	ASSERT(MUTEX_HELD(&zfs_dbgmsgs_lock));
78 
79 	if (n == 0)
80 		ksp->ks_private = list_head(&zfs_dbgmsgs);
81 	else if (zdm)
82 		ksp->ks_private = list_next(&zfs_dbgmsgs, zdm);
83 
84 	return (ksp->ks_private);
85 }
86 
87 static void
88 zfs_dbgmsg_purge(int max_size)
89 {
90 	zfs_dbgmsg_t *zdm;
91 	int size;
92 
93 	ASSERT(MUTEX_HELD(&zfs_dbgmsgs_lock));
94 
95 	while (zfs_dbgmsg_size > max_size) {
96 		zdm = list_remove_head(&zfs_dbgmsgs);
97 		if (zdm == NULL)
98 			return;
99 
100 		size = zdm->zdm_size;
101 		kmem_free(zdm, size);
102 		zfs_dbgmsg_size -= size;
103 	}
104 }
105 
106 static int
107 zfs_dbgmsg_update(kstat_t *ksp, int rw)
108 {
109 	if (rw == KSTAT_WRITE)
110 		zfs_dbgmsg_purge(0);
111 
112 	return (0);
113 }
114 
115 void
116 zfs_dbgmsg_init(void)
117 {
118 	list_create(&zfs_dbgmsgs, sizeof (zfs_dbgmsg_t),
119 	    offsetof(zfs_dbgmsg_t, zdm_node));
120 	mutex_init(&zfs_dbgmsgs_lock, NULL, MUTEX_DEFAULT, NULL);
121 
122 	zfs_dbgmsg_kstat = kstat_create("zfs", 0, "dbgmsg", "misc",
123 	    KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL);
124 	if (zfs_dbgmsg_kstat) {
125 		zfs_dbgmsg_kstat->ks_lock = &zfs_dbgmsgs_lock;
126 		zfs_dbgmsg_kstat->ks_ndata = UINT32_MAX;
127 		zfs_dbgmsg_kstat->ks_private = NULL;
128 		zfs_dbgmsg_kstat->ks_update = zfs_dbgmsg_update;
129 		kstat_set_raw_ops(zfs_dbgmsg_kstat, zfs_dbgmsg_headers,
130 		    zfs_dbgmsg_data, zfs_dbgmsg_addr);
131 		kstat_install(zfs_dbgmsg_kstat);
132 	}
133 }
134 
135 void
136 zfs_dbgmsg_fini(void)
137 {
138 	if (zfs_dbgmsg_kstat)
139 		kstat_delete(zfs_dbgmsg_kstat);
140 	/*
141 	 * TODO - decide how to make this permanent
142 	 */
143 #ifdef _KERNEL
144 	mutex_enter(&zfs_dbgmsgs_lock);
145 	zfs_dbgmsg_purge(0);
146 	mutex_exit(&zfs_dbgmsgs_lock);
147 	mutex_destroy(&zfs_dbgmsgs_lock);
148 #endif
149 }
150 
151 void
152 __zfs_dbgmsg(char *buf)
153 {
154 	zfs_dbgmsg_t *zdm;
155 	int size;
156 
157 	DTRACE_PROBE1(zfs__dbgmsg, char *, buf);
158 
159 	size = sizeof (zfs_dbgmsg_t) + strlen(buf);
160 	zdm = kmem_zalloc(size, KM_SLEEP);
161 	zdm->zdm_size = size;
162 	zdm->zdm_timestamp = gethrestime_sec();
163 	strcpy(zdm->zdm_msg, buf);
164 
165 	mutex_enter(&zfs_dbgmsgs_lock);
166 	list_insert_tail(&zfs_dbgmsgs, zdm);
167 	zfs_dbgmsg_size += size;
168 	zfs_dbgmsg_purge(MAX(zfs_dbgmsg_maxsize, 0));
169 	mutex_exit(&zfs_dbgmsgs_lock);
170 }
171 
172 void
173 __set_error(const char *file, const char *func, int line, int err)
174 {
175 	/*
176 	 * To enable this:
177 	 *
178 	 * $ echo 512 >/sys/module/zfs/parameters/zfs_flags
179 	 */
180 	if (zfs_flags & ZFS_DEBUG_SET_ERROR)
181 		__dprintf(B_FALSE, file, func, line, "error %lu", err);
182 }
183 
184 #ifdef _KERNEL
185 void
186 __dprintf(boolean_t dprint, const char *file, const char *func,
187     int line, const char *fmt, ...)
188 {
189 	const char *newfile;
190 	va_list adx;
191 	size_t size;
192 	char *buf;
193 	char *nl;
194 	int i;
195 
196 	size = 1024;
197 	buf = kmem_alloc(size, KM_SLEEP);
198 
199 	/*
200 	 * Get rid of annoying prefix to filename.
201 	 */
202 	newfile = strrchr(file, '/');
203 	if (newfile != NULL) {
204 		newfile = newfile + 1; /* Get rid of leading / */
205 	} else {
206 		newfile = file;
207 	}
208 
209 	i = snprintf(buf, size, "%s:%d:%s(): ", newfile, line, func);
210 
211 	if (i < size) {
212 		va_start(adx, fmt);
213 		(void) vsnprintf(buf + i, size - i, fmt, adx);
214 		va_end(adx);
215 	}
216 
217 	/*
218 	 * Get rid of trailing newline.
219 	 */
220 	nl = strrchr(buf, '\n');
221 	if (nl != NULL)
222 		*nl = '\0';
223 
224 	__zfs_dbgmsg(buf);
225 
226 	kmem_free(buf, size);
227 }
228 
229 #else
230 
231 void
232 zfs_dbgmsg_print(const char *tag)
233 {
234 	zfs_dbgmsg_t *zdm;
235 
236 	(void) printf("ZFS_DBGMSG(%s):\n", tag);
237 	mutex_enter(&zfs_dbgmsgs_lock);
238 	for (zdm = list_head(&zfs_dbgmsgs); zdm;
239 	    zdm = list_next(&zfs_dbgmsgs, zdm))
240 		(void) printf("%s\n", zdm->zdm_msg);
241 	mutex_exit(&zfs_dbgmsgs_lock);
242 }
243 #endif /* _KERNEL */
244 
245 /* BEGIN CSTYLED */
246 ZFS_MODULE_PARAM(zfs, zfs_, dbgmsg_enable, INT, ZMOD_RW,
247     "Enable ZFS debug message log");
248 
249 ZFS_MODULE_PARAM(zfs, zfs_, dbgmsg_maxsize, INT, ZMOD_RW,
250     "Maximum ZFS debug log size");
251 /* END CSTYLED */
252