1 /* ex - Exception Handling */
3 /* Copyright (c) 2005-2010 The SimGrid team */
4 /* Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com> */
5 /* Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/> */
6 /* Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/> */
7 /* All rights reserved. */
9 /* This code is inspirated from the OSSP version (as retrieved back in 2004)*/
10 /* It was heavily modified to fit the SimGrid framework. */
12 /* The OSSP version has the following copyright notice:
13 ** OSSP ex - Exception Handling
14 ** Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
15 ** Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>
16 ** Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>
18 ** This file is part of OSSP ex, an exception handling library
19 ** which can be found at http://www.ossp.org/pkg/lib/ex/.
21 ** Permission to use, copy, modify, and distribute this software for
22 ** any purpose with or without fee is hereby granted, provided that
23 ** the above copyright notice and this permission notice appear in all
26 ** THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESSED OR IMPLIED
27 ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
28 ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
30 ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
33 ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
34 ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
35 ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
36 ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 /* The extensions made for the SimGrid project can either be distributed */
41 /* under the same license, or under the LGPL v2.1 */
46 #include "xbt/sysdep.h"
48 #include "xbt/virtu.h"
52 /*-*-* Emergency debuging: define this when the exceptions get crazy *-*-*/
59 # define MAYDAY_SAVE(m) printf("%d %s:%d save %p\n", \
60 (*xbt_getpid)(),__FILE__,__LINE__, \
63 # define MAYDAY_RESTORE(m) printf("%d %s:%d restore %p\n", \
64 (*xbt_getpid)(),__FILE__,__LINE__, \
67 # define MAYDAY_CATCH(e) printf("%d %s:%d Catched '%s'\n", \
68 (*xbt_getpid)(),__FILE__,__LINE__, \
72 # define MAYDAY_SAVE(m)
73 # define MAYDAY_RESTORE(m)
74 # define MAYDAY_CATCH(e)
77 /*-*-* end of debugging stuff *-*-*/
79 #if defined(__EX_MCTX_MCSC__)
80 #include <ucontext.h> /* POSIX.1 ucontext(3) */
81 #define __ex_mctx_struct ucontext_t uc;
82 #define __ex_mctx_save(mctx) (getcontext(&(mctx)->uc) == 0)
83 #define __ex_mctx_restored(mctx) /* noop */
84 #define __ex_mctx_restore(mctx) (void)setcontext(&(mctx)->uc)
86 #elif defined(__EX_MCTX_SSJLJ__)
87 #include <setjmp.h> /* POSIX.1 sigjmp_buf(3) */
88 #define __ex_mctx_struct sigjmp_buf jb;
89 #define __ex_mctx_save(mctx) (sigsetjmp((mctx)->jb, 1) == 0)
90 #define __ex_mctx_restored(mctx) /* noop */
91 #define __ex_mctx_restore(mctx) (void)siglongjmp((mctx)->jb, 1)
93 #elif defined(__EX_MCTX_SJLJ__) || !defined(__EX_MCTX_CUSTOM__) || defined(__EX_MAYDAY)
94 #include <setjmp.h> /* ISO-C jmp_buf(3) */
95 #define __ex_mctx_struct jmp_buf jb;
96 #define __ex_mctx_save(mctx) ( MAYDAY_SAVE(mctx) setjmp((mctx)->jb) == 0)
97 #define __ex_mctx_restored(mctx) /* noop */
98 #define __ex_mctx_restore(mctx) ( MAYDAY_RESTORE(mctx) (void)longjmp((mctx)->jb, 1))
101 /* declare the machine context type */
103 __ex_mctx_struct} __ex_mctx_t;
105 /** @addtogroup XBT_ex
106 * @brief A set of macros providing exception a la C++ in ANSI C (grounding feature)
108 * This module is a small ISO-C++ style exception handling library
109 * for use in the ISO-C language. It allows you to use the paradigm
110 * of throwing and catching exceptions in order to reduce the amount
111 * of error handling code without hindering program robustness.
113 * This is achieved by directly transferring exceptional return codes
114 * (and the program control flow) from the location where the exception
115 * is raised (throw point) to the location where it is handled (catch
116 * point) -- usually from a deeply nested sub-routine to a parent
117 * routine. All intermediate routines no longer have to make sure that
118 * the exceptional return codes from sub-routines are correctly passed
119 * back to the parent.
121 * These features are brought to you by a modified version of the libex
122 * library, one of the numerous masterpiece of Ralf S. Engelschall.
124 * \htmlonly <div class="toc">\endhtmlonly
126 * @section XBT_ex_toc TABLE OF CONTENTS
128 * - \ref XBT_ex_intro
130 * - \ref XBT_ex_pitfalls
132 * \htmlonly </div> \endhtmlonly
134 * @section XBT_ex_intro DESCRIPTION
136 * In SimGrid, an exception is a triple <\a msg , \a category , \a value>
137 * where \a msg is a human-readable text describing the exceptional
138 * condition, \a code an integer describing what went wrong and \a value
139 * providing a sort of sub-category. (this is different in the original libex).
141 * @section XBT_ex_base BASIC USAGE
143 * \em TRY \b TRIED_BLOCK [\em CLEANUP \b CLEANUP_BLOCK] \em CATCH (variable) \b CATCH_BLOCK
145 * This is the primary syntactical construct provided. It is modeled after the
146 * ISO-C++ try-catch clause and should sound familiar to most of you.
148 * Any exception thrown directly from the TRIED_BLOCK block or from called
149 * subroutines is caught. Cleanups which must be done after this block
150 * (whenever an exception arised or not) should be placed into the optionnal
151 * CLEANUP_BLOCK. The code dealing with the exceptions when they arise should
152 * be placed into the (mandatory) CATCH_BLOCK.
155 * In absence of exception, the control flow goes into the blocks TRIED_BLOCK
156 * and CLEANUP_BLOCK (if present); The CATCH_BLOCK block is then ignored.
158 * When an exception is thrown, the control flow goes through the following
159 * blocks: TRIED_BLOCK (up to the statement throwing the exception),
160 * CLEANUP_BLOCK (if any) and CATCH_BLOCK. The exception is stored in a
161 * variable for inspection inside the CATCH_BLOCK. This variable must be
162 * declared in the outter scope, but its value is only valid within the
166 * - TRY, CLEANUP and CATCH cannot be used separately, they work
167 * only in combination and form a language clause as a whole.
168 * - In contrast to the syntax of other languages (such as C++ or Jave) there
169 * is only one CATCH block and not multiple ones (all exceptions are
170 * of the same \em xbt_ex_t C type).
171 * - the variable of CATCH can naturally be reused in subsequent
173 * - it is possible to nest TRY clauses.
175 * The TRY block is a regular ISO-C language statement block, but
177 * <center><b>it is not
178 * allowed to jump into it via "goto" or longjmp(3) or out of it via "break",
179 * "return", "goto" or longjmp(3)</b>.</center>
181 * This is because there is some hidden setup and
182 * cleanup that needs to be done regardless of whether an exception is
183 * caught. Bypassing these steps will break the exception handling facility.
184 * The symptom are likely to be a segfault at the next exception raising point,
185 * ie far away from the point where you did the mistake. If you suspect
186 * that kind of error in your code, have a look at the little script
187 * <tt>tools/xbt_exception_checker</tt> in the CVS. It extracts all the TRY
188 * blocks from a set of C files you give it and display them (and only
189 * them) on the standard output. You can then grep for the forbidden
190 * keywords on that output.
192 * The CLEANUP and CATCH blocks are regular ISO-C language statement
193 * blocks without any restrictions. You are even allowed to throw (and, in the
194 * CATCH block, to re-throw) exceptions.
196 * There is one subtle detail you should remember about TRY blocks:
197 * Variables used in the CLEANUP or CATCH clauses must be declared with
198 * the storage class "volatile", otherwise they might contain outdated
199 * information if an exception is thrown.
202 * This is because you usually do not know which commands in the TRY
203 * were already successful before the exception was thrown (logically speaking)
204 * and because the underlying ISO-C setjmp(3) facility applies those
205 * restrictions (technically speaking). As a matter of fact, value changes
206 * between the TRY and the THROW may be discarded if you forget the
207 * "volatile" keyword.
209 * \section XBT_ex_pitfalls PROGRAMMING PITFALLS
211 * Exception handling is a very elegant and efficient way of dealing with
212 * exceptional situation. Nevertheless it requires additional discipline in
213 * programming and there are a few pitfalls one must be aware of. Look the
214 * following code which shows some pitfalls and contains many errors (assuming
215 * a mallocex() function which throws an exception if malloc(3) fails):
219 * \until end_of_bad_example
221 * This example raises a few issues:
222 * -# \b variable \b scope \n
223 * Variables which are used in the CLEANUP or CATCH clauses must be
224 * declared before the TRY clause, otherwise they only exist inside the
225 * TRY block. In the example above, cp1, cp2 and cp3 only exist in the
226 * TRY block and are invisible from the CLEANUP and CATCH
228 * -# \b variable \b initialization \n
229 * Variables which are used in the CLEANUP or CATCH clauses must
230 * be initialized before the point of the first possible THROW is
231 * reached. In the example above, CLEANUP would have trouble using cp3
232 * if mallocex() throws a exception when allocating a TOOBIG buffer.
233 * -# \b volatile \b variable \n
234 * Variables which are used in the CLEANUP or CATCH clauses MUST BE
235 * DECLARED AS "volatile", otherwise they might contain outdated
236 * information when an exception is thrown.
237 * -# \b clean \b before \b catch \n
238 * The CLEANUP clause is not only place before the CATCH clause in
239 * the source code, it also occures before in the control flow. So,
240 * resources being cleaned up cannot be used in the CATCH block. In the
241 * example, c3 gets freed before the printf placed in CATCH.
242 * -# \b variable \b uninitialization \n
243 * If resources are passed out of the scope of the
244 * TRY/CLEANUP/CATCH construct, they naturally shouldn't get
245 * cleaned up. The example above does free(3) cp1 in CLEANUP although
246 * its value was affected to globalcontext->first, invalidating this
249 * The following is fixed version of the code (annotated with the pitfall items
253 * \until end_of_good_example
258 /** @brief different kind of errors */
260 unknown_error = 0,/**< unknown error */
261 arg_error, /**< Invalid argument */
262 bound_error, /**< Out of bounds argument */
263 mismatch_error, /**< The provided ID does not match */
264 not_found_error, /**< The searched element was not found */
266 system_error, /**< a syscall did fail */
267 network_error, /**< error while sending/receiving data */
268 timeout_error, /**< not quick enough, dude */
269 thread_error, /**< error while [un]locking */
270 host_error, /**< host failed */
271 tracing_error /**< error during the simulation tracing */
274 XBT_PUBLIC(const char *) xbt_ex_catname(xbt_errcat_t cat);
276 /** @brief Structure describing an exception */
278 char *msg; /**< human readable message */
279 xbt_errcat_t category;
280 /**< category like HTTP (what went wrong) */
281 int value; /**< like errno (why did it went wrong) */
284 /**< whether it was raised remotely */
285 char *host;/**< NULL locally thrown exceptions; full hostname if remote ones */
286 /* FIXME: host should be hostname:port[#thread] */
288 /**< Name of the process who thrown this */
289 int pid; /**< PID of the process who thrown this */
290 char *file;/**< Thrown point */
291 int line; /**< Thrown point */
292 char *func;/**< Thrown point */
295 char **bt_strings; /* only filed on display (or before the network propagation) */
296 void *bt[XBT_BACKTRACE_SIZE];
299 /* declare the context type (private) */
301 __ex_mctx_t *ctx_mctx; /* permanent machine context of enclosing try/catch */
302 volatile int ctx_caught; /* temporary flag whether exception was caught */
303 volatile xbt_ex_t ctx_ex; /* temporary exception storage */
306 /* the static and dynamic initializers for a context structure */
307 #define XBT_CTX_INITIALIZER \
308 { NULL, 0, { /* content */ NULL, unknown_error, 0, \
309 /* throw point*/ 0,NULL, NULL,0, NULL, 0, NULL,\
310 /* backtrace */ 0,NULL,{NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL} } }
311 #define XBT_CTX_INITIALIZE(ctx) \
313 (ctx)->ctx_mctx = NULL; \
314 (ctx)->ctx_caught = 0; \
315 (ctx)->ctx_ex.msg = NULL; \
316 (ctx)->ctx_ex.category = 0; \
317 (ctx)->ctx_ex.value = 0; \
318 (ctx)->ctx_ex.remote = 0; \
319 (ctx)->ctx_ex.host = NULL; \
320 (ctx)->ctx_ex.procname = NULL; \
321 (ctx)->ctx_ex.pid = 0; \
322 (ctx)->ctx_ex.file = NULL; \
323 (ctx)->ctx_ex.line = 0; \
324 (ctx)->ctx_ex.func = NULL; \
325 (ctx)->ctx_ex.bt[0] = NULL; \
326 (ctx)->ctx_ex.bt[1] = NULL; \
327 (ctx)->ctx_ex.bt[2] = NULL; \
328 (ctx)->ctx_ex.bt[3] = NULL; \
329 (ctx)->ctx_ex.bt[4] = NULL; \
330 (ctx)->ctx_ex.bt[5] = NULL; \
331 (ctx)->ctx_ex.bt[6] = NULL; \
332 (ctx)->ctx_ex.bt[7] = NULL; \
333 (ctx)->ctx_ex.bt[8] = NULL; \
334 (ctx)->ctx_ex.bt[9] = NULL; \
335 (ctx)->ctx_ex.used = 0; \
336 (ctx)->ctx_ex.bt_strings = NULL; \
339 /* the exception context */
340 typedef ex_ctx_t *(*ex_ctx_cb_t) (void);
341 XBT_PUBLIC_DATA(ex_ctx_cb_t) __xbt_ex_ctx;
342 extern ex_ctx_t *__xbt_ex_ctx_default(void);
344 /* the termination handler */
345 typedef void (*ex_term_cb_t) (xbt_ex_t *);
346 XBT_PUBLIC_DATA(ex_term_cb_t) __xbt_ex_terminate;
347 extern void __xbt_ex_terminate_default(xbt_ex_t * e);
349 /** @brief Introduce a block where exception may be dealed with
354 ex_ctx_t *__xbt_ex_ctx_ptr = __xbt_ex_ctx(); \
355 int __ex_cleanup = 0; \
356 __ex_mctx_t *__ex_mctx_en; \
357 __ex_mctx_t __ex_mctx_me; \
358 __ex_mctx_en = __xbt_ex_ctx_ptr->ctx_mctx; \
359 __xbt_ex_ctx_ptr->ctx_mctx = &__ex_mctx_me; \
360 if (__ex_mctx_save(&__ex_mctx_me)) { \
363 /** @brief optional(!) block for cleanup
369 __xbt_ex_ctx_ptr->ctx_caught = 0; \
371 __ex_mctx_restored(&__ex_mctx_me); \
372 __xbt_ex_ctx_ptr->ctx_caught = 1; \
374 __xbt_ex_ctx_ptr->ctx_mctx = __ex_mctx_en; \
381 # define XBT_EX_T_CPLUSPLUSCAST (xbt_ex_t&)
383 # define XBT_EX_T_CPLUSPLUSCAST
387 /** @brief the block for catching (ie, deal with) an exception
393 if (!(__ex_cleanup)) \
394 __xbt_ex_ctx_ptr->ctx_caught = 0; \
396 if (!(__ex_cleanup)) { \
397 __ex_mctx_restored(&__ex_mctx_me); \
398 __xbt_ex_ctx_ptr->ctx_caught = 1; \
401 __xbt_ex_ctx_ptr->ctx_mctx = __ex_mctx_en; \
403 if ( !(__xbt_ex_ctx()->ctx_caught) \
404 || ((e) = XBT_EX_T_CPLUSPLUSCAST __xbt_ex_ctx()->ctx_ex, MAYDAY_CATCH(e) 0)) { \
408 #define DO_THROW(e) \
409 /* deal with the exception */ \
410 if (__xbt_ex_ctx()->ctx_mctx == NULL) \
411 __xbt_ex_terminate((xbt_ex_t *)&(e)); /* not catched */\
413 __ex_mctx_restore(__xbt_ex_ctx()->ctx_mctx); /* catched somewhere */ \
414 abort() /* nope, stupid GCC, we won't survive a THROW (this won't be reached) */
416 /** @brief Helper macro for THROWS0-6
419 * @param c: category code (integer)
420 * @param v: value (integer)
421 * @param m: message text
423 * If called from within a TRY/CATCH construct, this exception
424 * is copied into the CATCH relevant variable program control flow
425 * is derouted to the CATCH (after the optional sg_cleanup).
427 * If no TRY/CATCH construct embeeds this call, the program calls
430 * The THROW can be performed everywhere, including inside TRY,
431 * CLEANUP and CATCH blocks.
434 #define _THROW(c,v,m) \
435 do { /* change this sequence into one block */ \
436 ex_ctx_t *_throw_ctx = __xbt_ex_ctx(); \
437 /* build the exception */ \
438 _throw_ctx->ctx_ex.msg = (m); \
439 _throw_ctx->ctx_ex.category = (xbt_errcat_t)(c); \
440 _throw_ctx->ctx_ex.value = (v); \
441 _throw_ctx->ctx_ex.remote = 0; \
442 _throw_ctx->ctx_ex.host = (char*)NULL; \
443 _throw_ctx->ctx_ex.procname = (char*)xbt_procname(); \
444 _throw_ctx->ctx_ex.pid = (*xbt_getpid)(); \
445 _throw_ctx->ctx_ex.file = (char*)__FILE__; \
446 _throw_ctx->ctx_ex.line = __LINE__; \
447 _throw_ctx->ctx_ex.func = (char*)_XBT_FUNCTION; \
448 _throw_ctx->ctx_ex.bt_strings = NULL; \
449 xbt_backtrace_current( (xbt_ex_t *) &(_throw_ctx->ctx_ex) ); \
450 DO_THROW(_throw_ctx->ctx_ex); \
452 /* __xbt_ex_ctx()->ctx_ex.used = backtrace((void**)__xbt_ex_ctx()->ctx_ex.bt,XBT_BACKTRACE_SIZE); */
454 /** @brief Builds and throws an exception with a string taking no arguments
456 #define THROW0(c,v,m) _THROW(c,v,(m?bprintf(m):NULL))
457 /** @brief Builds and throws an exception with a string taking one argument
459 #define THROW1(c,v,m,a1) _THROW(c,v,bprintf(m,a1))
460 /** @brief Builds and throws an exception with a string taking two arguments
462 #define THROW2(c,v,m,a1,a2) _THROW(c,v,bprintf(m,a1,a2))
463 /** @brief Builds and throws an exception with a string taking three arguments
465 #define THROW3(c,v,m,a1,a2,a3) _THROW(c,v,bprintf(m,a1,a2,a3))
466 /** @brief Builds and throws an exception with a string taking four arguments
468 #define THROW4(c,v,m,a1,a2,a3,a4) _THROW(c,v,bprintf(m,a1,a2,a3,a4))
469 /** @brief Builds and throws an exception with a string taking five arguments
471 #define THROW5(c,v,m,a1,a2,a3,a4,a5) _THROW(c,v,bprintf(m,a1,a2,a3,a4,a5))
472 /** @brief Builds and throws an exception with a string taking six arguments
474 #define THROW6(c,v,m,a1,a2,a3,a4,a5,a6) _THROW(c,v,bprintf(m,a1,a2,a3,a4,a5,a6))
475 /** @brief Builds and throws an exception with a string taking seven arguments
477 #define THROW7(c,v,m,a1,a2,a3,a4,a5,a6,a7) _THROW(c,v,bprintf(m,a1,a2,a3,a4,a5,a6,a7))
479 #define THROW_IMPOSSIBLE THROW0(unknown_error,0,"The Impossible Did Happen (yet again)")
480 #define THROW_UNIMPLEMENTED THROW1(unknown_error,0,"Function %s unimplemented",_XBT_FUNCTION)
483 # define DIE_IMPOSSIBLE xbt_assert0(0,"The Impossible Did Happen (yet again)")
485 # define DIE_IMPOSSIBLE exit(1);
488 /** @brief re-throwing of an already caught exception (ie, pass it to the upper catch block)
493 if (__xbt_ex_ctx()->ctx_mctx == NULL) \
494 __xbt_ex_terminate((xbt_ex_t *)&(__xbt_ex_ctx()->ctx_ex)); \
496 __ex_mctx_restore(__xbt_ex_ctx()->ctx_mctx); \
502 #define _XBT_PRE_RETHROW \
504 char *_xbt_ex_internal_msg = __xbt_ex_ctx()->ctx_ex.msg; \
505 __xbt_ex_ctx()->ctx_ex.msg = bprintf(
506 #define _XBT_POST_RETHROW \
507 _xbt_ex_internal_msg); \
508 free(_xbt_ex_internal_msg); \
513 /** @brief like THROW0, but adding some details to the message of an existing exception
516 #define RETHROW0(msg) _XBT_PRE_RETHROW msg, _XBT_POST_RETHROW
517 /** @brief like THROW1, but adding some details to the message of an existing exception
520 #define RETHROW1(msg,a) _XBT_PRE_RETHROW msg,a, _XBT_POST_RETHROW
521 /** @brief like THROW2, but adding some details to the message of an existing exception
524 #define RETHROW2(msg,a,b) _XBT_PRE_RETHROW msg,a,b, _XBT_POST_RETHROW
525 /** @brief like THROW3, but adding some details to the message of an existing exception
528 #define RETHROW3(msg,a,b,c) _XBT_PRE_RETHROW msg,a,b,c, _XBT_POST_RETHROW
529 /** @brief like THROW4, but adding some details to the message of an existing exception
532 #define RETHROW4(msg,a,b,c,d) _XBT_PRE_RETHROW msg,a,b,c,d, _XBT_POST_RETHROW
533 /** @brief like THROW5, but adding some details to the message of an existing exception
536 #define RETHROW5(msg,a,b,c,d,e) _XBT_PRE_RETHROW msg,a,b,c,d,e, _XBT_POST_RETHROW
538 /** @brief Exception destructor */
539 XBT_PUBLIC(void) xbt_ex_free(xbt_ex_t e);
541 /** @brief Shows a backtrace of the current location */
542 XBT_PUBLIC(void) xbt_backtrace_display_current(void);
543 /** @brief Captures a backtrace for further use */
544 XBT_PUBLIC(void) xbt_backtrace_current(xbt_ex_t * e);
545 /** @brief Display a previously captured backtrace */
546 XBT_PUBLIC(void) xbt_backtrace_display(xbt_ex_t * e);
551 #endif /* __XBT_EX_H__ */