Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
support for backtraces on windows is gone
[simgrid.git] / src / xbt / ex.c
1 /* ex - Exception Handling                                                  */
2
3 /* Copyright (c) 2005-2015. The SimGrid Team.
4  * All rights reserved.                                                     */
5
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 code is inspirated from the OSSP version (as retrieved back in 2004)*/
12 /* It was heavily modified to fit the SimGrid framework.                    */
13
14 /* The OSSP version has the following copyright notice:
15 **  OSSP ex - Exception Handling
16 **  Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
17 **  Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>
18 **  Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>
19 **
20 **  This file is part of OSSP ex, an exception handling library
21 **  which can be found at http://www.ossp.org/pkg/lib/ex/.
22 **
23 **  Permission to use, copy, modify, and distribute this software for
24 **  any purpose with or without fee is hereby granted, provided that
25 **  the above copyright notice and this permission notice appear in all
26 **  copies.
27 **
28 **  THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESSED OR IMPLIED
29 **  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30 **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
31 **  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
32 **  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
35 **  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
36 **  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37 **  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
38 **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 **  SUCH DAMAGE.
40  */
41
42 /* The extensions made for the SimGrid project can either be distributed    */
43 /* under the same license, or under the LGPL v2.1                           */
44
45 #include <stdio.h>
46 #include <stdlib.h>
47
48 #include "src/internal_config.h"           /* execinfo when available */
49 #include "xbt/ex.h"
50 #include "xbt/str.h"
51 #include "xbt/synchro_core.h"
52 #include "src/xbt_modinter.h"       /* backtrace initialization headers */
53
54 #include "src/xbt/ex_interface.h"
55 #include "simgrid/sg_config.h"  /* Configuration mechanism of SimGrid */
56
57 #include "simgrid/simix.h" /* SIMIX_process_self_get_name() */
58
59 #undef HAVE_BACKTRACE
60 #if HAVE_EXECINFO_H && HAVE_POPEN && defined(ADDR2LINE)
61 # define HAVE_BACKTRACE 1       /* Hello linux box */
62 #endif
63
64 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex, xbt, "Exception mecanism");
65
66 XBT_EXPORT_NO_IMPORT(const xbt_running_ctx_t) __xbt_ex_ctx_initializer = XBT_RUNNING_CTX_INITIALIZER;
67
68 /* default __ex_ctx callback function */
69 xbt_running_ctx_t *__xbt_ex_ctx_default(void)
70 {
71   /* Don't scream: this is a default which is never used (so, yes,
72      there is one setjump container by running entity).
73
74      This default gets overriden in xbt/xbt_os_thread.c so that it works in
75      real life and in simulation when using threads to implement the simulation
76      processes (ie, with pthreads and on windows).
77
78      It also gets overriden in xbt/context.c when using ucontexts (as well as
79      in Java for now, but after the java overhaul, it will get cleaned out)
80    */
81   static xbt_running_ctx_t ctx = XBT_RUNNING_CTX_INITIALIZER;
82
83   return &ctx;
84 }
85
86 /* Change raw libc symbols to file names and line numbers */
87 void xbt_ex_setup_backtrace(xbt_ex_t * e);
88
89 void xbt_backtrace_display(xbt_ex_t * e)
90 {
91   xbt_ex_setup_backtrace(e);
92
93 #ifdef HAVE_BACKTRACE
94   if (e->used == 0) {
95     fprintf(stderr, "(backtrace not set)\n");
96   } else {
97     fprintf(stderr, "Backtrace (displayed in process %s):\n", SIMIX_process_self_get_name());
98     for (int i = 1; i < e->used; i++)       /* no need to display "xbt_backtrace_display" */
99       fprintf(stderr, "---> %s\n", e->bt_strings[i] + 4);
100   }
101
102   /* don't fool xbt_ex_free with uninitialized msg field */
103   e->msg = NULL;
104   xbt_ex_free(*e);
105 #else
106   XBT_ERROR("No backtrace on this arch");
107 #endif
108 }
109
110 /** \brief show the backtrace of the current point (lovely while debuging) */
111 void xbt_backtrace_display_current(void)
112 {
113   xbt_ex_t e;
114   xbt_backtrace_current(&e);
115   xbt_backtrace_display(&e);
116 }
117
118 #if HAVE_EXECINFO_H && HAVE_POPEN && defined(ADDR2LINE)
119 # include "src/xbt/backtrace_linux.c"
120 #else
121 # include "src/xbt/backtrace_dummy.c"
122 #endif
123
124 /** @brief shows an exception content and the associated stack if available */
125 void xbt_ex_display(xbt_ex_t * e)
126 {
127   char *thrower = NULL;
128   if (e->pid != xbt_getpid())
129     thrower = bprintf(" on process %d",e->pid);
130
131   fprintf(stderr, "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
132           "** %s\n"
133           "** Thrown by %s()%s\n",
134           xbt_binary_name, xbt_getpid(), xbt_ex_catname(e->category), e->value, e->msg,
135           e->procname, thrower ? thrower : " in this process");
136   XBT_CRITICAL("%s", e->msg);
137   xbt_free(thrower);
138
139   if (xbt_initialized==0 || smx_cleaned) {
140     fprintf(stderr, "Ouch. SimGrid is not initialized yet, or already closing. No backtrace available.\n");
141     return; /* Not started yet or already closing. Trying to generate a backtrace would probably fail */
142   }
143
144   if (!e->bt_strings)
145     xbt_ex_setup_backtrace(e);
146
147 #ifdef HAVE_BACKTRACE
148   if (e->used && e->bt_strings) {
149     /* We have everything to build neat backtraces */
150     int i;
151     int cutpath = 0;
152     TRY { // We don't want to have an exception while checking how to deal with the one we already have, do we?
153       cutpath = xbt_cfg_get_boolean("exception/cutpath");
154     } CATCH_ANONYMOUS { }
155
156     fprintf(stderr, "\n");
157     for (i = 0; i < e->used; i++) {
158         
159       if (cutpath) {
160         char* p = e->bt_strings[i];
161         xbt_str_rtrim(p, ":0123456789");
162         char* filename = strrchr(p, '/')+1;
163         char* end_of_message  = strrchr(p, ' ');
164
165         int length = strlen(p)-strlen(end_of_message);
166         char* dest = malloc(length);
167
168         memcpy(dest, &p[0], length);
169         dest[length] = 0;
170
171         fprintf(stderr, "%s %s\n", dest, filename);
172
173         free(dest);
174       }
175       else {
176         fprintf(stderr, "%s\n", e->bt_strings[i]);
177       }
178     }
179   } else
180 #endif
181     fprintf(stderr, "\n"
182         "**   In %s() at %s:%d\n"
183         "**   (no backtrace available)\n", e->func, e->file, e->line);
184 }
185
186 /* default __ex_terminate callback function */
187 void __xbt_ex_terminate_default(xbt_ex_t * e)
188 {
189   xbt_ex_display(e);
190   xbt_abort();
191 }
192
193 /* the externally visible API */
194 XBT_EXPORT_NO_IMPORT(xbt_running_ctx_fetcher_t) __xbt_running_ctx_fetch = &__xbt_ex_ctx_default;
195 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
196
197 void xbt_ex_free(xbt_ex_t e)
198 {
199   free(e.msg);
200
201   if (e.bt_strings) {
202     for (int i = 0; i < e.used; i++)
203       free(e.bt_strings[i]);
204     free(e.bt_strings);
205   }
206 }
207
208 /** \brief returns a short name for the given exception category */
209 const char *xbt_ex_catname(xbt_errcat_t cat)
210 {
211   switch (cat) {
212   case unknown_error:
213     return "unknown error";
214   case arg_error:
215     return "invalid argument";
216   case bound_error:
217     return "out of bounds";
218   case mismatch_error:
219     return "mismatch";
220   case not_found_error:
221     return "not found";
222   case system_error:
223     return "system error";
224   case network_error:
225     return "network error";
226   case timeout_error:
227     return "timeout";
228   case cancel_error:
229     return "action canceled";
230   case thread_error:
231     return "thread error";
232   case host_error:
233     return "host failed";
234   case tracing_error:
235     return "tracing error";
236   case io_error:
237     return "io error";
238   case vm_error:
239     return "vm error";
240   }
241   return "INVALID ERROR";
242 }
243
244 #ifdef SIMGRID_TEST
245 #include <stdio.h>
246 #include "xbt/ex.h"
247
248 XBT_TEST_SUITE("xbt_ex", "Exception Handling");
249
250 XBT_TEST_UNIT("controlflow", test_controlflow, "basic nested control flow")
251 {
252   xbt_ex_t ex;
253   volatile int n = 1;
254
255   xbt_test_add("basic nested control flow");
256
257   TRY {
258     if (n != 1)
259       xbt_test_fail("M1: n=%d (!= 1)", n);
260     n++;
261     TRY {
262       if (n != 2)
263         xbt_test_fail("M2: n=%d (!= 2)", n);
264       n++;
265       THROWF(unknown_error, 0, "something");
266     } CATCH(ex) {
267       if (n != 3)
268         xbt_test_fail("M3: n=%d (!= 3)", n);
269       n++;
270       xbt_ex_free(ex);
271     }
272     n++;
273     TRY {
274       if (n != 5)
275         xbt_test_fail("M2: n=%d (!= 5)", n);
276       n++;
277       THROWF(unknown_error, 0, "something");
278     } CATCH_ANONYMOUS {
279       if (n != 6)
280         xbt_test_fail("M3: n=%d (!= 6)", n);
281       n++;
282       RETHROW;
283       n++;
284     }
285     xbt_test_fail("MX: n=%d (shouldn't reach this point)", n);
286   } CATCH(ex) {
287     if (n != 7)
288       xbt_test_fail("M4: n=%d (!= 7)", n);
289     n++;
290     xbt_ex_free(ex);
291   }
292   if (n != 8)
293     xbt_test_fail("M5: n=%d (!= 8)", n);
294 }
295
296 XBT_TEST_UNIT("value", test_value, "exception value passing")
297 {
298   xbt_ex_t ex;
299
300   TRY {
301     THROWF(unknown_error, 2, "toto");
302   } CATCH(ex) {
303     xbt_test_add("exception value passing");
304     if (ex.category != unknown_error)
305       xbt_test_fail("category=%d (!= 1)", (int)ex.category);
306     if (ex.value != 2)
307       xbt_test_fail("value=%d (!= 2)", ex.value);
308     if (strcmp(ex.msg, "toto"))
309       xbt_test_fail("message=%s (!= toto)", ex.msg);
310     xbt_ex_free(ex);
311   }
312 }
313
314 XBT_TEST_UNIT("variables", test_variables, "variable value preservation")
315 {
316   xbt_ex_t ex;
317   int r1;
318   int XBT_ATTRIB_UNUSED r2;
319   int v1;
320   volatile int v2;
321
322   r1 = r2 = v1 = v2 = 1234;
323   TRY {
324     r2 = 5678;
325     v2 = 5678;
326     THROWF(unknown_error, 0, "toto");
327   } CATCH(ex) {
328     xbt_test_add("variable preservation");
329     if (r1 != 1234)
330       xbt_test_fail("r1=%d (!= 1234)", r1);
331     if (v1 != 1234)
332       xbt_test_fail("v1=%d (!= 1234)", v1);
333     /* r2 is allowed to be destroyed because not volatile */
334     if (v2 != 5678)
335       xbt_test_fail("v2=%d (!= 5678)", v2);
336     xbt_ex_free(ex);
337   }
338 }
339
340 XBT_TEST_UNIT("cleanup", test_cleanup, "cleanup handling")
341 {
342   xbt_ex_t ex;
343   volatile int v1;
344   int c;
345
346   xbt_test_add("cleanup handling");
347
348   v1 = 1234;
349   c = 0;
350   TRY {
351     v1 = 5678;
352     THROWF(1, 2, "blah");
353   }
354   TRY_CLEANUP {
355     if (v1 != 5678)
356       xbt_test_fail("v1 = %d (!= 5678)", v1);
357     c = 1;
358   } CATCH(ex) {
359     if (v1 != 5678)
360       xbt_test_fail("v1 = %d (!= 5678)", v1);
361     if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg, "blah")))
362       xbt_test_fail("unexpected exception contents");
363     xbt_ex_free(ex);
364   }
365   if (!c)
366     xbt_test_fail("xbt_ex_free not executed");
367 }
368
369 /*
370  * The following is the example included in the documentation. It's a good
371  * idea to check its syntax even if we don't try to run it.
372  * And actually, it allows to put comments in the code despite doxygen.
373  */
374 static char *mallocex(int size)
375 {
376   return NULL;
377 }
378
379 #define SMALLAMOUNT 10
380 #define TOOBIG 100000000
381
382 typedef struct {
383   char *first;
384 } global_context_t;
385
386 static void good_example(void)
387 {
388   global_context_t *global_context = xbt_malloc(sizeof(global_context_t));
389
390   /* GOOD_EXAMPLE */
391   {                             /*01 */
392     char *volatile /*03 */ cp1 = NULL /*02 */ ;
393     char *volatile /*03 */ cp2 = NULL /*02 */ ;
394     char *volatile /*03 */ cp3 = NULL /*02 */ ;
395     TRY {
396       cp1 = mallocex(SMALLAMOUNT);
397       global_context->first = cp1;
398       cp1 = NULL /*05 give away */ ;
399       cp2 = mallocex(TOOBIG);
400       cp3 = mallocex(SMALLAMOUNT);
401       strcpy(cp1, "foo");
402       strcpy(cp2, "bar");
403     }
404     TRY_CLEANUP {               /*04 */
405       printf("cp3=%s", cp3 == NULL /*02 */ ? "" : cp3);
406       free(cp3);
407       free(cp2);
408       /*05 cp1 was given away */
409     }
410     CATCH_ANONYMOUS {
411       /*05 global context untouched */
412       RETHROW;
413     }
414   }
415   /* end_of_good_example */
416 }
417 #endif                          /* SIMGRID_TEST */