Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
be more robust to strange failures when handling exceptions
[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 "src/portable.h"           /* execinfo when available */
49 #include "xbt/ex.h"
50 #include "xbt/str.h"
51 #include "xbt/synchro_core.h"
52 #include "src/xbt_modinter.h"       /* backtrace initialization headers */
53
54 #include "src/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 "src/xbt/backtrace_linux.c"
128 #elif (defined(_XBT_WIN32) && defined (_M_IX86)) && !defined(__GNUC__)
129 # include "src/xbt/backtrace_windows.c"
130 #else
131 # include "src/xbt/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 (xbt_initialized==0 || smx_cleaned) {
152           fprintf(stderr, "Ouch. SimGrid is not initialized yet, or already closing. No backtrace available.\n");
153           return; /* Not started yet or already closing. Trying to generate a backtrace would probably fail */
154   }
155
156   if (!e->bt_strings)
157     xbt_ex_setup_backtrace(e);
158
159 #ifdef HAVE_BACKTRACE
160   if (e->used && e->bt_strings) {
161     /* We have everything to build neat backtraces */
162     int i;
163     int cutpath = 0;
164     TRY { // We don't want to have an exception while checking how to deal with the one we already have, do we?
165       cutpath = sg_cfg_get_boolean("exception/cutpath");
166     } CATCH_ANONYMOUS { }
167
168     fprintf(stderr, "\n");
169     for (i = 0; i < e->used; i++) {
170         
171       if (cutpath) {
172         char* p = e->bt_strings[i];
173         xbt_str_rtrim(p, ":0123456789");
174         char* filename = strrchr(p, '/')+1;
175         char* end_of_message  = strrchr(p, ' ');
176
177         int length = strlen(p)-strlen(end_of_message);
178         char* dest = malloc(length);
179
180         memcpy(dest, &p[0], length);
181         dest[length] = 0;
182
183         fprintf(stderr, "%s %s\n", dest, filename);
184
185         free(dest);
186       }
187       else {
188         fprintf(stderr, "%s\n", e->bt_strings[i]);
189       }
190     }
191
192   } else
193 #endif
194   {
195     fprintf(stderr,
196             "\n"
197             "**   In %s() at %s:%d\n"
198             "**   (no backtrace available)\n",
199             e->func, e->file, e->line);
200   }
201 }
202
203
204 /* default __ex_terminate callback function */
205 void __xbt_ex_terminate_default(xbt_ex_t * e)
206 {
207   xbt_ex_display(e);
208   xbt_abort();
209 }
210
211 /* the externally visible API */
212 XBT_EXPORT_NO_IMPORT(xbt_running_ctx_fetcher_t) __xbt_running_ctx_fetch = &__xbt_ex_ctx_default;
213 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
214
215
216 void xbt_ex_free(xbt_ex_t e)
217 {
218   int i;
219
220   free(e.msg);
221
222   if (e.bt_strings) {
223     for (i = 0; i < e.used; i++)
224       free(e.bt_strings[i]);
225     free(e.bt_strings);
226   }
227   /* memset(e,0,sizeof(xbt_ex_t)); */
228 }
229
230 /** \brief returns a short name for the given exception category */
231 const char *xbt_ex_catname(xbt_errcat_t cat)
232 {
233   switch (cat) {
234   case unknown_error:
235     return "unknown error";
236   case arg_error:
237     return "invalid argument";
238   case bound_error:
239     return "out of bounds";
240   case mismatch_error:
241     return "mismatch";
242   case not_found_error:
243     return "not found";
244   case system_error:
245     return "system error";
246   case network_error:
247     return "network error";
248   case timeout_error:
249     return "timeout";
250   case cancel_error:
251     return "action canceled";
252   case thread_error:
253     return "thread error";
254   case host_error:
255     return "host failed";
256   case tracing_error:
257     return "tracing error";
258   case io_error:
259     return "io error";
260   case vm_error:
261     return "vm error";
262
263   }
264   return "INVALID ERROR";
265 }
266
267
268 #ifdef SIMGRID_TEST
269 #include <stdio.h>
270 #include "xbt/ex.h"
271
272 XBT_TEST_SUITE("xbt_ex", "Exception Handling");
273
274 XBT_TEST_UNIT("controlflow", test_controlflow, "basic nested control flow")
275 {
276   xbt_ex_t ex;
277   volatile int n = 1;
278
279   xbt_test_add("basic nested control flow");
280
281   TRY {
282     if (n != 1)
283       xbt_test_fail("M1: n=%d (!= 1)", n);
284     n++;
285     TRY {
286       if (n != 2)
287         xbt_test_fail("M2: n=%d (!= 2)", n);
288       n++;
289       THROWF(unknown_error, 0, "something");
290     }
291     CATCH(ex) {
292       if (n != 3)
293         xbt_test_fail("M3: n=%d (!= 3)", n);
294       n++;
295       xbt_ex_free(ex);
296     }
297     n++;
298     TRY {
299       if (n != 5)
300         xbt_test_fail("M2: n=%d (!= 5)", n);
301       n++;
302       THROWF(unknown_error, 0, "something");
303     }
304     CATCH_ANONYMOUS {
305       if (n != 6)
306         xbt_test_fail("M3: n=%d (!= 6)", n);
307       n++;
308       RETHROW;
309       n++;
310     }
311     xbt_test_fail("MX: n=%d (shouldn't reach this point)", n);
312   }
313   CATCH(ex) {
314     if (n != 7)
315       xbt_test_fail("M4: n=%d (!= 7)", n);
316     n++;
317     xbt_ex_free(ex);
318   }
319   if (n != 8)
320     xbt_test_fail("M5: n=%d (!= 8)", n);
321 }
322
323 XBT_TEST_UNIT("value", test_value, "exception value passing")
324 {
325   xbt_ex_t ex;
326
327   TRY {
328     THROWF(unknown_error, 2, "toto");
329   }
330   CATCH(ex) {
331     xbt_test_add("exception value passing");
332     if (ex.category != unknown_error)
333       xbt_test_fail("category=%d (!= 1)", (int)ex.category);
334     if (ex.value != 2)
335       xbt_test_fail("value=%d (!= 2)", ex.value);
336     if (strcmp(ex.msg, "toto"))
337       xbt_test_fail("message=%s (!= toto)", ex.msg);
338     xbt_ex_free(ex);
339   }
340 }
341
342 XBT_TEST_UNIT("variables", test_variables, "variable value preservation")
343 {
344   xbt_ex_t ex;
345   int r1;
346   int XBT_ATTRIB_UNUSED r2;
347   int v1;
348   volatile int v2;
349
350   r1 = r2 = v1 = v2 = 1234;
351   TRY {
352     r2 = 5678;
353     v2 = 5678;
354     THROWF(unknown_error, 0, "toto");
355   }
356   CATCH(ex) {
357     xbt_test_add("variable preservation");
358     if (r1 != 1234)
359       xbt_test_fail("r1=%d (!= 1234)", r1);
360     if (v1 != 1234)
361       xbt_test_fail("v1=%d (!= 1234)", v1);
362     /* r2 is allowed to be destroyed because not volatile */
363     if (v2 != 5678)
364       xbt_test_fail("v2=%d (!= 5678)", v2);
365     xbt_ex_free(ex);
366   }
367 }
368
369 XBT_TEST_UNIT("cleanup", test_cleanup, "cleanup handling")
370 {
371   xbt_ex_t ex;
372   volatile int v1;
373   int c;
374
375   xbt_test_add("cleanup handling");
376
377   v1 = 1234;
378   c = 0;
379   TRY {
380     v1 = 5678;
381     THROWF(1, 2, "blah");
382   }
383   TRY_CLEANUP {
384     if (v1 != 5678)
385       xbt_test_fail("v1 = %d (!= 5678)", v1);
386     c = 1;
387   }
388   CATCH(ex) {
389     if (v1 != 5678)
390       xbt_test_fail("v1 = %d (!= 5678)", v1);
391     if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg, "blah")))
392       xbt_test_fail("unexpected exception contents");
393     xbt_ex_free(ex);
394   }
395   if (!c)
396     xbt_test_fail("xbt_ex_free not executed");
397 }
398
399
400 /*
401  * The following is the example included in the documentation. It's a good
402  * idea to check its syntax even if we don't try to run it.
403  * And actually, it allows to put comments in the code despite doxygen.
404  */
405 static char *mallocex(int size)
406 {
407   return NULL;
408 }
409
410 #define SMALLAMOUNT 10
411 #define TOOBIG 100000000
412
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 */