Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
a543fb8fce74aae29875347b50f2d42779137247
[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   if (e->pid != xbt_getpid())
137     thrower = bprintf(" on process %d",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_binary_name, 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->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 = &__xbt_ex_terminate_default;
183
184
185 void xbt_ex_free(xbt_ex_t e)
186 {
187   int i;
188
189   free(e.msg);
190
191   if (e.bt_strings) {
192     for (i = 0; i < e.used; i++)
193       free(e.bt_strings[i]);
194     free(e.bt_strings);
195   }
196   /* memset(e,0,sizeof(xbt_ex_t)); */
197 }
198
199 /** \brief returns a short name for the given exception category */
200 const char *xbt_ex_catname(xbt_errcat_t cat)
201 {
202   switch (cat) {
203   case unknown_error:
204     return "unknown error";
205   case arg_error:
206     return "invalid argument";
207   case bound_error:
208     return "out of bounds";
209   case mismatch_error:
210     return "mismatch";
211   case not_found_error:
212     return "not found";
213   case system_error:
214     return "system error";
215   case network_error:
216     return "network error";
217   case timeout_error:
218     return "timeout";
219   case cancel_error:
220     return "action canceled";
221   case thread_error:
222     return "thread error";
223   case host_error:
224     return "host failed";
225   case tracing_error:
226     return "tracing error";
227   case io_error:
228     return "io error";
229   case vm_error:
230     return "vm error";
231
232   }
233   return "INVALID ERROR";
234 }
235
236
237 #ifdef SIMGRID_TEST
238 #include <stdio.h>
239 #include "xbt/ex.h"
240
241 XBT_TEST_SUITE("xbt_ex", "Exception Handling");
242
243 XBT_TEST_UNIT("controlflow", test_controlflow, "basic nested control flow")
244 {
245   xbt_ex_t ex;
246   volatile int n = 1;
247
248   xbt_test_add("basic nested control flow");
249
250   TRY {
251     if (n != 1)
252       xbt_test_fail("M1: n=%d (!= 1)", n);
253     n++;
254     TRY {
255       if (n != 2)
256         xbt_test_fail("M2: n=%d (!= 2)", n);
257       n++;
258       THROWF(unknown_error, 0, "something");
259     }
260     CATCH(ex) {
261       if (n != 3)
262         xbt_test_fail("M3: n=%d (!= 3)", n);
263       n++;
264       xbt_ex_free(ex);
265     }
266     n++;
267     TRY {
268       if (n != 5)
269         xbt_test_fail("M2: n=%d (!= 5)", n);
270       n++;
271       THROWF(unknown_error, 0, "something");
272     }
273     CATCH_ANONYMOUS {
274       if (n != 6)
275         xbt_test_fail("M3: n=%d (!= 6)", n);
276       n++;
277       RETHROW;
278       n++;
279     }
280     xbt_test_fail("MX: n=%d (shouldn't reach this point)", n);
281   }
282   CATCH(ex) {
283     if (n != 7)
284       xbt_test_fail("M4: n=%d (!= 7)", n);
285     n++;
286     xbt_ex_free(ex);
287   }
288   if (n != 8)
289     xbt_test_fail("M5: n=%d (!= 8)", n);
290 }
291
292 XBT_TEST_UNIT("value", test_value, "exception value passing")
293 {
294   xbt_ex_t ex;
295
296   TRY {
297     THROWF(unknown_error, 2, "toto");
298   }
299   CATCH(ex) {
300     xbt_test_add("exception value passing");
301     if (ex.category != unknown_error)
302       xbt_test_fail("category=%d (!= 1)", (int)ex.category);
303     if (ex.value != 2)
304       xbt_test_fail("value=%d (!= 2)", ex.value);
305     if (strcmp(ex.msg, "toto"))
306       xbt_test_fail("message=%s (!= toto)", ex.msg);
307     xbt_ex_free(ex);
308   }
309 }
310
311 XBT_TEST_UNIT("variables", test_variables, "variable value preservation")
312 {
313   xbt_ex_t ex;
314   int r1;
315   int _XBT_GNUC_UNUSED r2;
316   int v1;
317   volatile int v2;
318
319   r1 = r2 = v1 = v2 = 1234;
320   TRY {
321     r2 = 5678;
322     v2 = 5678;
323     THROWF(unknown_error, 0, "toto");
324   }
325   CATCH(ex) {
326     xbt_test_add("variable preservation");
327     if (r1 != 1234)
328       xbt_test_fail("r1=%d (!= 1234)", r1);
329     if (v1 != 1234)
330       xbt_test_fail("v1=%d (!= 1234)", v1);
331     /* r2 is allowed to be destroyed because not volatile */
332     if (v2 != 5678)
333       xbt_test_fail("v2=%d (!= 5678)", v2);
334     xbt_ex_free(ex);
335   }
336 }
337
338 XBT_TEST_UNIT("cleanup", test_cleanup, "cleanup handling")
339 {
340   xbt_ex_t ex;
341   volatile int v1;
342   int c;
343
344   xbt_test_add("cleanup handling");
345
346   v1 = 1234;
347   c = 0;
348   TRY {
349     v1 = 5678;
350     THROWF(1, 2, "blah");
351   }
352   TRY_CLEANUP {
353     if (v1 != 5678)
354       xbt_test_fail("v1 = %d (!= 5678)", v1);
355     c = 1;
356   }
357   CATCH(ex) {
358     if (v1 != 5678)
359       xbt_test_fail("v1 = %d (!= 5678)", v1);
360     if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg, "blah")))
361       xbt_test_fail("unexpected exception contents");
362     xbt_ex_free(ex);
363   }
364   if (!c)
365     xbt_test_fail("xbt_ex_free not executed");
366 }
367
368
369 /*
370  * The following is the example included in the documentation. It's a good
371  * idea to check its syntax even if we don't try to run it.
372  * And actually, it allows to put comments in the code despite doxygen.
373  */
374 static char *mallocex(int size)
375 {
376   return NULL;
377 }
378
379 #define SMALLAMOUNT 10
380 #define TOOBIG 100000000
381
382 #if 0                           /* this contains syntax errors, actually */
383 static void bad_example(void)
384 {
385   struct {
386     char *first;
387   } *globalcontext;
388   ex_t ex;
389
390   /* BAD_EXAMPLE */
391   TRY {
392     char *cp1, *cp2, *cp3;
393
394     cp1 = mallocex(SMALLAMOUNT);
395     globalcontext->first = cp1;
396     cp2 = mallocex(TOOBIG);
397     cp3 = mallocex(SMALLAMOUNT);
398     strcpy(cp1, "foo");
399     strcpy(cp2, "bar");
400   }
401   TRY_CLEANUP {
402     free(cp3);
403     free(cp2);
404     free(cp1);
405   }
406   CATCH_ANONYMOUS {
407     printf("cp3=%s", cp3);
408     RETHROW;
409   }
410   /* end_of_bad_example */
411 }
412 #endif
413 typedef struct {
414   char *first;
415 } global_context_t;
416
417 static void good_example(void)
418 {
419   global_context_t *global_context = xbt_malloc(sizeof(global_context_t));
420
421   /* GOOD_EXAMPLE */
422   {                             /*01 */
423     char *volatile /*03 */ cp1 = NULL /*02 */ ;
424     char *volatile /*03 */ cp2 = NULL /*02 */ ;
425     char *volatile /*03 */ cp3 = NULL /*02 */ ;
426     TRY {
427       cp1 = mallocex(SMALLAMOUNT);
428       global_context->first = cp1;
429       cp1 = NULL /*05 give away */ ;
430       cp2 = mallocex(TOOBIG);
431       cp3 = mallocex(SMALLAMOUNT);
432       strcpy(cp1, "foo");
433       strcpy(cp2, "bar");
434     }
435     TRY_CLEANUP {               /*04 */
436       printf("cp3=%s", cp3 == NULL /*02 */ ? "" : cp3);
437       free(cp3);
438       free(cp2);
439       /*05 cp1 was given away */
440     }
441     CATCH_ANONYMOUS {
442       /*05 global context untouched */
443       RETHROW;
444     }
445   }
446   /* end_of_good_example */
447 }
448 #endif                          /* SIMGRID_TEST */