Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Do not require doxygen in maintainer mode
[simgrid.git] / src / xbt / ex.c
1 /* $Id$ */
2
3 /* ex - Exception Handling (modified to fit into SimGrid from OSSP version) */
4
5 /*  Copyright (c) 2005, 2006, 2007 Martin Quinson                           */
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 program is free software; you can redistribute it and/or modify it
12  * under the terms of the license (GNU LGPL) which comes with this package. */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16
17 #include "portable.h" /* execinfo when available */
18 #include "xbt/ex.h"
19 #include "xbt/str.h"
20 #include "xbt/module.h" /* xbt_binary_name */
21 #include "xbt_modinter.h" /* backtrace initialization headers */
22 #include "xbt/synchro.h" /* xbt_thread_self */
23
24 #include "gras/Virtu/virtu_interface.h" /* gras_os_myname */
25 #include "xbt/ex_interface.h"
26
27 #undef HAVE_BACKTRACE
28 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
29 # define HAVE_BACKTRACE 1 /* Hello linux box */
30 #endif
31
32 #if defined(WIN32) && defined(_M_IX86) && !defined(__GNUC__)
33 # define HAVE_BACKTRACE 1 /* Hello x86 windows box */
34 #endif
35
36
37 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex,xbt,"Exception mecanism");
38
39 /* default __ex_ctx callback function */
40 ex_ctx_t *__xbt_ex_ctx_default(void) {
41   /* Don't scream: this is a default which is never used (so, yes,
42      there is one setjump container by running entity).
43
44      This default gets overriden in xbt/xbt_os_thread.c so that it works in
45      real life and in simulation when using threads to implement the simulation
46      processes (ie, with pthreads and on windows).
47
48      It also gets overriden in xbt/context.c when using ucontextes (as well as
49      in Java for now, but after the java overhaul, it will get cleaned out)
50    */
51   static ex_ctx_t ctx = XBT_CTX_INITIALIZER;
52
53   return &ctx;
54 }
55
56 /* Change raw libc symbols to file names and line numbers */
57 void xbt_ex_setup_backtrace(xbt_ex_t *e);
58
59 void xbt_backtrace_display(xbt_ex_t *e) {
60   xbt_ex_setup_backtrace(e);
61
62 #ifdef HAVE_BACKTRACE
63   if (e->used == 0) {
64     fprintf(stderr,"(backtrace not set)\n");
65   } else {
66     int i;
67
68     fprintf(stderr,"Backtrace (displayed in thread %p):\n",
69             (void*)xbt_thread_self());
70     for (i=1; i<e->used; i++) /* no need to display "xbt_display_backtrace" */
71       fprintf(stderr,"---> %s\n",e->bt_strings[i] +4);
72   }
73
74   /* don't fool xbt_ex_free with uninitialized msg field */
75   e->msg=NULL;
76   e->remote=0;
77   xbt_ex_free(*e);
78 #else
79
80   ERROR0("No backtrace on this arch");
81 #endif
82 }
83
84 /** \brief show the backtrace of the current point (lovely while debuging) */
85 void xbt_backtrace_display_current(void) {
86   xbt_ex_t e;
87   xbt_backtrace_current(&e);
88   xbt_backtrace_display(&e);
89 }
90
91 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
92 # include "backtrace_linux.c"
93 #elif (defined(WIN32) && defined (_M_IX86)) && !defined(__GNUC__)
94 # include "backtrace_windows.c"
95 #else
96 # include "backtrace_dummy.c"
97 #endif
98
99 /** @brief shows an exception content and the associated stack if available */
100 void xbt_ex_display(xbt_ex_t *e)  {
101   char *thrower=NULL;
102
103   if (e->remote)
104     thrower = bprintf(" on host %s(%d)",e->host,e->pid);
105
106   fprintf(stderr,
107           "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
108           "** %s\n"
109           "** Thrown by %s()%s\n",
110           gras_os_myname(),(*xbt_getpid)(),
111           xbt_ex_catname(e->category), e->value, e->msg,
112           e->procname,thrower?thrower:" in this process");
113   CRITICAL1("%s",e->msg);
114
115   if (!e->remote && !e->bt_strings)
116     xbt_ex_setup_backtrace(e);
117
118 #ifdef HAVE_BACKTRACE
119   /* We have everything to build neat backtraces */
120   {
121     int i;
122
123     fprintf(stderr,"\n");
124     for (i=0; i<e->used; i++)
125       fprintf(stderr,"%s\n",e->bt_strings[i]);
126
127   }
128 #else
129   fprintf(stderr," at %s:%d:%s (no backtrace available on that arch)\n",
130           e->file,e->line,e->func);
131 #endif
132 }
133
134
135 /* default __ex_terminate callback function */
136 void __xbt_ex_terminate_default(xbt_ex_t *e)  {
137   xbt_ex_display(e);
138
139   abort();
140 }
141
142 /* the externally visible API */
143 XBT_EXPORT_NO_IMPORT(ex_ctx_cb_t)  __xbt_ex_ctx       = &__xbt_ex_ctx_default;
144 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
145
146
147 void xbt_ex_free(xbt_ex_t e) {
148   int i;
149
150   if (e.msg) free(e.msg);
151   if (e.remote) {
152     free(e.procname);
153     free(e.file);
154     free(e.func);
155     free(e.host);
156   }
157
158   if (e.bt_strings) {
159     for (i=0; i<e.used; i++)
160       free((char*)e.bt_strings[i]);
161     free((char **)e.bt_strings);
162   }
163   /* memset(e,0,sizeof(xbt_ex_t)); */
164 }
165
166 /** \brief returns a short name for the given exception category */
167 const char * xbt_ex_catname(xbt_errcat_t cat) {
168   switch (cat) {
169     case unknown_error:   return  "unknown_err";
170     case arg_error:       return "invalid_arg";
171     case mismatch_error:  return "mismatch";
172     case not_found_error: return "not found";
173     case system_error:    return "system_err";
174     case network_error:   return "network_err";
175     case timeout_error:   return "timeout";
176     case thread_error:    return "thread_err";
177     default:              return "INVALID_ERR";
178   }
179 }
180
181
182 #ifdef SIMGRID_TEST
183 #include <stdio.h>
184 #include "xbt/ex.h"
185
186 XBT_TEST_SUITE("xbt_ex","Exception Handling");
187
188 XBT_TEST_UNIT("controlflow",test_controlflow, "basic nested control flow") {
189   xbt_ex_t ex;
190   volatile int n=1;
191
192   xbt_test_add0("basic nested control flow");
193
194   TRY {
195     if (n != 1)
196       xbt_test_fail1("M1: n=%d (!= 1)", n);
197     n++;
198     TRY {
199       if (n != 2)
200         xbt_test_fail1("M2: n=%d (!= 2)", n);
201       n++;
202       THROW0(unknown_error,0,"something");
203     } CATCH (ex) {
204       if (n != 3)
205         xbt_test_fail1("M3: n=%d (!= 3)", n);
206       n++;
207       xbt_ex_free(ex);
208     }
209     n++;
210     TRY {
211       if (n != 5)
212         xbt_test_fail1("M2: n=%d (!= 5)", n);
213       n++;
214       THROW0(unknown_error,0,"something");
215     } CATCH (ex) {
216       if (n != 6)
217         xbt_test_fail1("M3: n=%d (!= 6)", n);
218       n++;
219       RETHROW;
220       n++;
221     }
222     xbt_test_fail1("MX: n=%d (shouldn't reach this point)", n);
223   }
224   CATCH(ex) {
225     if (n != 7)
226       xbt_test_fail1("M4: n=%d (!= 7)", n);
227     n++;
228     xbt_ex_free(ex);
229   }
230   if (n != 8)
231     xbt_test_fail1("M5: n=%d (!= 8)", n);
232 }
233
234 XBT_TEST_UNIT("value",test_value,"exception value passing") {
235   xbt_ex_t ex;
236
237   TRY {
238     THROW0(unknown_error, 2, "toto");
239   } CATCH(ex) {
240     xbt_test_add0("exception value passing");
241     if (ex.category != unknown_error)
242       xbt_test_fail1("category=%d (!= 1)", ex.category);
243     if (ex.value != 2)
244       xbt_test_fail1("value=%d (!= 2)", ex.value);
245     if (strcmp(ex.msg,"toto"))
246       xbt_test_fail1("message=%s (!= toto)", ex.msg);
247     xbt_ex_free(ex);
248   }
249 }
250
251 XBT_TEST_UNIT("variables",test_variables,"variable value preservation") {
252   xbt_ex_t ex;
253   int r1, r2;
254   volatile int v1, v2;
255
256   r1 = r2 = v1 = v2 = 1234;
257   TRY {
258     r2 = 5678;
259     v2 = 5678;
260     THROW0(unknown_error, 0, "toto");
261   } CATCH(ex) {
262     xbt_test_add0("variable preservation");
263     if (r1 != 1234)
264       xbt_test_fail1("r1=%d (!= 1234)", r1);
265     if (v1 != 1234)
266       xbt_test_fail1("v1=%d (!= 1234)", v1);
267     /* r2 is allowed to be destroyed because not volatile */
268     if (v2 != 5678)
269       xbt_test_fail1("v2=%d (!= 5678)", v2);
270     xbt_ex_free(ex);
271   }
272 }
273
274 XBT_TEST_UNIT("cleanup",test_cleanup,"cleanup handling") {
275   xbt_ex_t ex;
276   volatile int v1;
277   int c;
278
279   xbt_test_add0("cleanup handling");
280
281   v1 = 1234;
282   c = 0;
283   TRY {
284     v1 = 5678;
285     THROW0(1, 2, "blah");
286   } CLEANUP {
287     if (v1 != 5678)
288       xbt_test_fail1("v1 = %d (!= 5678)", v1);
289     c = 1;
290   } CATCH(ex) {
291     if (v1 != 5678)
292       xbt_test_fail1("v1 = %d (!= 5678)", v1);
293     if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg,"blah")))
294       xbt_test_fail0("unexpected exception contents");
295     xbt_ex_free(ex);
296   }
297   if (!c)
298     xbt_test_fail0("xbt_ex_free not executed");
299 }
300
301
302 /*
303  * The following is the example included in the documentation. It's a good
304  * idea to check its syntax even if we don't try to run it.
305  * And actually, it allows to put comments in the code despite doxygen.
306  */
307 static char *mallocex(int size) {
308   return NULL;
309 }
310 #define SMALLAMOUNT 10
311 #define TOOBIG 100000000
312
313 #if 0 /* this contains syntax errors, actually */
314 static void bad_example(void) {
315   struct {char*first;} *globalcontext;
316   ex_t ex;
317
318   /* BAD_EXAMPLE */
319   TRY {
320     char *cp1, *cp2, *cp3;
321
322     cp1 = mallocex(SMALLAMOUNT);
323     globalcontext->first = cp1;
324     cp2 = mallocex(TOOBIG);
325     cp3 = mallocex(SMALLAMOUNT);
326     strcpy(cp1, "foo");
327     strcpy(cp2, "bar");
328   } CLEANUP {
329     if (cp3 != NULL) free(cp3);
330     if (cp2 != NULL) free(cp2);
331     if (cp1 != NULL) free(cp1);
332   } CATCH(ex) {
333     printf("cp3=%s", cp3);
334     RETHROW;
335   }
336   /* end_of_bad_example */
337 }
338 #endif
339 typedef struct {char *first;} global_context_t;
340
341 static void good_example(void) {
342   global_context_t *global_context=malloc(sizeof(global_context_t));
343   xbt_ex_t ex;
344
345   /* GOOD_EXAMPLE */
346   { /*01*/
347     char * volatile /*03*/ cp1 = NULL /*02*/;
348     char * volatile /*03*/ cp2 = NULL /*02*/;
349     char * volatile /*03*/ cp3 = NULL /*02*/;
350     TRY {
351       cp1 = mallocex(SMALLAMOUNT);
352       global_context->first = cp1;
353       cp1 = NULL /*05 give away*/;
354       cp2 = mallocex(TOOBIG);
355       cp3 = mallocex(SMALLAMOUNT);
356       strcpy(cp1, "foo");
357       strcpy(cp2, "bar");
358     } CLEANUP { /*04*/
359       printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
360       if (cp3 != NULL)
361         free(cp3);
362       if (cp2 != NULL)
363         free(cp2);
364       /*05 cp1 was given away */
365     } CATCH(ex) {
366       /*05 global context untouched */
367       RETHROW;
368     }
369   }
370   /* end_of_good_example */
371 }
372 #endif /* SIMGRID_TEST */