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);
84 extern char **environ; /* the environment, as specified by the opengroup */
86 void xbt_ex_setup_backtrace(xbt_ex_t *e) {
87 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
89 /* to get the backtrace from the libc */
90 char **backtrace = backtrace_symbols (e->bt, e->used);
92 /* To build the commandline of addr2line */
95 /* to extract the addresses from the backtrace */
96 char **addrs=xbt_new(char*,e->used);
99 /* To read the output of addr2line */
101 char line_func[1024],line_pos[1024];
103 /* size (in char) of pointers on this arch */
106 /* To search for the right executable path when not trivial */
107 struct stat stat_buf;
108 char *binary_name = NULL;
110 /* Some arches only have stubs of backtrace, no implementation (hppa comes to mind) */
114 /* build the commandline */
115 if (stat(xbt_binary_name,&stat_buf)) {
116 /* Damn. binary not in current dir. We'll have to dig the PATH to find it */
118 for (i=0; environ[i]; i++) {
119 if (!strncmp("PATH=",environ[i], 5)) {
120 xbt_dynar_t path=xbt_str_split(environ[i] + 5, ":");
123 xbt_dynar_foreach(path, cpt, data) {
126 binary_name = bprintf("%s/%s",data,xbt_binary_name);
127 if (!stat(binary_name,&stat_buf)) {
129 DEBUG1("Looked in the PATH for the binary. Found %s",binary_name);
130 xbt_dynar_free(&path);
134 if (stat(binary_name,&stat_buf)) {
137 e->bt_strings = xbt_new(char*,1);
138 e->bt_strings[0] = bprintf("(binary '%s' not found the path)",xbt_binary_name);
141 xbt_dynar_free(&path);
146 binary_name = xbt_strdup(xbt_binary_name);
148 cmd = curr = xbt_new(char,strlen(ADDR2LINE)+25+strlen(binary_name)+32*e->used);
150 curr += sprintf(curr,"%s -f -e %s ",ADDR2LINE,binary_name);
153 for (i=0; i<e->used;i++) {
154 /* retrieve this address */
155 DEBUG2("Retrieving address number %d from '%s'", i, backtrace[i]);
156 snprintf(buff,256,"%s",strchr(backtrace[i],'[')+1);
159 if (strcmp(buff,"(nil)"))
160 addrs[i]=bprintf("%s", buff);
162 addrs[i]=bprintf("0x0");
163 DEBUG3("Set up a new address: %d, '%s'(%p)", i, addrs[i], addrs[i]);
165 /* Add it to the command line args */
166 curr+=sprintf(curr,"%s ",addrs[i]);
168 addr_len = strlen(addrs[0]);
170 /* parse the output and build a new backtrace */
171 e->bt_strings = xbt_new(char*,e->used);
173 VERB1("Fire a first command: '%s'", cmd);
174 pipe = popen(cmd, "r");
176 CRITICAL0("Cannot fork addr2line to display the backtrace");
180 for (i=0; i<e->used; i++) {
181 DEBUG2("Looking for symbol %d, addr = '%s'", i, addrs[i]);
182 fgets(line_func,1024,pipe);
183 line_func[strlen(line_func)-1]='\0';
184 fgets(line_pos,1024,pipe);
185 line_pos[strlen(line_pos)-1]='\0';
187 if (strcmp("??",line_func)) {
188 DEBUG2("Found static symbol %s() at %s", line_func, line_pos);
189 e->bt_strings[i] = bprintf("** In %s() at %s", line_func,line_pos);
191 /* Damn. The symbol is in a dynamic library. Let's get wild */
196 long int addr,offset=0;
203 /* let's look for the offset of this library in our addressing space */
204 maps_name=bprintf("/proc/%d/maps",(int)getpid());
205 maps=fopen(maps_name,"r");
207 sscanf(addrs[i],"%lx",&addr);
208 sprintf(maps_buff,"%#lx",addr);
210 if (strcmp(addrs[i],maps_buff)) {
211 CRITICAL2("Cannot parse backtrace address '%s' (addr=%#lx)",
214 DEBUG2("addr=%s (as string) =%#lx (as number)",addrs[i],addr);
217 long int first, last;
218 if (fgets(maps_buff,512,maps) == NULL)
221 maps_buff[strlen(maps_buff) -1]='\0';
222 DEBUG1("map line: %s", maps_buff);
224 sscanf(maps_buff,"%lx",&first);
225 p=strchr(maps_buff,'-')+1;
226 sscanf(p,"%lx",&last);
227 if (first < addr && addr < last) {
232 DEBUG3("%#lx in [%#lx-%#lx]", addr, first,last);
233 DEBUG0("Symbol found, map lines not further displayed (even if looking for next ones)");
240 VERB0("Problem while reading the maps file. Following backtrace will be mangled.");
241 DEBUG1("No dynamic. Static symbol: %s", backtrace[i]);
242 e->bt_strings[i] = bprintf("** In ?? (%s)", backtrace[i]);
246 /* Ok, Found the offset of the maps line containing the searched symbol.
247 We now need to substract this from the address we got from backtrace.
251 addrs[i] = bprintf("0x%0*lx",addr_len-2,addr-offset);
252 DEBUG2("offset=%#lx new addr=%s",offset,addrs[i]);
254 /* Got it. We have our new address. Let's get the library path and we
256 p = xbt_strdup(backtrace[i]);
258 /* library path not displayed in the map file either... */
260 sprintf(line_func,"??");
267 /* Here we go, fire an addr2line up */
268 subcmd = bprintf("%s -f -e %s %s",ADDR2LINE,p, addrs[i]);
270 VERB1("Fire a new command: '%s'",subcmd);
271 subpipe = popen(subcmd,"r");
273 CRITICAL0("Cannot fork addr2line to display the backtrace");
276 fgets(line_func,1024,subpipe);
277 line_func[strlen(line_func)-1]='\0';
278 fgets(line_pos,1024,subpipe);
279 line_pos[strlen(line_pos)-1]='\0';
284 /* check whether the trick worked */
285 if (strcmp("??",line_func)) {
286 DEBUG2("Found dynamic symbol %s() at %s", line_func, line_pos);
287 e->bt_strings[i] = bprintf("** In %s() at %s", line_func,line_pos);
289 /* damn, nothing to do here. Let's print the raw address */
290 DEBUG1("Dynamic symbol not found. Raw address = %s", backtrace[i]);
291 e->bt_strings[i] = bprintf("** In ?? at %s", backtrace[i]);
297 /* Mask the bottom of the stack */
298 if (!strncmp("main",line_func,strlen("main"))) {
300 for (j=i+1; j<e->used; j++)
305 if (!strncmp("__context_wrapper",line_func,strlen("__context_wrapper"))) {
307 for (j=i+1; j<e->used; j++)
321 /** @brief shows an exception content and the associated stack if available */
322 void xbt_ex_display(xbt_ex_t *e) {
326 bprintf(" on host %s(%d)",e->host,e->pid);
329 "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
331 "** Thrown by %s()%s\n",
332 gras_os_myname(),(*xbt_getpid)(),
333 xbt_ex_catname(e->category), e->value, e->msg,
334 e->procname,thrower?thrower:" in this process");
335 CRITICAL1("%s",e->msg);
340 if (!e->remote && !e->bt_strings)
341 xbt_ex_setup_backtrace(e);
343 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
344 /* We have everything to build neat backtraces */
348 fprintf(stderr,"\n");
349 for (i=0; i<e->used; i++)
350 fprintf(stderr,"%s\n",e->bt_strings[i]);
354 fprintf(stderr," at %s:%d:%s (no backtrace available on that arch)\n",
355 e->file,e->line,e->func);
361 /* default __ex_terminate callback function */
362 void __xbt_ex_terminate_default(xbt_ex_t *e) {
368 /* the externally visible API */
369 XBT_PUBLIC_DATA(ex_ctx_cb_t) __xbt_ex_ctx = &__xbt_ex_ctx_default;
370 XBT_PUBLIC_DATA(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
373 void xbt_ex_free(xbt_ex_t e) {
376 if (e.msg) free(e.msg);
385 for (i=0; i<e.used; i++)
386 free((char*)e.bt_strings[i]);
387 free((char **)e.bt_strings);
389 /* memset(e,0,sizeof(xbt_ex_t)); */
392 /** \brief returns a short name for the given exception category */
393 const char * xbt_ex_catname(xbt_errcat_t cat) {
395 case unknown_error: return "unknown_err";
396 case arg_error: return "invalid_arg";
397 case mismatch_error: return "mismatch";
398 case not_found_error: return "not found";
399 case system_error: return "system_err";
400 case network_error: return "network_err";
401 case timeout_error: return "timeout";
402 case thread_error: return "thread_err";
403 default: return "INVALID_ERR";
407 #ifndef HAVE_EXECINFO_H
408 /* dummy implementation. We won't use the result, but ex.h needs it to be defined */
409 int backtrace (void **__array, int __size) {
419 XBT_TEST_SUITE("xbt_ex","Exception Handling");
421 XBT_TEST_UNIT("controlflow",test_controlflow, "basic nested control flow") {
425 xbt_test_add0("basic nested control flow");
429 xbt_test_fail1("M1: n=%d (!= 1)", n);
433 xbt_test_fail1("M2: n=%d (!= 2)", n);
435 THROW0(unknown_error,0,"something");
438 xbt_test_fail1("M3: n=%d (!= 3)", n);
445 xbt_test_fail1("M2: n=%d (!= 5)", n);
447 THROW0(unknown_error,0,"something");
450 xbt_test_fail1("M3: n=%d (!= 6)", n);
455 xbt_test_fail1("MX: n=%d (shouldn't reach this point)", n);
459 xbt_test_fail1("M4: n=%d (!= 7)", n);
464 xbt_test_fail1("M5: n=%d (!= 8)", n);
467 XBT_TEST_UNIT("value",test_value,"exception value passing") {
471 THROW0(unknown_error, 2, "toto");
473 xbt_test_add0("exception value passing");
474 if (ex.category != unknown_error)
475 xbt_test_fail1("category=%d (!= 1)", ex.category);
477 xbt_test_fail1("value=%d (!= 2)", ex.value);
478 if (strcmp(ex.msg,"toto"))
479 xbt_test_fail1("message=%s (!= toto)", ex.msg);
484 XBT_TEST_UNIT("variables",test_variables,"variable value preservation") {
489 r1 = r2 = v1 = v2 = 1234;
493 THROW0(unknown_error, 0, "toto");
495 xbt_test_add0("variable preservation");
497 xbt_test_fail1("r1=%d (!= 1234)", r1);
499 xbt_test_fail1("v1=%d (!= 1234)", v1);
500 /* r2 is allowed to be destroyed because not volatile */
502 xbt_test_fail1("v2=%d (!= 5678)", v2);
507 XBT_TEST_UNIT("cleanup",test_cleanup,"cleanup handling") {
512 xbt_test_add0("cleanup handling");
518 THROW0(1, 2, "blah");
521 xbt_test_fail1("v1 = %d (!= 5678)", v1);
525 xbt_test_fail1("v1 = %d (!= 5678)", v1);
526 if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg,"blah")))
527 xbt_test_fail0("unexpected exception contents");
531 xbt_test_fail0("xbt_ex_free not executed");
536 * The following is the example included in the documentation. It's a good
537 * idea to check its syntax even if we don't try to run it.
538 * And actually, it allows to put comments in the code despite doxygen.
540 static char *mallocex(int size) {
543 #define SMALLAMOUNT 10
544 #define TOOBIG 100000000
546 #if 0 /* this contains syntax errors, actually */
547 static void bad_example(void) {
548 struct {char*first;} *globalcontext;
553 char *cp1, *cp2, *cp3;
555 cp1 = mallocex(SMALLAMOUNT);
556 globalcontext->first = cp1;
557 cp2 = mallocex(TOOBIG);
558 cp3 = mallocex(SMALLAMOUNT);
562 if (cp3 != NULL) free(cp3);
563 if (cp2 != NULL) free(cp2);
564 if (cp1 != NULL) free(cp1);
566 printf("cp3=%s", cp3);
569 /* end_of_bad_example */
572 typedef struct {char *first;} global_context_t;
574 static void good_example(void) {
575 global_context_t *global_context=malloc(sizeof(global_context_t));
580 char * volatile /*03*/ cp1 = NULL /*02*/;
581 char * volatile /*03*/ cp2 = NULL /*02*/;
582 char * volatile /*03*/ cp3 = NULL /*02*/;
584 cp1 = mallocex(SMALLAMOUNT);
585 global_context->first = cp1;
586 cp1 = NULL /*05 give away*/;
587 cp2 = mallocex(TOOBIG);
588 cp3 = mallocex(SMALLAMOUNT);
592 printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
597 /*05 cp1 was given away */
599 /*05 global context untouched */
603 /* end_of_good_example */
605 #endif /* SIMGRID_TEST */