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 */
20 #include "xbt/module.h" /* xbt_binary_name */
21 #include "xbt/synchro.h" /* xbt_thread_self */
23 #include "gras/Virtu/virtu_interface.h" /* gras_os_myname */
24 #include "xbt/ex_interface.h"
26 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex,xbt,"Exception mecanism");
28 /* default __ex_ctx callback function */
29 ex_ctx_t *__xbt_ex_ctx_default(void) {
30 /* Don't scream: this is a default which is never used (so, yes,
31 there is one setjump container by running entity).
33 This default gets overriden in xbt/xbt_os_thread.c so that it works in
34 real life and in simulation when using threads to implement the simulation
35 processes (ie, with pthreads and on windows).
37 It also gets overriden in xbt/context.c when using ucontextes (as well as
38 in Java for now, but after the java overhaul, it will get cleaned out)
40 static ex_ctx_t ctx = XBT_CTX_INITIALIZER;
45 /* Change raw libc symbols to file names and line numbers */
46 void xbt_ex_setup_backtrace(xbt_ex_t *e);
48 void xbt_backtrace_current(xbt_ex_t *e) {
49 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
50 e->used = backtrace((void**)e->bt,XBT_BACKTRACE_SIZE);
52 xbt_ex_setup_backtrace(e);
56 void xbt_backtrace_display(xbt_ex_t *e) {
57 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
61 fprintf(stderr,"(backtrace not set)\n");
63 fprintf(stderr,"Backtrace (displayed in thread %p):\n",
64 (void*)xbt_thread_self());
65 for (i=1; i<e->used; i++) /* no need to display "xbt_display_backtrace" */
66 fprintf(stderr,"---> %s\n",e->bt_strings[i] +4);
69 /* don't fool xbt_ex_free with uninitialized msg field */
74 ERROR0("No backtrace on this arch");
78 /** \brief show the backtrace of the current point (lovely while debuging) */
79 void xbt_backtrace_display_current(void) {
81 xbt_backtrace_current(&e);
82 xbt_backtrace_display(&e);
86 extern char **environ; /* the environment, as specified by the opengroup */
89 void xbt_ex_setup_backtrace(xbt_ex_t *e) {
90 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
92 /* to get the backtrace from the libc */
93 char **backtrace = backtrace_symbols (e->bt, e->used);
95 /* To build the commandline of addr2line */
98 /* to extract the addresses from the backtrace */
99 char **addrs=xbt_new(char*,e->used);
102 /* To read the output of addr2line */
104 char line_func[1024],line_pos[1024];
106 /* size (in char) of pointers on this arch */
109 /* To search for the right executable path when not trivial */
110 struct stat stat_buf;
111 char *binary_name = NULL;
113 /* Some arches only have stubs of backtrace, no implementation (hppa comes to mind) */
117 /* build the commandline */
118 if (stat(xbt_binary_name,&stat_buf)) {
119 /* Damn. binary not in current dir. We'll have to dig the PATH to find it */
121 for (i=0; environ[i]; i++) {
122 if (!strncmp("PATH=",environ[i], 5)) {
123 xbt_dynar_t path=xbt_str_split(environ[i] + 5, ":");
126 xbt_dynar_foreach(path, cpt, data) {
129 binary_name = bprintf("%s/%s",data,xbt_binary_name);
130 if (!stat(binary_name,&stat_buf)) {
132 DEBUG1("Looked in the PATH for the binary. Found %s",binary_name);
133 xbt_dynar_free(&path);
137 if (stat(binary_name,&stat_buf)) {
140 e->bt_strings = xbt_new(char*,1);
141 e->bt_strings[0] = bprintf("(binary '%s' not found the path)",xbt_binary_name);
144 xbt_dynar_free(&path);
149 binary_name = xbt_strdup(xbt_binary_name);
151 cmd = curr = xbt_new(char,strlen(ADDR2LINE)+25+strlen(binary_name)+32*e->used);
153 curr += sprintf(curr,"%s -f -e %s ",ADDR2LINE,binary_name);
156 for (i=0; i<e->used;i++) {
157 /* retrieve this address */
158 DEBUG2("Retrieving address number %d from '%s'", i, backtrace[i]);
159 snprintf(buff,256,"%s",strchr(backtrace[i],'[')+1);
162 if (strcmp(buff,"(nil)"))
163 addrs[i]=bprintf("%s", buff);
165 addrs[i]=bprintf("0x0");
166 DEBUG3("Set up a new address: %d, '%s'(%p)", i, addrs[i], addrs[i]);
168 /* Add it to the command line args */
169 curr+=sprintf(curr,"%s ",addrs[i]);
171 addr_len = strlen(addrs[0]);
173 /* parse the output and build a new backtrace */
174 e->bt_strings = xbt_new(char*,e->used);
176 VERB1("Fire a first command: '%s'", cmd);
177 pipe = popen(cmd, "r");
179 CRITICAL0("Cannot fork addr2line to display the backtrace");
183 for (i=0; i<e->used; i++) {
184 DEBUG2("Looking for symbol %d, addr = '%s'", i, addrs[i]);
185 fgets(line_func,1024,pipe);
186 line_func[strlen(line_func)-1]='\0';
187 fgets(line_pos,1024,pipe);
188 line_pos[strlen(line_pos)-1]='\0';
190 if (strcmp("??",line_func)) {
191 DEBUG2("Found static symbol %s() at %s", line_func, line_pos);
192 e->bt_strings[i] = bprintf("** In %s() at %s", line_func,line_pos);
194 /* Damn. The symbol is in a dynamic library. Let's get wild */
199 long int addr,offset=0;
206 /* let's look for the offset of this library in our addressing space */
207 maps_name=bprintf("/proc/%d/maps",(int)getpid());
208 maps=fopen(maps_name,"r");
210 sscanf(addrs[i],"%lx",&addr);
211 sprintf(maps_buff,"%#lx",addr);
213 if (strcmp(addrs[i],maps_buff)) {
214 CRITICAL2("Cannot parse backtrace address '%s' (addr=%#lx)",
217 DEBUG2("addr=%s (as string) =%#lx (as number)",addrs[i],addr);
220 long int first, last;
221 if (fgets(maps_buff,512,maps) == NULL)
224 maps_buff[strlen(maps_buff) -1]='\0';
225 DEBUG1("map line: %s", maps_buff);
227 sscanf(maps_buff,"%lx",&first);
228 p=strchr(maps_buff,'-')+1;
229 sscanf(p,"%lx",&last);
230 if (first < addr && addr < last) {
235 DEBUG3("%#lx in [%#lx-%#lx]", addr, first,last);
236 DEBUG0("Symbol found, map lines not further displayed (even if looking for next ones)");
243 VERB0("Problem while reading the maps file. Following backtrace will be mangled.");
244 DEBUG1("No dynamic. Static symbol: %s", backtrace[i]);
245 e->bt_strings[i] = bprintf("** In ?? (%s)", backtrace[i]);
249 /* Ok, Found the offset of the maps line containing the searched symbol.
250 We now need to substract this from the address we got from backtrace.
254 addrs[i] = bprintf("0x%0*lx",addr_len-2,addr-offset);
255 DEBUG2("offset=%#lx new addr=%s",offset,addrs[i]);
257 /* Got it. We have our new address. Let's get the library path and we
259 p = xbt_strdup(backtrace[i]);
261 /* library path not displayed in the map file either... */
263 sprintf(line_func,"??");
270 /* Here we go, fire an addr2line up */
271 subcmd = bprintf("%s -f -e %s %s",ADDR2LINE,p, addrs[i]);
273 VERB1("Fire a new command: '%s'",subcmd);
274 subpipe = popen(subcmd,"r");
276 CRITICAL0("Cannot fork addr2line to display the backtrace");
279 fgets(line_func,1024,subpipe);
280 line_func[strlen(line_func)-1]='\0';
281 fgets(line_pos,1024,subpipe);
282 line_pos[strlen(line_pos)-1]='\0';
287 /* check whether the trick worked */
288 if (strcmp("??",line_func)) {
289 DEBUG2("Found dynamic symbol %s() at %s", line_func, line_pos);
290 e->bt_strings[i] = bprintf("** In %s() at %s", line_func,line_pos);
292 /* damn, nothing to do here. Let's print the raw address */
293 DEBUG1("Dynamic symbol not found. Raw address = %s", backtrace[i]);
294 e->bt_strings[i] = bprintf("** In ?? at %s", backtrace[i]);
300 /* Mask the bottom of the stack */
301 if (!strncmp("main",line_func,strlen("main")) ||
302 !strncmp("xbt_thread_context_wrapper",line_func,strlen("xbt_thread_context_wrapper"))) {
304 for (j=i+1; j<e->used; j++)
308 if (!strncmp("xbt_thread_context_wrapper",line_func,strlen("xbt_thread_context_wrapper"))) {
310 e->bt_strings[i] = bprintf("** (in a separate thread)");
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 */