xref: /haiku/src/add-ons/kernel/file_systems/udf/UdfDebug.cpp (revision f2ced752a08ff5d2618826bcd3ae3976c9f3e92e)
1 //----------------------------------------------------------------------
2 //  This software is part of the OpenBeOS distribution and is covered
3 //  by the OpenBeOS license.
4 //
5 //  This version copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net
6 //  Initial version copyright (c) 2002 Axel Dörfler, axeld@pinc-software.de
7 //----------------------------------------------------------------------
8 
9 /*! \file Debug.cpp
10 
11 	Support code for handy debugging macros.
12 */
13 
14 #include "UdfDebug.h"
15 
16 #include <stdlib.h>
17 #include <KernelExport.h>
18 #include <TLS.h>
19 
20 //----------------------------------------------------------------------
21 // Long-winded overview of the debug output macros:
22 //----------------------------------------------------------------------
23 /*! \def DEBUG_INIT()
24 	\brief Increases the indentation level, prints out the enclosing function's
25 	name, and creates a \c DebugHelper object on the stack to automatically
26 	decrease the indentation level upon function exit.
27 
28 	This macro should be called at the very beginning of any function in
29 	which you wish to use any of the other debugging macros.
30 
31 	If DEBUG is undefined, does nothing.
32 */
33 //----------------------------------------------------------------------
34 /*! \def PRINT(x)
35 	\brief Prints out the enclosing function's name followed by the contents
36 	of \a x at the current indentation level.
37 
38 	\param x A printf-style format string enclosed in an extra set of parenteses,
39 	         e.g. PRINT(("%d\n", 0));
40 
41 	If DEBUG is undefined, does nothing.
42 */
43 //----------------------------------------------------------------------
44 /*! \def LPRINT(x)
45 	\brief Identical to \c PRINT(x), except that the line number in the source
46 	file at which the macro is invoked is also printed.
47 
48 	\param x A printf-style format string enclosed in an extra set of parenteses,
49 	         e.g. PRINT(("%d\n", 0));
50 
51 	If DEBUG is undefined, does nothing.
52 */
53 //----------------------------------------------------------------------
54 /*! \def SIMPLE_PRINT(x)
55 	\brief Directly prints the contents of \a x with no extra formatting or
56 	information included (just like a straight \c printf() call).
57 
58 	\param x A printf-style format string enclosed in an extra set of parenteses,
59 	         e.g. PRINT(("%d\n", 0));
60 
61 	If DEBUG is undefined, does nothing.
62 */
63 //----------------------------------------------------------------------
64 /*! \def PRINT_INDENT()
65 	\brief Prints out enough indentation characters to indent the current line
66 	to the current indentation level (assuming the cursor was flush left to
67 	begin with...).
68 
69 	This function is called by the other \c *PRINT* macros, and isn't really
70 	intended for general consumption, but you might find it useful.
71 
72 	If DEBUG is undefined, does nothing.
73 */
74 //----------------------------------------------------------------------
75 /*! \def REPORT_ERROR(error)
76 	\brief Calls \c LPRINT(x) with a format string listing the error
77 	code in \c error (assumed to be a \c status_t value) and the
78 	corresponding text error code returned by a call to \c strerror().
79 
80 	This function is called by the \c RETURN* macros, and isn't really
81 	intended for general consumption, but you might find it useful.
82 
83 	\param error A \c status_t error code to report.
84 
85 	If DEBUG is undefined, does nothing.
86 */
87 //----------------------------------------------------------------------
88 /*! \def RETURN_ERROR(error)
89 	\brief Calls \c REPORT_ERROR(error) if error is a an error code (i.e.
90 	negative), otherwise remains silent. In either case, the enclosing
91 	function is then exited with a call to \c "return error;".
92 
93 	\param error A \c status_t error code to report (if negative) and return.
94 
95 	If DEBUG is undefined, silently returns the value in \c error.
96 */
97 //----------------------------------------------------------------------
98 /*! \def RETURN(error)
99 	\brief Prints out a description of the error code being returned
100 	(which, in this case, may be either "erroneous" or "successful")
101 	and then exits the enclosing function with a call to \c "return error;".
102 
103 	\param error A \c status_t error code to report and return.
104 
105 	If DEBUG is undefined, silently returns the value in \c error.
106 */
107 //----------------------------------------------------------------------
108 /*! \def FATAL(x)
109 	\brief Prints out a fatal error message.
110 
111 	This one's still a work in progress...
112 
113 	\param x A printf-style format string enclosed in an extra set of parenteses,
114 	         e.g. PRINT(("%d\n", 0));
115 
116 	If DEBUG is undefined, does nothing.
117 */
118 //----------------------------------------------------------------------
119 /*! \def INFORM(x)
120 	\brief Directly prints the contents of \a x with no extra formatting or
121 	information included (just like a straight \c printf() call). Does so
122 	whether \c DEBUG is defined or not.
123 
124 	\param x A printf-style format string enclosed in an extra set of parenteses,
125 	         e.g. PRINT(("%d\n", 0));
126 
127 	I'll say it again: Prints its output regardless to DEBUG being defined or
128 	undefined.
129 */
130 //----------------------------------------------------------------------
131 /*! \def DBG(x)
132 	\brief If debug is defined, \a x is passed along to the code and
133 	executed unmodified. If \c DEBUG is undefined, the contents of
134 	\a x disappear into the ether.
135 
136 	\param x Damn near anything resembling valid C\C++.
137 */
138 //----------------------------------------------------------------------
139 /*! \def DIE(x)
140 	\brief Drops the user into the appropriate debugger (user or kernel)
141 	after printing out the handy message bundled in the parenthesee
142 	enclosed printf-style format string found in \a x.
143 
144 	\param x A printf-style format string enclosed in an extra set of parenteses,
145 	         e.g. PRINT(("%d\n", 0));
146 */
147 
148 
149 //----------------------------------------------------------------------
150 // declarations
151 //----------------------------------------------------------------------
152 
153 static void indent(uint8 tabCount);
154 static void unindent(uint8 tabCount);
155 #if !_KERNEL_MODE
156 	static int32 get_tls_handle();
157 #endif
158 
159 //! Used to keep the tls handle from being allocated more than once.
160 vint32 tls_spinlock = 0;
161 
162 /*! \brief Used to flag whether the tls handle has been allocated yet.
163 
164 	Not sure if this really needs to be \c volatile or not...
165 */
166 volatile bool tls_handle_initialized = false;
167 
168 //! The tls handle of the tls var used to store indentation info.
169 int32 tls_handle = 0;
170 
171 //----------------------------------------------------------------------
172 // public functions
173 //----------------------------------------------------------------------
174 
175 /*! \brief Returns the current debug indentation level for the
176 	current thread.
177 
178 	NOTE: indentation is currently unsupported for R5::kernelland due
179 	to lack of thread local storage support.
180 */
181 int32
182 _get_debug_indent_level()
183 {
184 #if !_KERNEL_MODE
185 	return (int32)tls_get(get_tls_handle());
186 #else
187 	return 1;
188 #endif
189 }
190 
191 //----------------------------------------------------------------------
192 // static functions
193 //----------------------------------------------------------------------
194 
195 /*! \brief Increases the current debug indentation level for
196 	the current thread by 1.
197 */
198 void
199 indent(uint8 tabCount)
200 {
201 #if !_KERNEL_MODE
202 	tls_set(get_tls_handle(), (void*)(_get_debug_indent_level()+tabCount));
203 #endif
204 }
205 
206 /*! \brief Decreases the current debug indentation level for
207 	the current thread by 1.
208 */
209 void
210 unindent(uint8 tabCount)
211 {
212 #if !_KERNEL_MODE
213 	tls_set(get_tls_handle(), (void*)(_get_debug_indent_level()-tabCount));
214 #endif
215 }
216 
217 #if !_KERNEL_MODE
218 /*! \brief Returns the thread local storage handle used to store
219 	indentation information, allocating the handle first if
220 	necessary.
221 */
222 int32
223 get_tls_handle()
224 {
225 	// Init the tls handle if this is the first call.
226 	if (!tls_handle_initialized) {
227 		if (atomic_or(&tls_spinlock, 1) == 0) {
228 			// First one in gets to init
229 			tls_handle = tls_allocate();
230 			tls_handle_initialized = true;
231 			atomic_and(&tls_spinlock, 0);
232 		} else {
233 			// All others must wait patiently
234 			while (!tls_handle_initialized) {
235 				snooze(1);
236 			}
237 		}
238 	}
239 	return tls_handle;
240 }
241 #endif
242 
243 /*! \brief Helper class for initializing the debugging output
244 	file.
245 
246 	Note that this hummer isn't threadsafe, but it doesn't really
247 	matter for our concerns, since the worst it'll result in is
248 	a dangling file descriptor, and that would be in the case of
249 	two or more volumes being mounted almost simultaneously...
250 	not too big of a worry.
251 */
252 class DebugOutputFile {
253 public:
254 	DebugOutputFile(const char *filename = NULL)
255 		: fFile(-1)
256 	{
257 		Init(filename);
258 	}
259 
260 	~DebugOutputFile() {
261 		if (fFile >= 0)
262 			close(fFile);
263 	}
264 
265 	void Init(const char *filename) {
266 		if (fFile < 0 && filename)
267 			fFile = open(filename, O_RDWR | O_CREAT | O_TRUNC);
268 	}
269 
270 	int File() const { return fFile; }
271 private:
272 	int fFile;
273 };
274 
275 DebugOutputFile *out = NULL;
276 
277 /*!	\brief It doesn't appear that the constructor for the global
278 	\c out variable is called when built as an R5 filesystem add-on,
279 	so this function needs to be called in udf_mount to let the
280 	magic happen.
281 */
282 void initialize_debugger(const char *filename)
283 {
284 #if DEBUG_TO_FILE
285 	if (!out) {
286 		out = new(nothrow) DebugOutputFile(filename);
287 		dbg_printf("out was NULL!\n");
288 	} else {
289 		DebugOutputFile *temp = out;
290 		out = new(nothrow) DebugOutputFile(filename);
291 		dbg_printf("out was %p!\n", temp);
292 	}
293 #endif
294 }
295 
296 // dbg_printf, stolen from Ingo's ReiserFS::Debug.cpp.
297 void
298 dbg_printf(const char *format,...)
299 {
300 #if DEBUG_TO_FILE
301 	if (!out)
302 		return;
303 
304 	char buffer[1024];
305 	va_list args;
306 	va_start(args, format);
307 	// no vsnprintf() on PPC
308 	#if defined(__INTEL__) && !_KERNEL_MODE
309 		vsnprintf(buffer, sizeof(buffer) - 1, format, args);
310 	#else
311 		vsprintf(buffer, format, args);
312 	#endif
313 	va_end(args);
314 	buffer[sizeof(buffer) - 1] = '\0';
315 	write(out->File(), buffer, strlen(buffer));
316 #endif
317 }
318 
319 //----------------------------------------------------------------------
320 // DebugHelper
321 //----------------------------------------------------------------------
322 
323 /*! \brief Increases the current indentation level.
324 */
325 DebugHelper::DebugHelper(const char *className, uint8 tabCount)
326 	: fTabCount(tabCount)
327 	, fClassName(NULL)
328 {
329 	indent(fTabCount);
330 	if (className) {
331 		fClassName = (char*)malloc(strlen(className)+1);
332 		if (fClassName)
333 			strcpy(fClassName, className);
334 	}
335 }
336 
337 /*! \brief Decreases the current indentation level.
338 */
339 DebugHelper::~DebugHelper()
340 {
341 	unindent(fTabCount);
342 	free(fClassName);
343 }
344 
345