Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
c2e2e599334f71c261ae2fcf95390e0890a69c7d
[simgrid.git] / src / xbt / ex.c
1 /* $Id$ */
2
3 /* ex - Exception Handling (modified to fit into SimGrid from OSSP version) */
4
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.                                                    */
10
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. */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16
17 #include "portable.h" /* execinfo when available */
18 #include "xbt/ex.h"
19 #include "xbt/module.h" /* xbt_binary_name */
20
21 #include "gras/Virtu/virtu_interface.h" /* gras_os_myname */
22 #include "xbt/ex_interface.h"
23
24 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex,xbt,"Exception mecanism");
25
26 /* default __ex_ctx callback function */
27 ex_ctx_t *__xbt_ex_ctx_default(void) {
28     static ex_ctx_t ctx = XBT_CTX_INITIALIZER;
29
30     return &ctx;
31 }
32
33 /* Change raw libc symbols to file names and line numbers */
34 void xbt_ex_setup_backtrace(xbt_ex_t *e);
35
36 void xbt_backtrace_current(xbt_ex_t *e) {
37 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
38   e->used     = backtrace((void**)e->bt,XBT_BACKTRACE_SIZE);
39   e->bt_strings = NULL;
40   xbt_ex_setup_backtrace(e);
41 #endif
42 }
43
44 void xbt_backtrace_display(xbt_ex_t *e) {
45 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
46   int i;
47
48   if (e->used == 0) {
49      fprintf(stderr,"(backtrace not set)\n");
50   } else {      
51      fprintf(stderr,"Backtrace:\n");
52      for (i=1; i<e->used; i++) /* no need to display "xbt_display_backtrace" */
53        fprintf(stderr,"---> %s\n",e->bt_strings[i] +4);
54   }
55    
56   /* don't fool xbt_ex_free with uninitialized msg field */
57   e->msg=NULL;
58   e->remote=0;
59   xbt_ex_free(*e);
60 #else 
61   ERROR0("No backtrace on this arch");
62 #endif
63 }
64
65 /** \brief show the backtrace of the current point (lovely while debuging) */
66 void xbt_backtrace_display_current(void) {
67   xbt_ex_t e;
68   xbt_backtrace_current(&e);
69   xbt_backtrace_display(&e);
70 }
71
72 extern char **environ; /* the environment, as specified by the opengroup */
73
74 void xbt_ex_setup_backtrace(xbt_ex_t *e)  {
75 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
76   int i;
77   /* to get the backtrace from the libc */
78   char **backtrace = backtrace_symbols (e->bt, e->used);
79   
80   /* To build the commandline of addr2line */
81   char *cmd, *curr;
82   
83   /* to extract the addresses from the backtrace */
84   char **addrs=xbt_new(char*,e->used);
85   char buff[256],*p;
86   
87   /* To read the output of addr2line */
88   FILE *pipe;
89   char line_func[1024],line_pos[1024];
90
91   /* size (in char) of pointers on this arch */
92   int addr_len=0;
93
94   /* To search for the right executable path when not trivial */
95   struct stat stat_buf;
96   char *binary_name = NULL;
97    
98   /* Some arches only have stubs of backtrace, no implementation (hppa comes to mind) */
99   if (!e->used)
100      return;
101    
102   /* build the commandline */
103   if (stat(xbt_binary_name,&stat_buf)) {
104     /* Damn. binary not in current dir. We'll have to dig the PATH to find it */
105     int i;
106     for (i=0; environ[i]; i++) {
107       if (!strncmp("PATH=",environ[i], 5)) {    
108         xbt_dynar_t path=xbt_str_split(environ[i] + 5, ":");
109         int cpt;
110         char *data;
111         xbt_dynar_foreach(path, cpt, data) {
112           if (binary_name)
113             free(binary_name);
114           binary_name = bprintf("%s/%s",data,xbt_binary_name);
115           if (!stat(binary_name,&stat_buf)) {
116             /* Found. */
117             DEBUG1("Looked in the PATH for the binary. Found %s",binary_name);
118             xbt_dynar_free(&path);
119             break;
120           } 
121         }
122         if (stat(binary_name,&stat_buf)) {
123           /* not found */
124           e->used = 1;
125           e->bt_strings = xbt_new(char*,1);
126           e->bt_strings[0] = bprintf("(binary '%s' not found the path)",xbt_binary_name);
127           return;
128         }
129         xbt_dynar_free(&path);
130         break;
131       } 
132     }
133   } else {
134     binary_name = xbt_strdup(xbt_binary_name);
135   }      
136   cmd = curr = xbt_new(char,strlen(ADDR2LINE)+25+strlen(binary_name)+32*e->used);
137    
138   curr += sprintf(curr,"%s -f -e %s ",ADDR2LINE,binary_name);
139   free(binary_name);
140    
141   for (i=0; i<e->used;i++) {
142     /* retrieve this address */
143     DEBUG2("Retrieving address number %d from '%s'", i, backtrace[i]);
144     snprintf(buff,256,"%s",strchr(backtrace[i],'[')+1);
145     p=strchr(buff,']');
146     *p='\0';
147     if (strcmp(buff,"(nil)"))
148        addrs[i]=bprintf("%s", buff);
149     else
150        addrs[i]=bprintf("0x0");
151     DEBUG3("Set up a new address: %d, '%s'(%p)", i, addrs[i], addrs[i]);
152      
153     /* Add it to the command line args */
154     curr+=sprintf(curr,"%s ",addrs[i]);
155   } 
156   addr_len = strlen(addrs[0]);
157
158   /* parse the output and build a new backtrace */
159   e->bt_strings = xbt_new(char*,e->used);
160   
161   VERB1("Fire a first command: '%s'", cmd);
162   pipe = popen(cmd, "r");
163   if (!pipe) {
164     CRITICAL0("Cannot fork addr2line to display the backtrace");
165     abort();
166   }
167
168   for (i=0; i<e->used; i++) {
169     DEBUG2("Looking for symbol %d, addr = '%s'", i, addrs[i]); 
170     fgets(line_func,1024,pipe);
171     line_func[strlen(line_func)-1]='\0';
172     fgets(line_pos,1024,pipe);
173     line_pos[strlen(line_pos)-1]='\0';
174
175     if (strcmp("??",line_func)) {
176       DEBUG2("Found static symbol %s() at %s", line_func, line_pos);
177       e->bt_strings[i] = bprintf("**   In %s() at %s", line_func,line_pos);
178     } else {
179       /* Damn. The symbol is in a dynamic library. Let's get wild */
180       char *maps_name;
181       FILE *maps;
182       char maps_buff[512];
183
184       long int addr,offset=0;
185       char *p,*p2;
186
187       char *subcmd;
188       FILE *subpipe;
189       int found=0;
190
191       /* let's look for the offset of this library in our addressing space */
192       maps_name=bprintf("/proc/%d/maps",(int)getpid());
193       maps=fopen(maps_name,"r");
194
195       sscanf(addrs[i],"%lx",&addr);
196       sprintf(maps_buff,"%#lx",addr);
197       
198       if (strcmp(addrs[i],maps_buff)) {
199         CRITICAL2("Cannot parse backtrace address '%s' (addr=%#lx)",
200                   addrs[i], addr);
201       }
202       DEBUG2("addr=%s (as string) =%#lx (as number)",addrs[i],addr);
203
204       while (!found) {
205         long int first, last;
206         if (fgets(maps_buff,512,maps) == NULL) 
207           break;
208         if (i==0) {
209           maps_buff[strlen(maps_buff) -1]='\0';
210           DEBUG1("map line: %s", maps_buff);
211         }
212         sscanf(maps_buff,"%lx",&first);
213         p=strchr(maps_buff,'-')+1;
214         sscanf(p,"%lx",&last);
215         if (first < addr && addr < last) {
216           offset = first;
217           found=1;
218         }
219         if (found) {          
220            DEBUG3("%#lx in [%#lx-%#lx]", addr, first,last);
221            DEBUG0("Symbol found, map lines not further displayed (even if looking for next ones)");
222         }
223       }
224       fclose(maps);
225       free(maps_name);
226
227       if (!found) {
228         VERB0("Problem while reading the maps file. Following backtrace will be mangled.");
229         DEBUG1("No dynamic. Static symbol: %s", backtrace[i]);
230         e->bt_strings[i] = bprintf("**   In ?? (%s)", backtrace[i]);
231         continue;
232       }
233
234       /* Ok, Found the offset of the maps line containing the searched symbol. 
235          We now need to substract this from the address we got from backtrace.
236       */
237       
238       free(addrs[i]);
239       addrs[i] = bprintf("0x%0*lx",addr_len-2,addr-offset);
240       DEBUG2("offset=%#lx new addr=%s",offset,addrs[i]);
241
242       /* Got it. We have our new address. Let's get the library path and we 
243          are set */ 
244       p  = xbt_strdup(backtrace[i]);
245       if (p[0]=='[') {
246          /* library path not displayed in the map file either... */
247          free(p);
248          sprintf(line_func,"??");
249       } else {
250          p2 = strrchr(p,'(');
251          if (p2) *p2= '\0';
252          p2 = strrchr(p,' ');
253          if(p2) *p2= '\0';
254       
255          /* Here we go, fire an addr2line up */
256          subcmd = bprintf("%s -f -e %s %s",ADDR2LINE,p, addrs[i]);
257          free(p);
258          VERB1("Fire a new command: '%s'",subcmd);
259          subpipe = popen(subcmd,"r");
260          if (!subpipe) {
261             CRITICAL0("Cannot fork addr2line to display the backtrace");
262             abort();
263          }
264          fgets(line_func,1024,subpipe);
265          line_func[strlen(line_func)-1]='\0';
266          fgets(line_pos,1024,subpipe);
267          line_pos[strlen(line_pos)-1]='\0';
268          pclose(subpipe);
269          free(subcmd);
270       }
271
272       /* check whether the trick worked */
273       if (strcmp("??",line_func)) {
274         DEBUG2("Found dynamic symbol %s() at %s", line_func, line_pos);
275         e->bt_strings[i] = bprintf("**   In %s() at %s", line_func,line_pos);
276       } else {
277         /* damn, nothing to do here. Let's print the raw address */
278         DEBUG1("Dynamic symbol not found. Raw address = %s", backtrace[i]);
279         e->bt_strings[i] = bprintf("**   In ?? at %s", backtrace[i]);
280       }
281       
282     }
283     free(addrs[i]);
284      
285     /* Mask the bottom of the stack */    
286     if (!strncmp("main",line_func,strlen("main"))) {
287        int j;
288        for (j=i+1; j<e->used; j++)
289          free(addrs[j]);
290        e->used = i+1;
291     }
292      
293     if (!strncmp("__context_wrapper",line_func,strlen("__context_wrapper"))) {
294        int j;
295        for (j=i+1; j<e->used; j++)
296          free(addrs[j]);
297        e->used = i;
298     }
299      
300     
301   }
302   pclose(pipe);
303   free(addrs);
304   free(backtrace);
305   free(cmd);
306 #endif
307 }    
308
309 /** @brief shows an exception content and the associated stack if available */
310 void xbt_ex_display(xbt_ex_t *e)  {
311   char *thrower=NULL;
312
313   if (e->remote)
314     bprintf(" on host %s(%d)",e->host,e->pid);
315
316   fprintf(stderr,
317           "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n"
318           "** %s\n"
319           "** Thrown by %s()%s\n",
320           gras_os_myname(),(*xbt_getpid)(),
321           xbt_ex_catname(e->category), e->value, e->msg,
322           e->procname,thrower?thrower:" in this process");
323   CRITICAL1("%s",e->msg);
324
325   if (thrower)
326     free(thrower);
327
328   if (!e->remote && !e->bt_strings)
329     xbt_ex_setup_backtrace(e);
330
331 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
332   /* We have everything to build neat backtraces */
333   {
334     int i;
335     
336     fprintf(stderr,"\n");
337     for (i=0; i<e->used; i++)
338       fprintf(stderr,"%s\n",e->bt_strings[i]);
339     
340   }
341 #else
342   fprintf(stderr," at %s:%d:%s (no backtrace available on that arch)\n",  
343           e->file,e->line,e->func);
344 #endif
345   xbt_ex_free(*e);
346 }
347
348
349 /* default __ex_terminate callback function */
350 void __xbt_ex_terminate_default(xbt_ex_t *e)  {
351   xbt_ex_display(e);
352
353   abort();
354 }
355
356 /* the externally visible API */
357 XBT_PUBLIC_DATA(ex_ctx_cb_t)  __xbt_ex_ctx       = &__xbt_ex_ctx_default;
358 XBT_PUBLIC_DATA(ex_term_cb_t) __xbt_ex_terminate = &__xbt_ex_terminate_default;
359
360
361 void xbt_ex_free(xbt_ex_t e) {
362   int i;
363
364   if (e.msg) free(e.msg);
365   if (e.remote) {
366     free(e.procname);
367     free(e.file);
368     free(e.func);
369     free(e.host);
370   }
371
372   if (e.bt_strings) {   
373      for (i=0; i<e.used; i++) 
374        free((char*)e.bt_strings[i]);
375      free((char **)e.bt_strings);
376   }
377   /* memset(e,0,sizeof(xbt_ex_t)); */
378 }
379
380 /** \brief returns a short name for the given exception category */
381 const char * xbt_ex_catname(xbt_errcat_t cat) {
382   switch (cat) {
383   case unknown_error:   return  "unknown_err";
384   case arg_error:       return "invalid_arg";
385   case mismatch_error:  return "mismatch";
386   case not_found_error: return "not found";
387   case system_error:    return "system_err";
388   case network_error:   return "network_err";
389   case timeout_error:   return "timeout";
390   case thread_error:    return "thread_err";
391   default:              return "INVALID_ERR";
392   }
393 }
394
395 #ifndef HAVE_EXECINFO_H
396 /* dummy implementation. We won't use the result, but ex.h needs it to be defined */
397 int backtrace (void **__array, int __size) {
398   return 0;
399 }
400
401 #endif
402
403 #ifdef SIMGRID_TEST
404 #include <stdio.h>
405 #include "xbt/ex.h"
406
407 XBT_TEST_SUITE("xbt_ex","Exception Handling");
408
409 XBT_TEST_UNIT("controlflow",test_controlflow, "basic nested control flow") {
410     xbt_ex_t ex;
411     volatile int n=1;
412
413     xbt_test_add0("basic nested control flow");
414
415     TRY {
416         if (n != 1)
417             xbt_test_fail1("M1: n=%d (!= 1)", n);
418         n++;
419         TRY {
420             if (n != 2)
421                 xbt_test_fail1("M2: n=%d (!= 2)", n);
422             n++;
423             THROW0(unknown_error,0,"something");
424         } CATCH (ex) {
425             if (n != 3)
426                 xbt_test_fail1("M3: n=%d (!= 3)", n);
427             n++;
428             xbt_ex_free(ex);
429         }
430         n++;
431         TRY {
432             if (n != 5)
433                 xbt_test_fail1("M2: n=%d (!= 5)", n);
434             n++;
435             THROW0(unknown_error,0,"something");
436         } CATCH (ex) {
437             if (n != 6)
438                 xbt_test_fail1("M3: n=%d (!= 6)", n);
439             n++;
440             RETHROW;
441             n++;
442         }
443         xbt_test_fail1("MX: n=%d (shouldn't reach this point)", n);
444     }
445     CATCH(ex) {
446         if (n != 7)
447             xbt_test_fail1("M4: n=%d (!= 7)", n);
448         n++;
449         xbt_ex_free(ex);
450     }
451     if (n != 8)
452         xbt_test_fail1("M5: n=%d (!= 8)", n);
453 }
454
455 XBT_TEST_UNIT("value",test_value,"exception value passing") {
456     xbt_ex_t ex;
457
458     TRY {
459         THROW0(unknown_error, 2, "toto");
460     } CATCH(ex) {
461         xbt_test_add0("exception value passing");
462         if (ex.category != unknown_error)
463             xbt_test_fail1("category=%d (!= 1)", ex.category);
464         if (ex.value != 2)
465             xbt_test_fail1("value=%d (!= 2)", ex.value);
466         if (strcmp(ex.msg,"toto"))
467             xbt_test_fail1("message=%s (!= toto)", ex.msg);
468         xbt_ex_free(ex);
469     }
470 }
471
472 XBT_TEST_UNIT("variables",test_variables,"variable value preservation") {
473     xbt_ex_t ex;
474     int r1, r2;
475     volatile int v1, v2;
476
477     r1 = r2 = v1 = v2 = 1234;
478     TRY {
479         r2 = 5678;
480         v2 = 5678;
481         THROW0(unknown_error, 0, "toto");
482     } CATCH(ex) {
483         xbt_test_add0("variable preservation");
484         if (r1 != 1234)
485             xbt_test_fail1("r1=%d (!= 1234)", r1);
486         if (v1 != 1234)
487             xbt_test_fail1("v1=%d (!= 1234)", v1);
488         /* r2 is allowed to be destroyed because not volatile */
489         if (v2 != 5678)
490             xbt_test_fail1("v2=%d (!= 5678)", v2);
491         xbt_ex_free(ex);
492     }
493 }
494
495 XBT_TEST_UNIT("cleanup",test_cleanup,"cleanup handling") {
496     xbt_ex_t ex;
497     volatile int v1;
498     int c;
499
500     xbt_test_add0("cleanup handling");
501
502     v1 = 1234;
503     c = 0;
504     TRY {
505         v1 = 5678;
506         THROW0(1, 2, "blah");
507     } CLEANUP {
508         if (v1 != 5678)
509             xbt_test_fail1("v1 = %d (!= 5678)", v1);
510         c = 1;
511     } CATCH(ex) {
512         if (v1 != 5678)
513             xbt_test_fail1("v1 = %d (!= 5678)", v1);
514         if (!(ex.category == 1 && ex.value == 2 && !strcmp(ex.msg,"blah")))
515             xbt_test_fail0("unexpected exception contents");
516         xbt_ex_free(ex);
517     }
518     if (!c)
519         xbt_test_fail0("xbt_ex_free not executed");
520 }
521
522
523 /*
524  * The following is the example included in the documentation. It's a good 
525  * idea to check its syntax even if we don't try to run it.
526  * And actually, it allows to put comments in the code despite doxygen.
527  */ 
528 static char *mallocex(int size) {
529   return NULL;
530 }
531 #define SMALLAMOUNT 10
532 #define TOOBIG 100000000
533
534 #if 0 /* this contains syntax errors, actually */
535 static void bad_example(void) {
536   struct {char*first;} *globalcontext;
537   ex_t ex;
538
539   /* BAD_EXAMPLE */
540   TRY {
541     char *cp1, *cp2, *cp3;
542     
543     cp1 = mallocex(SMALLAMOUNT);
544     globalcontext->first = cp1;
545     cp2 = mallocex(TOOBIG);
546     cp3 = mallocex(SMALLAMOUNT);
547     strcpy(cp1, "foo");
548     strcpy(cp2, "bar");
549   } CLEANUP {
550     if (cp3 != NULL) free(cp3);
551     if (cp2 != NULL) free(cp2);
552     if (cp1 != NULL) free(cp1);
553   } CATCH(ex) {
554     printf("cp3=%s", cp3);
555     RETHROW;
556   }
557   /* end_of_bad_example */
558 }
559 #endif
560 typedef struct {char *first;} global_context_t;
561    
562 static void good_example(void) {
563   global_context_t *global_context=malloc(sizeof(global_context_t));
564   xbt_ex_t ex;
565
566   /* GOOD_EXAMPLE */
567   { /*01*/
568     char * volatile /*03*/ cp1 = NULL /*02*/;
569     char * volatile /*03*/ cp2 = NULL /*02*/;
570     char * volatile /*03*/ cp3 = NULL /*02*/;
571     TRY {
572       cp1 = mallocex(SMALLAMOUNT);
573       global_context->first = cp1;
574       cp1 = NULL /*05 give away*/;
575       cp2 = mallocex(TOOBIG);
576       cp3 = mallocex(SMALLAMOUNT);
577       strcpy(cp1, "foo");
578       strcpy(cp2, "bar");
579     } CLEANUP { /*04*/
580       printf("cp3=%s", cp3 == NULL /*02*/ ? "" : cp3);
581       if (cp3 != NULL)
582         free(cp3);
583       if (cp2 != NULL)
584         free(cp2);
585       /*05 cp1 was given away */
586     } CATCH(ex) {
587       /*05 global context untouched */
588       RETHROW;
589     }
590   }
591   /* end_of_good_example */
592 }
593 #endif /* SIMGRID_TEST */