Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Replace sprintf by snprintf.
[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 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex, xbt, "Exception mechanism");
60
61 XBT_EXPORT_NO_IMPORT(const xbt_running_ctx_t) __xbt_ex_ctx_initializer = XBT_RUNNING_CTX_INITIALIZER;
62
63 /* default __ex_ctx callback function */
64 xbt_running_ctx_t *__xbt_ex_ctx_default(void)
65 {
66   /* Don't scream: this is a default which is never used (so, yes,
67      there is one setjump container by running entity).
68
69      This default gets overriden in xbt/xbt_os_thread.c so that it works in
70      real life and in simulation when using threads to implement the simulation
71      processes (ie, with pthreads and on windows).
72
73      It also gets overriden in xbt/context.c when using ucontexts (as well as
74      in Java for now, but after the java overhaul, it will get cleaned out)
75    */
76   static xbt_running_ctx_t ctx = XBT_RUNNING_CTX_INITIALIZER;
77
78   return &ctx;
79 }
80
81 /* Change raw libc symbols to file names and line numbers */
82 void xbt_ex_setup_backtrace(xbt_ex_t * e);
83
84 void xbt_backtrace_display(xbt_ex_t * e)
85 {
86   xbt_ex_setup_backtrace(e);
87
88 #ifdef HAVE_BACKTRACE
89   if (e->used == 0) {
90     fprintf(stderr, "(backtrace not set)\n");
91   } else {
92     fprintf(stderr, "Backtrace (displayed in process %s):\n", SIMIX_process_self_get_name());
93     for (int i = 1; i < e->used; i++)       /* no need to display "xbt_backtrace_display" */
94       fprintf(stderr, "---> %s\n", e->bt_strings[i] + 4);
95   }
96
97   /* don't fool xbt_ex_free with uninitialized msg field */
98   e->msg = NULL;
99   xbt_ex_free(*e);
100 #else
101   XBT_ERROR("No backtrace on this arch");
102 #endif
103 }
104
105 /** \brief show the backtrace of the current point (lovely while debuging) */
106 void xbt_backtrace_display_current(void)
107 {
108   xbt_ex_t e;
109   xbt_backtrace_current(&e);
110   xbt_backtrace_display(&e);
111 }
112
113 #if HAVE_BACKTRACE && HAVE_EXECINFO_H && HAVE_POPEN && defined(ADDR2LINE)
114 # include "src/xbt/backtrace_linux.c"
115 #else
116 # include "src/xbt/backtrace_dummy.c"
117 #endif
118
119 /** @brief shows an exception content and the associated stack if available */
120 void xbt_ex_display(xbt_ex_t * e)
121 {
122   char *thrower = NULL;
123   if (e->pid != xbt_getpid())
124     thrower = bprintf(" on process %d",e->pid);
125
126   fprintf(stderr, "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
127           "** %s\n"
128           "** Thrown by %s()%s\n",
129           xbt_binary_name, xbt_getpid(), xbt_ex_catname(e->category), e->value, e->msg,
130           e->procname, thrower ? thrower : " in this process");
131   XBT_CRITICAL("%s", e->msg);
132   xbt_free(thrower);
133
134   if (xbt_initialized==0 || smx_cleaned) {
135     fprintf(stderr, "Ouch. SimGrid is not initialized yet, or already closing. No backtrace available.\n");
136     return; /* Not started yet or already closing. Trying to generate a backtrace would probably fail */
137   }
138
139   if (!e->bt_strings)
140     xbt_ex_setup_backtrace(e);
141
142 #ifdef HAVE_BACKTRACE
143   if (e->used && e->bt_strings) {
144     /* We have everything to build neat backtraces */
145     int i;
146     int cutpath = 0;
147     TRY { // We don't want to have an exception while checking how to deal with the one we already have, do we?
148       cutpath = xbt_cfg_get_boolean("exception/cutpath");
149     } CATCH_ANONYMOUS { }
150
151     fprintf(stderr, "\n");
152     for (i = 0; i < e->used; i++) {
153         
154       if (cutpath) {
155         char* p = e->bt_strings[i];
156         xbt_str_rtrim(p, ":0123456789");
157         char* filename = strrchr(p, '/')+1;
158         char* end_of_message  = strrchr(p, ' ');
159
160         int length = strlen(p)-strlen(end_of_message);
161         char* dest = malloc(length);
162
163         memcpy(dest, &p[0], length);
164         dest[length] = 0;
165
166         fprintf(stderr, "%s %s\n", dest, filename);
167
168         free(dest);
169       }
170       else {
171         fprintf(stderr, "%s\n", e->bt_strings[i]);
172       }
173     }
174   } else
175 #endif
176     fprintf(stderr, "\n"
177         "**   In %s() at %s:%d\n"
178         "**   (no backtrace available)\n", e->func, e->file, e->line);
179 }
180
181 /* default __ex_terminate callback function */
182 void __xbt_ex_terminate_default(xbt_ex_t * e)
183 {
184   xbt_ex_display(e);
185   xbt_abort();
186 }
187
188 /* the externally visible API */
189 XBT_EXPORT_NO_IMPORT(xbt_running_ctx_fetcher_t) __xbt_running_ctx_fetch = &__xbt_ex_ctx_default;
190 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
191
192 void xbt_ex_free(xbt_ex_t e)
193 {
194   free(e.msg);
195
196   if (e.bt_strings) {
197     for (int i = 0; i < e.used; i++)
198       free(e.bt_strings[i]);
199     free(e.bt_strings);
200   }
201 }
202
203 /** \brief returns a short name for the given exception category */
204 const char *xbt_ex_catname(xbt_errcat_t cat)
205 {
206   switch (cat) {
207   case unknown_error:
208     return "unknown error";
209   case arg_error:
210     return "invalid argument";
211   case bound_error:
212     return "out of bounds";
213   case mismatch_error:
214     return "mismatch";
215   case not_found_error:
216     return "not found";
217   case system_error:
218     return "system error";
219   case network_error:
220     return "network error";
221   case timeout_error:
222     return "timeout";
223   case cancel_error:
224     return "action canceled";
225   case thread_error:
226     return "thread error";
227   case host_error:
228     return "host failed";
229   case tracing_error:
230     return "tracing error";
231   case io_error:
232     return "io error";
233   case vm_error:
234     return "vm error";
235   }
236   return "INVALID ERROR";
237 }
238
239 #ifdef SIMGRID_TEST
240 #include <stdio.h>
241 #include "xbt/ex.h"
242
243 XBT_TEST_SUITE("xbt_ex", "Exception Handling");
244
245 XBT_TEST_UNIT("controlflow", test_controlflow, "basic nested control flow")
246 {
247   xbt_ex_t ex;
248   volatile int n = 1;
249
250   xbt_test_add("basic nested control flow");
251
252   TRY {
253     if (n != 1)
254       xbt_test_fail("M1: n=%d (!= 1)", n);
255     n++;
256     TRY {
257       if (n != 2)
258         xbt_test_fail("M2: n=%d (!= 2)", n);
259       n++;
260       THROWF(unknown_error, 0, "something");
261     } CATCH(ex) {
262       if (n != 3)
263         xbt_test_fail("M3: n=%d (!= 3)", n);
264       n++;
265       xbt_ex_free(ex);
266     }
267     n++;
268     TRY {
269       if (n != 5)
270         xbt_test_fail("M2: n=%d (!= 5)", n);
271       n++;
272       THROWF(unknown_error, 0, "something");
273     } CATCH_ANONYMOUS {
274       if (n != 6)
275         xbt_test_fail("M3: n=%d (!= 6)", n);
276       n++;
277       RETHROW;
278       n++;
279     }
280     xbt_test_fail("MX: n=%d (shouldn't reach this point)", n);
281   } CATCH(ex) {
282     if (n != 7)
283       xbt_test_fail("M4: n=%d (!= 7)", n);
284     n++;
285     xbt_ex_free(ex);
286   }
287   if (n != 8)
288     xbt_test_fail("M5: n=%d (!= 8)", n);
289 }
290
291 XBT_TEST_UNIT("value", test_value, "exception value passing")
292 {
293   xbt_ex_t ex;
294
295   TRY {
296     THROWF(unknown_error, 2, "toto");
297   } CATCH(ex) {
298     xbt_test_add("exception value passing");
299     if (ex.category != unknown_error)
300       xbt_test_fail("category=%d (!= 1)", (int)ex.category);
301     if (ex.value != 2)
302       xbt_test_fail("value=%d (!= 2)", ex.value);
303     if (strcmp(ex.msg, "toto"))
304       xbt_test_fail("message=%s (!= toto)", ex.msg);
305     xbt_ex_free(ex);
306   }
307 }
308
309 XBT_TEST_UNIT("variables", test_variables, "variable value preservation")
310 {
311   xbt_ex_t ex;
312   int r1;
313   int XBT_ATTRIB_UNUSED r2;
314   int v1;
315   volatile int v2;
316
317   r1 = r2 = v1 = v2 = 1234;
318   TRY {
319     r2 = 5678;
320     v2 = 5678;
321     THROWF(unknown_error, 0, "toto");
322   } CATCH(ex) {
323     xbt_test_add("variable preservation");
324     if (r1 != 1234)
325       xbt_test_fail("r1=%d (!= 1234)", r1);
326     if (v1 != 1234)
327       xbt_test_fail("v1=%d (!= 1234)", v1);
328     /* r2 is allowed to be destroyed because not volatile */
329     if (v2 != 5678)
330       xbt_test_fail("v2=%d (!= 5678)", v2);
331     xbt_ex_free(ex);
332   }
333 }
334
335 XBT_TEST_UNIT("cleanup", test_cleanup, "cleanup handling")
336 {
337   xbt_ex_t ex;
338   volatile int v1;
339   int c;
340
341   xbt_test_add("cleanup handling");
342
343   v1 = 1234;
344   c = 0;
345   TRY {
346     v1 = 5678;
347     THROWF(1, 2, "blah");
348   }
349   TRY_CLEANUP {
350     if (v1 != 5678)
351       xbt_test_fail("v1 = %d (!= 5678)", v1);
352     c = 1;
353   } CATCH(ex) {
354     if (v1 != 5678)
355       xbt_test_fail("v1 = %d (!= 5678)", v1);
356     if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg, "blah")))
357       xbt_test_fail("unexpected exception contents");
358     xbt_ex_free(ex);
359   }
360   if (!c)
361     xbt_test_fail("xbt_ex_free not executed");
362 }
363
364 /*
365  * The following is the example included in the documentation. It's a good
366  * idea to check its syntax even if we don't try to run it.
367  * And actually, it allows to put comments in the code despite doxygen.
368  */
369 static char *mallocex(int size)
370 {
371   return NULL;
372 }
373
374 #define SMALLAMOUNT 10
375 #define TOOBIG 100000000
376
377 typedef struct {
378   char *first;
379 } global_context_t;
380
381 static void good_example(void)
382 {
383   global_context_t *global_context = xbt_malloc(sizeof(global_context_t));
384
385   /* GOOD_EXAMPLE */
386   {                             /*01 */
387     char *volatile /*03 */ cp1 = NULL /*02 */ ;
388     char *volatile /*03 */ cp2 = NULL /*02 */ ;
389     char *volatile /*03 */ cp3 = NULL /*02 */ ;
390     TRY {
391       cp1 = mallocex(SMALLAMOUNT);
392       global_context->first = cp1;
393       cp1 = NULL /*05 give away */ ;
394       cp2 = mallocex(TOOBIG);
395       cp3 = mallocex(SMALLAMOUNT);
396       strcpy(cp1, "foo");
397       strcpy(cp2, "bar");
398     }
399     TRY_CLEANUP {               /*04 */
400       printf("cp3=%s", cp3 == NULL /*02 */ ? "" : cp3);
401       free(cp3);
402       free(cp2);
403       /*05 cp1 was given away */
404     }
405     CATCH_ANONYMOUS {
406       /*05 global context untouched */
407       RETHROW;
408     }
409   }
410   /* end_of_good_example */
411 }
412 #endif                          /* SIMGRID_TEST */