Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
move sg_cmdline to xbt/virtu.h module, and rename it to xbt_cmdline
[simgrid.git] / src / xbt / ex.c
1 /* ex - Exception Handling                                                  */
2
3 /*  Copyright (c) 2005-2010 The SimGrid team                                */
4 /*  Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>       */
5 /*  Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>         */
6 /*  Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>           */
7 /*  All rights reserved.                                                    */
8
9 /* This code is inspirated from the OSSP version (as retrieved back in 2004)*/
10 /* It was heavily modified to fit the SimGrid framework.                    */
11
12 /* The OSSP version has the following copyright notice:
13 **  OSSP ex - Exception Handling
14 **  Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
15 **  Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>
16 **  Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>
17 **
18 **  This file is part of OSSP ex, an exception handling library
19 **  which can be found at http://www.ossp.org/pkg/lib/ex/.
20 **
21 **  Permission to use, copy, modify, and distribute this software for
22 **  any purpose with or without fee is hereby granted, provided that
23 **  the above copyright notice and this permission notice appear in all
24 **  copies.
25 **
26 **  THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESSED OR IMPLIED
27 **  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
28 **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 **  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
30 **  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
33 **  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
34 **  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
35 **  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
36 **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 **  SUCH DAMAGE.
38  */
39
40 /* The extensions made for the SimGrid project can either be distributed    */
41 /* under the same license, or under the LGPL v2.1                           */
42
43 #include <stdio.h>
44 #include <stdlib.h>
45
46 #include "portable.h"           /* execinfo when available */
47 #include "xbt/ex.h"
48 #include "xbt/str.h"
49 #include "xbt_modinter.h"       /* backtrace initialization headers */
50
51 #include "xbt/ex_interface.h"
52
53 #undef HAVE_BACKTRACE
54 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
55 # define HAVE_BACKTRACE 1       /* Hello linux box */
56 #endif
57
58 #if defined(_XBT_WIN32) && defined(_M_IX86) && !defined(__GNUC__)
59 # define HAVE_BACKTRACE 1       /* Hello x86 windows box */
60 #endif
61
62
63 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex, xbt, "Exception mecanism");
64
65 XBT_EXPORT_NO_IMPORT(const xbt_running_ctx_t) __xbt_ex_ctx_initializer = XBT_RUNNING_CTX_INITIALIZER;
66
67 /* default __ex_ctx callback function */
68 xbt_running_ctx_t *__xbt_ex_ctx_default(void)
69 {
70   /* Don't scream: this is a default which is never used (so, yes,
71      there is one setjump container by running entity).
72
73      This default gets overriden in xbt/xbt_os_thread.c so that it works in
74      real life and in simulation when using threads to implement the simulation
75      processes (ie, with pthreads and on windows).
76
77      It also gets overriden in xbt/context.c when using ucontextes (as well as
78      in Java for now, but after the java overhaul, it will get cleaned out)
79    */
80   static xbt_running_ctx_t ctx = XBT_RUNNING_CTX_INITIALIZER;
81
82   return &ctx;
83 }
84
85 /* Change raw libc symbols to file names and line numbers */
86 void xbt_ex_setup_backtrace(xbt_ex_t * e);
87
88 void xbt_backtrace_display(xbt_ex_t * e)
89 {
90   xbt_ex_setup_backtrace(e);
91
92 #ifdef HAVE_BACKTRACE
93   if (e->used == 0) {
94     fprintf(stderr, "(backtrace not set)\n");
95   } else {
96     int i;
97
98     fprintf(stderr, "Backtrace (displayed in thread %p):\n",
99             (void *) xbt_thread_self());
100     for (i = 1; i < e->used; i++)       /* no need to display "xbt_backtrace_display" */
101       fprintf(stderr, "---> %s\n", e->bt_strings[i] + 4);
102   }
103
104   /* don't fool xbt_ex_free with uninitialized msg field */
105   e->msg = NULL;
106   e->remote = 0;
107   xbt_ex_free(*e);
108 #else
109
110   XBT_ERROR("No backtrace on this arch");
111 #endif
112 }
113
114 /** \brief show the backtrace of the current point (lovely while debuging) */
115 void xbt_backtrace_display_current(void)
116 {
117   xbt_ex_t e;
118   xbt_backtrace_current(&e);
119   xbt_backtrace_display(&e);
120 }
121
122 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
123 # include "backtrace_linux.c"
124 #elif (defined(_XBT_WIN32) && defined (_M_IX86)) && !defined(__GNUC__)
125 # include "backtrace_windows.c"
126 #else
127 # include "backtrace_dummy.c"
128 #endif
129
130 /** @brief shows an exception content and the associated stack if available */
131 void xbt_ex_display(xbt_ex_t * e)
132 {
133   char *thrower = NULL;
134
135   if (e->remote)
136     thrower = bprintf(" on host %s(%d)", e->host, e->pid);
137
138   fprintf(stderr,
139           "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
140           "** %s\n"
141           "** Thrown by %s()%s\n",
142           xbt_binary_name, xbt_getpid(),
143           xbt_ex_catname(e->category), e->value, e->msg,
144           e->procname, thrower ? thrower : " in this process");
145   XBT_CRITICAL("%s", e->msg);
146   xbt_free(thrower);
147
148   if (!e->remote && !e->bt_strings)
149     xbt_ex_setup_backtrace(e);
150
151 #ifdef HAVE_BACKTRACE
152   if (e->used && e->bt_strings) {
153     /* We have everything to build neat backtraces */
154     int i;
155
156     fprintf(stderr, "\n");
157     for (i = 0; i < e->used; i++)
158       fprintf(stderr, "%s\n", e->bt_strings[i]);
159
160   } else
161 #endif
162   {
163     fprintf(stderr,
164             "\n"
165             "**   In %s() at %s:%d\n"
166             "**   (no backtrace available)\n",
167             e->func, e->file, e->line);
168   }
169 }
170
171
172 /* default __ex_terminate callback function */
173 void __xbt_ex_terminate_default(xbt_ex_t * e)
174 {
175   xbt_ex_display(e);
176   xbt_abort();
177 }
178
179 /* the externally visible API */
180 XBT_EXPORT_NO_IMPORT(xbt_running_ctx_fetcher_t) __xbt_running_ctx_fetch = &__xbt_ex_ctx_default;
181 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate =
182     &__xbt_ex_terminate_default;
183
184
185 void xbt_ex_free(xbt_ex_t e)
186 {
187   int i;
188
189   free(e.msg);
190   if (e.remote) {
191     free(e.procname);
192     free(e.file);
193     free(e.func);
194     free(e.host);
195   }
196
197   if (e.bt_strings) {
198     for (i = 0; i < e.used; i++)
199       free(e.bt_strings[i]);
200     free(e.bt_strings);
201   }
202   /* memset(e,0,sizeof(xbt_ex_t)); */
203 }
204
205 /** \brief returns a short name for the given exception category */
206 const char *xbt_ex_catname(xbt_errcat_t cat)
207 {
208   switch (cat) {
209   case unknown_error:
210     return "unknown error";
211   case arg_error:
212     return "invalid argument";
213   case bound_error:
214     return "out of bounds";
215   case mismatch_error:
216     return "mismatch";
217   case not_found_error:
218     return "not found";
219   case system_error:
220     return "system error";
221   case network_error:
222     return "network error";
223   case timeout_error:
224     return "timeout";
225   case cancel_error:
226     return "action canceled";
227   case thread_error:
228     return "thread error";
229   case host_error:
230     return "host failed";
231   case tracing_error:
232     return "tracing error";
233   case io_error:
234     return "io error";
235   }
236   return "INVALID ERROR";
237 }
238
239
240 #ifdef SIMGRID_TEST
241 #include <stdio.h>
242 #include "xbt/ex.h"
243
244 XBT_TEST_SUITE("xbt_ex", "Exception Handling");
245
246 XBT_TEST_UNIT("controlflow", test_controlflow, "basic nested control flow")
247 {
248   xbt_ex_t ex;
249   volatile int n = 1;
250
251   xbt_test_add("basic nested control flow");
252
253   TRY {
254     if (n != 1)
255       xbt_test_fail("M1: n=%d (!= 1)", n);
256     n++;
257     TRY {
258       if (n != 2)
259         xbt_test_fail("M2: n=%d (!= 2)", n);
260       n++;
261       THROWF(unknown_error, 0, "something");
262     }
263     CATCH(ex) {
264       if (n != 3)
265         xbt_test_fail("M3: n=%d (!= 3)", n);
266       n++;
267       xbt_ex_free(ex);
268     }
269     n++;
270     TRY {
271       if (n != 5)
272         xbt_test_fail("M2: n=%d (!= 5)", n);
273       n++;
274       THROWF(unknown_error, 0, "something");
275     }
276     CATCH_ANONYMOUS {
277       if (n != 6)
278         xbt_test_fail("M3: n=%d (!= 6)", n);
279       n++;
280       RETHROW;
281       n++;
282     }
283     xbt_test_fail("MX: n=%d (shouldn't reach this point)", n);
284   }
285   CATCH(ex) {
286     if (n != 7)
287       xbt_test_fail("M4: n=%d (!= 7)", n);
288     n++;
289     xbt_ex_free(ex);
290   }
291   if (n != 8)
292     xbt_test_fail("M5: n=%d (!= 8)", n);
293 }
294
295 XBT_TEST_UNIT("value", test_value, "exception value passing")
296 {
297   xbt_ex_t ex;
298
299   TRY {
300     THROWF(unknown_error, 2, "toto");
301   }
302   CATCH(ex) {
303     xbt_test_add("exception value passing");
304     if (ex.category != unknown_error)
305       xbt_test_fail("category=%d (!= 1)", (int)ex.category);
306     if (ex.value != 2)
307       xbt_test_fail("value=%d (!= 2)", ex.value);
308     if (strcmp(ex.msg, "toto"))
309       xbt_test_fail("message=%s (!= toto)", ex.msg);
310     xbt_ex_free(ex);
311   }
312 }
313
314 XBT_TEST_UNIT("variables", test_variables, "variable value preservation")
315 {
316   xbt_ex_t ex;
317   int r1;
318   int _XBT_GNUC_UNUSED r2;
319   volatile int v1, v2;
320
321   r1 = r2 = v1 = v2 = 1234;
322   TRY {
323     r2 = 5678;
324     v2 = 5678;
325     THROWF(unknown_error, 0, "toto");
326   }
327   CATCH(ex) {
328     xbt_test_add("variable preservation");
329     if (r1 != 1234)
330       xbt_test_fail("r1=%d (!= 1234)", r1);
331     if (v1 != 1234)
332       xbt_test_fail("v1=%d (!= 1234)", v1);
333     /* r2 is allowed to be destroyed because not volatile */
334     if (v2 != 5678)
335       xbt_test_fail("v2=%d (!= 5678)", v2);
336     xbt_ex_free(ex);
337   }
338 }
339
340 XBT_TEST_UNIT("cleanup", test_cleanup, "cleanup handling")
341 {
342   xbt_ex_t ex;
343   volatile int v1;
344   int c;
345
346   xbt_test_add("cleanup handling");
347
348   v1 = 1234;
349   c = 0;
350   TRY {
351     v1 = 5678;
352     THROWF(1, 2, "blah");
353   }
354   TRY_CLEANUP {
355     if (v1 != 5678)
356       xbt_test_fail("v1 = %d (!= 5678)", v1);
357     c = 1;
358   }
359   CATCH(ex) {
360     if (v1 != 5678)
361       xbt_test_fail("v1 = %d (!= 5678)", v1);
362     if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg, "blah")))
363       xbt_test_fail("unexpected exception contents");
364     xbt_ex_free(ex);
365   }
366   if (!c)
367     xbt_test_fail("xbt_ex_free not executed");
368 }
369
370
371 /*
372  * The following is the example included in the documentation. It's a good
373  * idea to check its syntax even if we don't try to run it.
374  * And actually, it allows to put comments in the code despite doxygen.
375  */
376 static char *mallocex(int size)
377 {
378   return NULL;
379 }
380
381 #define SMALLAMOUNT 10
382 #define TOOBIG 100000000
383
384 #if 0                           /* this contains syntax errors, actually */
385 static void bad_example(void)
386 {
387   struct {
388     char *first;
389   } *globalcontext;
390   ex_t ex;
391
392   /* BAD_EXAMPLE */
393   TRY {
394     char *cp1, *cp2, *cp3;
395
396     cp1 = mallocex(SMALLAMOUNT);
397     globalcontext->first = cp1;
398     cp2 = mallocex(TOOBIG);
399     cp3 = mallocex(SMALLAMOUNT);
400     strcpy(cp1, "foo");
401     strcpy(cp2, "bar");
402   }
403   TRY_CLEANUP {
404     free(cp3);
405     free(cp2);
406     free(cp1);
407   }
408   CATCH_ANONYMOUS {
409     printf("cp3=%s", cp3);
410     RETHROW;
411   }
412   /* end_of_bad_example */
413 }
414 #endif
415 typedef struct {
416   char *first;
417 } global_context_t;
418
419 static void good_example(void)
420 {
421   global_context_t *global_context = malloc(sizeof(global_context_t));
422
423   /* GOOD_EXAMPLE */
424   {                             /*01 */
425     char *volatile /*03 */ cp1 = NULL /*02 */ ;
426     char *volatile /*03 */ cp2 = NULL /*02 */ ;
427     char *volatile /*03 */ cp3 = NULL /*02 */ ;
428     TRY {
429       cp1 = mallocex(SMALLAMOUNT);
430       global_context->first = cp1;
431       cp1 = NULL /*05 give away */ ;
432       cp2 = mallocex(TOOBIG);
433       cp3 = mallocex(SMALLAMOUNT);
434       strcpy(cp1, "foo");
435       strcpy(cp2, "bar");
436     }
437     TRY_CLEANUP {               /*04 */
438       printf("cp3=%s", cp3 == NULL /*02 */ ? "" : cp3);
439       free(cp3);
440       free(cp2);
441       /*05 cp1 was given away */
442     }
443     CATCH_ANONYMOUS {
444       /*05 global context untouched */
445       RETHROW;
446     }
447   }
448   /* end_of_good_example */
449 }
450 #endif                          /* SIMGRID_TEST */