Logo AND Algorithmique Numérique Distribuée

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