Logo AND Algorithmique Numérique Distribuée

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