Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Introduce log appenders
[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 */