Logo AND Algorithmique Numérique Distribuée

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