Logo AND Algorithmique Numérique Distribuée

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