Logo AND Algorithmique Numérique Distribuée

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