Logo AND Algorithmique Numérique Distribuée

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