Logo AND Algorithmique Numérique Distribuée

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