3 /* ex - Exception Handling (modified to fit into SimGrid from OSSP version) */
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. */
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. */
17 #include "portable.h" /* execinfo when available */
19 #include "xbt/module.h" /* xbt_binary_name */
20 #include "xbt/synchro.h" /* xbt_thread_self */
22 #include "gras/Virtu/virtu_interface.h" /* gras_os_myname */
23 #include "xbt/ex_interface.h"
25 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex,xbt,"Exception mecanism");
27 /* default __ex_ctx callback function */
28 ex_ctx_t *__xbt_ex_ctx_default(void) {
29 /* Don't scream: this is a default which is never used (so, yes,
30 there is one setjump container by running entity).
32 This default gets overriden in xbt/xbt_os_thread.c so that it works in
33 real life and in simulation when using threads to implement the simulation
34 processes (ie, with pthreads and on windows).
36 It also gets overriden in xbt/context.c when using ucontextes (as well as
37 in Java for now, but after the java overhaul, it will get cleaned out)
39 static ex_ctx_t ctx = XBT_CTX_INITIALIZER;
44 /* Change raw libc symbols to file names and line numbers */
45 void xbt_ex_setup_backtrace(xbt_ex_t *e);
47 void xbt_backtrace_current(xbt_ex_t *e) {
48 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
49 e->used = backtrace((void**)e->bt,XBT_BACKTRACE_SIZE);
51 xbt_ex_setup_backtrace(e);
55 void xbt_backtrace_display(xbt_ex_t *e) {
56 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
60 fprintf(stderr,"(backtrace not set)\n");
62 fprintf(stderr,"Backtrace (displayed in thread %p):\n",
63 (void*)xbt_thread_self());
64 for (i=1; i<e->used; i++) /* no need to display "xbt_display_backtrace" */
65 fprintf(stderr,"---> %s\n",e->bt_strings[i] +4);
68 /* don't fool xbt_ex_free with uninitialized msg field */
73 ERROR0("No backtrace on this arch");
77 /** \brief show the backtrace of the current point (lovely while debuging) */
78 void xbt_backtrace_display_current(void) {
80 xbt_backtrace_current(&e);
81 xbt_backtrace_display(&e);
85 extern char **environ; /* the environment, as specified by the opengroup */
88 void xbt_ex_setup_backtrace(xbt_ex_t *e) {
89 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
91 /* to get the backtrace from the libc */
92 char **backtrace = backtrace_symbols (e->bt, e->used);
94 /* To build the commandline of addr2line */
97 /* to extract the addresses from the backtrace */
98 char **addrs=xbt_new(char*,e->used);
101 /* To read the output of addr2line */
103 char line_func[1024],line_pos[1024];
105 /* size (in char) of pointers on this arch */
108 /* To search for the right executable path when not trivial */
109 struct stat stat_buf;
110 char *binary_name = NULL;
112 /* Some arches only have stubs of backtrace, no implementation (hppa comes to mind) */
116 /* build the commandline */
117 if (stat(xbt_binary_name,&stat_buf)) {
118 /* Damn. binary not in current dir. We'll have to dig the PATH to find it */
120 for (i=0; environ[i]; i++) {
121 if (!strncmp("PATH=",environ[i], 5)) {
122 xbt_dynar_t path=xbt_str_split(environ[i] + 5, ":");
125 xbt_dynar_foreach(path, cpt, data) {
128 binary_name = bprintf("%s/%s",data,xbt_binary_name);
129 if (!stat(binary_name,&stat_buf)) {
131 DEBUG1("Looked in the PATH for the binary. Found %s",binary_name);
132 xbt_dynar_free(&path);
136 if (stat(binary_name,&stat_buf)) {
139 e->bt_strings = xbt_new(char*,1);
140 e->bt_strings[0] = bprintf("(binary '%s' not found the path)",xbt_binary_name);
143 xbt_dynar_free(&path);
148 binary_name = xbt_strdup(xbt_binary_name);
150 cmd = curr = xbt_new(char,strlen(ADDR2LINE)+25+strlen(binary_name)+32*e->used);
152 curr += sprintf(curr,"%s -f -e %s ",ADDR2LINE,binary_name);
155 for (i=0; i<e->used;i++) {
156 /* retrieve this address */
157 DEBUG2("Retrieving address number %d from '%s'", i, backtrace[i]);
158 snprintf(buff,256,"%s",strchr(backtrace[i],'[')+1);
161 if (strcmp(buff,"(nil)"))
162 addrs[i]=bprintf("%s", buff);
164 addrs[i]=bprintf("0x0");
165 DEBUG3("Set up a new address: %d, '%s'(%p)", i, addrs[i], addrs[i]);
167 /* Add it to the command line args */
168 curr+=sprintf(curr,"%s ",addrs[i]);
170 addr_len = strlen(addrs[0]);
172 /* parse the output and build a new backtrace */
173 e->bt_strings = xbt_new(char*,e->used);
175 VERB1("Fire a first command: '%s'", cmd);
176 pipe = popen(cmd, "r");
178 CRITICAL0("Cannot fork addr2line to display the backtrace");
182 for (i=0; i<e->used; i++) {
183 DEBUG2("Looking for symbol %d, addr = '%s'", i, addrs[i]);
184 fgets(line_func,1024,pipe);
185 line_func[strlen(line_func)-1]='\0';
186 fgets(line_pos,1024,pipe);
187 line_pos[strlen(line_pos)-1]='\0';
189 if (strcmp("??",line_func)) {
190 DEBUG2("Found static symbol %s() at %s", line_func, line_pos);
191 e->bt_strings[i] = bprintf("** In %s() at %s", line_func,line_pos);
193 /* Damn. The symbol is in a dynamic library. Let's get wild */
198 long int addr,offset=0;
205 /* let's look for the offset of this library in our addressing space */
206 maps_name=bprintf("/proc/%d/maps",(int)getpid());
207 maps=fopen(maps_name,"r");
209 sscanf(addrs[i],"%lx",&addr);
210 sprintf(maps_buff,"%#lx",addr);
212 if (strcmp(addrs[i],maps_buff)) {
213 CRITICAL2("Cannot parse backtrace address '%s' (addr=%#lx)",
216 DEBUG2("addr=%s (as string) =%#lx (as number)",addrs[i],addr);
219 long int first, last;
220 if (fgets(maps_buff,512,maps) == NULL)
223 maps_buff[strlen(maps_buff) -1]='\0';
224 DEBUG1("map line: %s", maps_buff);
226 sscanf(maps_buff,"%lx",&first);
227 p=strchr(maps_buff,'-')+1;
228 sscanf(p,"%lx",&last);
229 if (first < addr && addr < last) {
234 DEBUG3("%#lx in [%#lx-%#lx]", addr, first,last);
235 DEBUG0("Symbol found, map lines not further displayed (even if looking for next ones)");
242 VERB0("Problem while reading the maps file. Following backtrace will be mangled.");
243 DEBUG1("No dynamic. Static symbol: %s", backtrace[i]);
244 e->bt_strings[i] = bprintf("** In ?? (%s)", backtrace[i]);
248 /* Ok, Found the offset of the maps line containing the searched symbol.
249 We now need to substract this from the address we got from backtrace.
253 addrs[i] = bprintf("0x%0*lx",addr_len-2,addr-offset);
254 DEBUG2("offset=%#lx new addr=%s",offset,addrs[i]);
256 /* Got it. We have our new address. Let's get the library path and we
258 p = xbt_strdup(backtrace[i]);
260 /* library path not displayed in the map file either... */
262 sprintf(line_func,"??");
269 /* Here we go, fire an addr2line up */
270 subcmd = bprintf("%s -f -e %s %s",ADDR2LINE,p, addrs[i]);
272 VERB1("Fire a new command: '%s'",subcmd);
273 subpipe = popen(subcmd,"r");
275 CRITICAL0("Cannot fork addr2line to display the backtrace");
278 fgets(line_func,1024,subpipe);
279 line_func[strlen(line_func)-1]='\0';
280 fgets(line_pos,1024,subpipe);
281 line_pos[strlen(line_pos)-1]='\0';
286 /* check whether the trick worked */
287 if (strcmp("??",line_func)) {
288 DEBUG2("Found dynamic symbol %s() at %s", line_func, line_pos);
289 e->bt_strings[i] = bprintf("** In %s() at %s", line_func,line_pos);
291 /* damn, nothing to do here. Let's print the raw address */
292 DEBUG1("Dynamic symbol not found. Raw address = %s", backtrace[i]);
293 e->bt_strings[i] = bprintf("** In ?? at %s", backtrace[i]);
299 /* Mask the bottom of the stack */
300 if (!strncmp("main",line_func,strlen("main"))) {
302 for (j=i+1; j<e->used; j++)
307 if (!strncmp("__context_wrapper",line_func,strlen("__context_wrapper"))) {
309 for (j=i+1; j<e->used; j++)
323 /** @brief shows an exception content and the associated stack if available */
324 void xbt_ex_display(xbt_ex_t *e) {
328 thrower = bprintf(" on host %s(%d)",e->host,e->pid);
331 "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
333 "** Thrown by %s()%s\n",
334 gras_os_myname(),(*xbt_getpid)(),
335 xbt_ex_catname(e->category), e->value, e->msg,
336 e->procname,thrower?thrower:" in this process");
337 CRITICAL1("%s",e->msg);
342 if (!e->remote && !e->bt_strings)
343 xbt_ex_setup_backtrace(e);
345 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
346 /* We have everything to build neat backtraces */
350 fprintf(stderr,"\n");
351 for (i=0; i<e->used; i++)
352 fprintf(stderr,"%s\n",e->bt_strings[i]);
356 fprintf(stderr," at %s:%d:%s (no backtrace available on that arch)\n",
357 e->file,e->line,e->func);
363 /* default __ex_terminate callback function */
364 void __xbt_ex_terminate_default(xbt_ex_t *e) {
370 /* the externally visible API */
371 XBT_EXPORT_NO_IMPORT(ex_ctx_cb_t) __xbt_ex_ctx = &__xbt_ex_ctx_default;
372 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
375 void xbt_ex_free(xbt_ex_t e) {
378 if (e.msg) free(e.msg);
387 for (i=0; i<e.used; i++)
388 free((char*)e.bt_strings[i]);
389 free((char **)e.bt_strings);
391 /* memset(e,0,sizeof(xbt_ex_t)); */
394 /** \brief returns a short name for the given exception category */
395 const char * xbt_ex_catname(xbt_errcat_t cat) {
397 case unknown_error: return "unknown_err";
398 case arg_error: return "invalid_arg";
399 case mismatch_error: return "mismatch";
400 case not_found_error: return "not found";
401 case system_error: return "system_err";
402 case network_error: return "network_err";
403 case timeout_error: return "timeout";
404 case thread_error: return "thread_err";
405 default: return "INVALID_ERR";
409 #ifndef HAVE_EXECINFO_H
410 /* dummy implementation. We won't use the result, but ex.h needs it to be defined */
411 int backtrace (void **__array, int __size) {
421 XBT_TEST_SUITE("xbt_ex","Exception Handling");
423 XBT_TEST_UNIT("controlflow",test_controlflow, "basic nested control flow") {
427 xbt_test_add0("basic nested control flow");
431 xbt_test_fail1("M1: n=%d (!= 1)", n);
435 xbt_test_fail1("M2: n=%d (!= 2)", n);
437 THROW0(unknown_error,0,"something");
440 xbt_test_fail1("M3: n=%d (!= 3)", n);
447 xbt_test_fail1("M2: n=%d (!= 5)", n);
449 THROW0(unknown_error,0,"something");
452 xbt_test_fail1("M3: n=%d (!= 6)", n);
457 xbt_test_fail1("MX: n=%d (shouldn't reach this point)", n);
461 xbt_test_fail1("M4: n=%d (!= 7)", n);
466 xbt_test_fail1("M5: n=%d (!= 8)", n);
469 XBT_TEST_UNIT("value",test_value,"exception value passing") {
473 THROW0(unknown_error, 2, "toto");
475 xbt_test_add0("exception value passing");
476 if (ex.category != unknown_error)
477 xbt_test_fail1("category=%d (!= 1)", ex.category);
479 xbt_test_fail1("value=%d (!= 2)", ex.value);
480 if (strcmp(ex.msg,"toto"))
481 xbt_test_fail1("message=%s (!= toto)", ex.msg);
486 XBT_TEST_UNIT("variables",test_variables,"variable value preservation") {
491 r1 = r2 = v1 = v2 = 1234;
495 THROW0(unknown_error, 0, "toto");
497 xbt_test_add0("variable preservation");
499 xbt_test_fail1("r1=%d (!= 1234)", r1);
501 xbt_test_fail1("v1=%d (!= 1234)", v1);
502 /* r2 is allowed to be destroyed because not volatile */
504 xbt_test_fail1("v2=%d (!= 5678)", v2);
509 XBT_TEST_UNIT("cleanup",test_cleanup,"cleanup handling") {
514 xbt_test_add0("cleanup handling");
520 THROW0(1, 2, "blah");
523 xbt_test_fail1("v1 = %d (!= 5678)", v1);
527 xbt_test_fail1("v1 = %d (!= 5678)", v1);
528 if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg,"blah")))
529 xbt_test_fail0("unexpected exception contents");
533 xbt_test_fail0("xbt_ex_free not executed");
538 * The following is the example included in the documentation. It's a good
539 * idea to check its syntax even if we don't try to run it.
540 * And actually, it allows to put comments in the code despite doxygen.
542 static char *mallocex(int size) {
545 #define SMALLAMOUNT 10
546 #define TOOBIG 100000000
548 #if 0 /* this contains syntax errors, actually */
549 static void bad_example(void) {
550 struct {char*first;} *globalcontext;
555 char *cp1, *cp2, *cp3;
557 cp1 = mallocex(SMALLAMOUNT);
558 globalcontext->first = cp1;
559 cp2 = mallocex(TOOBIG);
560 cp3 = mallocex(SMALLAMOUNT);
564 if (cp3 != NULL) free(cp3);
565 if (cp2 != NULL) free(cp2);
566 if (cp1 != NULL) free(cp1);
568 printf("cp3=%s", cp3);
571 /* end_of_bad_example */
574 typedef struct {char *first;} global_context_t;
576 static void good_example(void) {
577 global_context_t *global_context=malloc(sizeof(global_context_t));
582 char * volatile /*03*/ cp1 = NULL /*02*/;
583 char * volatile /*03*/ cp2 = NULL /*02*/;
584 char * volatile /*03*/ cp3 = NULL /*02*/;
586 cp1 = mallocex(SMALLAMOUNT);
587 global_context->first = cp1;
588 cp1 = NULL /*05 give away*/;
589 cp2 = mallocex(TOOBIG);
590 cp3 = mallocex(SMALLAMOUNT);
594 printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
599 /*05 cp1 was given away */
601 /*05 global context untouched */
605 /* end_of_good_example */
607 #endif /* SIMGRID_TEST */