Logo AND Algorithmique Numérique Distribuée

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