1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 /*!
34 \class SoDebugError SoDebugError.h Inventor/errors/SoDebugError.h
35 \brief The SoDebugError class is the internal debugging message passing mechanism.
36
37 \ingroup errors
38
39 This class basically serves two purposes:
40
41 1) It is the message interface through which error and warning
42 conditions are passed to the programmer which is using the Coin
43 library API for building applications. These messages are generated
44 when API methods are used in an incorrect manner, or if actions on
45 the library has caused it to enter an inconsistent state. Coin
46 programmers should then quickly be able to trace down errors in
47 their application code.
48
49 For this service to be available, the Coin library must be compiled
50 with debugging information. For release builds, most of this
51 "programmer's safety net" is removed to gain maximum speed and to
52 use minimum space (build the smallest possible library).
53
54 2) Coin application programmers can call the SoDebugError methods
55 within their own code as a convenient way of debugging application
56 code, replacing the usual cascades of fprintf() calls.
57
58
59 Coin supports an environment variable to set conditional
60 breakpoints. The COIN_DEBUG_BREAK environment variable can be set
61 to any number of functions in the form of a list separated by commas
62 or spaces. The functionnames must be given as
63 "classname::functioname" (ie without return type, parenthesis or
64 argument types or names). If a debug message is posted from one of
65 those functions, your program will be stopped (using assert(0)).
66
67 This can be useful if you want to get core-dumps or enter a debugger
68 whenever for instance a warning or an error is posted from some
69 function (e.g. "SbVec3f::normalize"). This feature is only enabled
70 in the debug version of Coin.
71 */
72
73 // *************************************************************************
74
75 #include <Inventor/errors/SoDebugError.h>
76
77 #include <cassert>
78 #include <cstdarg>
79 #include <cstdio>
80 #include <cstdlib>
81 #include <cstring>
82
83 #include <Inventor/C/errors/debugerror.h>
84 #include <Inventor/C/tidbits.h>
85 #include <Inventor/SbName.h>
86 #include <Inventor/SoType.h>
87 #include <Inventor/lists/SbList.h>
88
89 #include "coindefs.h"
90 #include "tidbitsp.h"
91
92 #ifndef COIN_WORKAROUND_NO_USING_STD_FUNCS
93 using std::strcmp;
94 using std::strlen;
95 using std::strcpy;
96 using std::strchr;
97 using std::memcpy;
98 #endif // !COIN_WORKAROUND_NO_USING_STD_FUNCS
99
100 #ifdef HAVE_UNISTD_H
101 #include <sys/types.h>
102 #include <unistd.h>
103 #endif
104
105 #if defined(_MSC_VER)
106 #if _MSC_VER >= COIN_MSVC_8_0_VERSION
107 #include <intrin.h>
108 #endif
109 #if _MSC_VER >= COIN_MSVC_7_0_VERSION
110 #define COIN_DEBUGGER_BREAK(x) __debugbreak()
111 #else
112 #define COIN_DEBUGGER_BREAK(x) __asm { int 3 }
113 #endif
114 #else
115 #if defined (MINGW32) || defined (CYGWIN)
116 #include <windows.h>
117 #define COIN_DEBUGGER_BREAK(x) ::DebugBreak()
118 #else
119 #if defined (_POSIX_VERSION)
120 #include <signal.h>
121 #define COIN_DEBUGGER_BREAK(x) ::raise(SIGINT)
122 #else
123 #define COIN_DEBUGGER_BREAK(x) assert(0 && (x))
124 #endif
125 #endif
126 #endif
127
128 // *************************************************************************
129
130 SoType SoDebugError::classTypeId STATIC_SOTYPE_INIT;
131 SoErrorCB * SoDebugError::callback = SoError::defaultHandlerCB;
132 void * SoDebugError::callbackData = NULL;
133
134 // *************************************************************************
135
136 #if COIN_DEBUG
137
138 // variables for run-time breakpoints
139 static int num_breakpoints = 0;
140 static char ** breakpoints = NULL;
141
142 extern "C" {
143
144 static void
debug_break_cleanup(void)145 debug_break_cleanup(void)
146 {
147 for (int i = 0; i < num_breakpoints; i++) {
148 delete[] breakpoints[i];
149 }
150 delete[] breakpoints;
151 breakpoints = NULL;
152 num_breakpoints = 0;
153 }
154
155 } // extern "C"
156
157 #endif // COIN_DEBUG
158
159 /*!
160 \enum SoDebugError::Severity
161
162 Specifies the available severity levels of the debug messages.
163 */
164
165
166 // Documented for parent class.
167 void
initClass(void)168 SoDebugError::initClass(void)
169 {
170 SoDebugError::callback = SoError::defaultHandlerCB;
171 SoDebugError::callbackData = NULL;
172 SoDebugError::classTypeId =
173 SoType::createType(SoError::getClassTypeId(), "DebugError");
174
175 // FIXME: the following "tokenizer" code could perhaps be factored
176 // out and moved to e.g. SbStringList? 20030820 mortene.
177
178 #if COIN_DEBUG
179 const char * env = coin_getenv("COIN_DEBUG_BREAK");
180 if (env) {
181 num_breakpoints = 1;
182 const char * ptr = env;
183 while (*ptr) {
184 if (*ptr == ' ' || *ptr == ',') num_breakpoints++;
185 ptr++;
186 }
187 breakpoints = new char*[num_breakpoints];
188 coin_atexit(debug_break_cleanup, CC_ATEXIT_MSG_SUBSYSTEM);
189 const size_t envstrlen = strlen(env);
190 char * cpy = new char[envstrlen + 1];
191 (void)strcpy(cpy, env);
192 ptr = cpy;
193 const char * end = strchr(ptr, ' ');
194 const char * tst = strchr(ptr, ',');
195 if (end == NULL || (tst && tst < end)) end = tst;
196 if (end == NULL) end = strchr(ptr, '\0');
197 int i = 0;
198 while (end && i < num_breakpoints) {
199 const ptrdiff_t len = end - ptr;
200 breakpoints[i] = new char[len + 1];
201 (void)memcpy(breakpoints[i], ptr, len);
202 breakpoints[i][len] = '\0';
203 i++;
204 ptr = end+1;
205 if (ptr < (cpy + envstrlen)) {
206 end = strchr(ptr, ' ');
207 tst = strchr(ptr, ',');
208 if (end == NULL || (tst && tst < end)) end = tst;
209 if (end == NULL) end = strchr(ptr, 0);
210 }
211 }
212 num_breakpoints = i; // just in case parsing failed for some reason
213 delete[] cpy;
214 }
215 #endif // COIN_DEBUG
216 }
217
218 void
callbackForwarder(const struct cc_debugerror * error,void * COIN_UNUSED_ARG (data))219 SoDebugError::callbackForwarder(const struct cc_debugerror * error,
220 void * COIN_UNUSED_ARG(data)
221 )
222 {
223 SoDebugError wrappederr;
224
225 switch (cc_debugerror_get_severity(error)) {
226 case CC_DEBUGERROR_ERROR:
227 wrappederr.severity = SoDebugError::ERROR;
228 break;
229 case CC_DEBUGERROR_WARNING:
230 wrappederr.severity = SoDebugError::WARNING;
231 break;
232 case CC_DEBUGERROR_INFO:
233 wrappederr.severity = SoDebugError::INFO;
234 break;
235 default:
236 assert(FALSE);
237 break;
238 }
239
240 const cc_string * dbgstr =
241 cc_error_get_debug_string(reinterpret_cast<const cc_error *>(error));
242 const char * dbgstrc = cc_string_get_text(dbgstr);
243 wrappederr.setDebugString(dbgstrc);
244
245 assert(SoDebugError::callback != NULL);
246 SoDebugError::callback(&wrappederr, SoDebugError::callbackData);
247 }
248
249 // Documented for parent class.
250 void
setHandlerCallback(SoErrorCB * const function,void * const data)251 SoDebugError::setHandlerCallback(SoErrorCB * const function, void * const data)
252 {
253 if (SoDebugError::callback == SoError::defaultHandlerCB) {
254 // The user is overriding the default handler, so set up a
255 // "converter" callback function that makes an SoDebugError out of
256 // an cc_debugerror and forwards control to the callback function
257 // given as an argument to setHandlerCallback().
258 cc_debugerror_set_handler_callback(
259 reinterpret_cast<cc_debugerror_cb *>(SoDebugError::callbackForwarder), NULL);
260 }
261
262 SoDebugError::callback = function;
263 SoDebugError::callbackData = data;
264 }
265
266 // Documented for parent class.
267 SoErrorCB *
getHandlerCallback(void)268 SoDebugError::getHandlerCallback(void)
269 {
270 return SoDebugError::callback;
271 }
272
273 // Documented for parent class.
274 void *
getHandlerData(void)275 SoDebugError::getHandlerData(void)
276 {
277 return SoDebugError::callbackData;
278 }
279
280 // Documented for parent class.
281 SoType
getClassTypeId(void)282 SoDebugError::getClassTypeId(void)
283 {
284 return SoDebugError::classTypeId;
285 }
286
287 // Documented for parent class.
288 SoType
getTypeId(void) const289 SoDebugError::getTypeId(void) const
290 {
291 return SoDebugError::classTypeId;
292 }
293
294 /*!
295 Returns severity level of current message.
296
297 You can use this to filter out debug messages and only let warnings
298 and/or errors pass through to the end-user if you have set your own
299 handler callback.
300
301 \sa setHandlerCallback()
302 */
303 SoDebugError::Severity
getSeverity(void) const304 SoDebugError::getSeverity(void) const
305 {
306 return this->severity;
307 }
308
309 #if COIN_DEBUG
310
311 static inline void
check_breakpoints(const char * source)312 check_breakpoints(const char * source)
313 {
314 for (int i = 0; i < num_breakpoints; i++) {
315 if (strcmp(breakpoints[i], source) == 0) {
316 COIN_DEBUGGER_BREAK("Coin debug break");
317 }
318 }
319 }
320
321 #else // COIN_DEBUG
322
323 static inline void
check_breakpoints(const char *)324 check_breakpoints(const char *)
325 {
326 // should be empty
327 }
328
329 #endif // ! COIN_DEBUG
330
331
332 void
commonPostHandling(Severity severity,const char * type,const char * source,const SbString & s)333 SoDebugError::commonPostHandling(Severity severity, const char * type,
334 const char * source, const SbString & s)
335 {
336 SoDebugError error;
337 error.severity = severity;
338 error.setDebugString("Coin ");
339 error.appendToDebugString(type);
340 error.appendToDebugString(" in ");
341 error.appendToDebugString(source);
342 error.appendToDebugString("(): ");
343 error.appendToDebugString(s.getString());
344
345 if (SoDebugError::callback != SoError::defaultHandlerCB) {
346 SoDebugError::callback(&error, SoDebugError::callbackData);
347 }
348 else {
349 error.handleError();
350 }
351
352 check_breakpoints(source);
353 }
354
355 #define SODEBUGERROR_POST(SEVERITY, TYPE) \
356 va_list args; \
357 va_start(args, format); \
358 SbString s; \
359 s.vsprintf(format, args); \
360 va_end(args); \
361 \
362 SoDebugError::commonPostHandling(SEVERITY, TYPE, source, s)
363
364
365 /*!
366 This method posts a message with severity level "ERROR". \a source should
367 be the name of the calling function.
368 */
369 void
post(const char * const source,const char * const format,...)370 SoDebugError::post(const char * const source, const char * const format, ...)
371 {
372 SODEBUGERROR_POST(ERROR, "error");
373 }
374
375 /*!
376 This method posts a message with severity level "WARNING". \a source should
377 be the name of the calling function.
378 */
379 void
postWarning(const char * const source,const char * const format,...)380 SoDebugError::postWarning(const char * const source, const char * const format, ...)
381 {
382 SODEBUGERROR_POST(WARNING, "warning");
383 }
384
385 /*!
386 This method posts a message with severity level "INFO". \a source should
387 be the name of the calling function.
388 */
389 void
postInfo(const char * const source,const char * const format,...)390 SoDebugError::postInfo(const char * const source, const char * const format, ...)
391 {
392 SODEBUGERROR_POST(INFO, "info");
393 }
394
395 // Documented for parent class.
396 SoErrorCB *
getHandler(void * & data) const397 SoDebugError::getHandler(void * & data) const
398 {
399 // FIXME: why the heck isn't this a static method? Looks to me like
400 // an API bug. 20020526 mortene.
401
402 data = SoDebugError::callbackData;
403 return SoDebugError::callback;
404 }
405
406 #undef SODEBUGERROR_POST
407 #undef COIN_DEBUGGER_BREAK
408