Logo AND Algorithmique Numérique Distribuée

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