Logo AND Algorithmique Numérique Distribuée

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