Logo AND Algorithmique Numérique Distribuée

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