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")) ||
301 !strncmp("xbt_thread_context_wrapper",line_func,strlen("xbt_thread_context_wrapper"))) {
303 for (j=i+1; j<e->used; j++)
307 if (!strncmp("xbt_thread_context_wrapper",line_func,strlen("xbt_thread_context_wrapper"))) {
309 e->bt_strings[i] = bprintf("** (in a separate thread)");
322 /** @brief shows an exception content and the associated stack if available */
323 void xbt_ex_display(xbt_ex_t *e) {
327 thrower = bprintf(" on host %s(%d)",e->host,e->pid);
330 "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
332 "** Thrown by %s()%s\n",
333 gras_os_myname(),(*xbt_getpid)(),
334 xbt_ex_catname(e->category), e->value, e->msg,
335 e->procname,thrower?thrower:" in this process");
336 CRITICAL1("%s",e->msg);
341 if (!e->remote && !e->bt_strings)
342 xbt_ex_setup_backtrace(e);
344 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
345 /* We have everything to build neat backtraces */
349 fprintf(stderr,"\n");
350 for (i=0; i<e->used; i++)
351 fprintf(stderr,"%s\n",e->bt_strings[i]);
355 fprintf(stderr," at %s:%d:%s (no backtrace available on that arch)\n",
356 e->file,e->line,e->func);
362 /* default __ex_terminate callback function */
363 void __xbt_ex_terminate_default(xbt_ex_t *e) {
369 /* the externally visible API */
370 XBT_EXPORT_NO_IMPORT(ex_ctx_cb_t) __xbt_ex_ctx = &__xbt_ex_ctx_default;
371 XBT_EXPORT_NO_IMPORT(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
374 void xbt_ex_free(xbt_ex_t e) {
377 if (e.msg) free(e.msg);
386 for (i=0; i<e.used; i++)
387 free((char*)e.bt_strings[i]);
388 free((char **)e.bt_strings);
390 /* memset(e,0,sizeof(xbt_ex_t)); */
393 /** \brief returns a short name for the given exception category */
394 const char * xbt_ex_catname(xbt_errcat_t cat) {
396 case unknown_error: return "unknown_err";
397 case arg_error: return "invalid_arg";
398 case mismatch_error: return "mismatch";
399 case not_found_error: return "not found";
400 case system_error: return "system_err";
401 case network_error: return "network_err";
402 case timeout_error: return "timeout";
403 case thread_error: return "thread_err";
404 default: return "INVALID_ERR";
408 #ifndef HAVE_EXECINFO_H
409 /* dummy implementation. We won't use the result, but ex.h needs it to be defined */
410 int backtrace (void **__array, int __size) {
420 XBT_TEST_SUITE("xbt_ex","Exception Handling");
422 XBT_TEST_UNIT("controlflow",test_controlflow, "basic nested control flow") {
426 xbt_test_add0("basic nested control flow");
430 xbt_test_fail1("M1: n=%d (!= 1)", n);
434 xbt_test_fail1("M2: n=%d (!= 2)", n);
436 THROW0(unknown_error,0,"something");
439 xbt_test_fail1("M3: n=%d (!= 3)", n);
446 xbt_test_fail1("M2: n=%d (!= 5)", n);
448 THROW0(unknown_error,0,"something");
451 xbt_test_fail1("M3: n=%d (!= 6)", n);
456 xbt_test_fail1("MX: n=%d (shouldn't reach this point)", n);
460 xbt_test_fail1("M4: n=%d (!= 7)", n);
465 xbt_test_fail1("M5: n=%d (!= 8)", n);
468 XBT_TEST_UNIT("value",test_value,"exception value passing") {
472 THROW0(unknown_error, 2, "toto");
474 xbt_test_add0("exception value passing");
475 if (ex.category != unknown_error)
476 xbt_test_fail1("category=%d (!= 1)", ex.category);
478 xbt_test_fail1("value=%d (!= 2)", ex.value);
479 if (strcmp(ex.msg,"toto"))
480 xbt_test_fail1("message=%s (!= toto)", ex.msg);
485 XBT_TEST_UNIT("variables",test_variables,"variable value preservation") {
490 r1 = r2 = v1 = v2 = 1234;
494 THROW0(unknown_error, 0, "toto");
496 xbt_test_add0("variable preservation");
498 xbt_test_fail1("r1=%d (!= 1234)", r1);
500 xbt_test_fail1("v1=%d (!= 1234)", v1);
501 /* r2 is allowed to be destroyed because not volatile */
503 xbt_test_fail1("v2=%d (!= 5678)", v2);
508 XBT_TEST_UNIT("cleanup",test_cleanup,"cleanup handling") {
513 xbt_test_add0("cleanup handling");
519 THROW0(1, 2, "blah");
522 xbt_test_fail1("v1 = %d (!= 5678)", v1);
526 xbt_test_fail1("v1 = %d (!= 5678)", v1);
527 if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg,"blah")))
528 xbt_test_fail0("unexpected exception contents");
532 xbt_test_fail0("xbt_ex_free not executed");
537 * The following is the example included in the documentation. It's a good
538 * idea to check its syntax even if we don't try to run it.
539 * And actually, it allows to put comments in the code despite doxygen.
541 static char *mallocex(int size) {
544 #define SMALLAMOUNT 10
545 #define TOOBIG 100000000
547 #if 0 /* this contains syntax errors, actually */
548 static void bad_example(void) {
549 struct {char*first;} *globalcontext;
554 char *cp1, *cp2, *cp3;
556 cp1 = mallocex(SMALLAMOUNT);
557 globalcontext->first = cp1;
558 cp2 = mallocex(TOOBIG);
559 cp3 = mallocex(SMALLAMOUNT);
563 if (cp3 != NULL) free(cp3);
564 if (cp2 != NULL) free(cp2);
565 if (cp1 != NULL) free(cp1);
567 printf("cp3=%s", cp3);
570 /* end_of_bad_example */
573 typedef struct {char *first;} global_context_t;
575 static void good_example(void) {
576 global_context_t *global_context=malloc(sizeof(global_context_t));
581 char * volatile /*03*/ cp1 = NULL /*02*/;
582 char * volatile /*03*/ cp2 = NULL /*02*/;
583 char * volatile /*03*/ cp3 = NULL /*02*/;
585 cp1 = mallocex(SMALLAMOUNT);
586 global_context->first = cp1;
587 cp1 = NULL /*05 give away*/;
588 cp2 = mallocex(TOOBIG);
589 cp3 = mallocex(SMALLAMOUNT);
593 printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
598 /*05 cp1 was given away */
600 /*05 global context untouched */
604 /* end_of_good_example */
606 #endif /* SIMGRID_TEST */