Logo AND Algorithmique Numérique Distribuée

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