Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
kill the portable header
[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/internal_config.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 HAVE_EXECINFO_H && 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     fprintf(stderr, "Backtrace (displayed in process %s):\n", SIMIX_process_self_get_name());
103     for (int 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   XBT_ERROR("No backtrace on this arch");
112 #endif
113 }
114
115 /** \brief show the backtrace of the current point (lovely while debuging) */
116 void xbt_backtrace_display_current(void)
117 {
118   xbt_ex_t e;
119   xbt_backtrace_current(&e);
120   xbt_backtrace_display(&e);
121 }
122
123 #if HAVE_EXECINFO_H && HAVE_POPEN && defined(ADDR2LINE)
124 # include "src/xbt/backtrace_linux.c"
125 #else
126 # include "src/xbt/backtrace_dummy.c"
127 #endif
128
129 /** @brief shows an exception content and the associated stack if available */
130 void xbt_ex_display(xbt_ex_t * e)
131 {
132   char *thrower = NULL;
133   if (e->pid != xbt_getpid())
134     thrower = bprintf(" on process %d",e->pid);
135
136   fprintf(stderr,
137           "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
138           "** %s\n"
139           "** Thrown by %s()%s\n",
140           xbt_binary_name, xbt_getpid(),
141           xbt_ex_catname(e->category), e->value, e->msg,
142           e->procname, thrower ? thrower : " in this process");
143   XBT_CRITICAL("%s", e->msg);
144   xbt_free(thrower);
145
146   if (xbt_initialized==0 || smx_cleaned) {
147     fprintf(stderr, "Ouch. SimGrid is not initialized yet, or already closing. No backtrace available.\n");
148     return; /* Not started yet or already closing. Trying to generate a backtrace would probably fail */
149   }
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     int cutpath = 0;
159     TRY { // We don't want to have an exception while checking how to deal with the one we already have, do we?
160       cutpath = sg_cfg_get_boolean("exception/cutpath");
161     } CATCH_ANONYMOUS { }
162
163     fprintf(stderr, "\n");
164     for (i = 0; i < e->used; i++) {
165         
166       if (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     fprintf(stderr, "\n"
190         "**   In %s() at %s:%d\n"
191         "**   (no backtrace available)\n", e->func, e->file, e->line);
192 }
193
194
195 /* default __ex_terminate callback function */
196 void __xbt_ex_terminate_default(xbt_ex_t * e)
197 {
198   xbt_ex_display(e);
199   xbt_abort();
200 }
201
202 /* the externally visible API */
203 XBT_EXPORT_NO_IMPORT(xbt_running_ctx_fetcher_t) __xbt_running_ctx_fetch = &__xbt_ex_ctx_default;
204 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
205
206
207 void xbt_ex_free(xbt_ex_t e)
208 {
209   free(e.msg);
210
211   if (e.bt_strings) {
212     for (int i = 0; i < e.used; i++)
213       free(e.bt_strings[i]);
214     free(e.bt_strings);
215   }
216 }
217
218 /** \brief returns a short name for the given exception category */
219 const char *xbt_ex_catname(xbt_errcat_t cat)
220 {
221   switch (cat) {
222   case unknown_error:
223     return "unknown error";
224   case arg_error:
225     return "invalid argument";
226   case bound_error:
227     return "out of bounds";
228   case mismatch_error:
229     return "mismatch";
230   case not_found_error:
231     return "not found";
232   case system_error:
233     return "system error";
234   case network_error:
235     return "network error";
236   case timeout_error:
237     return "timeout";
238   case cancel_error:
239     return "action canceled";
240   case thread_error:
241     return "thread error";
242   case host_error:
243     return "host failed";
244   case tracing_error:
245     return "tracing error";
246   case io_error:
247     return "io error";
248   case vm_error:
249     return "vm error";
250
251   }
252   return "INVALID ERROR";
253 }
254
255
256 #ifdef SIMGRID_TEST
257 #include <stdio.h>
258 #include "xbt/ex.h"
259
260 XBT_TEST_SUITE("xbt_ex", "Exception Handling");
261
262 XBT_TEST_UNIT("controlflow", test_controlflow, "basic nested control flow")
263 {
264   xbt_ex_t ex;
265   volatile int n = 1;
266
267   xbt_test_add("basic nested control flow");
268
269   TRY {
270     if (n != 1)
271       xbt_test_fail("M1: n=%d (!= 1)", n);
272     n++;
273     TRY {
274       if (n != 2)
275         xbt_test_fail("M2: n=%d (!= 2)", n);
276       n++;
277       THROWF(unknown_error, 0, "something");
278     }
279     CATCH(ex) {
280       if (n != 3)
281         xbt_test_fail("M3: n=%d (!= 3)", n);
282       n++;
283       xbt_ex_free(ex);
284     }
285     n++;
286     TRY {
287       if (n != 5)
288         xbt_test_fail("M2: n=%d (!= 5)", n);
289       n++;
290       THROWF(unknown_error, 0, "something");
291     }
292     CATCH_ANONYMOUS {
293       if (n != 6)
294         xbt_test_fail("M3: n=%d (!= 6)", n);
295       n++;
296       RETHROW;
297       n++;
298     }
299     xbt_test_fail("MX: n=%d (shouldn't reach this point)", n);
300   }
301   CATCH(ex) {
302     if (n != 7)
303       xbt_test_fail("M4: n=%d (!= 7)", n);
304     n++;
305     xbt_ex_free(ex);
306   }
307   if (n != 8)
308     xbt_test_fail("M5: n=%d (!= 8)", n);
309 }
310
311 XBT_TEST_UNIT("value", test_value, "exception value passing")
312 {
313   xbt_ex_t ex;
314
315   TRY {
316     THROWF(unknown_error, 2, "toto");
317   }
318   CATCH(ex) {
319     xbt_test_add("exception value passing");
320     if (ex.category != unknown_error)
321       xbt_test_fail("category=%d (!= 1)", (int)ex.category);
322     if (ex.value != 2)
323       xbt_test_fail("value=%d (!= 2)", ex.value);
324     if (strcmp(ex.msg, "toto"))
325       xbt_test_fail("message=%s (!= toto)", ex.msg);
326     xbt_ex_free(ex);
327   }
328 }
329
330 XBT_TEST_UNIT("variables", test_variables, "variable value preservation")
331 {
332   xbt_ex_t ex;
333   int r1;
334   int XBT_ATTRIB_UNUSED r2;
335   int v1;
336   volatile int v2;
337
338   r1 = r2 = v1 = v2 = 1234;
339   TRY {
340     r2 = 5678;
341     v2 = 5678;
342     THROWF(unknown_error, 0, "toto");
343   }
344   CATCH(ex) {
345     xbt_test_add("variable preservation");
346     if (r1 != 1234)
347       xbt_test_fail("r1=%d (!= 1234)", r1);
348     if (v1 != 1234)
349       xbt_test_fail("v1=%d (!= 1234)", v1);
350     /* r2 is allowed to be destroyed because not volatile */
351     if (v2 != 5678)
352       xbt_test_fail("v2=%d (!= 5678)", v2);
353     xbt_ex_free(ex);
354   }
355 }
356
357 XBT_TEST_UNIT("cleanup", test_cleanup, "cleanup handling")
358 {
359   xbt_ex_t ex;
360   volatile int v1;
361   int c;
362
363   xbt_test_add("cleanup handling");
364
365   v1 = 1234;
366   c = 0;
367   TRY {
368     v1 = 5678;
369     THROWF(1, 2, "blah");
370   }
371   TRY_CLEANUP {
372     if (v1 != 5678)
373       xbt_test_fail("v1 = %d (!= 5678)", v1);
374     c = 1;
375   }
376   CATCH(ex) {
377     if (v1 != 5678)
378       xbt_test_fail("v1 = %d (!= 5678)", v1);
379     if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg, "blah")))
380       xbt_test_fail("unexpected exception contents");
381     xbt_ex_free(ex);
382   }
383   if (!c)
384     xbt_test_fail("xbt_ex_free not executed");
385 }
386
387
388 /*
389  * The following is the example included in the documentation. It's a good
390  * idea to check its syntax even if we don't try to run it.
391  * And actually, it allows to put comments in the code despite doxygen.
392  */
393 static char *mallocex(int size)
394 {
395   return NULL;
396 }
397
398 #define SMALLAMOUNT 10
399 #define TOOBIG 100000000
400
401 typedef struct {
402   char *first;
403 } global_context_t;
404
405 static void good_example(void)
406 {
407   global_context_t *global_context = xbt_malloc(sizeof(global_context_t));
408
409   /* GOOD_EXAMPLE */
410   {                             /*01 */
411     char *volatile /*03 */ cp1 = NULL /*02 */ ;
412     char *volatile /*03 */ cp2 = NULL /*02 */ ;
413     char *volatile /*03 */ cp3 = NULL /*02 */ ;
414     TRY {
415       cp1 = mallocex(SMALLAMOUNT);
416       global_context->first = cp1;
417       cp1 = NULL /*05 give away */ ;
418       cp2 = mallocex(TOOBIG);
419       cp3 = mallocex(SMALLAMOUNT);
420       strcpy(cp1, "foo");
421       strcpy(cp2, "bar");
422     }
423     TRY_CLEANUP {               /*04 */
424       printf("cp3=%s", cp3 == NULL /*02 */ ? "" : cp3);
425       free(cp3);
426       free(cp2);
427       /*05 cp1 was given away */
428     }
429     CATCH_ANONYMOUS {
430       /*05 global context untouched */
431       RETHROW;
432     }
433   }
434   /* end_of_good_example */
435 }
436 #endif                          /* SIMGRID_TEST */