Logo AND Algorithmique Numérique Distribuée

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