Logo AND Algorithmique Numérique Distribuée

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