Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Propagate the change of prototype (use exceptions, not int return) of context factori...
[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 (thrower)
116     free(thrower);
117
118   if (!e->remote && !e->bt_strings)
119     xbt_ex_setup_backtrace(e);
120
121 #ifdef HAVE_BACKTRACE
122   /* We have everything to build neat backtraces */
123   {
124     int i;
125     
126     fprintf(stderr,"\n");
127     for (i=0; i<e->used; i++)
128       fprintf(stderr,"%s\n",e->bt_strings[i]);
129     
130   }
131 #else
132   fprintf(stderr," at %s:%d:%s (no backtrace available on that arch)\n",  
133           e->file,e->line,e->func);
134 #endif
135   xbt_ex_free(*e);
136 }
137
138
139 /* default __ex_terminate callback function */
140 void __xbt_ex_terminate_default(xbt_ex_t *e)  {
141   xbt_ex_display(e);
142
143   abort();
144 }
145
146 /* the externally visible API */
147 XBT_EXPORT_NO_IMPORT(ex_ctx_cb_t)  __xbt_ex_ctx       = &__xbt_ex_ctx_default;
148 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
149
150
151 void xbt_ex_free(xbt_ex_t e) {
152   int i;
153
154   if (e.msg) free(e.msg);
155   if (e.remote) {
156     free(e.procname);
157     free(e.file);
158     free(e.func);
159     free(e.host);
160   }
161
162   if (e.bt_strings) {   
163      for (i=0; i<e.used; i++) 
164        free((char*)e.bt_strings[i]);
165      free((char **)e.bt_strings);
166   }
167   /* memset(e,0,sizeof(xbt_ex_t)); */
168 }
169
170 /** \brief returns a short name for the given exception category */
171 const char * xbt_ex_catname(xbt_errcat_t cat) {
172   switch (cat) {
173   case unknown_error:   return  "unknown_err";
174   case arg_error:       return "invalid_arg";
175   case mismatch_error:  return "mismatch";
176   case not_found_error: return "not found";
177   case system_error:    return "system_err";
178   case network_error:   return "network_err";
179   case timeout_error:   return "timeout";
180   case thread_error:    return "thread_err";
181   default:              return "INVALID_ERR";
182   }
183 }
184
185
186 #ifdef SIMGRID_TEST
187 #include <stdio.h>
188 #include "xbt/ex.h"
189
190 XBT_TEST_SUITE("xbt_ex","Exception Handling");
191
192 XBT_TEST_UNIT("controlflow",test_controlflow, "basic nested control flow") {
193     xbt_ex_t ex;
194     volatile int n=1;
195
196     xbt_test_add0("basic nested control flow");
197
198     TRY {
199         if (n != 1)
200             xbt_test_fail1("M1: n=%d (!= 1)", n);
201         n++;
202         TRY {
203             if (n != 2)
204                 xbt_test_fail1("M2: n=%d (!= 2)", n);
205             n++;
206             THROW0(unknown_error,0,"something");
207         } CATCH (ex) {
208             if (n != 3)
209                 xbt_test_fail1("M3: n=%d (!= 3)", n);
210             n++;
211             xbt_ex_free(ex);
212         }
213         n++;
214         TRY {
215             if (n != 5)
216                 xbt_test_fail1("M2: n=%d (!= 5)", n);
217             n++;
218             THROW0(unknown_error,0,"something");
219         } CATCH (ex) {
220             if (n != 6)
221                 xbt_test_fail1("M3: n=%d (!= 6)", n);
222             n++;
223             RETHROW;
224             n++;
225         }
226         xbt_test_fail1("MX: n=%d (shouldn't reach this point)", n);
227     }
228     CATCH(ex) {
229         if (n != 7)
230             xbt_test_fail1("M4: n=%d (!= 7)", n);
231         n++;
232         xbt_ex_free(ex);
233     }
234     if (n != 8)
235         xbt_test_fail1("M5: n=%d (!= 8)", n);
236 }
237
238 XBT_TEST_UNIT("value",test_value,"exception value passing") {
239     xbt_ex_t ex;
240
241     TRY {
242         THROW0(unknown_error, 2, "toto");
243     } CATCH(ex) {
244         xbt_test_add0("exception value passing");
245         if (ex.category != unknown_error)
246             xbt_test_fail1("category=%d (!= 1)", ex.category);
247         if (ex.value != 2)
248             xbt_test_fail1("value=%d (!= 2)", ex.value);
249         if (strcmp(ex.msg,"toto"))
250             xbt_test_fail1("message=%s (!= toto)", ex.msg);
251         xbt_ex_free(ex);
252     }
253 }
254
255 XBT_TEST_UNIT("variables",test_variables,"variable value preservation") {
256     xbt_ex_t ex;
257     int r1, r2;
258     volatile int v1, v2;
259
260     r1 = r2 = v1 = v2 = 1234;
261     TRY {
262         r2 = 5678;
263         v2 = 5678;
264         THROW0(unknown_error, 0, "toto");
265     } CATCH(ex) {
266         xbt_test_add0("variable preservation");
267         if (r1 != 1234)
268             xbt_test_fail1("r1=%d (!= 1234)", r1);
269         if (v1 != 1234)
270             xbt_test_fail1("v1=%d (!= 1234)", v1);
271         /* r2 is allowed to be destroyed because not volatile */
272         if (v2 != 5678)
273             xbt_test_fail1("v2=%d (!= 5678)", v2);
274         xbt_ex_free(ex);
275     }
276 }
277
278 XBT_TEST_UNIT("cleanup",test_cleanup,"cleanup handling") {
279     xbt_ex_t ex;
280     volatile int v1;
281     int c;
282
283     xbt_test_add0("cleanup handling");
284
285     v1 = 1234;
286     c = 0;
287     TRY {
288         v1 = 5678;
289         THROW0(1, 2, "blah");
290     } CLEANUP {
291         if (v1 != 5678)
292             xbt_test_fail1("v1 = %d (!= 5678)", v1);
293         c = 1;
294     } CATCH(ex) {
295         if (v1 != 5678)
296             xbt_test_fail1("v1 = %d (!= 5678)", v1);
297         if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg,"blah")))
298             xbt_test_fail0("unexpected exception contents");
299         xbt_ex_free(ex);
300     }
301     if (!c)
302         xbt_test_fail0("xbt_ex_free not executed");
303 }
304
305
306 /*
307  * The following is the example included in the documentation. It's a good 
308  * idea to check its syntax even if we don't try to run it.
309  * And actually, it allows to put comments in the code despite doxygen.
310  */ 
311 static char *mallocex(int size) {
312   return NULL;
313 }
314 #define SMALLAMOUNT 10
315 #define TOOBIG 100000000
316
317 #if 0 /* this contains syntax errors, actually */
318 static void bad_example(void) {
319   struct {char*first;} *globalcontext;
320   ex_t ex;
321
322   /* BAD_EXAMPLE */
323   TRY {
324     char *cp1, *cp2, *cp3;
325     
326     cp1 = mallocex(SMALLAMOUNT);
327     globalcontext->first = cp1;
328     cp2 = mallocex(TOOBIG);
329     cp3 = mallocex(SMALLAMOUNT);
330     strcpy(cp1, "foo");
331     strcpy(cp2, "bar");
332   } CLEANUP {
333     if (cp3 != NULL) free(cp3);
334     if (cp2 != NULL) free(cp2);
335     if (cp1 != NULL) free(cp1);
336   } CATCH(ex) {
337     printf("cp3=%s", cp3);
338     RETHROW;
339   }
340   /* end_of_bad_example */
341 }
342 #endif
343 typedef struct {char *first;} global_context_t;
344    
345 static void good_example(void) {
346   global_context_t *global_context=malloc(sizeof(global_context_t));
347   xbt_ex_t ex;
348
349   /* GOOD_EXAMPLE */
350   { /*01*/
351     char * volatile /*03*/ cp1 = NULL /*02*/;
352     char * volatile /*03*/ cp2 = NULL /*02*/;
353     char * volatile /*03*/ cp3 = NULL /*02*/;
354     TRY {
355       cp1 = mallocex(SMALLAMOUNT);
356       global_context->first = cp1;
357       cp1 = NULL /*05 give away*/;
358       cp2 = mallocex(TOOBIG);
359       cp3 = mallocex(SMALLAMOUNT);
360       strcpy(cp1, "foo");
361       strcpy(cp2, "bar");
362     } CLEANUP { /*04*/
363       printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
364       if (cp3 != NULL)
365         free(cp3);
366       if (cp2 != NULL)
367         free(cp2);
368       /*05 cp1 was given away */
369     } CATCH(ex) {
370       /*05 global context untouched */
371       RETHROW;
372     }
373   }
374   /* end_of_good_example */
375 }
376 #endif /* SIMGRID_TEST */