Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Avoid to duplicate initialization list.
[simgrid.git] / src / xbt / ex.c
1 /* ex - Exception Handling                                                  */
2
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.                                                    */
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/module.h"         /* xbt_binary_name */
50 #include "xbt_modinter.h"       /* backtrace initialization headers */
51 #include "xbt/synchro.h"        /* xbt_thread_self */
52
53 #include "gras/Virtu/virtu_interface.h" /* gras_os_myname */
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 ucontextes (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   e->remote = 0;
110   xbt_ex_free(*e);
111 #else
112
113   XBT_ERROR("No backtrace on this arch");
114 #endif
115 }
116
117 /** \brief show the backtrace of the current point (lovely while debuging) */
118 void xbt_backtrace_display_current(void)
119 {
120   xbt_ex_t e;
121   xbt_backtrace_current(&e);
122   xbt_backtrace_display(&e);
123 }
124
125 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
126 # include "backtrace_linux.c"
127 #elif (defined(_XBT_WIN32) && defined (_M_IX86)) && !defined(__GNUC__)
128 # include "backtrace_windows.c"
129 #else
130 # include "backtrace_dummy.c"
131 #endif
132
133 /** @brief shows an exception content and the associated stack if available */
134 void xbt_ex_display(xbt_ex_t * e)
135 {
136   char *thrower = NULL;
137
138   if (e->remote)
139     thrower = bprintf(" on host %s(%d)", e->host, e->pid);
140
141   fprintf(stderr,
142           "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
143           "** %s\n"
144           "** Thrown by %s()%s\n",
145           gras_os_myname(), xbt_getpid(),
146           xbt_ex_catname(e->category), e->value, e->msg,
147           e->procname, thrower ? thrower : " in this process");
148   XBT_CRITICAL("%s", e->msg);
149
150   if (!e->remote && !e->bt_strings)
151     xbt_ex_setup_backtrace(e);
152
153 #ifdef HAVE_BACKTRACE
154   /* We have everything to build neat backtraces */
155   {
156     int i;
157
158     if (!xbt_binary_name) {
159       fprintf(stderr, "variable 'xbt_binary_name' set to NULL. Cannot compute the backtrace\n");
160       return;
161     }
162     fprintf(stderr, "\n");
163     for (i = 0; i < e->used; i++)
164       fprintf(stderr, "%s\n", e->bt_strings[i]);
165
166   }
167 #else
168   fprintf(stderr, " at %s:%d:%s (no backtrace available on that arch)\n",
169           e->file, e->line, e->func);
170 #endif
171 }
172
173
174 /* default __ex_terminate callback function */
175 void __xbt_ex_terminate_default(xbt_ex_t * e)
176 {
177   xbt_ex_display(e);
178
179   abort();
180 }
181
182 /* the externally visible API */
183 XBT_EXPORT_NO_IMPORT(xbt_running_ctx_fetcher_t) __xbt_running_ctx_fetch = &__xbt_ex_ctx_default;
184 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate =
185     &__xbt_ex_terminate_default;
186
187
188 void xbt_ex_free(xbt_ex_t e)
189 {
190   int i;
191
192   free(e.msg);
193   if (e.remote) {
194     free(e.procname);
195     free(e.file);
196     free(e.func);
197     free(e.host);
198   }
199
200   if (e.bt_strings) {
201     for (i = 0; i < e.used; i++)
202       free(e.bt_strings[i]);
203     free(e.bt_strings);
204   }
205   /* memset(e,0,sizeof(xbt_ex_t)); */
206 }
207
208 /** \brief returns a short name for the given exception category */
209 const char *xbt_ex_catname(xbt_errcat_t cat)
210 {
211   switch (cat) {
212   case unknown_error:
213     return "unknown_err";
214   case arg_error:
215     return "invalid_arg";
216   case mismatch_error:
217     return "mismatch";
218   case not_found_error:
219     return "not found";
220   case system_error:
221     return "system_err";
222   case network_error:
223     return "network_err";
224   case timeout_error:
225     return "timeout";
226   case thread_error:
227     return "thread_err";
228   default:
229     return "INVALID_ERR";
230   }
231 }
232
233
234 #ifdef SIMGRID_TEST
235 #include <stdio.h>
236 #include "xbt/ex.h"
237
238 XBT_TEST_SUITE("xbt_ex", "Exception Handling");
239
240 XBT_TEST_UNIT("controlflow", test_controlflow, "basic nested control flow")
241 {
242   xbt_ex_t ex;
243   volatile int n = 1;
244
245   xbt_test_add("basic nested control flow");
246
247   TRY {
248     if (n != 1)
249       xbt_test_fail("M1: n=%d (!= 1)", n);
250     n++;
251     TRY {
252       if (n != 2)
253         xbt_test_fail("M2: n=%d (!= 2)", n);
254       n++;
255       THROWF(unknown_error, 0, "something");
256     }
257     CATCH(ex) {
258       if (n != 3)
259         xbt_test_fail("M3: n=%d (!= 3)", n);
260       n++;
261       xbt_ex_free(ex);
262     }
263     n++;
264     TRY {
265       if (n != 5)
266         xbt_test_fail("M2: n=%d (!= 5)", n);
267       n++;
268       THROWF(unknown_error, 0, "something");
269     }
270     CATCH_ANONYMOUS {
271       if (n != 6)
272         xbt_test_fail("M3: n=%d (!= 6)", n);
273       n++;
274       RETHROW;
275       n++;
276     }
277     xbt_test_fail("MX: n=%d (shouldn't reach this point)", n);
278   }
279   CATCH(ex) {
280     if (n != 7)
281       xbt_test_fail("M4: n=%d (!= 7)", n);
282     n++;
283     xbt_ex_free(ex);
284   }
285   if (n != 8)
286     xbt_test_fail("M5: n=%d (!= 8)", n);
287 }
288
289 XBT_TEST_UNIT("value", test_value, "exception value passing")
290 {
291   xbt_ex_t ex;
292
293   TRY {
294     THROWF(unknown_error, 2, "toto");
295   }
296   CATCH(ex) {
297     xbt_test_add("exception value passing");
298     if (ex.category != unknown_error)
299       xbt_test_fail("category=%d (!= 1)", ex.category);
300     if (ex.value != 2)
301       xbt_test_fail("value=%d (!= 2)", ex.value);
302     if (strcmp(ex.msg, "toto"))
303       xbt_test_fail("message=%s (!= toto)", ex.msg);
304     xbt_ex_free(ex);
305   }
306 }
307
308 XBT_TEST_UNIT("variables", test_variables, "variable value preservation")
309 {
310   xbt_ex_t ex;
311   int r1, r2;
312   volatile int v1, v2;
313
314   r1 = r2 = v1 = v2 = 1234;
315   TRY {
316     r2 = 5678;
317     v2 = 5678;
318     THROWF(unknown_error, 0, "toto");
319   }
320   CATCH(ex) {
321     xbt_test_add("variable preservation");
322     if (r1 != 1234)
323       xbt_test_fail("r1=%d (!= 1234)", r1);
324     if (v1 != 1234)
325       xbt_test_fail("v1=%d (!= 1234)", v1);
326     /* r2 is allowed to be destroyed because not volatile */
327     if (v2 != 5678)
328       xbt_test_fail("v2=%d (!= 5678)", v2);
329     xbt_ex_free(ex);
330   }
331 }
332
333 XBT_TEST_UNIT("cleanup", test_cleanup, "cleanup handling")
334 {
335   xbt_ex_t ex;
336   volatile int v1;
337   int c;
338
339   xbt_test_add("cleanup handling");
340
341   v1 = 1234;
342   c = 0;
343   TRY {
344     v1 = 5678;
345     THROWF(1, 2, "blah");
346   }
347   TRY_CLEANUP {
348     if (v1 != 5678)
349       xbt_test_fail("v1 = %d (!= 5678)", v1);
350     c = 1;
351   }
352   CATCH(ex) {
353     if (v1 != 5678)
354       xbt_test_fail("v1 = %d (!= 5678)", v1);
355     if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg, "blah")))
356       xbt_test_fail("unexpected exception contents");
357     xbt_ex_free(ex);
358   }
359   if (!c)
360     xbt_test_fail("xbt_ex_free not executed");
361 }
362
363
364 /*
365  * The following is the example included in the documentation. It's a good
366  * idea to check its syntax even if we don't try to run it.
367  * And actually, it allows to put comments in the code despite doxygen.
368  */
369 static char *mallocex(int size)
370 {
371   return NULL;
372 }
373
374 #define SMALLAMOUNT 10
375 #define TOOBIG 100000000
376
377 #if 0                           /* this contains syntax errors, actually */
378 static void bad_example(void)
379 {
380   struct {
381     char *first;
382   } *globalcontext;
383   ex_t ex;
384
385   /* BAD_EXAMPLE */
386   TRY {
387     char *cp1, *cp2, *cp3;
388
389     cp1 = mallocex(SMALLAMOUNT);
390     globalcontext->first = cp1;
391     cp2 = mallocex(TOOBIG);
392     cp3 = mallocex(SMALLAMOUNT);
393     strcpy(cp1, "foo");
394     strcpy(cp2, "bar");
395   }
396   TRY_CLEANUP {
397     free(cp3);
398     free(cp2);
399     free(cp1);
400   }
401   CATCH_ANONYMOUS {
402     printf("cp3=%s", cp3);
403     RETHROW;
404   }
405   /* end_of_bad_example */
406 }
407 #endif
408 typedef struct {
409   char *first;
410 } global_context_t;
411
412 static void good_example(void)
413 {
414   global_context_t *global_context = malloc(sizeof(global_context_t));
415
416   /* GOOD_EXAMPLE */
417   {                             /*01 */
418     char *volatile /*03 */ cp1 = NULL /*02 */ ;
419     char *volatile /*03 */ cp2 = NULL /*02 */ ;
420     char *volatile /*03 */ cp3 = NULL /*02 */ ;
421     TRY {
422       cp1 = mallocex(SMALLAMOUNT);
423       global_context->first = cp1;
424       cp1 = NULL /*05 give away */ ;
425       cp2 = mallocex(TOOBIG);
426       cp3 = mallocex(SMALLAMOUNT);
427       strcpy(cp1, "foo");
428       strcpy(cp2, "bar");
429     }
430     TRY_CLEANUP {               /*04 */
431       printf("cp3=%s", cp3 == NULL /*02 */ ? "" : cp3);
432       free(cp3);
433       free(cp2);
434       /*05 cp1 was given away */
435     }
436     CATCH_ANONYMOUS {
437       /*05 global context untouched */
438       RETHROW;
439     }
440   }
441   /* end_of_good_example */
442 }
443 #endif                          /* SIMGRID_TEST */