Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Dig through git history, and update copyright lines.
[simgrid.git] / src / xbt / ex.c
1 /* ex - Exception Handling                                                  */
2
3 /*  Copyright (c) 2005-2013. 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.                                                    */
8
9 /* This code is inspirated from the OSSP version (as retrieved back in 2004)*/
10 /* It was heavily modified to fit the SimGrid framework.                    */
11
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/>
17 **
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/.
20 **
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
24 **  copies.
25 **
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
37 **  SUCH DAMAGE.
38  */
39
40 /* The extensions made for the SimGrid project can either be distributed    */
41 /* under the same license, or under the LGPL v2.1                           */
42
43 #include <stdio.h>
44 #include <stdlib.h>
45
46 #include "portable.h"           /* execinfo when available */
47 #include "xbt/ex.h"
48 #include "xbt/str.h"
49 #include "xbt/synchro_core.h"
50 #include "xbt_modinter.h"       /* backtrace initialization headers */
51
52 #include "xbt/ex_interface.h"
53
54 #undef HAVE_BACKTRACE
55 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
56 # define HAVE_BACKTRACE 1       /* Hello linux box */
57 #endif
58
59 #if defined(_XBT_WIN32) && defined(_M_IX86) && !defined(__GNUC__)
60 # define HAVE_BACKTRACE 1       /* Hello x86 windows box */
61 #endif
62
63
64 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex, xbt, "Exception mecanism");
65
66 XBT_EXPORT_NO_IMPORT(const xbt_running_ctx_t) __xbt_ex_ctx_initializer = XBT_RUNNING_CTX_INITIALIZER;
67
68 /* default __ex_ctx callback function */
69 xbt_running_ctx_t *__xbt_ex_ctx_default(void)
70 {
71   /* Don't scream: this is a default which is never used (so, yes,
72      there is one setjump container by running entity).
73
74      This default gets overriden in xbt/xbt_os_thread.c so that it works in
75      real life and in simulation when using threads to implement the simulation
76      processes (ie, with pthreads and on windows).
77
78      It also gets overriden in xbt/context.c when using ucontextes (as well as
79      in Java for now, but after the java overhaul, it will get cleaned out)
80    */
81   static xbt_running_ctx_t ctx = XBT_RUNNING_CTX_INITIALIZER;
82
83   return &ctx;
84 }
85
86 /* Change raw libc symbols to file names and line numbers */
87 void xbt_ex_setup_backtrace(xbt_ex_t * e);
88
89 void xbt_backtrace_display(xbt_ex_t * e)
90 {
91   xbt_ex_setup_backtrace(e);
92
93 #ifdef HAVE_BACKTRACE
94   if (e->used == 0) {
95     fprintf(stderr, "(backtrace not set)\n");
96   } else {
97     int i;
98
99     fprintf(stderr, "Backtrace (displayed in thread %p):\n",
100             (void *) xbt_thread_self());
101     for (i = 1; i < e->used; i++)       /* no need to display "xbt_backtrace_display" */
102       fprintf(stderr, "---> %s\n", e->bt_strings[i] + 4);
103   }
104
105   /* don't fool xbt_ex_free with uninitialized msg field */
106   e->msg = NULL;
107   xbt_ex_free(*e);
108 #else
109
110   XBT_ERROR("No backtrace on this arch");
111 #endif
112 }
113
114 /** \brief show the backtrace of the current point (lovely while debuging) */
115 void xbt_backtrace_display_current(void)
116 {
117   xbt_ex_t e;
118   xbt_backtrace_current(&e);
119   xbt_backtrace_display(&e);
120 }
121
122 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
123 # include "backtrace_linux.c"
124 #elif (defined(_XBT_WIN32) && defined (_M_IX86)) && !defined(__GNUC__)
125 # include "backtrace_windows.c"
126 #else
127 # include "backtrace_dummy.c"
128 #endif
129
130 /** @brief shows an exception content and the associated stack if available */
131 void xbt_ex_display(xbt_ex_t * e)
132 {
133   char *thrower = NULL;
134
135   fprintf(stderr,
136           "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
137           "** %s\n"
138           "** Thrown by %s()%s\n",
139           xbt_binary_name, xbt_getpid(),
140           xbt_ex_catname(e->category), e->value, e->msg,
141           e->procname, thrower ? thrower : " in this process");
142   XBT_CRITICAL("%s", e->msg);
143   xbt_free(thrower);
144
145   if (!e->bt_strings)
146     xbt_ex_setup_backtrace(e);
147
148 #ifdef HAVE_BACKTRACE
149   if (e->used && e->bt_strings) {
150     /* We have everything to build neat backtraces */
151     int i;
152
153     fprintf(stderr, "\n");
154     for (i = 0; i < e->used; i++)
155       fprintf(stderr, "%s\n", e->bt_strings[i]);
156
157   } else
158 #endif
159   {
160     fprintf(stderr,
161             "\n"
162             "**   In %s() at %s:%d\n"
163             "**   (no backtrace available)\n",
164             e->func, e->file, e->line);
165   }
166 }
167
168
169 /* default __ex_terminate callback function */
170 void __xbt_ex_terminate_default(xbt_ex_t * e)
171 {
172   xbt_ex_display(e);
173   xbt_abort();
174 }
175
176 /* the externally visible API */
177 XBT_EXPORT_NO_IMPORT(xbt_running_ctx_fetcher_t) __xbt_running_ctx_fetch = &__xbt_ex_ctx_default;
178 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate =
179     &__xbt_ex_terminate_default;
180
181
182 void xbt_ex_free(xbt_ex_t e)
183 {
184   int i;
185
186   free(e.msg);
187
188   if (e.bt_strings) {
189     for (i = 0; i < e.used; i++)
190       free(e.bt_strings[i]);
191     free(e.bt_strings);
192   }
193   /* memset(e,0,sizeof(xbt_ex_t)); */
194 }
195
196 /** \brief returns a short name for the given exception category */
197 const char *xbt_ex_catname(xbt_errcat_t cat)
198 {
199   switch (cat) {
200   case unknown_error:
201     return "unknown error";
202   case arg_error:
203     return "invalid argument";
204   case bound_error:
205     return "out of bounds";
206   case mismatch_error:
207     return "mismatch";
208   case not_found_error:
209     return "not found";
210   case system_error:
211     return "system error";
212   case network_error:
213     return "network error";
214   case timeout_error:
215     return "timeout";
216   case cancel_error:
217     return "action canceled";
218   case thread_error:
219     return "thread error";
220   case host_error:
221     return "host failed";
222   case tracing_error:
223     return "tracing error";
224   case io_error:
225     return "io error";
226   }
227   return "INVALID ERROR";
228 }
229
230
231 #ifdef SIMGRID_TEST
232 #include <stdio.h>
233 #include "xbt/ex.h"
234
235 XBT_TEST_SUITE("xbt_ex", "Exception Handling");
236
237 XBT_TEST_UNIT("controlflow", test_controlflow, "basic nested control flow")
238 {
239   xbt_ex_t ex;
240   volatile int n = 1;
241
242   xbt_test_add("basic nested control flow");
243
244   TRY {
245     if (n != 1)
246       xbt_test_fail("M1: n=%d (!= 1)", n);
247     n++;
248     TRY {
249       if (n != 2)
250         xbt_test_fail("M2: n=%d (!= 2)", n);
251       n++;
252       THROWF(unknown_error, 0, "something");
253     }
254     CATCH(ex) {
255       if (n != 3)
256         xbt_test_fail("M3: n=%d (!= 3)", n);
257       n++;
258       xbt_ex_free(ex);
259     }
260     n++;
261     TRY {
262       if (n != 5)
263         xbt_test_fail("M2: n=%d (!= 5)", n);
264       n++;
265       THROWF(unknown_error, 0, "something");
266     }
267     CATCH_ANONYMOUS {
268       if (n != 6)
269         xbt_test_fail("M3: n=%d (!= 6)", n);
270       n++;
271       RETHROW;
272       n++;
273     }
274     xbt_test_fail("MX: n=%d (shouldn't reach this point)", n);
275   }
276   CATCH(ex) {
277     if (n != 7)
278       xbt_test_fail("M4: n=%d (!= 7)", n);
279     n++;
280     xbt_ex_free(ex);
281   }
282   if (n != 8)
283     xbt_test_fail("M5: n=%d (!= 8)", n);
284 }
285
286 XBT_TEST_UNIT("value", test_value, "exception value passing")
287 {
288   xbt_ex_t ex;
289
290   TRY {
291     THROWF(unknown_error, 2, "toto");
292   }
293   CATCH(ex) {
294     xbt_test_add("exception value passing");
295     if (ex.category != unknown_error)
296       xbt_test_fail("category=%d (!= 1)", (int)ex.category);
297     if (ex.value != 2)
298       xbt_test_fail("value=%d (!= 2)", ex.value);
299     if (strcmp(ex.msg, "toto"))
300       xbt_test_fail("message=%s (!= toto)", ex.msg);
301     xbt_ex_free(ex);
302   }
303 }
304
305 XBT_TEST_UNIT("variables", test_variables, "variable value preservation")
306 {
307   xbt_ex_t ex;
308   int r1;
309   int _XBT_GNUC_UNUSED r2;
310   int v1;
311   volatile int v2;
312
313   r1 = r2 = v1 = v2 = 1234;
314   TRY {
315     r2 = 5678;
316     v2 = 5678;
317     THROWF(unknown_error, 0, "toto");
318   }
319   CATCH(ex) {
320     xbt_test_add("variable preservation");
321     if (r1 != 1234)
322       xbt_test_fail("r1=%d (!= 1234)", r1);
323     if (v1 != 1234)
324       xbt_test_fail("v1=%d (!= 1234)", v1);
325     /* r2 is allowed to be destroyed because not volatile */
326     if (v2 != 5678)
327       xbt_test_fail("v2=%d (!= 5678)", v2);
328     xbt_ex_free(ex);
329   }
330 }
331
332 XBT_TEST_UNIT("cleanup", test_cleanup, "cleanup handling")
333 {
334   xbt_ex_t ex;
335   volatile int v1;
336   int c;
337
338   xbt_test_add("cleanup handling");
339
340   v1 = 1234;
341   c = 0;
342   TRY {
343     v1 = 5678;
344     THROWF(1, 2, "blah");
345   }
346   TRY_CLEANUP {
347     if (v1 != 5678)
348       xbt_test_fail("v1 = %d (!= 5678)", v1);
349     c = 1;
350   }
351   CATCH(ex) {
352     if (v1 != 5678)
353       xbt_test_fail("v1 = %d (!= 5678)", v1);
354     if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg, "blah")))
355       xbt_test_fail("unexpected exception contents");
356     xbt_ex_free(ex);
357   }
358   if (!c)
359     xbt_test_fail("xbt_ex_free not executed");
360 }
361
362
363 /*
364  * The following is the example included in the documentation. It's a good
365  * idea to check its syntax even if we don't try to run it.
366  * And actually, it allows to put comments in the code despite doxygen.
367  */
368 static char *mallocex(int size)
369 {
370   return NULL;
371 }
372
373 #define SMALLAMOUNT 10
374 #define TOOBIG 100000000
375
376 #if 0                           /* this contains syntax errors, actually */
377 static void bad_example(void)
378 {
379   struct {
380     char *first;
381   } *globalcontext;
382   ex_t ex;
383
384   /* BAD_EXAMPLE */
385   TRY {
386     char *cp1, *cp2, *cp3;
387
388     cp1 = mallocex(SMALLAMOUNT);
389     globalcontext->first = cp1;
390     cp2 = mallocex(TOOBIG);
391     cp3 = mallocex(SMALLAMOUNT);
392     strcpy(cp1, "foo");
393     strcpy(cp2, "bar");
394   }
395   TRY_CLEANUP {
396     free(cp3);
397     free(cp2);
398     free(cp1);
399   }
400   CATCH_ANONYMOUS {
401     printf("cp3=%s", cp3);
402     RETHROW;
403   }
404   /* end_of_bad_example */
405 }
406 #endif
407 typedef struct {
408   char *first;
409 } global_context_t;
410
411 static void good_example(void)
412 {
413   global_context_t *global_context = xbt_malloc(sizeof(global_context_t));
414
415   /* GOOD_EXAMPLE */
416   {                             /*01 */
417     char *volatile /*03 */ cp1 = NULL /*02 */ ;
418     char *volatile /*03 */ cp2 = NULL /*02 */ ;
419     char *volatile /*03 */ cp3 = NULL /*02 */ ;
420     TRY {
421       cp1 = mallocex(SMALLAMOUNT);
422       global_context->first = cp1;
423       cp1 = NULL /*05 give away */ ;
424       cp2 = mallocex(TOOBIG);
425       cp3 = mallocex(SMALLAMOUNT);
426       strcpy(cp1, "foo");
427       strcpy(cp2, "bar");
428     }
429     TRY_CLEANUP {               /*04 */
430       printf("cp3=%s", cp3 == NULL /*02 */ ? "" : cp3);
431       free(cp3);
432       free(cp2);
433       /*05 cp1 was given away */
434     }
435     CATCH_ANONYMOUS {
436       /*05 global context untouched */
437       RETHROW;
438     }
439   }
440   /* end_of_good_example */
441 }
442 #endif                          /* SIMGRID_TEST */