1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1998, 2013 Oracle and/or its affiliates.  All rights reserved.
5  *
6  * $Id$
7  */
8 
9 #ifndef _DB_DEBUG_H_
10 #define	_DB_DEBUG_H_
11 
12 #if defined(__cplusplus)
13 extern "C" {
14 #endif
15 
16 /*
17  * Turn on additional error checking in gcc 3.X.
18  */
19 #if !defined(__GNUC__) || __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
20 #define	__attribute__(s)
21 #endif
22 
23 /*
24  * When running with #DIAGNOSTIC defined, we smash memory and do memory
25  * guarding with a special byte value.
26  */
27 #define	CLEAR_BYTE	0xdb
28 #define	GUARD_BYTE	0xdc
29 
30 /*
31  * DB assertions.
32  *
33  * Use __STDC__ rather than STDC_HEADERS, the #e construct is ANSI C specific.
34  */
35 #if defined(DIAGNOSTIC) && defined(__STDC__)
36 #define	DB_ASSERT(env, e)						\
37 	((e) ? (void)0 : __db_assert(env, #e, __FILE__, __LINE__))
38 #else
39 #define	DB_ASSERT(env, e)	NOP_STATEMENT
40 #endif
41 
42 /*
43  * "Shut that bloody compiler up!"
44  *
45  * Unused, or not-used-yet variable.  We need to write and then read the
46  * variable, some compilers are too bloody clever by half.
47  */
48 #define	COMPQUIET(n, v)	do {					        \
49 	(n) = (v);						        \
50 	(n) = (n);						        \
51 } while (0)
52 
53 /*
54  * Purify and other run-time tools complain about uninitialized reads/writes
55  * of structure fields whose only purpose is padding, as well as when heap
56  * memory that was never initialized is written to disk.
57  */
58 #ifdef	UMRW
59 #define	UMRW_SET(v)	(v) = 0
60 #else
61 #define	UMRW_SET(v)	NOP_STATEMENT
62 #endif
63 
64 /*
65  * Errors are in one of two areas: a Berkeley DB error, or a system-level
66  * error.  We use db_strerror to translate the former and __os_strerror to
67  * translate the latter.
68  */
69 typedef enum {
70 	DB_ERROR_NOT_SET=0,
71 	DB_ERROR_SET=1,
72 	DB_ERROR_SYSTEM=2
73 } db_error_set_t;
74 
75 /*
76  * Message handling.  Use a macro instead of a function because va_list
77  * references to variadic arguments cannot be reset to the beginning of the
78  * variadic argument list (and then rescanned), by functions other than the
79  * original routine that took the variadic list of arguments.
80  */
81 #if defined(STDC_HEADERS) || defined(__cplusplus)
82 #define	DB_REAL_ERR(dbenv, error, error_set, app_call, fmt) {		\
83 	va_list __ap;							\
84 									\
85 	/* Call the application's callback function, if specified. */	\
86 	va_start(__ap, fmt);						\
87 	if ((dbenv) != NULL && (dbenv)->db_errcall != NULL)		\
88 		__db_errcall(dbenv, error, error_set, fmt, __ap);	\
89 	va_end(__ap);							\
90 									\
91 	/*								\
92 	 * If the application specified a file descriptor, write to it.	\
93 	 * If we wrote to neither the application's callback routine or	\
94 	 * its file descriptor, and it's an application error message	\
95 	 * using {DbEnv,Db}.{err,errx} or the application has never	\
96 	 * configured an output channel, default by writing to stderr.	\
97 	 */								\
98 	va_start(__ap, fmt);						\
99 	if ((dbenv) == NULL ||						\
100 	    (dbenv)->db_errfile != NULL ||				\
101 	    ((dbenv)->db_errcall == NULL &&				\
102 	    ((app_call) || F_ISSET((dbenv)->env, ENV_NO_OUTPUT_SET))))	\
103 		__db_errfile(dbenv, error, error_set, fmt, __ap);	\
104 	va_end(__ap);							\
105 }
106 #else
107 #define	DB_REAL_ERR(dbenv, error, error_set, app_call, fmt) {		\
108 	va_list __ap;							\
109 									\
110 	/* Call the application's callback function, if specified. */	\
111 	va_start(__ap);							\
112 	if ((dbenv) != NULL && (dbenv)->db_errcall != NULL)		\
113 		__db_errcall(dbenv, error, error_set, fmt, __ap);	\
114 	va_end(__ap);							\
115 									\
116 	/*								\
117 	 * If the application specified a file descriptor, write to it.	\
118 	 * If we wrote to neither the application's callback routine or	\
119 	 * its file descriptor, and it's an application error message	\
120 	 * using {DbEnv,Db}.{err,errx} or the application has never	\
121 	 * configured an output channel, default by writing to stderr.	\
122 	 */								\
123 	va_start(__ap);							\
124 	if ((dbenv) == NULL ||						\
125 	    (dbenv)->db_errfile != NULL ||				\
126 	    ((dbenv)->db_errcall == NULL &&				\
127 	    ((app_call) || F_ISSET((dbenv)->env, ENV_NO_OUTPUT_SET))))	\
128 		 __db_errfile(env, error, error_set, fmt, __ap);	\
129 	va_end(__ap);							\
130 }
131 #endif
132 #if defined(STDC_HEADERS) || defined(__cplusplus)
133 #define	DB_REAL_MSG(dbenv, fmt) {					\
134 	va_list __ap;							\
135 									\
136 	/* Call the application's callback function, if specified. */	\
137 	va_start(__ap, fmt);						\
138 	if ((dbenv) != NULL && (dbenv)->db_msgcall != NULL)		\
139 		__db_msgcall(dbenv, fmt, __ap);				\
140 	va_end(__ap);							\
141 									\
142 	/*								\
143 	 * If the application specified a file descriptor, write to it.	\
144 	 * If we wrote to neither the application's callback routine or	\
145 	 * its file descriptor, write to stdout.			\
146 	 */								\
147 	va_start(__ap, fmt);						\
148 	if ((dbenv) == NULL ||						\
149 	    (dbenv)->db_msgfile != NULL ||				\
150 	    (dbenv)->db_msgcall == NULL) {				\
151 		__db_msgfile(dbenv, fmt, __ap);				\
152 	}								\
153 	va_end(__ap);							\
154 }
155 #else
156 #define	DB_REAL_MSG(dbenv, fmt) {					\
157 	va_list __ap;							\
158 									\
159 	/* Call the application's callback function, if specified. */	\
160 	va_start(__ap);							\
161 	if ((dbenv) != NULL && (dbenv)->db_msgcall != NULL)		\
162 		__db_msgcall(dbenv, fmt, __ap);				\
163 	va_end(__ap);							\
164 									\
165 	/*								\
166 	 * If the application specified a file descriptor, write to it.	\
167 	 * If we wrote to neither the application's callback routine or	\
168 	 * its file descriptor, write to stdout.			\
169 	 */								\
170 	va_start(__ap);							\
171 	if ((dbenv) == NULL ||						\
172 	    (dbenv)->db_msgfile != NULL ||				\
173 	    (dbenv)->db_msgcall == NULL) {				\
174 		__db_msgfile(dbenv, fmt, __ap);				\
175 	}								\
176 	va_end(__ap);							\
177 }
178 #endif
179 
180 /*
181  * Debugging macro to log operations.
182  *	If DEBUG_WOP is defined, log operations that modify the database.
183  *	If DEBUG_ROP is defined, log operations that read the database.
184  *
185  * D dbp
186  * T txn
187  * O operation (string)
188  * K key
189  * A data
190  * F flags
191  */
192 #define	LOG_OP(C, T, O, K, A, F) {					\
193 	DB_LSN __lsn;							\
194 	DBT __op;							\
195 	if (DBC_LOGGING((C))) {						\
196 		memset(&__op, 0, sizeof(__op));				\
197 		__op.data = O;						\
198 		__op.size = (u_int32_t)strlen(O) + 1;			\
199 		(void)__db_debug_log((C)->env, T, &__lsn, 0,		\
200 		    &__op, (C)->dbp->log_filename->id, K, A, F);	\
201 	}								\
202 }
203 #ifdef	DEBUG_ROP
204 #define	DEBUG_LREAD(C, T, O, K, A, F)	LOG_OP(C, T, O, K, A, F)
205 #else
206 #define	DEBUG_LREAD(C, T, O, K, A, F)
207 #endif
208 #ifdef	DEBUG_WOP
209 #define	DEBUG_LWRITE(C, T, O, K, A, F)	LOG_OP(C, T, O, K, A, F)
210 #else
211 #define	DEBUG_LWRITE(C, T, O, K, A, F)
212 #endif
213 
214 /*
215  * Hook for testing recovery at various places in the create/delete paths.
216  * Hook for testing subdb locks.
217  */
218 #if CONFIG_TEST
219 #define	DB_TEST_SUBLOCKS(env, flags) do {				\
220 	if ((env)->test_abort == DB_TEST_SUBDB_LOCKS)			\
221 		(flags) |= DB_LOCK_NOWAIT;				\
222 } while (0)
223 
224 #define	DB_ENV_TEST_RECOVERY(env, val, ret, name) do {			\
225 	int __ret;							\
226 	PANIC_CHECK((env));						\
227 	if ((env)->test_copy == (val)) {				\
228 		/* COPY the FILE */					\
229 		if ((__ret = __db_testcopy((env), NULL, (name))) != 0)	\
230 			(ret) = __env_panic((env), __ret);		\
231 	}								\
232 	if ((env)->test_abort == (val)) {				\
233 		/* ABORT the TXN */					\
234 		(env)->test_abort = 0;					\
235 		(ret) = EINVAL;						\
236 		goto db_tr_err;						\
237 	}								\
238 } while (0)
239 
240 #define	DB_TEST_RECOVERY(dbp, val, ret, name) do {			\
241 	ENV *__env = (dbp)->env;					\
242 	int __ret;							\
243 	PANIC_CHECK(__env);						\
244 	if (__env->test_copy == (val)) {				\
245 		/* Copy the file. */					\
246 		if (F_ISSET((dbp),					\
247 		    DB_AM_OPEN_CALLED) && (dbp)->mpf != NULL)		\
248 			(void)__db_sync(dbp);				\
249 		if ((__ret =						\
250 		    __db_testcopy(__env, (dbp), (name))) != 0)		\
251 			(ret) = __env_panic(__env, __ret);		\
252 	}								\
253 	if (__env->test_abort == (val)) {				\
254 		/* Abort the transaction. */				\
255 		__env->test_abort = 0;					\
256 		(ret) = EINVAL;						\
257 		goto db_tr_err;						\
258 	}								\
259 } while (0)
260 
261 #define	DB_TEST_RECOVERY_LABEL	db_tr_err:
262 
263 #define	DB_TEST_SET(field, val) do {					\
264 	if (field == (val))						\
265 		goto db_tr_err;						\
266 } while (0)
267 
268 #define	DB_TEST_WAIT(env, val)						\
269 	if ((val) != 0)							\
270 		__os_yield((env), (u_long)(val), 0)
271 #else
272 #define	DB_TEST_SUBLOCKS(env, flags)
273 #define	DB_ENV_TEST_RECOVERY(env, val, ret, name)
274 #define	DB_TEST_RECOVERY(dbp, val, ret, name)
275 #define	DB_TEST_RECOVERY_LABEL
276 #define	DB_TEST_SET(env, val)
277 #define	DB_TEST_WAIT(env, val)
278 #endif
279 
280 #if defined(__cplusplus)
281 }
282 #endif
283 #endif /* !_DB_DEBUG_H_ */
284