Logo AND Algorithmique Numérique Distribuée

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