Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
428080b406054cda53fc32040a4f7f1cf057993a
[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/str.h"
20 #include "xbt/module.h" /* xbt_binary_name */
21 #include "xbt/synchro.h" /* xbt_thread_self */
22
23 #include "gras/Virtu/virtu_interface.h" /* gras_os_myname */
24 #include "xbt/ex_interface.h"
25
26 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex,xbt,"Exception mecanism");
27
28 /* default __ex_ctx callback function */
29 ex_ctx_t *__xbt_ex_ctx_default(void) {
30   /* Don't scream: this is a default which is never used (so, yes, 
31      there is one setjump container by running entity).
32
33      This default gets overriden in xbt/xbt_os_thread.c so that it works in
34      real life and in simulation when using threads to implement the simulation
35      processes (ie, with pthreads and on windows).
36
37      It also gets overriden in xbt/context.c when using ucontextes (as well as
38      in Java for now, but after the java overhaul, it will get cleaned out)
39   */
40     static ex_ctx_t ctx = XBT_CTX_INITIALIZER;
41
42     return &ctx;
43 }
44
45 /* Change raw libc symbols to file names and line numbers */
46 void xbt_ex_setup_backtrace(xbt_ex_t *e);
47
48 void xbt_backtrace_current(xbt_ex_t *e) {
49 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
50   e->used     = backtrace((void**)e->bt,XBT_BACKTRACE_SIZE);
51   e->bt_strings = NULL;
52   xbt_ex_setup_backtrace(e);
53 #endif
54 }
55
56 void xbt_backtrace_display(xbt_ex_t *e) {
57 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
58   int i;
59
60   if (e->used == 0) {
61      fprintf(stderr,"(backtrace not set)\n");
62   } else {      
63      fprintf(stderr,"Backtrace (displayed in thread %p):\n",
64              (void*)xbt_thread_self());
65      for (i=1; i<e->used; i++) /* no need to display "xbt_display_backtrace" */
66        fprintf(stderr,"---> %s\n",e->bt_strings[i] +4);
67   }
68    
69   /* don't fool xbt_ex_free with uninitialized msg field */
70   e->msg=NULL;
71   e->remote=0;
72   xbt_ex_free(*e);
73 #else 
74   ERROR0("No backtrace on this arch");
75 #endif
76 }
77
78 /** \brief show the backtrace of the current point (lovely while debuging) */
79 void xbt_backtrace_display_current(void) {
80   xbt_ex_t e;
81   xbt_backtrace_current(&e);
82   xbt_backtrace_display(&e);
83 }
84
85 #ifndef WIN32
86 extern char **environ; /* the environment, as specified by the opengroup */
87 #endif
88
89 void xbt_ex_setup_backtrace(xbt_ex_t *e)  {
90 #if defined(HAVE_EXECINFO_H) && defined(HAVE_POPEN) && defined(ADDR2LINE)
91   int i;
92   /* to get the backtrace from the libc */
93   char **backtrace = backtrace_symbols (e->bt, e->used);
94   
95   /* To build the commandline of addr2line */
96   char *cmd, *curr;
97   
98   /* to extract the addresses from the backtrace */
99   char **addrs=xbt_new(char*,e->used);
100   char buff[256],*p;
101   
102   /* To read the output of addr2line */
103   FILE *pipe;
104   char line_func[1024],line_pos[1024];
105
106   /* size (in char) of pointers on this arch */
107   int addr_len=0;
108
109   /* To search for the right executable path when not trivial */
110   struct stat stat_buf;
111   char *binary_name = NULL;
112    
113   /* Some arches only have stubs of backtrace, no implementation (hppa comes to mind) */
114   if (!e->used)
115      return;
116    
117   /* build the commandline */
118   if (stat(xbt_binary_name,&stat_buf)) {
119     /* Damn. binary not in current dir. We'll have to dig the PATH to find it */
120     int i;
121     for (i=0; environ[i]; i++) {
122       if (!strncmp("PATH=",environ[i], 5)) {    
123         xbt_dynar_t path=xbt_str_split(environ[i] + 5, ":");
124         unsigned int cpt;
125         char *data;
126         xbt_dynar_foreach(path, cpt, data) {
127           if (binary_name)
128             free(binary_name);
129           binary_name = bprintf("%s/%s",data,xbt_binary_name);
130           if (!stat(binary_name,&stat_buf)) {
131             /* Found. */
132             DEBUG1("Looked in the PATH for the binary. Found %s",binary_name);
133             xbt_dynar_free(&path);
134             break;
135           } 
136         }
137         if (stat(binary_name,&stat_buf)) {
138           /* not found */
139           e->used = 1;
140           e->bt_strings = xbt_new(char*,1);
141           e->bt_strings[0] = bprintf("(binary '%s' not found the path)",xbt_binary_name);
142           return;
143         }
144         xbt_dynar_free(&path);
145         break;
146       } 
147     }
148   } else {
149     binary_name = xbt_strdup(xbt_binary_name);
150   }      
151   cmd = curr = xbt_new(char,strlen(ADDR2LINE)+25+strlen(binary_name)+32*e->used);
152    
153   curr += sprintf(curr,"%s -f -e %s ",ADDR2LINE,binary_name);
154   free(binary_name);
155    
156   for (i=0; i<e->used;i++) {
157     /* retrieve this address */
158     DEBUG2("Retrieving address number %d from '%s'", i, backtrace[i]);
159     snprintf(buff,256,"%s",strchr(backtrace[i],'[')+1);
160     p=strchr(buff,']');
161     *p='\0';
162     if (strcmp(buff,"(nil)"))
163        addrs[i]=bprintf("%s", buff);
164     else
165        addrs[i]=bprintf("0x0");
166     DEBUG3("Set up a new address: %d, '%s'(%p)", i, addrs[i], addrs[i]);
167      
168     /* Add it to the command line args */
169     curr+=sprintf(curr,"%s ",addrs[i]);
170   } 
171   addr_len = strlen(addrs[0]);
172
173   /* parse the output and build a new backtrace */
174   e->bt_strings = xbt_new(char*,e->used);
175   
176   VERB1("Fire a first command: '%s'", cmd);
177   pipe = popen(cmd, "r");
178   if (!pipe) {
179     CRITICAL0("Cannot fork addr2line to display the backtrace");
180     abort();
181   }
182
183   for (i=0; i<e->used; i++) {
184     DEBUG2("Looking for symbol %d, addr = '%s'", i, addrs[i]); 
185     fgets(line_func,1024,pipe);
186     line_func[strlen(line_func)-1]='\0';
187     fgets(line_pos,1024,pipe);
188     line_pos[strlen(line_pos)-1]='\0';
189
190     if (strcmp("??",line_func)) {
191       DEBUG2("Found static symbol %s() at %s", line_func, line_pos);
192       e->bt_strings[i] = bprintf("**   In %s() at %s", line_func,line_pos);
193     } else {
194       /* Damn. The symbol is in a dynamic library. Let's get wild */
195       char *maps_name;
196       FILE *maps;
197       char maps_buff[512];
198
199       long int addr,offset=0;
200       char *p,*p2;
201
202       char *subcmd;
203       FILE *subpipe;
204       int found=0;
205
206       /* let's look for the offset of this library in our addressing space */
207       maps_name=bprintf("/proc/%d/maps",(int)getpid());
208       maps=fopen(maps_name,"r");
209
210       sscanf(addrs[i],"%lx",&addr);
211       sprintf(maps_buff,"%#lx",addr);
212       
213       if (strcmp(addrs[i],maps_buff)) {
214         CRITICAL2("Cannot parse backtrace address '%s' (addr=%#lx)",
215                   addrs[i], addr);
216       }
217       DEBUG2("addr=%s (as string) =%#lx (as number)",addrs[i],addr);
218
219       while (!found) {
220         long int first, last;
221         if (fgets(maps_buff,512,maps) == NULL) 
222           break;
223         if (i==0) {
224           maps_buff[strlen(maps_buff) -1]='\0';
225           DEBUG1("map line: %s", maps_buff);
226         }
227         sscanf(maps_buff,"%lx",&first);
228         p=strchr(maps_buff,'-')+1;
229         sscanf(p,"%lx",&last);
230         if (first < addr && addr < last) {
231           offset = first;
232           found=1;
233         }
234         if (found) {          
235            DEBUG3("%#lx in [%#lx-%#lx]", addr, first,last);
236            DEBUG0("Symbol found, map lines not further displayed (even if looking for next ones)");
237         }
238       }
239       fclose(maps);
240       free(maps_name);
241
242       if (!found) {
243         VERB0("Problem while reading the maps file. Following backtrace will be mangled.");
244         DEBUG1("No dynamic. Static symbol: %s", backtrace[i]);
245         e->bt_strings[i] = bprintf("**   In ?? (%s)", backtrace[i]);
246         continue;
247       }
248
249       /* Ok, Found the offset of the maps line containing the searched symbol. 
250          We now need to substract this from the address we got from backtrace.
251       */
252       
253       free(addrs[i]);
254       addrs[i] = bprintf("0x%0*lx",addr_len-2,addr-offset);
255       DEBUG2("offset=%#lx new addr=%s",offset,addrs[i]);
256
257       /* Got it. We have our new address. Let's get the library path and we 
258          are set */ 
259       p  = xbt_strdup(backtrace[i]);
260       if (p[0]=='[') {
261          /* library path not displayed in the map file either... */
262          free(p);
263          sprintf(line_func,"??");
264       } else {
265          p2 = strrchr(p,'(');
266          if (p2) *p2= '\0';
267          p2 = strrchr(p,' ');
268          if(p2) *p2= '\0';
269       
270          /* Here we go, fire an addr2line up */
271          subcmd = bprintf("%s -f -e %s %s",ADDR2LINE,p, addrs[i]);
272          free(p);
273          VERB1("Fire a new command: '%s'",subcmd);
274          subpipe = popen(subcmd,"r");
275          if (!subpipe) {
276             CRITICAL0("Cannot fork addr2line to display the backtrace");
277             abort();
278          }
279          fgets(line_func,1024,subpipe);
280          line_func[strlen(line_func)-1]='\0';
281          fgets(line_pos,1024,subpipe);
282          line_pos[strlen(line_pos)-1]='\0';
283          pclose(subpipe);
284          free(subcmd);
285       }
286
287       /* check whether the trick worked */
288       if (strcmp("??",line_func)) {
289         DEBUG2("Found dynamic symbol %s() at %s", line_func, line_pos);
290         e->bt_strings[i] = bprintf("**   In %s() at %s", line_func,line_pos);
291       } else {
292         /* damn, nothing to do here. Let's print the raw address */
293         DEBUG1("Dynamic symbol not found. Raw address = %s", backtrace[i]);
294         e->bt_strings[i] = bprintf("**   In ?? at %s", backtrace[i]);
295       }
296       
297     }
298     free(addrs[i]);
299      
300     /* Mask the bottom of the stack */    
301     if (!strncmp("main",line_func,strlen("main")) ||
302         !strncmp("xbt_thread_context_wrapper",line_func,strlen("xbt_thread_context_wrapper"))) {
303        int j;
304        for (j=i+1; j<e->used; j++)
305          free(addrs[j]);
306        e->used = i;
307
308        if (!strncmp("xbt_thread_context_wrapper",line_func,strlen("xbt_thread_context_wrapper"))) {
309           e->used++;
310           e->bt_strings[i] = bprintf("**   (in a separate thread)");
311        }       
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_EXPORT_NO_IMPORT(ex_ctx_cb_t)  __xbt_ex_ctx       = &__xbt_ex_ctx_default;
372 XBT_EXPORT_NO_IMPORT(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 */